summaryrefslogtreecommitdiffstats
path: root/svx/source/svdraw
diff options
context:
space:
mode:
Diffstat (limited to 'svx/source/svdraw')
-rw-r--r--svx/source/svdraw/ActionDescriptionProvider.cxx66
-rw-r--r--svx/source/svdraw/MediaShellHelpers.cxx108
-rw-r--r--svx/source/svdraw/charthelper.cxx135
-rw-r--r--svx/source/svdraw/clonelist.cxx127
-rw-r--r--svx/source/svdraw/gradtrns.cxx527
-rw-r--r--svx/source/svdraw/gradtrns.hxx55
-rw-r--r--svx/source/svdraw/polypolygoneditor.cxx182
-rw-r--r--svx/source/svdraw/presetooxhandleadjustmentrelations.cxx343
-rw-r--r--svx/source/svdraw/presetooxhandleadjustmentrelations.hxx34
-rw-r--r--svx/source/svdraw/sdrhittesthelper.cxx176
-rw-r--r--svx/source/svdraw/sdrmasterpagedescriptor.cxx104
-rw-r--r--svx/source/svdraw/sdrpagewindow.cxx531
-rw-r--r--svx/source/svdraw/sdrpaintwindow.cxx346
-rw-r--r--svx/source/svdraw/sdrundomanager.cxx184
-rw-r--r--svx/source/svdraw/selectioncontroller.cxx124
-rw-r--r--svx/source/svdraw/svdattr.cxx2042
-rw-r--r--svx/source/svdraw/svdcrtv.cxx916
-rw-r--r--svx/source/svdraw/svddrag.cxx129
-rw-r--r--svx/source/svdraw/svddrgm1.hxx232
-rw-r--r--svx/source/svdraw/svddrgmt.cxx3860
-rw-r--r--svx/source/svdraw/svddrgv.cxx919
-rw-r--r--svx/source/svdraw/svdedtv.cxx1103
-rw-r--r--svx/source/svdraw/svdedtv1.cxx2019
-rw-r--r--svx/source/svdraw/svdedtv2.cxx2250
-rw-r--r--svx/source/svdraw/svdedxv.cxx2871
-rw-r--r--svx/source/svdraw/svdetc.cxx726
-rw-r--r--svx/source/svdraw/svdfmtf.cxx1641
-rw-r--r--svx/source/svdraw/svdfmtf.hxx173
-rw-r--r--svx/source/svdraw/svdglev.cxx400
-rw-r--r--svx/source/svdraw/svdglue.cxx394
-rw-r--r--svx/source/svdraw/svdhdl.cxx2675
-rw-r--r--svx/source/svdraw/svdhlpln.cxx109
-rw-r--r--svx/source/svdraw/svditer.cxx140
-rw-r--r--svx/source/svdraw/svdlayer.cxx361
-rw-r--r--svx/source/svdraw/svdmark.cxx791
-rw-r--r--svx/source/svdraw/svdmodel.cxx1941
-rw-r--r--svx/source/svdraw/svdmrkv.cxx2733
-rw-r--r--svx/source/svdraw/svdmrkv1.cxx547
-rw-r--r--svx/source/svdraw/svdoashp.cxx3245
-rw-r--r--svx/source/svdraw/svdoattr.cxx103
-rw-r--r--svx/source/svdraw/svdobj.cxx3375
-rw-r--r--svx/source/svdraw/svdobjplusdata.cxx62
-rw-r--r--svx/source/svdraw/svdobjuserdatalist.cxx36
-rw-r--r--svx/source/svdraw/svdocapt.cxx756
-rw-r--r--svx/source/svdraw/svdocirc.cxx1154
-rw-r--r--svx/source/svdraw/svdoedge.cxx2647
-rw-r--r--svx/source/svdraw/svdograf.cxx1274
-rw-r--r--svx/source/svdraw/svdogrp.cxx835
-rw-r--r--svx/source/svdraw/svdomeas.cxx1429
-rw-r--r--svx/source/svdraw/svdomedia.cxx457
-rw-r--r--svx/source/svdraw/svdoole2.cxx1990
-rw-r--r--svx/source/svdraw/svdopage.cxx175
-rw-r--r--svx/source/svdraw/svdopath.cxx2994
-rw-r--r--svx/source/svdraw/svdorect.cxx567
-rw-r--r--svx/source/svdraw/svdotext.cxx2174
-rw-r--r--svx/source/svdraw/svdotextdecomposition.cxx1698
-rw-r--r--svx/source/svdraw/svdotextpathdecomposition.cxx748
-rw-r--r--svx/source/svdraw/svdotxat.cxx457
-rw-r--r--svx/source/svdraw/svdotxdr.cxx248
-rw-r--r--svx/source/svdraw/svdotxed.cxx360
-rw-r--r--svx/source/svdraw/svdotxfl.cxx28
-rw-r--r--svx/source/svdraw/svdotxln.cxx277
-rw-r--r--svx/source/svdraw/svdotxtr.cxx496
-rw-r--r--svx/source/svdraw/svdouno.cxx502
-rw-r--r--svx/source/svdraw/svdoutl.cxx107
-rw-r--r--svx/source/svdraw/svdoutlinercache.cxx95
-rw-r--r--svx/source/svdraw/svdovirt.cxx567
-rw-r--r--svx/source/svdraw/svdpage.cxx1895
-rw-r--r--svx/source/svdraw/svdpagv.cxx894
-rw-r--r--svx/source/svdraw/svdpdf.cxx1052
-rw-r--r--svx/source/svdraw/svdpntv.cxx1206
-rw-r--r--svx/source/svdraw/svdpoev.cxx652
-rw-r--r--svx/source/svdraw/svdsnpv.cxx633
-rw-r--r--svx/source/svdraw/svdtext.cxx154
-rw-r--r--svx/source/svdraw/svdtrans.cxx867
-rw-r--r--svx/source/svdraw/svdundo.cxx1834
-rw-r--r--svx/source/svdraw/svdview.cxx1544
-rw-r--r--svx/source/svdraw/svdviter.cxx164
-rw-r--r--svx/source/svdraw/svdxcgv.cxx784
-rw-r--r--svx/source/svdraw/textchain.cxx128
-rw-r--r--svx/source/svdraw/textchaincursor.cxx203
-rw-r--r--svx/source/svdraw/textchainflow.cxx314
82 files changed, 73194 insertions, 0 deletions
diff --git a/svx/source/svdraw/ActionDescriptionProvider.cxx b/svx/source/svdraw/ActionDescriptionProvider.cxx
new file mode 100644
index 000000000..0dc5e895c
--- /dev/null
+++ b/svx/source/svdraw/ActionDescriptionProvider.cxx
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <svx/ActionDescriptionProvider.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+
+OUString ActionDescriptionProvider::createDescription( ActionType eActionType
+ , std::u16string_view rObjectName )
+{
+ TranslateId pResID;
+ switch( eActionType )
+ {
+ case ActionType::Insert:
+ pResID=STR_UndoInsertObj;
+ break;
+ case ActionType::Delete:
+ pResID= STR_EditDelete;
+ break;
+ case ActionType::Move:
+ pResID= STR_EditMove;
+ break;
+ case ActionType::Resize:
+ pResID= STR_EditResize;
+ break;
+ case ActionType::Rotate:
+ pResID= STR_EditRotate;
+ break;
+ case ActionType::Format:
+ pResID= STR_EditSetAttributes;
+ break;
+ case ActionType::MoveToTop:
+ pResID= STR_EditMovToTop;
+ break;
+ case ActionType::MoveToBottom:
+ pResID= STR_EditMovToBtm;
+ break;
+ case ActionType::PosSize:
+ pResID = STR_EditPosSize;
+ break;
+ }
+ if (!pResID)
+ return OUString();
+
+ OUString aStr(SvxResId(pResID));
+ return aStr.replaceAll("%1", rObjectName);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/MediaShellHelpers.cxx b/svx/source/svdraw/MediaShellHelpers.cxx
new file mode 100644
index 000000000..8cf760a7b
--- /dev/null
+++ b/svx/source/svdraw/MediaShellHelpers.cxx
@@ -0,0 +1,108 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_features.h>
+#include <svx/MediaShellHelpers.hxx>
+#include <avmedia/mediaitem.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <svl/whiter.hxx>
+#include <svx/sdr/contact/viewcontactofsdrmediaobj.hxx>
+#include <svx/svdmrkv.hxx>
+
+namespace svx::MediaShellHelpers
+{
+void GetState(const SdrMarkView* pSdrView, SfxItemSet& rSet)
+{
+ if (!pSdrView)
+ return;
+
+ SfxWhichIter aIter(rSet);
+
+ for (sal_uInt16 nWhich = aIter.FirstWhich(); nWhich; nWhich = aIter.NextWhich())
+ {
+ if (SID_AVMEDIA_TOOLBOX != nWhich)
+ continue;
+
+#if HAVE_FEATURE_AVMEDIA
+ const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList();
+ bool bDisable = true;
+
+ if (1 == rMarkList.GetMarkCount())
+ {
+ SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
+
+ if (dynamic_cast<SdrMediaObj*>(pObj))
+ {
+ ::avmedia::MediaItem aItem(SID_AVMEDIA_TOOLBOX);
+
+ static_cast<sdr::contact::ViewContactOfSdrMediaObj&>(pObj->GetViewContact())
+ .updateMediaItem(aItem);
+ rSet.Put(aItem);
+ bDisable = false;
+ }
+ }
+
+ if (bDisable)
+#endif
+ rSet.DisableItem(SID_AVMEDIA_TOOLBOX);
+ }
+}
+
+const ::avmedia::MediaItem* Execute(const SdrMarkView* pSdrView, SfxRequest const& rReq)
+{
+#if !HAVE_FEATURE_AVMEDIA
+ (void)pSdrView;
+ (void)rReq;
+ return nullptr;
+#else
+ if (!pSdrView)
+ return nullptr;
+
+ if (SID_AVMEDIA_TOOLBOX != rReq.GetSlot())
+ return nullptr;
+
+ const SfxItemSet* pArgs = rReq.GetArgs();
+ if (!pArgs)
+ return nullptr;
+
+ const ::avmedia::MediaItem* pMediaItem = pArgs->GetItemIfSet(SID_AVMEDIA_TOOLBOX, false);
+ if (!pMediaItem)
+ return nullptr;
+
+ const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList();
+
+ if (1 != rMarkList.GetMarkCount())
+ return nullptr;
+
+ SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
+
+ if (!dynamic_cast<SdrMediaObj*>(pObj))
+ return nullptr;
+
+ static_cast<sdr::contact::ViewContactOfSdrMediaObj&>(pObj->GetViewContact())
+ .executeMediaItem(*pMediaItem);
+
+ return pMediaItem;
+#endif
+}
+
+} // end of namespace svx::MediaShellHelpers
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/charthelper.cxx b/svx/source/svdraw/charthelper.cxx
new file mode 100644
index 000000000..cae747228
--- /dev/null
+++ b/svx/source/svdraw/charthelper.cxx
@@ -0,0 +1,135 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/charthelper.hxx>
+#include <tools/diagnose_ex.h>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/util/XUpdatable2.hpp>
+#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <com/sun/star/chart2/XChartDocument.hpp>
+#include <com/sun/star/drawing/FillStyle.hpp>
+#include <com/sun/star/drawing/LineStyle.hpp>
+#include <sdr/primitive2d/primitivefactory2d.hxx>
+
+using namespace ::com::sun::star;
+
+void ChartHelper::updateChart( const uno::Reference< ::frame::XModel >& rXModel )
+{
+ if (!rXModel.is())
+ return;
+
+ try
+ {
+ const uno::Reference< lang::XMultiServiceFactory > xChartFact(rXModel, uno::UNO_QUERY_THROW);
+ const uno::Reference< lang::XUnoTunnel > xChartView(xChartFact->createInstance("com.sun.star.chart2.ChartView"), uno::UNO_QUERY_THROW);
+ const uno::Reference<util::XUpdatable2> xUpdatable(xChartView, uno::UNO_QUERY_THROW);
+
+ xUpdatable->updateHard();
+ }
+ catch(uno::Exception&)
+ {
+ OSL_ENSURE(false, "Unexpected exception!");
+ }
+}
+
+drawinglayer::primitive2d::Primitive2DContainer ChartHelper::tryToGetChartContentAsPrimitive2DSequence(
+ const uno::Reference< ::frame::XModel >& rXModel,
+ basegfx::B2DRange& rRange)
+{
+ drawinglayer::primitive2d::Primitive2DContainer aRetval;
+
+ if (!rXModel.is())
+ return aRetval;
+
+ // don't broadcast until we're finished building, more efficient
+ rXModel->lockControllers();
+ updateChart(rXModel);
+ rXModel->unlockControllers();
+
+ try
+ {
+ const uno::Reference< drawing::XDrawPageSupplier > xDrawPageSupplier(rXModel, uno::UNO_QUERY_THROW);
+ const uno::Reference< container::XIndexAccess > xShapeAccess(xDrawPageSupplier->getDrawPage(), uno::UNO_QUERY_THROW);
+
+ if(xShapeAccess->getCount())
+ {
+ const sal_Int32 nShapeCount(xShapeAccess->getCount());
+ const uno::Sequence< beans::PropertyValue > aParams;
+ uno::Reference< drawing::XShape > xShape;
+
+ for(sal_Int32 a(0); a < nShapeCount; a++)
+ {
+ xShapeAccess->getByIndex(a) >>= xShape;
+
+ if(xShape.is())
+ {
+ PrimitiveFactory2D::createPrimitivesFromXShape(
+ xShape,
+ aParams,
+ aRetval);
+ }
+ }
+ }
+ }
+ catch(uno::Exception&)
+ {
+ OSL_ENSURE(false, "Unexpected exception!");
+ }
+
+ if(!aRetval.empty())
+ {
+ const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
+
+ rRange = aRetval.getB2DRange(aViewInformation2D);
+ }
+
+ return aRetval;
+}
+
+void ChartHelper::AdaptDefaultsForChart(
+ const uno::Reference < embed::XEmbeddedObject > & xEmbObj)
+{
+ if( !xEmbObj.is())
+ return;
+
+ uno::Reference< chart2::XChartDocument > xChartDoc( xEmbObj->getComponent(), uno::UNO_QUERY );
+ OSL_ENSURE( xChartDoc.is(), "Trying to set chart property to non-chart OLE" );
+ if( !xChartDoc.is())
+ return;
+
+ try
+ {
+ if (uno::Reference< beans::XPropertySet > xPageProp = xChartDoc->getPageBackground())
+ {
+ // set background to transparent (none)
+ xPageProp->setPropertyValue("FillStyle", uno::Any(drawing::FillStyle_NONE));
+ // set no border
+ xPageProp->setPropertyValue("LineStyle", uno::Any(drawing::LineStyle_NONE));
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ TOOLS_WARN_EXCEPTION("svx.svdraw", "");
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/clonelist.cxx b/svx/source/svdraw/clonelist.cxx
new file mode 100644
index 000000000..12f395ac1
--- /dev/null
+++ b/svx/source/svdraw/clonelist.cxx
@@ -0,0 +1,127 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+// #i13033#
+// New mechanism to hold a list of all original and cloned objects for later
+// re-creating the connections for contained connectors
+#include <clonelist.hxx>
+#include <svx/svdoedge.hxx>
+#include <svx/scene3d.hxx>
+
+void CloneList::AddPair(const SdrObject* pOriginal, SdrObject* pClone)
+{
+ maOriginalList.push_back(pOriginal);
+ maCloneList.push_back(pClone);
+
+ // look for subobjects, too.
+ bool bOriginalIsGroup(pOriginal->IsGroupObject());
+ bool bCloneIsGroup(pClone->IsGroupObject());
+
+ if(bOriginalIsGroup && dynamic_cast<const E3dObject* >(pOriginal) != nullptr && dynamic_cast<const E3dScene* >(pOriginal) == nullptr )
+ bOriginalIsGroup = false;
+
+ if(bCloneIsGroup && dynamic_cast<const E3dObject* >(pClone) != nullptr && dynamic_cast<const E3dScene* >(pClone) == nullptr)
+ bCloneIsGroup = false;
+
+ if(!(bOriginalIsGroup && bCloneIsGroup))
+ return;
+
+ const SdrObjList* pOriginalList = pOriginal->GetSubList();
+ SdrObjList* pCloneList = pClone->GetSubList();
+
+ if(pOriginalList && pCloneList
+ && pOriginalList->GetObjCount() == pCloneList->GetObjCount())
+ {
+ for(size_t a = 0; a < pOriginalList->GetObjCount(); ++a)
+ {
+ // recursive call
+ AddPair(pOriginalList->GetObj(a), pCloneList->GetObj(a));
+ }
+ }
+}
+
+const SdrObject* CloneList::GetOriginal(sal_uInt32 nIndex) const
+{
+ return maOriginalList[nIndex];
+}
+
+SdrObject* CloneList::GetClone(sal_uInt32 nIndex) const
+{
+ return maCloneList[nIndex];
+}
+
+void CloneList::CopyConnections() const
+{
+ sal_uInt32 cloneCount = maCloneList.size();
+
+ for(size_t a = 0; a < maOriginalList.size(); a++)
+ {
+ const SdrEdgeObj* pOriginalEdge = dynamic_cast<const SdrEdgeObj*>( GetOriginal(a) );
+ SdrEdgeObj* pCloneEdge = dynamic_cast<SdrEdgeObj*>( GetClone(a) );
+
+ if(pOriginalEdge && pCloneEdge)
+ {
+ SdrObject* pOriginalNode1 = pOriginalEdge->GetConnectedNode(true);
+ SdrObject* pOriginalNode2 = pOriginalEdge->GetConnectedNode(false);
+
+ if(pOriginalNode1)
+ {
+ std::vector<const SdrObject*>::const_iterator it = std::find(maOriginalList.begin(),
+ maOriginalList.end(),
+ pOriginalNode1);
+
+ sal_uInt32 nPos = it - maOriginalList.begin();
+
+ if(it != maOriginalList.end())
+ {
+ SdrObject *cObj = nullptr;
+
+ if (nPos < cloneCount)
+ cObj = GetClone(nPos);
+
+ if(pOriginalEdge->GetConnectedNode(true) != cObj)
+ pCloneEdge->ConnectToNode(true, cObj);
+ }
+ }
+
+ if(pOriginalNode2)
+ {
+ std::vector<const SdrObject*>::const_iterator it = std::find(maOriginalList.begin(),
+ maOriginalList.end(),
+ pOriginalNode2);
+
+ sal_uInt32 nPos = it - maOriginalList.begin();
+
+ if(it != maOriginalList.end())
+ {
+ SdrObject *cObj = nullptr;
+
+ if (nPos < cloneCount)
+ cObj = GetClone(nPos);
+
+ if(pOriginalEdge->GetConnectedNode(false) != cObj)
+ pCloneEdge->ConnectToNode(false, cObj);
+ }
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/gradtrns.cxx b/svx/source/svdraw/gradtrns.cxx
new file mode 100644
index 000000000..d665cd48c
--- /dev/null
+++ b/svx/source/svdraw/gradtrns.cxx
@@ -0,0 +1,527 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "gradtrns.hxx"
+#include <svx/svdobj.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <tools/helpers.hxx>
+#include <vcl/canvastools.hxx>
+
+
+void GradTransformer::GradToVec(GradTransGradient const & rG, GradTransVector& rV, const SdrObject* pObj)
+{
+ // handle start color
+ rV.aCol1 = rG.aGradient.GetStartColor();
+ if(100 != rG.aGradient.GetStartIntens())
+ {
+ const double fFact(static_cast<double>(rG.aGradient.GetStartIntens()) / 100.0);
+ rV.aCol1 = Color(rV.aCol1.getBColor() * fFact);
+ }
+
+ // handle end color
+ rV.aCol2 = rG.aGradient.GetEndColor();
+ if(100 != rG.aGradient.GetEndIntens())
+ {
+ const double fFact(static_cast<double>(rG.aGradient.GetEndIntens()) / 100.0);
+ rV.aCol2 = Color(rV.aCol2.getBColor() * fFact);
+ }
+
+ // calc the basic positions
+ const tools::Rectangle aObjectSnapRectangle(pObj->GetSnapRect());
+ const basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(aObjectSnapRectangle);
+ const basegfx::B2DPoint aCenter(aRange.getCenter());
+ basegfx::B2DPoint aStartPos, aEndPos;
+
+ switch(rG.aGradient.GetGradientStyle())
+ {
+ case css::awt::GradientStyle_LINEAR :
+ {
+ aStartPos = basegfx::B2DPoint(aCenter.getX(), aRange.getMinY());
+ aEndPos = basegfx::B2DPoint(aCenter.getX(), aRange.getMaximum().getY());
+
+ if(rG.aGradient.GetBorder())
+ {
+ basegfx::B2DVector aFullVec(aStartPos - aEndPos);
+ const double fLen = (aFullVec.getLength() * (100.0 - static_cast<double>(rG.aGradient.GetBorder()))) / 100.0;
+ aFullVec.normalize();
+ aStartPos = aEndPos + (aFullVec * fLen);
+ }
+
+ if(rG.aGradient.GetAngle())
+ {
+ const double fAngle = toRadians(rG.aGradient.GetAngle());
+ const basegfx::B2DHomMatrix aTransformation(basegfx::utils::createRotateAroundPoint(aCenter, -fAngle));
+
+ aStartPos *= aTransformation;
+ aEndPos *= aTransformation;
+ }
+ break;
+ }
+ case css::awt::GradientStyle_AXIAL :
+ {
+ aStartPos = aCenter;
+ aEndPos = basegfx::B2DPoint(aCenter.getX(), aRange.getMaximum().getY());
+
+ if(rG.aGradient.GetBorder())
+ {
+ basegfx::B2DVector aFullVec(aEndPos - aStartPos);
+ const double fLen = (aFullVec.getLength() * (100.0 - static_cast<double>(rG.aGradient.GetBorder()))) / 100.0;
+ aFullVec.normalize();
+ aEndPos = aStartPos + (aFullVec * fLen);
+ }
+
+ if(rG.aGradient.GetAngle())
+ {
+ const double fAngle = toRadians(rG.aGradient.GetAngle());
+ const basegfx::B2DHomMatrix aTransformation(basegfx::utils::createRotateAroundPoint(aCenter, -fAngle));
+
+ aStartPos *= aTransformation;
+ aEndPos *= aTransformation;
+ }
+ break;
+ }
+ case css::awt::GradientStyle_RADIAL :
+ case css::awt::GradientStyle_SQUARE :
+ {
+ aStartPos = basegfx::B2DPoint(aRange.getMinX(), aRange.getMaximum().getY());
+ aEndPos = basegfx::B2DPoint(aRange.getMinX(), aRange.getMinY());
+
+ if(rG.aGradient.GetBorder())
+ {
+ basegfx::B2DVector aFullVec(aStartPos - aEndPos);
+ const double fLen = (aFullVec.getLength() * (100.0 - static_cast<double>(rG.aGradient.GetBorder()))) / 100.0;
+ aFullVec.normalize();
+ aStartPos = aEndPos + (aFullVec * fLen);
+ }
+
+ if(rG.aGradient.GetAngle())
+ {
+ const double fAngle = toRadians(rG.aGradient.GetAngle());
+ const basegfx::B2DHomMatrix aTransformation(basegfx::utils::createRotateAroundPoint(aEndPos, -fAngle));
+
+ aStartPos *= aTransformation;
+ aEndPos *= aTransformation;
+ }
+
+ if(rG.aGradient.GetXOffset() || rG.aGradient.GetYOffset())
+ {
+ basegfx::B2DPoint aOffset(
+ (aRange.getWidth() * rG.aGradient.GetXOffset()) / 100.0,
+ (aRange.getHeight() * rG.aGradient.GetYOffset()) / 100.0);
+
+ aStartPos += aOffset;
+ aEndPos += aOffset;
+ }
+
+ break;
+ }
+ case css::awt::GradientStyle_ELLIPTICAL :
+ case css::awt::GradientStyle_RECT :
+ {
+ aStartPos = basegfx::B2DPoint(aRange.getMinX(), aCenter.getY());
+ aEndPos = basegfx::B2DPoint(aRange.getMinX(), aRange.getMinY());
+
+ if(rG.aGradient.GetBorder())
+ {
+ basegfx::B2DVector aFullVec(aStartPos - aEndPos);
+ const double fLen = (aFullVec.getLength() * (100.0 - static_cast<double>(rG.aGradient.GetBorder()))) / 100.0;
+ aFullVec.normalize();
+ aStartPos = aEndPos + (aFullVec * fLen);
+ }
+
+ if(rG.aGradient.GetAngle())
+ {
+ const double fAngle = toRadians(rG.aGradient.GetAngle());
+ const basegfx::B2DHomMatrix aTransformation(basegfx::utils::createRotateAroundPoint(aEndPos, -fAngle));
+
+ aStartPos *= aTransformation;
+ aEndPos *= aTransformation;
+ }
+
+ if(rG.aGradient.GetXOffset() || rG.aGradient.GetYOffset())
+ {
+ basegfx::B2DPoint aOffset(
+ (aRange.getWidth() * rG.aGradient.GetXOffset()) / 100.0,
+ (aRange.getHeight() * rG.aGradient.GetYOffset()) / 100.0);
+
+ aStartPos += aOffset;
+ aEndPos += aOffset;
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+
+ // set values for vector positions now
+ rV.maPositionA = aStartPos;
+ rV.maPositionB = aEndPos;
+}
+
+
+void GradTransformer::VecToGrad(GradTransVector const & rV, GradTransGradient& rG, GradTransGradient const & rGOld, const SdrObject* pObj,
+ bool bMoveSingle, bool bMoveFirst)
+{
+ // fill old gradient to new gradient to have a base
+ rG = rGOld;
+
+ // handle color changes
+ if(rV.aCol1 != rGOld.aGradient.GetStartColor())
+ {
+ rG.aGradient.SetStartColor(rV.aCol1);
+ rG.aGradient.SetStartIntens(100);
+ }
+ if(rV.aCol2 != rGOld.aGradient.GetEndColor())
+ {
+ rG.aGradient.SetEndColor(rV.aCol2);
+ rG.aGradient.SetEndIntens(100);
+ }
+
+ // calc the basic positions
+ const tools::Rectangle aObjectSnapRectangle(pObj->GetSnapRect());
+ const basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(aObjectSnapRectangle);
+ const basegfx::B2DPoint aCenter(aRange.getCenter());
+ basegfx::B2DPoint aStartPos(rV.maPositionA);
+ basegfx::B2DPoint aEndPos(rV.maPositionB);
+
+ switch(rG.aGradient.GetGradientStyle())
+ {
+ case css::awt::GradientStyle_LINEAR :
+ {
+ if(!bMoveSingle || !bMoveFirst)
+ {
+ basegfx::B2DVector aFullVec(aEndPos - aStartPos);
+
+ if(bMoveSingle)
+ {
+ aFullVec = aEndPos - aCenter;
+ }
+
+ aFullVec.normalize();
+
+ double fNewFullAngle(basegfx::rad2deg(atan2(aFullVec.getY(), aFullVec.getX())));
+ fNewFullAngle *= -10.0;
+ fNewFullAngle += 900.0;
+
+ // clip
+ while(fNewFullAngle < 0.0)
+ {
+ fNewFullAngle += 3600.0;
+ }
+
+ while(fNewFullAngle >= 3600.0)
+ {
+ fNewFullAngle -= 3600.0;
+ }
+
+ // to int and set
+ Degree10 nNewAngle( FRound(fNewFullAngle));
+
+ if(nNewAngle != rGOld.aGradient.GetAngle())
+ {
+ rG.aGradient.SetAngle(nNewAngle);
+ }
+ }
+
+ if(!bMoveSingle || bMoveFirst)
+ {
+ const basegfx::B2DVector aFullVec(aEndPos - aStartPos);
+ const basegfx::B2DPoint aBottomLeft(aRange.getMinX(), aRange.getMaximum().getY());
+ const basegfx::B2DPoint aTopLeft(aRange.getMinX(), aRange.getMinY());
+ const basegfx::B2DVector aOldVec(aBottomLeft - aTopLeft);
+ const double fFullLen(aFullVec.getLength());
+ const double fOldLen(aOldVec.getLength());
+ const double fNewBorder((fFullLen * 100.0) / fOldLen);
+ sal_Int32 nNewBorder(100 - FRound(fNewBorder));
+
+ // clip
+ if(nNewBorder < 0)
+ {
+ nNewBorder = 0;
+ }
+
+ if(nNewBorder > 100)
+ {
+ nNewBorder = 100;
+ }
+
+ // set
+ if(nNewBorder != rG.aGradient.GetBorder())
+ {
+ rG.aGradient.SetBorder(static_cast<sal_uInt16>(nNewBorder));
+ }
+ }
+
+ break;
+ }
+ case css::awt::GradientStyle_AXIAL :
+ {
+ if(!bMoveSingle || !bMoveFirst)
+ {
+ basegfx::B2DVector aFullVec(aEndPos - aCenter);
+ const basegfx::B2DVector aOldVec(basegfx::B2DPoint(aCenter.getX(), aRange.getMaximum().getY()) - aCenter);
+ const double fFullLen(aFullVec.getLength());
+ const double fOldLen(aOldVec.getLength());
+ const double fNewBorder((fFullLen * 100.0) / fOldLen);
+ sal_Int32 nNewBorder = 100 - FRound(fNewBorder);
+
+ // clip
+ if(nNewBorder < 0)
+ {
+ nNewBorder = 0;
+ }
+
+ if(nNewBorder > 100)
+ {
+ nNewBorder = 100;
+ }
+
+ // set
+ if(nNewBorder != rG.aGradient.GetBorder())
+ {
+ rG.aGradient.SetBorder(static_cast<sal_uInt16>(nNewBorder));
+ }
+
+ aFullVec.normalize();
+ double fNewFullAngle(basegfx::rad2deg(atan2(aFullVec.getY(), aFullVec.getX())));
+ fNewFullAngle *= -10.0;
+ fNewFullAngle += 900.0;
+
+ // clip
+ while(fNewFullAngle < 0.0)
+ {
+ fNewFullAngle += 3600.0;
+ }
+
+ while(fNewFullAngle >= 3600.0)
+ {
+ fNewFullAngle -= 3600.0;
+ }
+
+ // to int and set
+ const Degree10 nNewAngle(FRound(fNewFullAngle));
+
+ if(nNewAngle != rGOld.aGradient.GetAngle())
+ {
+ rG.aGradient.SetAngle(nNewAngle);
+ }
+ }
+
+ break;
+ }
+ case css::awt::GradientStyle_RADIAL :
+ case css::awt::GradientStyle_SQUARE :
+ {
+ if(!bMoveSingle || !bMoveFirst)
+ {
+ const basegfx::B2DPoint aTopLeft(aRange.getMinX(), aRange.getMinY());
+ const basegfx::B2DPoint aOffset(aEndPos - aTopLeft);
+ sal_Int32 nNewXOffset(FRound((aOffset.getX() * 100.0) / aRange.getWidth()));
+ sal_Int32 nNewYOffset(FRound((aOffset.getY() * 100.0) / aRange.getHeight()));
+
+ // clip
+ if(nNewXOffset < 0)
+ {
+ nNewXOffset = 0;
+ }
+
+ if(nNewXOffset > 100)
+ {
+ nNewXOffset = 100;
+ }
+
+ if(nNewYOffset < 0)
+ {
+ nNewYOffset = 0;
+ }
+
+ if(nNewYOffset > 100)
+ {
+ nNewYOffset = 100;
+ }
+
+ rG.aGradient.SetXOffset(static_cast<sal_uInt16>(nNewXOffset));
+ rG.aGradient.SetYOffset(static_cast<sal_uInt16>(nNewYOffset));
+
+ aStartPos -= aOffset;
+ aEndPos -= aOffset;
+ }
+
+ if(!bMoveSingle || bMoveFirst)
+ {
+ basegfx::B2DVector aFullVec(aStartPos - aEndPos);
+ const basegfx::B2DPoint aBottomLeft(aRange.getMinX(), aRange.getMaximum().getY());
+ const basegfx::B2DPoint aTopLeft(aRange.getMinX(), aRange.getMinY());
+ const basegfx::B2DVector aOldVec(aBottomLeft - aTopLeft);
+ const double fFullLen(aFullVec.getLength());
+ const double fOldLen(aOldVec.getLength());
+ const double fNewBorder((fFullLen * 100.0) / fOldLen);
+ sal_Int32 nNewBorder(100 - FRound(fNewBorder));
+
+ // clip
+ if(nNewBorder < 0)
+ {
+ nNewBorder = 0;
+ }
+
+ if(nNewBorder > 100)
+ {
+ nNewBorder = 100;
+ }
+
+ // set
+ if(nNewBorder != rG.aGradient.GetBorder())
+ {
+ rG.aGradient.SetBorder(static_cast<sal_uInt16>(nNewBorder));
+ }
+
+ // angle is not definitely necessary for these modes, but it makes
+ // controlling more fun for the user
+ aFullVec.normalize();
+ double fNewFullAngle(basegfx::rad2deg(atan2(aFullVec.getY(), aFullVec.getX())));
+ fNewFullAngle *= -10.0;
+ fNewFullAngle += 900.0;
+
+ // clip
+ while(fNewFullAngle < 0.0)
+ {
+ fNewFullAngle += 3600.0;
+ }
+
+ while(fNewFullAngle >= 3600.0)
+ {
+ fNewFullAngle -= 3600.0;
+ }
+
+ // to int and set
+ const Degree10 nNewAngle(FRound(fNewFullAngle));
+
+ if(nNewAngle != rGOld.aGradient.GetAngle())
+ {
+ rG.aGradient.SetAngle(nNewAngle);
+ }
+ }
+
+ break;
+ }
+ case css::awt::GradientStyle_ELLIPTICAL :
+ case css::awt::GradientStyle_RECT :
+ {
+ if(!bMoveSingle || !bMoveFirst)
+ {
+ const basegfx::B2DPoint aTopLeft(aRange.getMinX(), aRange.getMinY());
+ const basegfx::B2DPoint aOffset(aEndPos - aTopLeft);
+ sal_Int32 nNewXOffset(FRound((aOffset.getX() * 100.0) / aRange.getWidth()));
+ sal_Int32 nNewYOffset(FRound((aOffset.getY() * 100.0) / aRange.getHeight()));
+
+ // clip
+ if(nNewXOffset < 0)
+ {
+ nNewXOffset = 0;
+ }
+
+ if(nNewXOffset > 100)
+ {
+ nNewXOffset = 100;
+ }
+
+ if(nNewYOffset < 0)
+ {
+ nNewYOffset = 0;
+ }
+
+ if(nNewYOffset > 100)
+ {
+ nNewYOffset = 100;
+ }
+
+ rG.aGradient.SetXOffset(static_cast<sal_uInt16>(nNewXOffset));
+ rG.aGradient.SetYOffset(static_cast<sal_uInt16>(nNewYOffset));
+
+ aStartPos -= aOffset;
+ aEndPos -= aOffset;
+ }
+
+ if(!bMoveSingle || bMoveFirst)
+ {
+ basegfx::B2DVector aFullVec(aStartPos - aEndPos);
+ const basegfx::B2DPoint aTopLeft(aRange.getMinX(), aRange.getMinY());
+ const basegfx::B2DPoint aCenterLeft(aRange.getMinX(), aCenter.getY());
+ const basegfx::B2DVector aOldVec(aCenterLeft - aTopLeft);
+ const double fFullLen(aFullVec.getLength());
+ const double fOldLen(aOldVec.getLength());
+ const double fNewBorder((fFullLen * 100.0) / fOldLen);
+ sal_Int32 nNewBorder(100 - FRound(fNewBorder));
+
+ // clip
+ if(nNewBorder < 0)
+ {
+ nNewBorder = 0;
+ }
+
+ if(nNewBorder > 100)
+ {
+ nNewBorder = 100;
+ }
+
+ // set
+ if(nNewBorder != rG.aGradient.GetBorder())
+ {
+ rG.aGradient.SetBorder(static_cast<sal_uInt16>(nNewBorder));
+ }
+
+ // angle is not definitely necessary for these modes, but it makes
+ // controlling more fun for the user
+ aFullVec.normalize();
+ double fNewFullAngle(basegfx::rad2deg(atan2(aFullVec.getY(), aFullVec.getX())));
+ fNewFullAngle *= -10.0;
+ fNewFullAngle += 900.0;
+
+ // clip
+ while(fNewFullAngle < 0.0)
+ {
+ fNewFullAngle += 3600.0;
+ }
+
+ while(fNewFullAngle >= 3600.0)
+ {
+ fNewFullAngle -= 3600.0;
+ }
+
+ // to int and set
+ const Degree10 nNewAngle(FRound(fNewFullAngle));
+
+ if(nNewAngle != rGOld.aGradient.GetAngle())
+ {
+ rG.aGradient.SetAngle(nNewAngle);
+ }
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/gradtrns.hxx b/svx/source/svdraw/gradtrns.hxx
new file mode 100644
index 000000000..eee35582a
--- /dev/null
+++ b/svx/source/svdraw/gradtrns.hxx
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SVX_SOURCE_SVDRAW_GRADTRNS_HXX
+#define INCLUDED_SVX_SOURCE_SVDRAW_GRADTRNS_HXX
+
+#include <svx/xgrad.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+
+class SdrObject;
+
+class GradTransVector
+{
+public:
+ basegfx::B2DPoint maPositionA;
+ basegfx::B2DPoint maPositionB;
+ Color aCol1;
+ Color aCol2;
+};
+
+class GradTransGradient
+{
+public:
+ XGradient aGradient;
+};
+
+struct GradTransformer
+{
+ GradTransformer() = delete;
+
+ static void GradToVec(GradTransGradient const & rG, GradTransVector& rV,
+ const SdrObject* pObj);
+ static void VecToGrad(GradTransVector const & rV, GradTransGradient& rG,
+ GradTransGradient const & rGOld, const SdrObject* pObj, bool bMoveSingle, bool bMoveFirst);
+};
+
+#endif // INCLUDED_SVX_SOURCE_SVDRAW_GRADTRNS_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/polypolygoneditor.cxx b/svx/source/svdraw/polypolygoneditor.cxx
new file mode 100644
index 000000000..b3f6d3bc5
--- /dev/null
+++ b/svx/source/svdraw/polypolygoneditor.cxx
@@ -0,0 +1,182 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+
+#include <svx/polypolygoneditor.hxx>
+
+namespace sdr {
+
+PolyPolygonEditor::PolyPolygonEditor( const basegfx::B2DPolyPolygon& rPolyPolygon)
+: maPolyPolygon( rPolyPolygon )
+{
+}
+
+bool PolyPolygonEditor::DeletePoints( const o3tl::sorted_vector< sal_uInt16 >& rAbsPoints )
+{
+ bool bPolyPolyChanged = false;
+
+ auto aIter( rAbsPoints.rbegin() );
+ for( ; aIter != rAbsPoints.rend(); ++aIter )
+ {
+ sal_uInt32 nPoly, nPnt;
+ if( GetRelativePolyPoint(maPolyPolygon,(*aIter), nPoly, nPnt) )
+ {
+ // remove point
+ basegfx::B2DPolygon aCandidate(maPolyPolygon.getB2DPolygon(nPoly));
+
+ aCandidate.remove(nPnt);
+
+
+ if( aCandidate.count() < 2 )
+ {
+ maPolyPolygon.remove(nPoly);
+ }
+ else
+ {
+ maPolyPolygon.setB2DPolygon(nPoly, aCandidate);
+ }
+
+ bPolyPolyChanged = true;
+ }
+ }
+
+ return bPolyPolyChanged;
+}
+
+bool PolyPolygonEditor::SetSegmentsKind(SdrPathSegmentKind eKind, const o3tl::sorted_vector< sal_uInt16 >& rAbsPoints )
+{
+ bool bPolyPolyChanged = false;
+
+ auto aIter( rAbsPoints.rbegin() );
+ for( ; aIter != rAbsPoints.rend(); ++aIter )
+ {
+ sal_uInt32 nPolyNum, nPntNum;
+
+ if(PolyPolygonEditor::GetRelativePolyPoint(maPolyPolygon, (*aIter), nPolyNum, nPntNum))
+ {
+ // do change at aNewPolyPolygon. Take a look at edge.
+ basegfx::B2DPolygon aCandidate(maPolyPolygon.getB2DPolygon(nPolyNum));
+ const sal_uInt32 nCount(aCandidate.count());
+
+ if(nCount && (nPntNum + 1 < nCount || aCandidate.isClosed()))
+ {
+ bool bCandidateChanged(false);
+
+ // it's a valid edge, check control point usage
+ const sal_uInt32 nNextIndex((nPntNum + 1) % nCount);
+ const bool bContolUsed(aCandidate.areControlPointsUsed()
+ && (aCandidate.isNextControlPointUsed(nPntNum) || aCandidate.isPrevControlPointUsed(nNextIndex)));
+
+ if(bContolUsed)
+ {
+ if(SdrPathSegmentKind::Toggle == eKind || SdrPathSegmentKind::Line == eKind)
+ {
+ // remove control
+ aCandidate.resetNextControlPoint(nPntNum);
+ aCandidate.resetPrevControlPoint(nNextIndex);
+ bCandidateChanged = true;
+ }
+ }
+ else
+ {
+ if(SdrPathSegmentKind::Toggle == eKind || SdrPathSegmentKind::Curve == eKind)
+ {
+ // add control
+ const basegfx::B2DPoint aStart(aCandidate.getB2DPoint(nPntNum));
+ const basegfx::B2DPoint aEnd(aCandidate.getB2DPoint(nNextIndex));
+
+ aCandidate.setNextControlPoint(nPntNum, interpolate(aStart, aEnd, (1.0 / 3.0)));
+ aCandidate.setPrevControlPoint(nNextIndex, interpolate(aStart, aEnd, (2.0 / 3.0)));
+ bCandidateChanged = true;
+ }
+ }
+
+ if(bCandidateChanged)
+ {
+ maPolyPolygon.setB2DPolygon(nPolyNum, aCandidate);
+ bPolyPolyChanged = true;
+ }
+ }
+ }
+ }
+
+ return bPolyPolyChanged;
+}
+
+bool PolyPolygonEditor::SetPointsSmooth( basegfx::B2VectorContinuity eFlags, const o3tl::sorted_vector< sal_uInt16 >& rAbsPoints)
+{
+ bool bPolyPolygonChanged(false);
+
+ auto aIter( rAbsPoints.rbegin() );
+ for( ; aIter != rAbsPoints.rend(); ++aIter )
+ {
+ sal_uInt32 nPolyNum, nPntNum;
+
+ if(PolyPolygonEditor::GetRelativePolyPoint(maPolyPolygon, (*aIter), nPolyNum, nPntNum))
+ {
+ // do change at aNewPolyPolygon...
+ basegfx::B2DPolygon aCandidate(maPolyPolygon.getB2DPolygon(nPolyNum));
+
+ // set continuity in point, make sure there is a curve
+ bool bPolygonChanged = basegfx::utils::expandToCurveInPoint(aCandidate, nPntNum);
+ bPolygonChanged |= basegfx::utils::setContinuityInPoint(aCandidate, nPntNum, eFlags);
+
+ if(bPolygonChanged)
+ {
+ maPolyPolygon.setB2DPolygon(nPolyNum, aCandidate);
+ bPolyPolygonChanged = true;
+ }
+ }
+ }
+
+ return bPolyPolygonChanged;
+}
+
+bool PolyPolygonEditor::GetRelativePolyPoint( const basegfx::B2DPolyPolygon& rPoly, sal_uInt32 nAbsPnt, sal_uInt32& rPolyNum, sal_uInt32& rPointNum )
+{
+ const sal_uInt32 nPolyCount(rPoly.count());
+ sal_uInt32 nPolyNum(0);
+
+ while(nPolyNum < nPolyCount)
+ {
+ const sal_uInt32 nPointCount(rPoly.getB2DPolygon(nPolyNum).count());
+
+ if(nAbsPnt < nPointCount)
+ {
+ rPolyNum = nPolyNum;
+ rPointNum = nAbsPnt;
+
+ return true;
+ }
+ else
+ {
+ nPolyNum++;
+ nAbsPnt -= nPointCount;
+ }
+ }
+
+ return false;
+}
+
+} // end of namespace sdr
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/presetooxhandleadjustmentrelations.cxx b/svx/source/svdraw/presetooxhandleadjustmentrelations.cxx
new file mode 100644
index 000000000..528c8b35c
--- /dev/null
+++ b/svx/source/svdraw/presetooxhandleadjustmentrelations.cxx
@@ -0,0 +1,343 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <o3tl/string_view.hxx>
+#include <rtl/ustring.hxx>
+#include <unordered_map>
+#include "presetooxhandleadjustmentrelations.hxx"
+
+namespace
+{
+typedef std::unordered_map<OUString, OUString> HandleAdjRelHashMap;
+
+struct HandleAdjRel
+{
+ // Shape name without leading "ooxml-", underscore, zero based handle index
+ // e.g. The third handle in shape of type "ooxml-circularArrow" will be
+ // identified by key "circularArrow_2"
+ const char* sShape_Handle;
+
+ // 4 tokens with separator "|"
+ // first: RefX or RefR, na if not exists
+ // second: adj, or adj1 or adj2, etc. as in preset, na if not exists
+ // third: RefY or RefAngle, na if not exists
+ // forth: adj, or adj1 or adj2, etc. as in preset, na if not exists
+ // e.g. The third handle in shape <circularArrow> has in the preset
+ // the tag <ahPolar gdRefR="adj5" minR="0" maxR="25000"> .
+ // The resulting value in the map here is "RefR|adj5|na|na"
+ const char* sAdjReferences;
+};
+
+// The array initializer has been extracted from
+// oox/source/drawingml/customshapes/presetShapeDefinitions.xml
+// by using an XSLT file. That file is attached to tdf#126512.
+constexpr HandleAdjRel aHandleAdjRelArray[]
+ = { { "accentBorderCallout1_0", "RefX|adj2|RefY|adj1" },
+ { "accentBorderCallout1_1", "RefX|adj4|RefY|adj3" },
+ { "accentBorderCallout2_0", "RefX|adj2|RefY|adj1" },
+ { "accentBorderCallout2_1", "RefX|adj4|RefY|adj3" },
+ { "accentBorderCallout2_2", "RefX|adj6|RefY|adj5" },
+ { "accentBorderCallout3_0", "RefX|adj2|RefY|adj1" },
+ { "accentBorderCallout3_1", "RefX|adj4|RefY|adj3" },
+ { "accentBorderCallout3_2", "RefX|adj6|RefY|adj5" },
+ { "accentBorderCallout3_3", "RefX|adj8|RefY|adj7" },
+ { "accentCallout1_0", "RefX|adj2|RefY|adj1" },
+ { "accentCallout1_1", "RefX|adj4|RefY|adj3" },
+ { "accentCallout2_0", "RefX|adj2|RefY|adj1" },
+ { "accentCallout2_1", "RefX|adj4|RefY|adj3" },
+ { "accentCallout2_2", "RefX|adj6|RefY|adj5" },
+ { "accentCallout3_0", "RefX|adj2|RefY|adj1" },
+ { "accentCallout3_1", "RefX|adj4|RefY|adj3" },
+ { "accentCallout3_2", "RefX|adj6|RefY|adj5" },
+ { "accentCallout3_3", "RefX|adj8|RefY|adj7" },
+ { "arc_0", "na|na|RefAngle|adj1" },
+ { "arc_1", "na|na|RefAngle|adj2" },
+ { "bentArrow_0", "RefX|adj1|na|na" },
+ { "bentArrow_1", "na|na|RefY|adj2" },
+ { "bentArrow_2", "RefX|adj3|na|na" },
+ { "bentArrow_3", "RefX|adj4|na|na" },
+ { "bentConnector3_0", "RefX|adj1|na|na" },
+ { "bentConnector4_0", "RefX|adj1|na|na" },
+ { "bentConnector4_1", "na|na|RefY|adj2" },
+ { "bentConnector5_0", "RefX|adj1|na|na" },
+ { "bentConnector5_1", "na|na|RefY|adj2" },
+ { "bentConnector5_2", "RefX|adj3|na|na" },
+ { "bentUpArrow_0", "na|na|RefY|adj1" },
+ { "bentUpArrow_1", "RefX|adj2|na|na" },
+ { "bentUpArrow_2", "na|na|RefY|adj3" },
+ { "bevel_0", "RefX|adj|na|na" },
+ { "blockArc_0", "na|na|RefAngle|adj1" },
+ { "blockArc_1", "RefR|adj3|RefAngle|adj2" },
+ { "borderCallout1_0", "RefX|adj2|RefY|adj1" },
+ { "borderCallout1_1", "RefX|adj4|RefY|adj3" },
+ { "borderCallout2_0", "RefX|adj2|RefY|adj1" },
+ { "borderCallout2_1", "RefX|adj4|RefY|adj3" },
+ { "borderCallout2_2", "RefX|adj6|RefY|adj5" },
+ { "borderCallout3_0", "RefX|adj2|RefY|adj1" },
+ { "borderCallout3_1", "RefX|adj4|RefY|adj3" },
+ { "borderCallout3_2", "RefX|adj6|RefY|adj5" },
+ { "borderCallout3_3", "RefX|adj8|RefY|adj7" },
+ { "bracePair_0", "na|na|RefY|adj" },
+ { "bracketPair_0", "na|na|RefY|adj" },
+ { "callout1_0", "RefX|adj2|RefY|adj1" },
+ { "callout1_1", "RefX|adj4|RefY|adj3" },
+ { "callout2_0", "RefX|adj2|RefY|adj1" },
+ { "callout2_1", "RefX|adj4|RefY|adj3" },
+ { "callout2_2", "RefX|adj6|RefY|adj5" },
+ { "callout3_0", "RefX|adj2|RefY|adj1" },
+ { "callout3_1", "RefX|adj4|RefY|adj3" },
+ { "callout3_2", "RefX|adj6|RefY|adj5" },
+ { "callout3_3", "RefX|adj8|RefY|adj7" },
+ { "can_0", "na|na|RefY|adj" },
+ { "chevron_0", "RefX|adj|na|na" },
+ { "chord_0", "na|na|RefAngle|adj1" },
+ { "chord_1", "na|na|RefAngle|adj2" },
+ { "circularArrow_0", "na|na|RefAngle|adj2" },
+ { "circularArrow_1", "na|na|RefAngle|adj4" },
+ { "circularArrow_2", "RefR|adj1|RefAngle|adj3" },
+ { "circularArrow_3", "RefR|adj5|na|na" },
+ { "cloudCallout_0", "RefX|adj1|RefY|adj2" },
+ { "corner_0", "na|na|RefY|adj1" },
+ { "corner_1", "RefX|adj2|na|na" },
+ { "cube_0", "na|na|RefY|adj" },
+ { "curvedConnector3_0", "RefX|adj1|na|na" },
+ { "curvedConnector4_0", "RefX|adj1|na|na" },
+ { "curvedConnector4_1", "na|na|RefY|adj2" },
+ { "curvedConnector5_0", "RefX|adj1|na|na" },
+ { "curvedConnector5_1", "na|na|RefY|adj2" },
+ { "curvedConnector5_2", "RefX|adj3|na|na" },
+ { "curvedDownArrow_0", "RefX|adj1|na|na" },
+ { "curvedDownArrow_1", "RefX|adj2|na|na" },
+ { "curvedDownArrow_2", "na|na|RefY|adj3" },
+ { "curvedLeftArrow_0", "na|na|RefY|adj1" },
+ { "curvedLeftArrow_1", "na|na|RefY|adj2" },
+ { "curvedLeftArrow_2", "RefX|adj3|na|na" },
+ { "curvedRightArrow_0", "na|na|RefY|adj1" },
+ { "curvedRightArrow_1", "na|na|RefY|adj2" },
+ { "curvedRightArrow_2", "RefX|adj3|na|na" },
+ { "curvedUpArrow_0", "RefX|adj1|na|na" },
+ { "curvedUpArrow_1", "RefX|adj2|na|na" },
+ { "curvedUpArrow_2", "na|na|RefY|adj3" },
+ { "diagStripe_0", "na|na|RefY|adj" },
+ { "donut_0", "RefR|adj|na|na" },
+ { "doubleWave_0", "na|na|RefY|adj1" },
+ { "doubleWave_1", "RefX|adj2|na|na" },
+ { "downArrow_0", "RefX|adj1|na|na" },
+ { "downArrow_1", "na|na|RefY|adj2" },
+ { "downArrowCallout_0", "RefX|adj1|na|na" },
+ { "downArrowCallout_1", "RefX|adj2|na|na" },
+ { "downArrowCallout_2", "na|na|RefY|adj3" },
+ { "downArrowCallout_3", "na|na|RefY|adj4" },
+ { "ellipseRibbon_0", "na|na|RefY|adj1" },
+ { "ellipseRibbon_1", "RefX|adj2|na|na" },
+ { "ellipseRibbon_2", "na|na|RefY|adj3" },
+ { "ellipseRibbon2_0", "na|na|RefY|adj1" },
+ { "ellipseRibbon2_1", "RefX|adj2|na|na" },
+ { "ellipseRibbon2_2", "na|na|RefY|adj3" },
+ { "foldedCorner_0", "RefX|adj|na|na" },
+ { "frame_0", "RefX|adj1|na|na" },
+ { "gear6_0", "na|na|RefY|adj1" },
+ { "gear6_1", "RefX|adj2|na|na" },
+ { "gear9_0", "na|na|RefY|adj1" },
+ { "gear9_1", "RefX|adj2|na|na" },
+ { "halfFrame_0", "na|na|RefY|adj1" },
+ { "halfFrame_1", "RefX|adj2|na|na" },
+ { "hexagon_0", "RefX|adj|na|na" },
+ { "homePlate_0", "RefX|adj|na|na" },
+ { "horizontalScroll_0", "RefX|adj|na|na" },
+ { "leftArrow_0", "na|na|RefY|adj1" },
+ { "leftArrow_1", "RefX|adj2|na|na" },
+ { "leftArrowCallout_0", "na|na|RefY|adj1" },
+ { "leftArrowCallout_1", "na|na|RefY|adj2" },
+ { "leftArrowCallout_2", "RefX|adj3|na|na" },
+ { "leftArrowCallout_3", "RefX|adj4|na|na" },
+ { "leftBrace_0", "na|na|RefY|adj1" },
+ { "leftBrace_1", "na|na|RefY|adj2" },
+ { "leftBracket_0", "na|na|RefY|adj" },
+ { "leftCircularArrow_0", "na|na|RefAngle|adj2" },
+ { "leftCircularArrow_1", "na|na|RefAngle|adj4" },
+ { "leftCircularArrow_2", "RefR|adj1|RefAngle|adj3" },
+ { "leftCircularArrow_3", "RefR|adj5|na|na" },
+ { "leftRightArrow_0", "na|na|RefY|adj1" },
+ { "leftRightArrow_1", "RefX|adj2|na|na" },
+ { "leftRightArrowCallout_0", "na|na|RefY|adj1" },
+ { "leftRightArrowCallout_1", "na|na|RefY|adj2" },
+ { "leftRightArrowCallout_2", "RefX|adj3|na|na" },
+ { "leftRightArrowCallout_3", "RefX|adj4|na|na" },
+ { "leftRightCircularArrow_0", "na|na|RefAngle|adj2" },
+ { "leftRightCircularArrow_1", "na|na|RefAngle|adj4" },
+ { "leftRightCircularArrow_2", "RefR|adj1|RefAngle|adj3" },
+ { "leftRightCircularArrow_3", "RefR|adj5|na|na" },
+ { "leftRightRibbon_0", "na|na|RefY|adj1" },
+ { "leftRightRibbon_1", "RefX|adj2|na|na" },
+ { "leftRightRibbon_2", "na|na|RefY|adj3" },
+ { "leftRightUpArrow_0", "RefX|adj1|na|na" },
+ { "leftRightUpArrow_1", "RefX|adj2|na|na" },
+ { "leftRightUpArrow_2", "na|na|RefY|adj3" },
+ { "leftUpArrow_0", "na|na|RefY|adj1" },
+ { "leftUpArrow_1", "RefX|adj2|na|na" },
+ { "leftUpArrow_2", "na|na|RefY|adj3" },
+ { "mathDivide_0", "na|na|RefY|adj1" },
+ { "mathDivide_1", "na|na|RefY|adj2" },
+ { "mathDivide_2", "RefX|adj3|na|na" },
+ { "mathEqual_0", "na|na|RefY|adj1" },
+ { "mathEqual_1", "na|na|RefY|adj2" },
+ { "mathMinus_0", "na|na|RefY|adj1" },
+ { "mathMultiply_0", "na|na|RefY|adj1" },
+ { "mathNotEqual_0", "na|na|RefY|adj1" },
+ { "mathNotEqual_1", "na|na|RefAngle|adj2" },
+ { "mathNotEqual_2", "na|na|RefY|adj3" },
+ { "mathPlus_0", "na|na|RefY|adj1" },
+ { "moon_0", "RefX|adj|na|na" },
+ { "nonIsoscelesTrapezoid_0", "RefX|adj1|na|na" },
+ { "nonIsoscelesTrapezoid_1", "RefX|adj2|na|na" },
+ { "noSmoking_0", "RefR|adj|na|na" },
+ { "notchedRightArrow_0", "na|na|RefY|adj1" },
+ { "notchedRightArrow_1", "RefX|adj2|na|na" },
+ { "octagon_0", "RefX|adj|na|na" },
+ { "parallelogram_0", "RefX|adj|na|na" },
+ { "pie_0", "na|na|RefAngle|adj1" },
+ { "pie_1", "na|na|RefAngle|adj2" },
+ { "plaque_0", "RefX|adj|na|na" },
+ { "plus_0", "RefX|adj|na|na" },
+ { "quadArrow_0", "RefX|adj1|na|na" },
+ { "quadArrow_1", "RefX|adj2|na|na" },
+ { "quadArrow_2", "na|na|RefY|adj3" },
+ { "quadArrowCallout_0", "RefX|adj1|na|na" },
+ { "quadArrowCallout_1", "RefX|adj2|na|na" },
+ { "quadArrowCallout_2", "na|na|RefY|adj3" },
+ { "quadArrowCallout_3", "na|na|RefY|adj4" },
+ { "ribbon_0", "na|na|RefY|adj1" },
+ { "ribbon_1", "RefX|adj2|na|na" },
+ { "ribbon2_0", "na|na|RefY|adj1" },
+ { "ribbon2_1", "RefX|adj2|na|na" },
+ { "rightArrow_0", "na|na|RefY|adj1" },
+ { "rightArrow_1", "RefX|adj2|na|na" },
+ { "rightArrowCallout_0", "na|na|RefY|adj1" },
+ { "rightArrowCallout_1", "na|na|RefY|adj2" },
+ { "rightArrowCallout_2", "RefX|adj3|na|na" },
+ { "rightArrowCallout_3", "RefX|adj4|na|na" },
+ { "rightBrace_0", "na|na|RefY|adj1" },
+ { "rightBrace_1", "na|na|RefY|adj2" },
+ { "rightBracket_0", "na|na|RefY|adj" },
+ { "round1Rect_0", "RefX|adj|na|na" },
+ { "round2DiagRect_0", "RefX|adj1|na|na" },
+ { "round2DiagRect_1", "RefX|adj2|na|na" },
+ { "round2SameRect_0", "RefX|adj1|na|na" },
+ { "round2SameRect_1", "RefX|adj2|na|na" },
+ { "roundRect_0", "RefX|adj|na|na" },
+ { "smileyFace_0", "na|na|RefY|adj" },
+ { "snip1Rect_0", "RefX|adj|na|na" },
+ { "snip2DiagRect_0", "RefX|adj1|na|na" },
+ { "snip2DiagRect_1", "RefX|adj2|na|na" },
+ { "snip2SameRect_0", "RefX|adj1|na|na" },
+ { "snip2SameRect_1", "RefX|adj2|na|na" },
+ { "snipRoundRect_0", "RefX|adj1|na|na" },
+ { "snipRoundRect_1", "RefX|adj2|na|na" },
+ { "star10_0", "na|na|RefY|adj" },
+ { "star12_0", "na|na|RefY|adj" },
+ { "star16_0", "na|na|RefY|adj" },
+ { "star24_0", "na|na|RefY|adj" },
+ { "star32_0", "na|na|RefY|adj" },
+ { "star4_0", "na|na|RefY|adj" },
+ { "star5_0", "na|na|RefY|adj" },
+ { "star6_0", "na|na|RefY|adj" },
+ { "star7_0", "na|na|RefY|adj" },
+ { "star8_0", "na|na|RefY|adj" },
+ { "stripedRightArrow_0", "na|na|RefY|adj1" },
+ { "stripedRightArrow_1", "RefX|adj2|na|na" },
+ { "sun_0", "RefX|adj|na|na" },
+ { "swooshArrow_0", "na|na|RefY|adj1" },
+ { "swooshArrow_1", "RefX|adj2|na|na" },
+ { "teardrop_0", "RefX|adj|na|na" },
+ { "trapezoid_0", "RefX|adj|na|na" },
+ { "triangle_0", "RefX|adj|na|na" },
+ { "upArrowCallout_0", "RefX|adj1|na|na" },
+ { "upArrowCallout_1", "RefX|adj2|na|na" },
+ { "upArrowCallout_2", "na|na|RefY|adj3" },
+ { "upArrowCallout_3", "na|na|RefY|adj4" },
+ { "upDownArrow_0", "RefX|adj1|na|na" },
+ { "upDownArrow_1", "na|na|RefY|adj2" },
+ { "upArrow_0", "RefX|adj1|na|na" },
+ { "upArrow_1", "na|na|RefY|adj2" },
+ { "upDownArrowCallout_0", "RefX|adj1|na|na" },
+ { "upDownArrowCallout_1", "RefX|adj2|na|na" },
+ { "upDownArrowCallout_2", "na|na|RefY|adj3" },
+ { "upDownArrowCallout_3", "na|na|RefY|adj4" },
+ { "uturnArrow_0", "RefX|adj1|na|na" },
+ { "uturnArrow_1", "RefX|adj2|na|na" },
+ { "uturnArrow_2", "na|na|RefY|adj3" },
+ { "uturnArrow_3", "RefX|adj4|na|na" },
+ { "uturnArrow_4", "na|na|RefY|adj5" },
+ { "verticalScroll_0", "na|na|RefY|adj" },
+ { "wave_0", "na|na|RefY|adj1" },
+ { "wave_1", "RefX|adj2|na|na" },
+ { "wedgeEllipseCallout_0", "RefX|adj1|RefY|adj2" },
+ { "wedgeRectCallout_0", "RefX|adj1|RefY|adj2" },
+ { "wedgeRoundRectCallout_0", "RefX|adj1|RefY|adj2" } };
+}
+
+static sal_Int32 lcl_getAdjIndexFromToken(const sal_Int32 nTokenPos, std::u16string_view rMapValue)
+{
+ std::u16string_view sAdjRef = o3tl::getToken(rMapValue, nTokenPos, '|');
+ std::u16string_view sNumber; // number part from "adj1", "adj2" etc.
+ if (o3tl::starts_with(sAdjRef, u"adj", &sNumber))
+ {
+ if (sNumber.empty() || sNumber == u"1")
+ return 0;
+ else
+ return o3tl::toInt32(sNumber) - 1;
+ }
+ else
+ return -1;
+}
+
+void PresetOOXHandleAdj::GetOOXHandleAdjRelation(
+ std::u16string_view sFullOOXShapeName, const sal_Int32 nHandleIndex, OUString& rFirstRefType,
+ sal_Int32& rFirstAdjValueIndex, OUString& rSecondRefType, sal_Int32& rSecondAdjValueIndex)
+{
+ static const HandleAdjRelHashMap s_HashMap = []() {
+ HandleAdjRelHashMap aH;
+ aH.reserve(std::size(aHandleAdjRelArray));
+ for (const auto& item : aHandleAdjRelArray)
+ aH.emplace(OUString::createFromAscii(item.sShape_Handle),
+ OUString::createFromAscii(item.sAdjReferences));
+ return aH;
+ }();
+
+ std::u16string_view sKey;
+ OUString sValue;
+ rFirstRefType = "na";
+ rFirstAdjValueIndex = -1;
+ rSecondRefType = "na";
+ rSecondAdjValueIndex = -1;
+ if (o3tl::starts_with(sFullOOXShapeName, u"ooxml-", &sKey))
+ {
+ HandleAdjRelHashMap::const_iterator aHashIter(
+ s_HashMap.find(OUString::Concat(sKey) + "_" + OUString::number(nHandleIndex)));
+ if (aHashIter != s_HashMap.end())
+ sValue = (*aHashIter).second;
+ else
+ return;
+ }
+ else
+ return;
+
+ rFirstRefType = sValue.getToken(0, '|');
+ rFirstAdjValueIndex = lcl_getAdjIndexFromToken(1, sValue);
+ rSecondRefType = sValue.getToken(2, '|');
+ rSecondAdjValueIndex = lcl_getAdjIndexFromToken(3, sValue);
+ return;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/svx/source/svdraw/presetooxhandleadjustmentrelations.hxx b/svx/source/svdraw/presetooxhandleadjustmentrelations.hxx
new file mode 100644
index 000000000..b9404e598
--- /dev/null
+++ b/svx/source/svdraw/presetooxhandleadjustmentrelations.hxx
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SVX_SOURCE_SVDRAW_PRESETOOXHANDLEADJUSTMENTRELATIONS_HXX
+#define INCLUDED_SVX_SOURCE_SVDRAW_PRESETOOXHANDLEADJUSTMENTRELATIONS_HXX
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <rtl/ustring.hxx>
+
+namespace PresetOOXHandleAdj
+{
+/* This method is used in SdrObjCustomShape::MergeDefaultAttributes() */
+void GetOOXHandleAdjRelation(
+ std::u16string_view sFullOOXShapeName, /* e.g. "ooxml-circularArrow" */
+ const sal_Int32 nHandleIndex, /* index in sequence from property "Handles" */
+ OUString& rFirstRefType, /* Propertyname, same as by pptx import, e.g. "RefX" */
+ sal_Int32& rFirstAdjValueIndex, /* index in sequence from property "AdjustmentValues" */
+ OUString& rSecondRefType, /* Propertyname, same as by pptx import, e.g. "RefY" */
+ sal_Int32& rSecondAdjValueIndex /* index in sequence from property "AdjustmentValues" */
+);
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/svx/source/svdraw/sdrhittesthelper.cxx b/svx/source/svdraw/sdrhittesthelper.cxx
new file mode 100644
index 000000000..62ebde956
--- /dev/null
+++ b/svx/source/svdraw/sdrhittesthelper.cxx
@@ -0,0 +1,176 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <svx/sdrhittesthelper.hxx>
+#include <svx/obj3d.hxx>
+#include <svx/helperhittest3d.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/sdrpagewindow.hxx>
+#include <svx/sdr/contact/viewobjectcontact.hxx>
+#include <svx/sdr/contact/displayinfo.hxx>
+#include <svx/sdr/contact/objectcontact.hxx>
+#include <drawinglayer/processor2d/hittestprocessor2d.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/sdr/contact/viewcontact.hxx>
+
+
+// #i101872# new Object HitTest as View-tooling
+
+SdrObject* SdrObjectPrimitiveHit(
+ const SdrObject& rObject,
+ const Point& rPnt,
+ sal_uInt16 nTol,
+ const SdrPageView& rSdrPageView,
+ const SdrLayerIDSet* pVisiLayer,
+ bool bTextOnly,
+ drawinglayer::primitive2d::Primitive2DContainer* pHitContainer)
+{
+ SdrObject* pResult = nullptr;
+
+ if(rObject.GetSubList() && rObject.GetSubList()->GetObjCount())
+ {
+ // group or scene with content. Single 3D objects also have a
+ // true == rObject.GetSubList(), but no content
+ pResult = SdrObjListPrimitiveHit(*rObject.GetSubList(), rPnt, nTol, rSdrPageView, pVisiLayer, bTextOnly);
+ }
+ else
+ {
+ if( rObject.IsVisible() && (!pVisiLayer || pVisiLayer->IsSet(rObject.GetLayer())))
+ {
+ // single object, 3d object, empty scene or empty group. Check if
+ // it's a single 3D object
+ const E3dCompoundObject* pE3dCompoundObject = dynamic_cast< const E3dCompoundObject* >(&rObject);
+
+ if(pE3dCompoundObject)
+ {
+ const basegfx::B2DPoint aHitPosition(rPnt.X(), rPnt.Y());
+
+ if(checkHitSingle3DObject(aHitPosition, *pE3dCompoundObject))
+ {
+ pResult = const_cast< E3dCompoundObject* >(pE3dCompoundObject);
+ }
+ }
+ else
+ {
+ // not a single 3D object; Check in first PageWindow using primitives (only SC
+ // with split views uses multiple PageWindows nowadays)
+ if(rSdrPageView.PageWindowCount())
+ {
+ const double fLogicTolerance(nTol);
+ const basegfx::B2DPoint aHitPosition(rPnt.X(), rPnt.Y());
+ const sdr::contact::ViewObjectContact& rVOC = rObject.GetViewContact().GetViewObjectContact(
+ rSdrPageView.GetPageWindow(0)->GetObjectContact());
+
+ if(ViewObjectContactPrimitiveHit(rVOC, aHitPosition, fLogicTolerance, bTextOnly, pHitContainer))
+ {
+ pResult = const_cast< SdrObject* >(&rObject);
+ }
+ }
+ }
+ }
+ }
+
+ return pResult;
+}
+
+
+SdrObject* SdrObjListPrimitiveHit(
+ const SdrObjList& rList,
+ const Point& rPnt,
+ sal_uInt16 nTol,
+ const SdrPageView& rSdrPageView,
+ const SdrLayerIDSet* pVisiLayer,
+ bool bTextOnly)
+{
+ size_t nObjNum(rList.GetObjCount());
+ SdrObject* pRetval = nullptr;
+
+ while(!pRetval && nObjNum > 0)
+ {
+ nObjNum--;
+ SdrObject* pObj = rList.GetObj(nObjNum);
+
+ pRetval = SdrObjectPrimitiveHit(*pObj, rPnt, nTol, rSdrPageView, pVisiLayer, bTextOnly);
+ }
+
+ return pRetval;
+}
+
+
+bool ViewObjectContactPrimitiveHit(
+ const sdr::contact::ViewObjectContact& rVOC,
+ const basegfx::B2DPoint& rHitPosition,
+ double fLogicHitTolerance,
+ bool bTextOnly,
+ drawinglayer::primitive2d::Primitive2DContainer* pHitContainer)
+{
+ basegfx::B2DRange aObjectRange(rVOC.getObjectRange());
+
+ if(!aObjectRange.isEmpty())
+ {
+ // first do a rough B2DRange based HitTest; do not forget to
+ // include the HitTolerance if given
+ if(basegfx::fTools::more(fLogicHitTolerance, 0.0))
+ {
+ aObjectRange.grow(fLogicHitTolerance);
+ }
+
+ if(aObjectRange.isInside(rHitPosition))
+ {
+ // get primitive sequence
+ sdr::contact::DisplayInfo aDisplayInfo;
+ // have to make a copy of this container here, because it might be changed underneath us
+ const drawinglayer::primitive2d::Primitive2DContainer aSequence(rVOC.getPrimitive2DSequence(aDisplayInfo));
+
+ if(!aSequence.empty())
+ {
+ // create a HitTest processor
+ const drawinglayer::geometry::ViewInformation2D& rViewInformation2D = rVOC.GetObjectContact().getViewInformation2D();
+ drawinglayer::processor2d::HitTestProcessor2D aHitTestProcessor2D(
+ rViewInformation2D,
+ rHitPosition,
+ fLogicHitTolerance,
+ bTextOnly);
+
+ // ask for HitStack
+ aHitTestProcessor2D.collectHitStack(pHitContainer != nullptr);
+
+ // feed it with the primitives
+ aHitTestProcessor2D.process(aSequence);
+
+ // deliver result
+ if (aHitTestProcessor2D.getHit())
+ {
+ if (pHitContainer)
+ {
+ // fetch HitStack primitives if requested
+ *pHitContainer = aHitTestProcessor2D.getHitStack();
+ }
+
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/sdrmasterpagedescriptor.cxx b/svx/source/svdraw/sdrmasterpagedescriptor.cxx
new file mode 100644
index 000000000..1d0843918
--- /dev/null
+++ b/svx/source/svdraw/sdrmasterpagedescriptor.cxx
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/sdrmasterpagedescriptor.hxx>
+#include <sdr/contact/viewcontactofmasterpagedescriptor.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/xdef.hxx>
+#include <svx/xfillit0.hxx>
+#include <svl/itemset.hxx>
+
+using namespace com::sun::star;
+
+namespace sdr
+{
+ MasterPageDescriptor::MasterPageDescriptor(SdrPage& aOwnerPage, SdrPage& aUsedPage)
+ : maOwnerPage(aOwnerPage),
+ maUsedPage(aUsedPage)
+ {
+ // all layers visible
+ maVisibleLayers.SetAll();
+
+ // register at used page
+ maUsedPage.AddPageUser(*this);
+ }
+
+ MasterPageDescriptor::~MasterPageDescriptor()
+ {
+ // de-register at used page
+ maUsedPage.RemovePageUser(*this);
+
+ mpViewContact.reset();
+ }
+
+ // ViewContact part
+ sdr::contact::ViewContact& MasterPageDescriptor::GetViewContact() const
+ {
+ if(!mpViewContact)
+ {
+ mpViewContact.reset(
+ new sdr::contact::ViewContactOfMasterPageDescriptor(*const_cast< MasterPageDescriptor* >(this)) );
+ }
+
+ return *mpViewContact;
+ }
+
+ // this method is called from the destructor of the referenced page.
+ // do all necessary action to forget the page. It is not necessary to call
+ // RemovePageUser(), that is done from the destructor.
+ void MasterPageDescriptor::PageInDestruction(const SdrPage& /*rPage*/)
+ {
+ maOwnerPage.TRG_ClearMasterPage();
+ }
+
+ void MasterPageDescriptor::SetVisibleLayers(const SdrLayerIDSet& rNew)
+ {
+ if(rNew != maVisibleLayers)
+ {
+ maVisibleLayers = rNew;
+ GetViewContact().ActionChanged();
+ }
+ }
+
+
+ const SdrPageProperties* MasterPageDescriptor::getCorrectSdrPageProperties() const
+ {
+ const SdrPage* pCorrectPage = &GetOwnerPage();
+ const SdrPageProperties* pCorrectProperties = &pCorrectPage->getSdrPageProperties();
+
+ if(drawing::FillStyle_NONE == pCorrectProperties->GetItemSet().Get(XATTR_FILLSTYLE).GetValue())
+ {
+ pCorrectPage = &GetUsedPage();
+ pCorrectProperties = &pCorrectPage->getSdrPageProperties();
+ }
+
+ if(pCorrectPage->IsMasterPage() && !pCorrectProperties->GetStyleSheet())
+ {
+ // #i110846# Suppress SdrPage FillStyle for MasterPages without StyleSheets,
+ // else the PoolDefault (XFILL_COLOR and Blue8) will be used. Normally, all
+ // MasterPages should have a StyleSheet exactly for this reason, but historically
+ // e.g. the Notes MasterPage has no StyleSheet set (and there maybe others).
+ pCorrectProperties = nullptr;
+ }
+
+ return pCorrectProperties;
+ }
+} // end of namespace sdr
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/sdrpagewindow.cxx b/svx/source/svdraw/sdrpagewindow.cxx
new file mode 100644
index 000000000..01be77135
--- /dev/null
+++ b/svx/source/svdraw/sdrpagewindow.cxx
@@ -0,0 +1,531 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/sdrpagewindow.hxx>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/awt/PosSize.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <comphelper/lok.hxx>
+#include <comphelper/processfactory.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <svx/svdview.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <svx/sdr/contact/objectcontactofpageview.hxx>
+#include <svx/sdr/contact/displayinfo.hxx>
+#include <svx/fmview.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <svtools/optionsdrawinglayer.hxx>
+#include <tools/debug.hxx>
+#include <vcl/window.hxx>
+
+using namespace ::com::sun::star;
+
+struct SdrPageWindow::Impl
+{
+ // #110094# ObjectContact section
+ mutable sdr::contact::ObjectContact* mpObjectContact;
+
+ // the SdrPageView this window belongs to
+ SdrPageView& mrPageView;
+
+ // the PaintWindow to paint on. Here is access to OutDev etc.
+ // #i72752# change to pointer to allow patcing it in DrawLayer() if necessary
+ SdrPaintWindow* mpPaintWindow;
+ SdrPaintWindow* mpOriginalPaintWindow;
+
+ // UNO stuff for xControls
+ uno::Reference<awt::XControlContainer> mxControlContainer;
+
+ Impl( SdrPageView& rPageView, SdrPaintWindow& rPaintWindow ) :
+ mpObjectContact(nullptr),
+ mrPageView(rPageView),
+ mpPaintWindow(&rPaintWindow),
+ mpOriginalPaintWindow(nullptr)
+ {
+ }
+};
+
+
+uno::Reference<awt::XControlContainer> const & SdrPageWindow::GetControlContainer( bool _bCreateIfNecessary ) const
+{
+ if (!mpImpl->mxControlContainer.is() && _bCreateIfNecessary)
+ {
+ SdrView& rView = GetPageView().GetView();
+
+ const SdrPaintWindow& rPaintWindow( GetOriginalPaintWindow() ? *GetOriginalPaintWindow() : GetPaintWindow() );
+ if ( rPaintWindow.OutputToWindow() && !rView.IsPrintPreview() )
+ {
+ vcl::Window* pWindow = rPaintWindow.GetOutputDevice().GetOwnerWindow();
+ const_cast< SdrPageWindow* >( this )->mpImpl->mxControlContainer = VCLUnoHelper::CreateControlContainer( pWindow );
+
+ // #100394# xC->setVisible triggers window->Show() and this has
+ // problems when the view is not completely constructed which may
+ // happen when loading. This leads to accessibility broadcasts which
+ // throw asserts due to the not finished view. All this chain can be avoided
+ // since xC->setVisible is here called only for the side effect in
+ // UnoControlContainer::setVisible(...) which calls createPeer(...).
+ // This will now be called directly from here.
+
+ uno::Reference< awt::XControl > xControl(mpImpl->mxControlContainer, uno::UNO_QUERY);
+ if(xControl.is())
+ {
+ uno::Reference< uno::XInterface > xContext = xControl->getContext();
+ if(!xContext.is())
+ {
+ xControl->createPeer( uno::Reference<awt::XToolkit>(), uno::Reference<awt::XWindowPeer>() );
+ }
+ }
+ }
+ else
+ {
+ // Printer and VirtualDevice, or rather: no OutDev
+ uno::Reference< lang::XMultiServiceFactory > xFactory( ::comphelper::getProcessServiceFactory() );
+ const_cast< SdrPageWindow* >( this )->mpImpl->mxControlContainer.set(xFactory->createInstance("com.sun.star.awt.UnoControlContainer"), uno::UNO_QUERY);
+ uno::Reference< awt::XControlModel > xModel(xFactory->createInstance("com.sun.star.awt.UnoControlContainerModel"), uno::UNO_QUERY);
+ uno::Reference< awt::XControl > xControl(mpImpl->mxControlContainer, uno::UNO_QUERY);
+ if (xControl.is())
+ xControl->setModel(xModel);
+
+ OutputDevice& rOutDev = rPaintWindow.GetOutputDevice();
+ Point aPosPix = rOutDev.GetMapMode().GetOrigin();
+ Size aSizePix = rOutDev.GetOutputSizePixel();
+
+ uno::Reference< awt::XWindow > xContComp(mpImpl->mxControlContainer, uno::UNO_QUERY);
+ if( xContComp.is() )
+ xContComp->setPosSize(aPosPix.X(), aPosPix.Y(), aSizePix.Width(), aSizePix.Height(), awt::PosSize::POSSIZE);
+ }
+
+ FmFormView* pViewAsFormView = dynamic_cast< FmFormView* >( &rView );
+ if ( pViewAsFormView )
+ pViewAsFormView->InsertControlContainer(mpImpl->mxControlContainer);
+ }
+ return mpImpl->mxControlContainer;
+}
+
+SdrPageWindow::SdrPageWindow(SdrPageView& rPageView, SdrPaintWindow& rPaintWindow) :
+ mpImpl(new Impl(rPageView, rPaintWindow))
+{
+}
+
+SdrPageWindow::~SdrPageWindow()
+{
+ // #i26631#
+ ResetObjectContact();
+
+ if (!mpImpl->mxControlContainer.is())
+ return;
+
+ auto & rView = static_cast<SdrPaintView &>(GetPageView().GetView());
+
+ // notify derived views
+ FmFormView* pViewAsFormView = dynamic_cast< FmFormView* >( &rView );
+ if ( pViewAsFormView )
+ pViewAsFormView->RemoveControlContainer(mpImpl->mxControlContainer);
+
+ // dispose the control container
+ uno::Reference< lang::XComponent > xComponent(mpImpl->mxControlContainer, uno::UNO_QUERY);
+ xComponent->dispose();
+}
+
+SdrPageView& SdrPageWindow::GetPageView() const
+{
+ return mpImpl->mrPageView;
+}
+
+SdrPaintWindow& SdrPageWindow::GetPaintWindow() const
+{
+ return *mpImpl->mpPaintWindow;
+}
+
+const SdrPaintWindow* SdrPageWindow::GetOriginalPaintWindow() const
+{
+ return mpImpl->mpOriginalPaintWindow;
+}
+
+// OVERLAY MANAGER
+rtl::Reference< sdr::overlay::OverlayManager > const & SdrPageWindow::GetOverlayManager() const
+{
+ return GetPaintWindow().GetOverlayManager();
+}
+
+SdrPaintWindow* SdrPageWindow::patchPaintWindow(SdrPaintWindow& rPaintWindow)
+{
+ if (!mpImpl)
+ return nullptr;
+
+ if (!mpImpl->mpOriginalPaintWindow)
+ {
+ // first patch
+ mpImpl->mpOriginalPaintWindow = mpImpl->mpPaintWindow;
+ mpImpl->mpPaintWindow = &rPaintWindow;
+ mpImpl->mpOriginalPaintWindow->setPatched(&rPaintWindow);
+ return mpImpl->mpOriginalPaintWindow;
+ }
+ else
+ {
+ // second or more patch
+ auto pPreviousPaintWindow = mpImpl->mpPaintWindow;
+ mpImpl->mpPaintWindow = &rPaintWindow;
+ mpImpl->mpOriginalPaintWindow->setPatched(&rPaintWindow);
+ return pPreviousPaintWindow;
+ }
+}
+
+void SdrPageWindow::unpatchPaintWindow(SdrPaintWindow* pPreviousPaintWindow)
+{
+ if (pPreviousPaintWindow == mpImpl->mpOriginalPaintWindow)
+ {
+ // first patch
+ mpImpl->mpPaintWindow = mpImpl->mpOriginalPaintWindow;
+ mpImpl->mpOriginalPaintWindow->setPatched(nullptr);
+ mpImpl->mpOriginalPaintWindow = nullptr;
+ }
+ else
+ {
+ // second or more patch
+ mpImpl->mpPaintWindow = pPreviousPaintWindow;
+ mpImpl->mpOriginalPaintWindow->setPatched(pPreviousPaintWindow);
+ }
+}
+
+void SdrPageWindow::PrePaint()
+{
+ // give OC the chance to do ProcessDisplay preparations
+ if(HasObjectContact())
+ {
+ GetObjectContact().PrepareProcessDisplay();
+ }
+}
+
+void SdrPageWindow::PrepareRedraw(const vcl::Region& rReg)
+{
+ // give OC the chance to do ProcessDisplay preparations
+ if(HasObjectContact())
+ {
+ GetObjectContact().PrepareProcessDisplay();
+ }
+
+ // if necessary, remember changed RedrawArea at PaintWindow for usage with
+ // overlay and PreRenderDevice stuff
+ GetPaintWindow().SetRedrawRegion(rReg);
+}
+
+
+// clip test
+#ifdef CLIPPER_TEST
+#include <svx/svdopath.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <tools/helpers.hxx>
+#include <basegfx/polygon/b2dpolygoncutandtouch.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolygonclipper.hxx>
+
+// for ::std::sort
+#include <algorithm>
+
+namespace
+{
+ void impPaintStrokePolygon(const basegfx::B2DPolygon& rCandidate, OutputDevice& rOutDev, Color aColor)
+ {
+ basegfx::B2DPolygon aCandidate(rCandidate);
+
+ if(aCandidate.areControlPointsUsed())
+ {
+ aCandidate = basegfx::utils::adaptiveSubdivideByAngle(rCandidate);
+ }
+
+ if(aCandidate.count())
+ {
+ const sal_uInt32 nLoopCount(aCandidate.isClosed() ? aCandidate.count() : aCandidate.count() - 1);
+ rOutDev.SetFillColor();
+ rOutDev.SetLineColor(aColor);
+
+ for(sal_uInt32 a(0); a < nLoopCount; a++)
+ {
+ const basegfx::B2DPoint aBStart(aCandidate.getB2DPoint(a));
+ const basegfx::B2DPoint aBEnd(aCandidate.getB2DPoint((a + 1) % aCandidate.count()));
+ const Point aStart(FRound(aBStart.getX()), FRound(aBStart.getY()));
+ const Point aEnd(FRound(aBEnd.getX()), FRound(aBEnd.getY()));
+ rOutDev.DrawLine(aStart, aEnd);
+ }
+ }
+ }
+
+ void impTryTest(const SdrPageView& rPageView, OutputDevice& rOutDev)
+ {
+ if(rPageView.GetPage() && rPageView.GetPage()->GetObjCount() >= 2)
+ {
+ SdrPage* pPage = rPageView.GetPage();
+ SdrObject* pObjA = pPage->GetObj(0);
+
+ if(dynamic_cast<const SdrPathObj*>( pObjA))
+ {
+ basegfx::B2DPolyPolygon aPolyA(pObjA->GetPathPoly());
+ aPolyA = basegfx::utils::correctOrientations(aPolyA);
+
+ basegfx::B2DPolyPolygon aPolyB;
+
+ for(sal_uInt32 a(1); a < rPageView.GetPage()->GetObjCount(); a++)
+ {
+ SdrObject* pObjB = pPage->GetObj(a);
+
+ if(dynamic_cast<const SdrPathObj*>( pObjB))
+ {
+ basegfx::B2DPolyPolygon aCandidate(pObjB->GetPathPoly());
+ aCandidate = basegfx::utils::correctOrientations(aCandidate);
+ aPolyB.append(aCandidate);
+ }
+ }
+
+ if(aPolyA.count() && aPolyA.isClosed() && aPolyB.count())
+ {
+ // poly A is the clipregion, clip poly b against it. Algo depends on
+ // poly b being closed.
+ basegfx::B2DPolyPolygon aResult(basegfx::utils::clipPolyPolygonOnPolyPolygon(aPolyB, aPolyA));
+
+ for(auto const& rPolygon : aResult)
+ {
+ int nR = comphelper::rng::uniform_int_distribution(0, 254);
+ int nG = comphelper::rng::uniform_int_distribution(0, 254);
+ int nB = comphelper::rng::uniform_int_distribution(0, 254);
+ Color aColor(nR, nG, nB);
+ impPaintStrokePolygon(rPolygon, rOutDev, aColor);
+ }
+ }
+ }
+ }
+ }
+} // end of anonymous namespace
+#endif // CLIPPER_TEST
+
+
+void SdrPageWindow::RedrawAll( sdr::contact::ViewObjectContactRedirector* pRedirector )
+{
+ // set Redirector
+ GetObjectContact().SetViewObjectContactRedirector(pRedirector);
+
+ // set PaintingPageView
+ const SdrView& rView = mpImpl->mrPageView.GetView();
+ SdrModel& rModel = *(rView.GetModel());
+
+ // get to be processed layers
+ const bool bPrinter(GetPaintWindow().OutputToPrinter());
+ SdrLayerIDSet aProcessLayers = bPrinter ? mpImpl->mrPageView.GetPrintableLayers() : mpImpl->mrPageView.GetVisibleLayers();
+
+ // create PaintInfoRec; use Rectangle only temporarily
+ const vcl::Region& rRegion = GetPaintWindow().GetRedrawRegion();
+
+ // create processing data
+ sdr::contact::DisplayInfo aDisplayInfo;
+
+ // Draw all layers. do NOT draw form layer from CompleteRedraw, this is done separately
+ // as a single layer paint
+ const SdrLayerAdmin& rLayerAdmin = rModel.GetLayerAdmin();
+ const SdrLayerID nControlLayerId = rLayerAdmin.GetLayerID(rLayerAdmin.GetControlLayerName());
+ aProcessLayers.Clear(nControlLayerId);
+
+ // still something to paint?
+ if(!aProcessLayers.IsEmpty())
+ {
+ aDisplayInfo.SetProcessLayers(aProcessLayers);
+
+ // Set region as redraw area
+ aDisplayInfo.SetRedrawArea(rRegion);
+
+ // Draw/Impress
+ aDisplayInfo.SetPageProcessingActive(rView.IsPagePaintingAllowed()); // #i72889#
+
+ // paint page
+ GetObjectContact().ProcessDisplay(aDisplayInfo);
+ }
+
+ // reset redirector
+ GetObjectContact().SetViewObjectContactRedirector(nullptr);
+
+ // LineClip test
+#ifdef CLIPPER_TEST
+ if(true)
+ {
+ impTryTest(GetPageView(), GetPaintWindow().GetOutputDevice());
+ }
+#endif // CLIPPER_TEST
+}
+
+void SdrPageWindow::RedrawLayer(const SdrLayerID* pId,
+ sdr::contact::ViewObjectContactRedirector* pRedirector,
+ basegfx::B2IRectangle const*const pPageFrame)
+{
+ // set redirector
+ GetObjectContact().SetViewObjectContactRedirector(pRedirector);
+
+ // set PaintingPageView
+ const SdrView& rView = mpImpl->mrPageView.GetView();
+ SdrModel& rModel = *(rView.GetModel());
+
+ // get the layers to process
+ const bool bPrinter(GetPaintWindow().OutputToPrinter());
+ SdrLayerIDSet aProcessLayers = bPrinter ? mpImpl->mrPageView.GetPrintableLayers() : mpImpl->mrPageView.GetVisibleLayers();
+
+ // is the given layer visible at all?
+ if(aProcessLayers.IsSet(*pId))
+ {
+ // find out if we are painting the ControlLayer
+ const SdrLayerAdmin& rLayerAdmin = rModel.GetLayerAdmin();
+ const SdrLayerID nControlLayerId = rLayerAdmin.GetLayerID(rLayerAdmin.GetControlLayerName());
+ const bool bControlLayerProcessingActive(nControlLayerId == *pId);
+
+ // create PaintInfoRec, use Rectangle only temporarily
+ const vcl::Region& rRegion = GetPaintWindow().GetRedrawRegion();
+
+ // create processing data
+ sdr::contact::DisplayInfo aDisplayInfo;
+
+ // is it the control layer? If Yes, set flag
+ aDisplayInfo.SetControlLayerProcessingActive(bControlLayerProcessingActive);
+
+ // Draw just the one given layer
+ aProcessLayers.ClearAll();
+ aProcessLayers.Set(*pId);
+
+ aDisplayInfo.SetProcessLayers(aProcessLayers);
+
+ // Set region as redraw area
+ aDisplayInfo.SetRedrawArea(rRegion);
+
+ // Writer or calc, coming from original RedrawOneLayer.
+ // #i72889# no page painting for layer painting
+ aDisplayInfo.SetPageProcessingActive(false);
+
+ if (pPageFrame) // Writer page frame for anchor based clipping
+ {
+ aDisplayInfo.SetWriterPageFrame(*pPageFrame);
+ }
+
+ // paint page
+ GetObjectContact().ProcessDisplay(aDisplayInfo);
+ }
+
+ // reset redirector
+ GetObjectContact().SetViewObjectContactRedirector(nullptr);
+}
+
+// Invalidate call, used from ObjectContact(OfPageView) in InvalidatePartOfView(...)
+void SdrPageWindow::InvalidatePageWindow(const basegfx::B2DRange& rRange)
+{
+ if (GetPageView().IsVisible() && GetPaintWindow().OutputToWindow())
+ {
+ OutputDevice& rWindow(GetPaintWindow().GetOutputDevice());
+ basegfx::B2DRange aDiscreteRange(rRange);
+ aDiscreteRange.transform(rWindow.GetViewTransformation());
+
+ if (SvtOptionsDrawinglayer::IsAntiAliasing())
+ {
+ // invalidate one discrete unit more under the assumption that AA
+ // needs one pixel more
+ aDiscreteRange.grow(1.0);
+ }
+
+ // If the shapes use negative X coordinates, make them positive before sending
+ // the invalidation rectangle.
+ bool bNegativeX = mpImpl->mrPageView.GetView().IsNegativeX();
+
+ const tools::Rectangle aVCLDiscreteRectangle(
+ static_cast<tools::Long>(bNegativeX ? std::max(0.0, ceil(-aDiscreteRange.getMaxX())) : floor(aDiscreteRange.getMinX())),
+ static_cast<tools::Long>(floor(aDiscreteRange.getMinY())),
+ static_cast<tools::Long>(bNegativeX ? std::max(0.0, floor(-aDiscreteRange.getMinX())) : ceil(aDiscreteRange.getMaxX())),
+ static_cast<tools::Long>(ceil(aDiscreteRange.getMaxY())));
+
+ const bool bWasMapModeEnabled(rWindow.IsMapModeEnabled());
+ rWindow.EnableMapMode(false);
+ GetPageView().GetView().InvalidateOneWin(rWindow, aVCLDiscreteRectangle);
+ rWindow.EnableMapMode(bWasMapModeEnabled);
+ }
+ else if (comphelper::LibreOfficeKit::isActive())
+ {
+ // we don't really have to have a paint window with LOK; OTOH we know
+ // that the drawinglayer units are 100ths of mm, so they are easy to
+ // convert to twips
+
+ // If the shapes use negative X coordinates, make them positive before sending
+ // the invalidation rectangle.
+ bool bNegativeX = mpImpl->mrPageView.GetView().IsNegativeX();
+ const tools::Rectangle aRect100thMM(
+ static_cast<tools::Long>(bNegativeX ? std::max(0.0, ceil(-rRange.getMaxX())) : floor(rRange.getMinX())),
+ static_cast<tools::Long>(floor(rRange.getMinY())),
+ static_cast<tools::Long>(bNegativeX ? std::max(0.0, floor(-rRange.getMinX())) : ceil(rRange.getMaxX())),
+ static_cast<tools::Long>(ceil(rRange.getMaxY())));
+
+ const tools::Rectangle aRectTwips = o3tl::convert(aRect100thMM, o3tl::Length::mm100, o3tl::Length::twip);
+
+ if (SfxViewShell* pViewShell = SfxViewShell::Current())
+ SfxLokHelper::notifyInvalidation(pViewShell, &aRectTwips);
+ }
+}
+
+// ObjectContact section
+const sdr::contact::ObjectContact& SdrPageWindow::GetObjectContact() const
+{
+ if (!mpImpl->mpObjectContact)
+ {
+ mpImpl->mpObjectContact = GetPageView().GetView().createViewSpecificObjectContact(
+ const_cast<SdrPageWindow&>(*this),
+ "svx::svdraw::SdrPageWindow mpObjectContact");
+ }
+
+ return *mpImpl->mpObjectContact;
+}
+
+sdr::contact::ObjectContact& SdrPageWindow::GetObjectContact()
+{
+ if (!mpImpl->mpObjectContact)
+ {
+ mpImpl->mpObjectContact = GetPageView().GetView().createViewSpecificObjectContact(
+ *this,
+ "svx::svdraw::SdrPageWindow mpObjectContact" );
+ }
+
+ return *mpImpl->mpObjectContact;
+}
+
+bool SdrPageWindow::HasObjectContact() const
+{
+ return mpImpl->mpObjectContact != nullptr;
+}
+
+// #i26631#
+void SdrPageWindow::ResetObjectContact()
+{
+ if (mpImpl->mpObjectContact)
+ {
+ delete mpImpl->mpObjectContact;
+ mpImpl->mpObjectContact = nullptr;
+ }
+}
+
+void SdrPageWindow::SetDesignMode( bool _bDesignMode ) const
+{
+ const sdr::contact::ObjectContactOfPageView* pOC = dynamic_cast< const sdr::contact::ObjectContactOfPageView* >( &GetObjectContact() );
+ DBG_ASSERT( pOC, "SdrPageWindow::SetDesignMode: invalid object contact!" );
+ if ( pOC )
+ pOC->SetUNOControlsDesignMode( _bDesignMode );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/sdrpaintwindow.cxx b/svx/source/svdraw/sdrpaintwindow.cxx
new file mode 100644
index 000000000..6c3b328ba
--- /dev/null
+++ b/svx/source/svdraw/sdrpaintwindow.cxx
@@ -0,0 +1,346 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <comphelper/lok.hxx>
+#include <osl/diagnose.h>
+#include <svx/sdrpaintwindow.hxx>
+#include <sdr/overlay/overlaymanagerbuffered.hxx>
+#include <svx/svdpntv.hxx>
+#include <vcl/gdimtf.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/window.hxx>
+#include <svtools/optionsdrawinglayer.hxx>
+#include <set>
+#include <vector>
+
+namespace {
+
+//rhbz#1007697 do this in two loops, one to collect the candidates
+//and another to update them because updating a candidate can
+//trigger the candidate to be deleted, so asking for its
+//sibling after that is going to fail hard
+class CandidateMgr
+{
+ std::vector<VclPtr<vcl::Window> > m_aCandidates;
+ std::set<VclPtr<vcl::Window> > m_aDeletedCandidates;
+ DECL_LINK(WindowEventListener, VclWindowEvent&, void);
+public:
+ void PaintTransparentChildren(vcl::Window const & rWindow, tools::Rectangle const& rPixelRect);
+ ~CandidateMgr();
+};
+
+}
+
+IMPL_LINK(CandidateMgr, WindowEventListener, VclWindowEvent&, rEvent, void)
+{
+ vcl::Window* pWindow = rEvent.GetWindow();
+ if (rEvent.GetId() == VclEventId::ObjectDying)
+ {
+ m_aDeletedCandidates.insert(pWindow);
+ }
+}
+
+CandidateMgr::~CandidateMgr()
+{
+ for (VclPtr<vcl::Window>& pCandidate : m_aCandidates)
+ {
+ if (m_aDeletedCandidates.find(pCandidate) != m_aDeletedCandidates.end())
+ continue;
+ pCandidate->RemoveEventListener(LINK(this, CandidateMgr, WindowEventListener));
+ }
+}
+
+void PaintTransparentChildren(vcl::Window const & rWindow, tools::Rectangle const& rPixelRect)
+{
+ if (!rWindow.IsChildTransparentModeEnabled())
+ return;
+
+ CandidateMgr aManager;
+ aManager.PaintTransparentChildren(rWindow, rPixelRect);
+}
+
+void CandidateMgr::PaintTransparentChildren(vcl::Window const & rWindow, tools::Rectangle const& rPixelRect)
+{
+ vcl::Window * pCandidate = rWindow.GetWindow( GetWindowType::FirstChild );
+ while (pCandidate)
+ {
+ if (pCandidate->IsPaintTransparent())
+ {
+ const tools::Rectangle aCandidatePosSizePixel(
+ pCandidate->GetPosPixel(),
+ pCandidate->GetSizePixel());
+
+ if (aCandidatePosSizePixel.Overlaps(rPixelRect))
+ {
+ m_aCandidates.emplace_back(pCandidate);
+ pCandidate->AddEventListener(LINK(this, CandidateMgr, WindowEventListener));
+ }
+ }
+ pCandidate = pCandidate->GetWindow( GetWindowType::Next );
+ }
+
+ for (const auto& rpCandidate : m_aCandidates)
+ {
+ pCandidate = rpCandidate.get();
+ if (m_aDeletedCandidates.find(pCandidate) != m_aDeletedCandidates.end())
+ continue;
+ //rhbz#1007697 this can cause the window itself to be
+ //deleted. So we are listening to see if that happens
+ //and if so, then skip the update
+ pCandidate->Invalidate(InvalidateFlags::NoTransparent|InvalidateFlags::Children);
+ // important: actually paint the child here!
+ if (m_aDeletedCandidates.find(pCandidate) != m_aDeletedCandidates.end())
+ continue;
+ pCandidate->PaintImmediately();
+ }
+}
+
+SdrPreRenderDevice::SdrPreRenderDevice(OutputDevice& rOriginal)
+: mpOutputDevice(&rOriginal),
+ mpPreRenderDevice(VclPtr<VirtualDevice>::Create())
+{
+}
+
+SdrPreRenderDevice::~SdrPreRenderDevice()
+{
+ mpPreRenderDevice.disposeAndClear();
+}
+
+void SdrPreRenderDevice::PreparePreRenderDevice()
+{
+ // compare size of mpPreRenderDevice with size of visible area
+ if(mpPreRenderDevice->GetOutputSizePixel() != mpOutputDevice->GetOutputSizePixel())
+ {
+ mpPreRenderDevice->SetOutputSizePixel(mpOutputDevice->GetOutputSizePixel());
+ }
+
+ // Also compare the MapModes for zoom/scroll changes
+ if(mpPreRenderDevice->GetMapMode() != mpOutputDevice->GetMapMode())
+ {
+ mpPreRenderDevice->SetMapMode(mpOutputDevice->GetMapMode());
+ }
+
+ // #i29186#
+ mpPreRenderDevice->SetDrawMode(mpOutputDevice->GetDrawMode());
+ mpPreRenderDevice->SetSettings(mpOutputDevice->GetSettings());
+}
+
+void SdrPreRenderDevice::OutputPreRenderDevice(const vcl::Region& rExpandedRegion)
+{
+ // region to pixels
+ const vcl::Region aRegionPixel(mpOutputDevice->LogicToPixel(rExpandedRegion));
+ //RegionHandle aRegionHandle(aRegionPixel.BeginEnumRects());
+ //Rectangle aRegionRectanglePixel;
+
+ // MapModes off
+ bool bMapModeWasEnabledDest(mpOutputDevice->IsMapModeEnabled());
+ bool bMapModeWasEnabledSource(mpPreRenderDevice->IsMapModeEnabled());
+ mpOutputDevice->EnableMapMode(false);
+ mpPreRenderDevice->EnableMapMode(false);
+
+ RectangleVector aRectangles;
+ aRegionPixel.GetRegionRectangles(aRectangles);
+
+ for(const auto& rRect : aRectangles)
+ {
+ // for each rectangle, copy the area
+ const Point aTopLeft(rRect.TopLeft());
+ const Size aSize(rRect.GetSize());
+
+ mpOutputDevice->DrawOutDev(
+ aTopLeft, aSize,
+ aTopLeft, aSize,
+ *mpPreRenderDevice);
+ }
+
+ mpOutputDevice->EnableMapMode(bMapModeWasEnabledDest);
+ mpPreRenderDevice->EnableMapMode(bMapModeWasEnabledSource);
+}
+
+void SdrPaintView::InitOverlayManager(rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager)
+{
+ Color aColA(SvtOptionsDrawinglayer::GetStripeColorA());
+ Color aColB(SvtOptionsDrawinglayer::GetStripeColorB());
+
+ if (Application::GetSettings().GetStyleSettings().GetHighContrastMode())
+ {
+ aColA = aColB = Application::GetSettings().GetStyleSettings().GetHighlightColor();
+ aColB.Invert();
+ }
+
+ xOverlayManager->setStripeColorA(aColA);
+ xOverlayManager->setStripeColorB(aColB);
+ xOverlayManager->setStripeLengthPixel(SvtOptionsDrawinglayer::GetStripeLength());
+}
+
+rtl::Reference<sdr::overlay::OverlayManager> SdrPaintView::CreateOverlayManager(OutputDevice& rOutputDevice) const
+{
+ rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager;
+ // is it a window?
+ if (OUTDEV_WINDOW == rOutputDevice.GetOutDevType())
+ {
+ vcl::Window* pWindow = rOutputDevice.GetOwnerWindow();
+ // decide which OverlayManager to use
+ if (IsBufferedOverlayAllowed() && !pWindow->SupportsDoubleBuffering())
+ {
+ // buffered OverlayManager, buffers its background and refreshes from there
+ // for pure overlay changes (no system redraw). The 3rd parameter specifies
+ // whether that refresh itself will use a 2nd vdev to avoid flickering.
+ // Also hand over the old OverlayManager if existent; this means to take over
+ // the registered OverlayObjects from it
+ xOverlayManager = sdr::overlay::OverlayManagerBuffered::create(rOutputDevice);
+ }
+ else
+ {
+ // unbuffered OverlayManager, just invalidates places where changes
+ // take place
+ // Also hand over the old OverlayManager if existent; this means to take over
+ // the registered OverlayObjects from it
+ xOverlayManager = sdr::overlay::OverlayManager::create(rOutputDevice);
+ }
+
+ OSL_ENSURE(xOverlayManager.is(), "SdrPaintWindow::SdrPaintWindow: Could not allocate an overlayManager (!)");
+
+ // Request a repaint so that the buffered overlay manager fills
+ // its buffer properly. This is a workaround for missing buffer
+ // updates.
+ if (!comphelper::LibreOfficeKit::isActive())
+ {
+ pWindow->Invalidate();
+ }
+
+ InitOverlayManager(xOverlayManager);
+ }
+ return xOverlayManager;
+}
+
+void SdrPaintWindow::impCreateOverlayManager()
+{
+ // not yet one created?
+ if(!mxOverlayManager.is())
+ mxOverlayManager = mrPaintView.CreateOverlayManager(GetOutputDevice());
+}
+
+SdrPaintWindow::SdrPaintWindow(SdrPaintView& rNewPaintView, OutputDevice& rOut, vcl::Window* pWindow)
+: mpOutputDevice(&rOut),
+ mpWindow(pWindow),
+ mrPaintView(rNewPaintView),
+ mbTemporaryTarget(false), // #i72889#
+ mbOutputToWindow(OUTDEV_WINDOW == mpOutputDevice->GetOutDevType()),
+ mpPatched(nullptr)
+{
+}
+
+SdrPaintWindow::~SdrPaintWindow()
+{
+ mxOverlayManager.clear();
+
+ DestroyPreRenderDevice();
+}
+
+rtl::Reference< sdr::overlay::OverlayManager > const & SdrPaintWindow::GetOverlayManager() const
+{
+ if(!mxOverlayManager.is())
+ {
+ // Create buffered overlay manager by default.
+ const_cast< SdrPaintWindow* >(this)->impCreateOverlayManager();
+ }
+
+ return mxOverlayManager;
+}
+
+tools::Rectangle SdrPaintWindow::GetVisibleArea() const
+{
+ Size aVisSizePixel(GetOutputDevice().GetOutputSizePixel());
+ return GetOutputDevice().PixelToLogic(tools::Rectangle(Point(0,0), aVisSizePixel));
+}
+
+bool SdrPaintWindow::OutputToRecordingMetaFile() const
+{
+ GDIMetaFile* pMetaFile = mpOutputDevice->GetConnectMetaFile();
+ return (pMetaFile && pMetaFile->IsRecord() && !pMetaFile->IsPause());
+}
+
+void SdrPaintWindow::PreparePreRenderDevice()
+{
+ const bool bPrepareBufferedOutput(
+ mrPaintView.IsBufferedOutputAllowed()
+ && !OutputToPrinter()
+ && !mpOutputDevice->IsVirtual()
+ && !OutputToRecordingMetaFile());
+
+ if(bPrepareBufferedOutput)
+ {
+ if(!mpPreRenderDevice)
+ {
+ mpPreRenderDevice.reset(new SdrPreRenderDevice(*mpOutputDevice));
+ }
+ }
+ else
+ {
+ DestroyPreRenderDevice();
+ }
+
+ if(mpPreRenderDevice)
+ {
+ mpPreRenderDevice->PreparePreRenderDevice();
+ }
+}
+
+void SdrPaintWindow::DestroyPreRenderDevice()
+{
+ mpPreRenderDevice.reset();
+}
+
+void SdrPaintWindow::OutputPreRenderDevice(const vcl::Region& rExpandedRegion)
+{
+ if(mpPreRenderDevice)
+ {
+ mpPreRenderDevice->OutputPreRenderDevice(rExpandedRegion);
+ }
+}
+
+// #i73602# add flag if buffer shall be used
+void SdrPaintWindow::DrawOverlay(const vcl::Region& rRegion)
+{
+ // ## force creation of OverlayManager since the first repaint needs to
+ // save the background to get a controlled start into overlay mechanism
+ impCreateOverlayManager();
+
+ if(mxOverlayManager.is() && !OutputToPrinter())
+ {
+ if(mpPreRenderDevice)
+ {
+ mxOverlayManager->completeRedraw(rRegion, &mpPreRenderDevice->GetPreRenderDevice());
+ }
+ else
+ {
+ mxOverlayManager->completeRedraw(rRegion);
+ }
+ }
+}
+
+
+void SdrPaintWindow::SetRedrawRegion(const vcl::Region& rNew)
+{
+ maRedrawRegion = rNew;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/sdrundomanager.cxx b/svx/source/svdraw/sdrundomanager.cxx
new file mode 100644
index 000000000..3d5fde475
--- /dev/null
+++ b/svx/source/svdraw/sdrundomanager.cxx
@@ -0,0 +1,184 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/sdrundomanager.hxx>
+#include <svx/svdundo.hxx>
+#include <sfx2/objsh.hxx>
+#include <svl/hint.hxx>
+
+SdrUndoManager::SdrUndoManager()
+ : EditUndoManager(20 /*nMaxUndoActionCount*/)
+ , mpLastUndoActionBeforeTextEdit(nullptr)
+ , mnRedoActionCountBeforeTextEdit(0)
+ , mbEndTextEditTriggeredFromUndo(false)
+ , m_pDocSh(nullptr)
+{
+}
+
+SdrUndoManager::~SdrUndoManager() {}
+
+bool SdrUndoManager::Undo()
+{
+ if (isTextEditActive())
+ {
+ bool bRetval(false);
+
+ // we are in text edit mode
+ if (GetUndoActionCount() && mpLastUndoActionBeforeTextEdit != GetUndoAction())
+ {
+ // there is an undo action for text edit, trigger it
+ bRetval = EditUndoManager::Undo();
+ }
+ else
+ {
+ // no more text edit undo, end text edit
+ mbEndTextEditTriggeredFromUndo = true;
+ maEndTextEditHdl.Call(this);
+ mbEndTextEditTriggeredFromUndo = false;
+ }
+
+ return bRetval;
+ }
+ else
+ {
+ // no undo triggered up to now, trigger local one
+ return SfxUndoManager::Undo();
+ }
+}
+
+bool SdrUndoManager::Redo()
+{
+ bool bRetval(false);
+ bool bClearRedoStack(false);
+
+ if (isTextEditActive())
+ {
+ // we are in text edit mode
+ bRetval = EditUndoManager::Redo();
+ }
+
+ if (!bRetval)
+ {
+ // Check if the current and thus to-be undone UndoAction is a SdrUndoDiagramModelData action
+ const bool bCurrentIsDiagramChange(
+ GetRedoActionCount()
+ && nullptr != dynamic_cast<SdrUndoDiagramModelData*>(GetRedoAction()));
+
+ // no redo triggered up to now, trigger local one
+ bRetval = SfxUndoManager::Redo();
+
+ // it was a SdrUndoDiagramModelData action and we have more Redo actions
+ if (bCurrentIsDiagramChange && GetRedoActionCount())
+ {
+ const bool bNextIsDiagramChange(
+ nullptr != dynamic_cast<SdrUndoDiagramModelData*>(GetRedoAction()));
+
+ // We have more Redo-actions and the 'next' one to be executed is *not* a
+ // SdrUndoDiagramModelData-action. This means that the already executed
+ // one had done a re-Layout/Re-create of the Diagram XShape/SdrObject
+ // representation based on the restored Diagram ModelData. When the next
+ // Redo action is something else (and thus will not itself re-create
+ // XShapes/SdrShapes) it may be that it is an UnGroup/Delete where a former
+ // as-content-of-Diagram created XShape/SdrShape is referenced, an action
+ // that references a XShape/SdrShape by pointer/reference. That
+ // pointer/reference *cannot* be valid anymore (now).
+
+ // The problem here is that Undo/Redo actions historically reference
+ // XShapes/SdrShapes by pointer/reference, e.g. deleting means: remove
+ // from an SdrObjList and add to an Undo action. I is *not*
+ // address/incarnation-invariant in the sense to remember e.g. to
+ // remove the Nth object in the list (that would work).
+
+ // It might be possible to solve/correct this better, but since it's
+ // a rare corner case just avoid the possible crash when continuing Redos
+ // by clearing the Redo-Stack here as a consequence
+ bClearRedoStack = !bNextIsDiagramChange;
+ }
+ }
+
+ if (bClearRedoStack)
+ {
+ // clear Redo-Stack (explanation see above)
+ ClearRedo();
+ }
+
+ return bRetval;
+}
+
+void SdrUndoManager::Clear()
+{
+ if (isTextEditActive())
+ {
+ while (GetUndoActionCount() && mpLastUndoActionBeforeTextEdit != GetUndoAction())
+ {
+ RemoveLastUndoAction();
+ }
+
+ // urgently needed: RemoveLastUndoAction does NOT correct the Redo stack by itself (!)
+ ClearRedo();
+ }
+ else
+ {
+ // call parent
+ EditUndoManager::Clear();
+ }
+}
+
+void SdrUndoManager::SetEndTextEditHdl(const Link<SdrUndoManager*, void>& rLink)
+{
+ maEndTextEditHdl = rLink;
+
+ if (isTextEditActive())
+ {
+ // text edit start, remember last non-textedit action for later cleanup
+ mpLastUndoActionBeforeTextEdit = GetUndoActionCount() ? GetUndoAction() : nullptr;
+ mnRedoActionCountBeforeTextEdit = GetRedoActionCount();
+ }
+ else
+ {
+ // text edit ends, pop all textedit actions up to the remembered non-textedit action from the start
+ // to set back the UndoManager to the state before text edit started. If that action is already gone
+ // (due to being removed from the undo stack in the meantime), all need to be removed anyways
+ while (GetUndoActionCount() && mpLastUndoActionBeforeTextEdit != GetUndoAction())
+ {
+ RemoveLastUndoAction();
+ }
+
+ // urgently needed: RemoveLastUndoAction does NOT correct the Redo stack by itself (!)
+ ClearRedo();
+
+ // forget marker again
+ mpLastUndoActionBeforeTextEdit = nullptr;
+ mnRedoActionCountBeforeTextEdit = 0;
+ }
+}
+
+bool SdrUndoManager::isTextEditActive() const { return maEndTextEditHdl.IsSet(); }
+
+void SdrUndoManager::SetDocShell(SfxObjectShell* pDocShell) { m_pDocSh = pDocShell; }
+
+void SdrUndoManager::EmptyActionsChanged()
+{
+ if (m_pDocSh)
+ {
+ m_pDocSh->Broadcast(SfxHint(SfxHintId::DocumentRepair));
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/selectioncontroller.cxx b/svx/source/svdraw/selectioncontroller.cxx
new file mode 100644
index 000000000..5f6f51312
--- /dev/null
+++ b/svx/source/svdraw/selectioncontroller.cxx
@@ -0,0 +1,124 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <svx/selectioncontroller.hxx>
+
+namespace sdr
+{
+
+bool SelectionController::onKeyInput(const KeyEvent& /*rKEvt*/, vcl::Window* /*pWin*/)
+{
+ return false;
+}
+
+bool SelectionController::onMouseButtonDown(const MouseEvent& /*rMEvt*/, vcl::Window* /*pWin*/)
+{
+ return false;
+}
+
+bool SelectionController::onMouseButtonUp(const MouseEvent& /*rMEvt*/, vcl::Window* /*pWin*/)
+{
+ return false;
+}
+
+bool SelectionController::onMouseMove(const MouseEvent& /*rMEvt*/, vcl::Window* /*pWin*/)
+{
+ return false;
+}
+
+void SelectionController::onSelectionHasChanged()
+{
+}
+
+void SelectionController::onSelectAll()
+{
+}
+
+void SelectionController::GetState( SfxItemSet& /*rSet*/ )
+{
+}
+
+void SelectionController::Execute( SfxRequest& /*rReq*/ )
+{
+}
+
+bool SelectionController::DeleteMarked()
+{
+ return false;
+}
+
+bool SelectionController::GetAttributes(SfxItemSet& /*rTargetSet*/, bool /*bOnlyHardAttr*/) const
+{
+ return false;
+}
+
+bool SelectionController::SetAttributes(const SfxItemSet& /*rSet*/, bool /*bReplaceAll*/)
+{
+ return false;
+}
+
+bool SelectionController::GetStyleSheet( SfxStyleSheet* &/*rpStyleSheet*/ ) const
+{
+ return false;
+}
+
+bool SelectionController::SetStyleSheet( SfxStyleSheet* /*pStyleSheet*/, bool /*bDontRemoveHardAttr*/ )
+{
+ return false;
+}
+
+SdrObject* SelectionController::GetMarkedSdrObjClone( SdrModel& /*rTargetModel*/ )
+{
+ return nullptr;
+}
+
+bool SelectionController::PasteObjModel( const SdrModel& /*rModel*/ )
+{
+ return false;
+}
+
+bool SelectionController::ApplyFormatPaintBrush( SfxItemSet& /*rFormatSet*/, bool /*bNoCharacterFormats*/, bool /*bNoParagraphFormats*/ )
+{
+ return false;
+}
+
+bool SelectionController::hasSelectedCells() const
+{
+ return false;
+}
+
+void SelectionController::getSelectedCells(table::CellPos& /*rFirstPos*/, table::CellPos& /*rLastPos*/)
+{
+}
+
+bool SelectionController::setCursorLogicPosition(const Point& /*rPosition*/, bool /*bPoint*/)
+{
+ return false;
+}
+
+
+bool SelectionController::ChangeFontSize(bool /*bGrow*/, const FontList* /*pFontList*/)
+{
+ return false;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdattr.cxx b/svx/source/svdraw/svdattr.cxx
new file mode 100644
index 000000000..c0057b6ae
--- /dev/null
+++ b/svx/source/svdraw/svdattr.cxx
@@ -0,0 +1,2042 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/drawing/TextFitToSizeType.hpp>
+#include <com/sun/star/drawing/TextHorizontalAdjust.hpp>
+#include <com/sun/star/drawing/TextVerticalAdjust.hpp>
+#include <com/sun/star/drawing/TextAnimationKind.hpp>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/drawing/TextAnimationDirection.hpp>
+#include <com/sun/star/drawing/ConnectorType.hpp>
+#include <com/sun/star/drawing/MeasureKind.hpp>
+#include <com/sun/star/drawing/MeasureTextHorzPos.hpp>
+#include <com/sun/star/drawing/MeasureTextVertPos.hpp>
+#include <com/sun/star/drawing/CircleKind.hpp>
+
+#include <editeng/boxitem.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/lineitem.hxx>
+#include <editeng/xmlcnitm.hxx>
+#include <editeng/writingmodeitem.hxx>
+#include <editeng/charrotateitem.hxx>
+#include <osl/diagnose.h>
+#include <i18nutil/unicode.hxx>
+#include <tools/bigint.hxx>
+#include <unotools/intlwrapper.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include <svx/sdgcpitm.hxx>
+#include <svx/sdtfchim.hxx>
+#include <svx/sdasitm.hxx>
+#include <sdgcoitm.hxx>
+#include <svx/sdggaitm.hxx>
+#include <sdginitm.hxx>
+#include <svx/sdgluitm.hxx>
+#include <svx/sdgmoitm.hxx>
+#include <sdgtritm.hxx>
+#include <svx/sdprcitm.hxx>
+#include <svx/sdtaaitm.hxx>
+#include <svx/sdtacitm.hxx>
+#include <svx/sdtaditm.hxx>
+#include <svx/sdtaiitm.hxx>
+#include <svx/sdtaitm.hxx>
+#include <svx/sdtakitm.hxx>
+#include <svx/sdtayitm.hxx>
+#include <svx/sdtfsitm.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdpool.hxx>
+#include <svx/svdtrans.hxx>
+#include <svx/svx3ditems.hxx>
+#include <svx/svxids.hrc>
+#include <sxallitm.hxx>
+#include <sxcaitm.hxx>
+#include <svx/sxcecitm.hxx>
+#include <svx/sxcgitm.hxx>
+#include <sxcikitm.hxx>
+#include <svx/sxcllitm.hxx>
+#include <svx/sxctitm.hxx>
+#include <svx/sxekitm.hxx>
+#include <svx/sxelditm.hxx>
+#include <svx/sxenditm.hxx>
+#include <sxfiitm.hxx>
+#include <sxlayitm.hxx>
+#include <sxlogitm.hxx>
+#include <svx/sxmbritm.hxx>
+#include <sxmfsitm.hxx>
+#include <sxmkitm.hxx>
+#include <sxmoitm.hxx>
+#include <sxmovitm.hxx>
+#include <sxmsitm.hxx>
+#include <sxmtaitm.hxx>
+#include <svx/sxmtfitm.hxx>
+#include <svx/sxmtpitm.hxx>
+#include <svx/sxmtritm.hxx>
+#include <svx/sxmuitm.hxx>
+#include <svx/xcolit.hxx>
+#include <sxoneitm.hxx>
+#include <sxopitm.hxx>
+#include <sxreaitm.hxx>
+#include <sxreoitm.hxx>
+#include <sxroaitm.hxx>
+#include <sxrooitm.hxx>
+#include <sxsaitm.hxx>
+#include <sxsalitm.hxx>
+#include <sxsiitm.hxx>
+#include <sxsoitm.hxx>
+#include <sxtraitm.hxx>
+#include <libxml/xmlwriter.h>
+
+using namespace ::com::sun::star;
+
+SdrItemPool::SdrItemPool(
+ SfxItemPool* _pMaster)
+: XOutdevItemPool(_pMaster)
+{
+ // prepare some constants
+ const Color aNullCol(COL_BLACK);
+ const sal_Int32 nDefEdgeDist(500); // Defaulting hard for Draw (100TH_MM) currently. MapMode will have to be taken into account in the future.
+
+ // init the non-persistent items
+ for(sal_uInt16 i(SDRATTR_NOTPERSIST_FIRST); i <= SDRATTR_NOTPERSIST_LAST; i++)
+ {
+ mpLocalItemInfos[i - SDRATTR_START]._bPoolable = false;
+ }
+
+ // init own PoolDefaults
+ std::vector<SfxPoolItem*>& rPoolDefaults = *mpLocalPoolDefaults;
+ rPoolDefaults[SDRATTR_SHADOW -SDRATTR_START]=new SdrOnOffItem(SDRATTR_SHADOW, false);
+ rPoolDefaults[SDRATTR_SHADOWCOLOR -SDRATTR_START]=new XColorItem(SDRATTR_SHADOWCOLOR, aNullCol);
+ rPoolDefaults[SDRATTR_SHADOWXDIST -SDRATTR_START]=new SdrMetricItem(SDRATTR_SHADOWXDIST, 0);
+ rPoolDefaults[SDRATTR_SHADOWYDIST -SDRATTR_START]=new SdrMetricItem(SDRATTR_SHADOWYDIST, 0);
+ rPoolDefaults[SDRATTR_SHADOWSIZEX -SDRATTR_START]=new SdrMetricItem(SDRATTR_SHADOWSIZEX, 100000);
+ rPoolDefaults[SDRATTR_SHADOWSIZEY -SDRATTR_START]=new SdrMetricItem(SDRATTR_SHADOWSIZEY, 100000);
+ rPoolDefaults[SDRATTR_SHADOWTRANSPARENCE-SDRATTR_START]=new SdrPercentItem(SDRATTR_SHADOWTRANSPARENCE, 0);
+ rPoolDefaults[SDRATTR_SHADOWBLUR -SDRATTR_START]=new SdrMetricItem(SDRATTR_SHADOWBLUR, 0);
+ rPoolDefaults[SDRATTR_SHADOW3D -SDRATTR_START]=new SfxVoidItem(SDRATTR_SHADOW3D );
+ rPoolDefaults[SDRATTR_SHADOWPERSP -SDRATTR_START]=new SfxVoidItem(SDRATTR_SHADOWPERSP );
+ rPoolDefaults[SDRATTR_CAPTIONTYPE -SDRATTR_START]=new SdrCaptionTypeItem ;
+ rPoolDefaults[SDRATTR_CAPTIONFIXEDANGLE-SDRATTR_START]=new SdrOnOffItem(SDRATTR_CAPTIONFIXEDANGLE, true);
+ rPoolDefaults[SDRATTR_CAPTIONANGLE -SDRATTR_START]=new SdrCaptionAngleItem ;
+ rPoolDefaults[SDRATTR_CAPTIONGAP -SDRATTR_START]=new SdrCaptionGapItem ;
+ rPoolDefaults[SDRATTR_CAPTIONESCDIR -SDRATTR_START]=new SdrCaptionEscDirItem ;
+ rPoolDefaults[SDRATTR_CAPTIONESCISREL -SDRATTR_START]=new SdrCaptionEscIsRelItem ;
+ rPoolDefaults[SDRATTR_CAPTIONESCREL -SDRATTR_START]=new SdrCaptionEscRelItem ;
+ rPoolDefaults[SDRATTR_CAPTIONESCABS -SDRATTR_START]=new SdrCaptionEscAbsItem ;
+ rPoolDefaults[SDRATTR_CAPTIONLINELEN -SDRATTR_START]=new SdrCaptionLineLenItem ;
+ rPoolDefaults[SDRATTR_CAPTIONFITLINELEN-SDRATTR_START]=new SdrCaptionFitLineLenItem;
+ rPoolDefaults[SDRATTR_CORNER_RADIUS -SDRATTR_START]=new SdrMetricItem(SDRATTR_CORNER_RADIUS, 0);
+ rPoolDefaults[SDRATTR_TEXT_MINFRAMEHEIGHT -SDRATTR_START]=new SdrMetricItem(SDRATTR_TEXT_MINFRAMEHEIGHT, 0);
+ rPoolDefaults[SDRATTR_TEXT_AUTOGROWHEIGHT -SDRATTR_START]=new SdrOnOffItem(SDRATTR_TEXT_AUTOGROWHEIGHT, true);
+ rPoolDefaults[SDRATTR_TEXT_FITTOSIZE -SDRATTR_START]=new SdrTextFitToSizeTypeItem;
+ rPoolDefaults[SDRATTR_TEXT_LEFTDIST -SDRATTR_START]=new SdrMetricItem(SDRATTR_TEXT_LEFTDIST, 0);
+ rPoolDefaults[SDRATTR_TEXT_RIGHTDIST -SDRATTR_START]=new SdrMetricItem(SDRATTR_TEXT_RIGHTDIST, 0);
+ rPoolDefaults[SDRATTR_TEXT_UPPERDIST -SDRATTR_START]=new SdrMetricItem(SDRATTR_TEXT_UPPERDIST, 0);
+ rPoolDefaults[SDRATTR_TEXT_LOWERDIST -SDRATTR_START]=new SdrMetricItem(SDRATTR_TEXT_LOWERDIST, 0);
+ rPoolDefaults[SDRATTR_TEXT_VERTADJUST -SDRATTR_START]=new SdrTextVertAdjustItem;
+ rPoolDefaults[SDRATTR_TEXT_MAXFRAMEHEIGHT -SDRATTR_START]=new SdrMetricItem(SDRATTR_TEXT_MAXFRAMEHEIGHT, 0);
+ rPoolDefaults[SDRATTR_TEXT_MINFRAMEWIDTH -SDRATTR_START]=new SdrMetricItem(SDRATTR_TEXT_MINFRAMEWIDTH, 0);
+ rPoolDefaults[SDRATTR_TEXT_MAXFRAMEWIDTH -SDRATTR_START]=new SdrMetricItem(SDRATTR_TEXT_MAXFRAMEWIDTH, 0);
+ rPoolDefaults[SDRATTR_TEXT_AUTOGROWWIDTH -SDRATTR_START]=new SdrOnOffItem(SDRATTR_TEXT_AUTOGROWWIDTH, false);
+ rPoolDefaults[SDRATTR_TEXT_HORZADJUST -SDRATTR_START]=new SdrTextHorzAdjustItem;
+ rPoolDefaults[SDRATTR_TEXT_ANIKIND -SDRATTR_START]=new SdrTextAniKindItem;
+ rPoolDefaults[SDRATTR_TEXT_ANIDIRECTION -SDRATTR_START]=new SdrTextAniDirectionItem;
+ rPoolDefaults[SDRATTR_TEXT_ANISTARTINSIDE -SDRATTR_START]=new SdrTextAniStartInsideItem;
+ rPoolDefaults[SDRATTR_TEXT_ANISTOPINSIDE -SDRATTR_START]=new SdrTextAniStopInsideItem;
+ rPoolDefaults[SDRATTR_TEXT_ANICOUNT -SDRATTR_START]=new SdrTextAniCountItem;
+ rPoolDefaults[SDRATTR_TEXT_ANIDELAY -SDRATTR_START]=new SdrTextAniDelayItem;
+ rPoolDefaults[SDRATTR_TEXT_ANIAMOUNT -SDRATTR_START]=new SdrTextAniAmountItem;
+ rPoolDefaults[SDRATTR_TEXT_CONTOURFRAME -SDRATTR_START]=new SdrOnOffItem(SDRATTR_TEXT_CONTOURFRAME, false);
+ rPoolDefaults[SDRATTR_XMLATTRIBUTES -SDRATTR_START]=new SvXMLAttrContainerItem( SDRATTR_XMLATTRIBUTES );
+ rPoolDefaults[SDRATTR_TEXT_CHAINNEXTNAME -SDRATTR_START]=new SfxStringItem(SDRATTR_TEXT_CHAINNEXTNAME, "");
+ rPoolDefaults[SDRATTR_TEXT_USEFIXEDCELLHEIGHT -SDRATTR_START]=new SdrTextFixedCellHeightItem;
+ rPoolDefaults[SDRATTR_TEXT_WORDWRAP -SDRATTR_START]=new SdrOnOffItem(SDRATTR_TEXT_WORDWRAP, true);
+ rPoolDefaults[SDRATTR_EDGEKIND -SDRATTR_START]=new SdrEdgeKindItem;
+ rPoolDefaults[SDRATTR_EDGENODE1HORZDIST-SDRATTR_START]=new SdrEdgeNode1HorzDistItem(nDefEdgeDist);
+ rPoolDefaults[SDRATTR_EDGENODE1VERTDIST-SDRATTR_START]=new SdrEdgeNode1VertDistItem(nDefEdgeDist);
+ rPoolDefaults[SDRATTR_EDGENODE2HORZDIST-SDRATTR_START]=new SdrEdgeNode2HorzDistItem(nDefEdgeDist);
+ rPoolDefaults[SDRATTR_EDGENODE2VERTDIST-SDRATTR_START]=new SdrEdgeNode2VertDistItem(nDefEdgeDist);
+ rPoolDefaults[SDRATTR_EDGENODE1GLUEDIST-SDRATTR_START]=new SdrEdgeNode1GlueDistItem;
+ rPoolDefaults[SDRATTR_EDGENODE2GLUEDIST-SDRATTR_START]=new SdrEdgeNode2GlueDistItem;
+ rPoolDefaults[SDRATTR_EDGELINEDELTACOUNT-SDRATTR_START]=new SdrEdgeLineDeltaCountItem;
+ rPoolDefaults[SDRATTR_EDGELINE1DELTA -SDRATTR_START]=new SdrMetricItem(SDRATTR_EDGELINE1DELTA, 0);
+ rPoolDefaults[SDRATTR_EDGELINE2DELTA -SDRATTR_START]=new SdrMetricItem(SDRATTR_EDGELINE2DELTA, 0);
+ rPoolDefaults[SDRATTR_EDGELINE3DELTA -SDRATTR_START]=new SdrMetricItem(SDRATTR_EDGELINE3DELTA, 0);
+ rPoolDefaults[SDRATTR_MEASUREKIND -SDRATTR_START]=new SdrMeasureKindItem;
+ rPoolDefaults[SDRATTR_MEASURETEXTHPOS -SDRATTR_START]=new SdrMeasureTextHPosItem;
+ rPoolDefaults[SDRATTR_MEASURETEXTVPOS -SDRATTR_START]=new SdrMeasureTextVPosItem;
+ rPoolDefaults[SDRATTR_MEASURELINEDIST -SDRATTR_START]=new SdrMetricItem(SDRATTR_MEASURELINEDIST, 800);
+ rPoolDefaults[SDRATTR_MEASUREHELPLINEOVERHANG -SDRATTR_START]=new SdrMetricItem(SDRATTR_MEASUREHELPLINEOVERHANG, 200);
+ rPoolDefaults[SDRATTR_MEASUREHELPLINEDIST -SDRATTR_START]=new SdrMetricItem(SDRATTR_MEASUREHELPLINEDIST, 100);
+ rPoolDefaults[SDRATTR_MEASUREHELPLINE1LEN -SDRATTR_START]=new SdrMetricItem(SDRATTR_MEASUREHELPLINE1LEN, 0);
+ rPoolDefaults[SDRATTR_MEASUREHELPLINE2LEN -SDRATTR_START]=new SdrMetricItem(SDRATTR_MEASUREHELPLINE2LEN, 0);
+ rPoolDefaults[SDRATTR_MEASUREBELOWREFEDGE -SDRATTR_START]=new SdrMeasureBelowRefEdgeItem;
+ rPoolDefaults[SDRATTR_MEASURETEXTROTA90 -SDRATTR_START]=new SdrMeasureTextRota90Item;
+ rPoolDefaults[SDRATTR_MEASURETEXTUPSIDEDOWN -SDRATTR_START]=new SdrMeasureTextUpsideDownItem;
+ rPoolDefaults[SDRATTR_MEASUREOVERHANG -SDRATTR_START]=new SdrMeasureOverhangItem(600);
+ rPoolDefaults[SDRATTR_MEASUREUNIT -SDRATTR_START]=new SdrMeasureUnitItem;
+ rPoolDefaults[SDRATTR_MEASURESCALE -SDRATTR_START]=new SdrMeasureScaleItem;
+ rPoolDefaults[SDRATTR_MEASURESHOWUNIT -SDRATTR_START]=new SdrYesNoItem(SDRATTR_MEASURESHOWUNIT, false);
+ rPoolDefaults[SDRATTR_MEASUREFORMATSTRING -SDRATTR_START]=new SdrMeasureFormatStringItem();
+ rPoolDefaults[SDRATTR_MEASURETEXTAUTOANGLE -SDRATTR_START]=new SdrMeasureTextAutoAngleItem();
+ rPoolDefaults[SDRATTR_MEASURETEXTAUTOANGLEVIEW-SDRATTR_START]=new SdrMeasureTextAutoAngleViewItem();
+ rPoolDefaults[SDRATTR_MEASURETEXTISFIXEDANGLE -SDRATTR_START]=new SdrMeasureTextIsFixedAngleItem();
+ rPoolDefaults[SDRATTR_MEASURETEXTFIXEDANGLE -SDRATTR_START]=new SdrMeasureTextFixedAngleItem();
+ rPoolDefaults[SDRATTR_MEASUREDECIMALPLACES -SDRATTR_START]=new SdrMeasureDecimalPlacesItem();
+ rPoolDefaults[SDRATTR_CIRCKIND -SDRATTR_START]=new SdrCircKindItem;
+ rPoolDefaults[SDRATTR_CIRCSTARTANGLE-SDRATTR_START]=new SdrAngleItem(SDRATTR_CIRCSTARTANGLE, 0_deg100);
+ rPoolDefaults[SDRATTR_CIRCENDANGLE -SDRATTR_START]=new SdrAngleItem(SDRATTR_CIRCENDANGLE, 36000_deg100);
+ rPoolDefaults[SDRATTR_OBJMOVEPROTECT -SDRATTR_START]=new SdrYesNoItem(SDRATTR_OBJMOVEPROTECT, false);
+ rPoolDefaults[SDRATTR_OBJSIZEPROTECT -SDRATTR_START]=new SdrYesNoItem(SDRATTR_OBJSIZEPROTECT, false);
+ rPoolDefaults[SDRATTR_OBJPRINTABLE -SDRATTR_START]=new SdrObjPrintableItem;
+ rPoolDefaults[SDRATTR_OBJVISIBLE -SDRATTR_START]=new SdrObjVisibleItem;
+ rPoolDefaults[SDRATTR_LAYERID -SDRATTR_START]=new SdrLayerIdItem(SdrLayerID(0));
+ rPoolDefaults[SDRATTR_LAYERNAME -SDRATTR_START]=new SdrLayerNameItem;
+ rPoolDefaults[SDRATTR_OBJECTNAME -SDRATTR_START]=new SfxStringItem(SDRATTR_OBJECTNAME);
+ rPoolDefaults[SDRATTR_ALLPOSITIONX -SDRATTR_START]=new SdrAllPositionXItem;
+ rPoolDefaults[SDRATTR_ALLPOSITIONY -SDRATTR_START]=new SdrAllPositionYItem;
+ rPoolDefaults[SDRATTR_ALLSIZEWIDTH -SDRATTR_START]=new SdrAllSizeWidthItem;
+ rPoolDefaults[SDRATTR_ALLSIZEHEIGHT -SDRATTR_START]=new SdrAllSizeHeightItem;
+ rPoolDefaults[SDRATTR_ONEPOSITIONX -SDRATTR_START]=new SdrOnePositionXItem;
+ rPoolDefaults[SDRATTR_ONEPOSITIONY -SDRATTR_START]=new SdrOnePositionYItem;
+ rPoolDefaults[SDRATTR_ONESIZEWIDTH -SDRATTR_START]=new SdrOneSizeWidthItem;
+ rPoolDefaults[SDRATTR_ONESIZEHEIGHT -SDRATTR_START]=new SdrOneSizeHeightItem;
+ rPoolDefaults[SDRATTR_LOGICSIZEWIDTH -SDRATTR_START]=new SdrLogicSizeWidthItem;
+ rPoolDefaults[SDRATTR_LOGICSIZEHEIGHT-SDRATTR_START]=new SdrLogicSizeHeightItem;
+ rPoolDefaults[SDRATTR_ROTATEANGLE -SDRATTR_START]=new SdrAngleItem(SDRATTR_ROTATEANGLE, 0_deg100);
+ rPoolDefaults[SDRATTR_SHEARANGLE -SDRATTR_START]=new SdrShearAngleItem;
+ rPoolDefaults[SDRATTR_MOVEX -SDRATTR_START]=new SdrMoveXItem;
+ rPoolDefaults[SDRATTR_MOVEY -SDRATTR_START]=new SdrMoveYItem;
+ rPoolDefaults[SDRATTR_RESIZEXONE -SDRATTR_START]=new SdrResizeXOneItem;
+ rPoolDefaults[SDRATTR_RESIZEYONE -SDRATTR_START]=new SdrResizeYOneItem;
+ rPoolDefaults[SDRATTR_ROTATEONE -SDRATTR_START]=new SdrRotateOneItem;
+ rPoolDefaults[SDRATTR_HORZSHEARONE -SDRATTR_START]=new SdrHorzShearOneItem;
+ rPoolDefaults[SDRATTR_VERTSHEARONE -SDRATTR_START]=new SdrVertShearOneItem;
+ rPoolDefaults[SDRATTR_RESIZEXALL -SDRATTR_START]=new SdrResizeXAllItem;
+ rPoolDefaults[SDRATTR_RESIZEYALL -SDRATTR_START]=new SdrResizeYAllItem;
+ rPoolDefaults[SDRATTR_ROTATEALL -SDRATTR_START]=new SdrRotateAllItem;
+ rPoolDefaults[SDRATTR_HORZSHEARALL -SDRATTR_START]=new SdrHorzShearAllItem;
+ rPoolDefaults[SDRATTR_VERTSHEARALL -SDRATTR_START]=new SdrVertShearAllItem;
+ rPoolDefaults[SDRATTR_TRANSFORMREF1X -SDRATTR_START]=new SdrTransformRef1XItem;
+ rPoolDefaults[SDRATTR_TRANSFORMREF1Y -SDRATTR_START]=new SdrTransformRef1YItem;
+ rPoolDefaults[SDRATTR_TRANSFORMREF2X -SDRATTR_START]=new SdrTransformRef2XItem;
+ rPoolDefaults[SDRATTR_TRANSFORMREF2Y -SDRATTR_START]=new SdrTransformRef2YItem;
+ rPoolDefaults[SDRATTR_TEXTDIRECTION -SDRATTR_START]=new SvxWritingModeItem(css::text::WritingMode_LR_TB, SDRATTR_TEXTDIRECTION);
+ rPoolDefaults[ SDRATTR_GRAFRED - SDRATTR_START] = new SdrGrafRedItem;
+ rPoolDefaults[ SDRATTR_GRAFGREEN - SDRATTR_START] = new SdrGrafGreenItem;
+ rPoolDefaults[ SDRATTR_GRAFBLUE - SDRATTR_START] = new SdrGrafBlueItem;
+ rPoolDefaults[ SDRATTR_GRAFLUMINANCE - SDRATTR_START] = new SdrGrafLuminanceItem;
+ rPoolDefaults[ SDRATTR_GRAFCONTRAST - SDRATTR_START] = new SdrGrafContrastItem;
+ rPoolDefaults[ SDRATTR_GRAFGAMMA - SDRATTR_START] = new SdrGrafGamma100Item;
+ rPoolDefaults[ SDRATTR_GRAFTRANSPARENCE - SDRATTR_START] = new SdrGrafTransparenceItem;
+ rPoolDefaults[ SDRATTR_GRAFINVERT - SDRATTR_START] = new SdrGrafInvertItem;
+ rPoolDefaults[ SDRATTR_GRAFMODE - SDRATTR_START] = new SdrGrafModeItem;
+ rPoolDefaults[ SDRATTR_GRAFCROP - SDRATTR_START] = new SdrGrafCropItem;
+ rPoolDefaults[ SDRATTR_3DOBJ_PERCENT_DIAGONAL - SDRATTR_START ] = new SfxUInt16Item(SDRATTR_3DOBJ_PERCENT_DIAGONAL, 10);
+ rPoolDefaults[ SDRATTR_3DOBJ_BACKSCALE - SDRATTR_START ] = new SfxUInt16Item(SDRATTR_3DOBJ_BACKSCALE, 100);
+ rPoolDefaults[ SDRATTR_3DOBJ_DEPTH - SDRATTR_START ] = new SfxUInt32Item(SDRATTR_3DOBJ_DEPTH, 1000);
+ rPoolDefaults[ SDRATTR_3DOBJ_HORZ_SEGS - SDRATTR_START ] = new SfxUInt32Item(SDRATTR_3DOBJ_HORZ_SEGS, 24);
+ rPoolDefaults[ SDRATTR_3DOBJ_VERT_SEGS - SDRATTR_START ] = new SfxUInt32Item(SDRATTR_3DOBJ_VERT_SEGS, 24);
+ rPoolDefaults[ SDRATTR_3DOBJ_END_ANGLE - SDRATTR_START ] = new SfxUInt32Item(SDRATTR_3DOBJ_END_ANGLE, 3600);
+ rPoolDefaults[ SDRATTR_3DOBJ_DOUBLE_SIDED - SDRATTR_START ] = new SfxBoolItem(SDRATTR_3DOBJ_DOUBLE_SIDED, false);
+ rPoolDefaults[ SDRATTR_3DOBJ_NORMALS_KIND - SDRATTR_START ] = new Svx3DNormalsKindItem;
+ rPoolDefaults[ SDRATTR_3DOBJ_NORMALS_INVERT - SDRATTR_START ] = new SfxBoolItem(SDRATTR_3DOBJ_NORMALS_INVERT, false);
+ rPoolDefaults[ SDRATTR_3DOBJ_TEXTURE_PROJ_X - SDRATTR_START ] = new Svx3DTextureProjectionXItem;
+ rPoolDefaults[ SDRATTR_3DOBJ_TEXTURE_PROJ_Y - SDRATTR_START ] = new Svx3DTextureProjectionYItem;
+ rPoolDefaults[ SDRATTR_3DOBJ_SHADOW_3D - SDRATTR_START ] = new SfxBoolItem(SDRATTR_3DOBJ_SHADOW_3D, false);
+ rPoolDefaults[ SDRATTR_3DOBJ_MAT_COLOR - SDRATTR_START ] = new SvxColorItem(Color(0x0000b8ff), SDRATTR_3DOBJ_MAT_COLOR);
+ rPoolDefaults[ SDRATTR_3DOBJ_MAT_EMISSION - SDRATTR_START ] = new SvxColorItem(Color(0x00000000), SDRATTR_3DOBJ_MAT_EMISSION);
+ rPoolDefaults[ SDRATTR_3DOBJ_MAT_SPECULAR - SDRATTR_START ] = new SvxColorItem(Color(0x00ffffff), SDRATTR_3DOBJ_MAT_SPECULAR);
+ rPoolDefaults[ SDRATTR_3DOBJ_MAT_SPECULAR_INTENSITY - SDRATTR_START ] = new SfxUInt16Item(SDRATTR_3DOBJ_MAT_SPECULAR_INTENSITY, 15);
+ rPoolDefaults[ SDRATTR_3DOBJ_TEXTURE_KIND - SDRATTR_START ] = new Svx3DTextureKindItem;
+ rPoolDefaults[ SDRATTR_3DOBJ_TEXTURE_MODE - SDRATTR_START ] = new Svx3DTextureModeItem;
+ rPoolDefaults[ SDRATTR_3DOBJ_TEXTURE_FILTER - SDRATTR_START ] = new SfxBoolItem(SDRATTR_3DOBJ_TEXTURE_FILTER, false);
+ rPoolDefaults[ SDRATTR_3DOBJ_SMOOTH_NORMALS - SDRATTR_START ] = new Svx3DSmoothNormalsItem;
+ rPoolDefaults[ SDRATTR_3DOBJ_SMOOTH_LIDS - SDRATTR_START ] = new Svx3DSmoothLidsItem;
+ rPoolDefaults[ SDRATTR_3DOBJ_CHARACTER_MODE - SDRATTR_START ] = new Svx3DCharacterModeItem;
+ rPoolDefaults[ SDRATTR_3DOBJ_CLOSE_FRONT - SDRATTR_START ] = new Svx3DCloseFrontItem;
+ rPoolDefaults[ SDRATTR_3DOBJ_CLOSE_BACK - SDRATTR_START ] = new Svx3DCloseBackItem;
+ rPoolDefaults[ SDRATTR_3DOBJ_REDUCED_LINE_GEOMETRY - SDRATTR_START ] = new Svx3DReducedLineGeometryItem;
+ rPoolDefaults[ SDRATTR_3DSCENE_PERSPECTIVE - SDRATTR_START ] = new Svx3DPerspectiveItem;
+ rPoolDefaults[ SDRATTR_3DSCENE_DISTANCE - SDRATTR_START ] = new SfxUInt32Item(SDRATTR_3DSCENE_DISTANCE, 100);
+ rPoolDefaults[ SDRATTR_3DSCENE_FOCAL_LENGTH - SDRATTR_START ] = new SfxUInt32Item(SDRATTR_3DSCENE_FOCAL_LENGTH, 100);
+ rPoolDefaults[ SDRATTR_3DSCENE_TWO_SIDED_LIGHTING - SDRATTR_START ] = new SfxBoolItem(SDRATTR_3DSCENE_TWO_SIDED_LIGHTING, false);
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTCOLOR_1 - SDRATTR_START ] = new SvxColorItem(Color(ColorTransparency, 0xffcccccc), SDRATTR_3DSCENE_LIGHTCOLOR_1);
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTCOLOR_2 - SDRATTR_START ] = new SvxColorItem(Color(0x00000000), SDRATTR_3DSCENE_LIGHTCOLOR_2);
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTCOLOR_3 - SDRATTR_START ] = new SvxColorItem(Color(0x00000000), SDRATTR_3DSCENE_LIGHTCOLOR_3);
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTCOLOR_4 - SDRATTR_START ] = new SvxColorItem(Color(0x00000000), SDRATTR_3DSCENE_LIGHTCOLOR_4);
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTCOLOR_5 - SDRATTR_START ] = new SvxColorItem(Color(0x00000000), SDRATTR_3DSCENE_LIGHTCOLOR_5);
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTCOLOR_6 - SDRATTR_START ] = new SvxColorItem(Color(0x00000000), SDRATTR_3DSCENE_LIGHTCOLOR_6);
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTCOLOR_7 - SDRATTR_START ] = new SvxColorItem(Color(0x00000000), SDRATTR_3DSCENE_LIGHTCOLOR_7);
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTCOLOR_8 - SDRATTR_START ] = new SvxColorItem(Color(0x00000000), SDRATTR_3DSCENE_LIGHTCOLOR_8);
+ rPoolDefaults[ SDRATTR_3DSCENE_AMBIENTCOLOR - SDRATTR_START ] = new SvxColorItem(Color(0x00666666), SDRATTR_3DSCENE_AMBIENTCOLOR);
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTON_1 - SDRATTR_START ] = new SfxBoolItem(SDRATTR_3DSCENE_LIGHTON_1, true);
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTON_2 - SDRATTR_START ] = new SfxBoolItem(SDRATTR_3DSCENE_LIGHTON_2, false);
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTON_3 - SDRATTR_START ] = new SfxBoolItem(SDRATTR_3DSCENE_LIGHTON_3, false);
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTON_4 - SDRATTR_START ] = new SfxBoolItem(SDRATTR_3DSCENE_LIGHTON_4, false);
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTON_5 - SDRATTR_START ] = new SfxBoolItem(SDRATTR_3DSCENE_LIGHTON_5, false);
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTON_6 - SDRATTR_START ] = new SfxBoolItem(SDRATTR_3DSCENE_LIGHTON_6, false);
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTON_7 - SDRATTR_START ] = new SfxBoolItem(SDRATTR_3DSCENE_LIGHTON_7, false);
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTON_8 - SDRATTR_START ] = new SfxBoolItem(SDRATTR_3DSCENE_LIGHTON_8, false);
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTDIRECTION_1 - SDRATTR_START ] = new SvxB3DVectorItem(SDRATTR_3DSCENE_LIGHTDIRECTION_1, basegfx::B3DVector(0.57735026918963, 0.57735026918963, 0.57735026918963));
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTDIRECTION_2 - SDRATTR_START ] = new SvxB3DVectorItem(SDRATTR_3DSCENE_LIGHTDIRECTION_2, basegfx::B3DVector(0.0,0.0,1.0));
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTDIRECTION_3 - SDRATTR_START ] = new SvxB3DVectorItem(SDRATTR_3DSCENE_LIGHTDIRECTION_3, basegfx::B3DVector(0.0,0.0,1.0));
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTDIRECTION_4 - SDRATTR_START ] = new SvxB3DVectorItem(SDRATTR_3DSCENE_LIGHTDIRECTION_4, basegfx::B3DVector(0.0,0.0,1.0));
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTDIRECTION_5 - SDRATTR_START ] = new SvxB3DVectorItem(SDRATTR_3DSCENE_LIGHTDIRECTION_5, basegfx::B3DVector(0.0,0.0,1.0));
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTDIRECTION_6 - SDRATTR_START ] = new SvxB3DVectorItem(SDRATTR_3DSCENE_LIGHTDIRECTION_6, basegfx::B3DVector(0.0,0.0,1.0));
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTDIRECTION_7 - SDRATTR_START ] = new SvxB3DVectorItem(SDRATTR_3DSCENE_LIGHTDIRECTION_7, basegfx::B3DVector(0.0,0.0,1.0));
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTDIRECTION_8 - SDRATTR_START ] = new SvxB3DVectorItem(SDRATTR_3DSCENE_LIGHTDIRECTION_8, basegfx::B3DVector(0.0,0.0,1.0));
+ rPoolDefaults[ SDRATTR_3DSCENE_SHADOW_SLANT - SDRATTR_START ] = new SfxUInt16Item(SDRATTR_3DSCENE_SHADOW_SLANT, 0);
+ rPoolDefaults[ SDRATTR_3DSCENE_SHADE_MODE - SDRATTR_START ] = new Svx3DShadeModeItem;
+ rPoolDefaults[ SDRATTR_CUSTOMSHAPE_ENGINE - SDRATTR_START ] = new SfxStringItem(SDRATTR_CUSTOMSHAPE_ENGINE, "");
+ rPoolDefaults[ SDRATTR_CUSTOMSHAPE_DATA - SDRATTR_START ] = new SfxStringItem(SDRATTR_CUSTOMSHAPE_DATA, "");
+ rPoolDefaults[ SDRATTR_CUSTOMSHAPE_GEOMETRY - SDRATTR_START ] = new SdrCustomShapeGeometryItem;
+
+ SvxBoxItem* pboxItem = new SvxBoxItem( SDRATTR_TABLE_BORDER );
+ pboxItem->SetAllDistances( 100 );
+ rPoolDefaults[ SDRATTR_TABLE_BORDER - SDRATTR_START ] = pboxItem;
+
+ SvxBoxInfoItem* pBoxInfoItem = new SvxBoxInfoItem( SDRATTR_TABLE_BORDER_INNER );
+
+ pBoxInfoItem->SetTable( true );
+ pBoxInfoItem->SetDist( true); // always show margin field
+ pBoxInfoItem->SetValid( SvxBoxInfoItemValidFlags::DISABLE ); // some lines may have DontCare state only in tables
+
+ rPoolDefaults[ SDRATTR_TABLE_BORDER_INNER - SDRATTR_START ] = pBoxInfoItem;
+ rPoolDefaults[ SDRATTR_TABLE_BORDER_TLBR - SDRATTR_START ] = new SvxLineItem( SDRATTR_TABLE_BORDER_TLBR );
+ rPoolDefaults[ SDRATTR_TABLE_BORDER_BLTR - SDRATTR_START ] = new SvxLineItem( SDRATTR_TABLE_BORDER_BLTR );
+ rPoolDefaults[ SDRATTR_TABLE_TEXT_ROTATION - SDRATTR_START ] = new SvxTextRotateItem(0_deg10, SDRATTR_TABLE_TEXT_ROTATION);
+
+ rPoolDefaults[ SDRATTR_GLOW_RADIUS - SDRATTR_START ] = new SdrMetricItem(SDRATTR_GLOW_RADIUS, 0);
+ rPoolDefaults[ SDRATTR_GLOW_COLOR - SDRATTR_START ] = new XColorItem(SDRATTR_GLOW_COLOR, aNullCol);
+ rPoolDefaults[ SDRATTR_GLOW_TRANSPARENCY - SDRATTR_START ] = new SdrPercentItem(SDRATTR_GLOW_TRANSPARENCY, 0);
+
+ rPoolDefaults[SDRATTR_SOFTEDGE_RADIUS - SDRATTR_START] = new SdrMetricItem(SDRATTR_SOFTEDGE_RADIUS, 0);
+
+ rPoolDefaults[SDRATTR_TEXTCOLUMNS_NUMBER - SDRATTR_START] = new SfxInt16Item(SDRATTR_TEXTCOLUMNS_NUMBER, 1);
+ rPoolDefaults[SDRATTR_TEXTCOLUMNS_SPACING - SDRATTR_START] = new SdrMetricItem(SDRATTR_TEXTCOLUMNS_SPACING, 0);
+
+ // set own ItemInfos
+ mpLocalItemInfos[SDRATTR_SHADOW-SDRATTR_START]._nSID=SID_ATTR_FILL_SHADOW;
+ mpLocalItemInfos[SDRATTR_SHADOWCOLOR-SDRATTR_START]._nSID=SID_ATTR_SHADOW_COLOR;
+ mpLocalItemInfos[SDRATTR_SHADOWTRANSPARENCE-SDRATTR_START]._nSID=SID_ATTR_SHADOW_TRANSPARENCE;
+ mpLocalItemInfos[SDRATTR_SHADOWBLUR-SDRATTR_START]._nSID=SID_ATTR_SHADOW_BLUR;
+ mpLocalItemInfos[SDRATTR_SHADOWXDIST-SDRATTR_START]._nSID=SID_ATTR_SHADOW_XDISTANCE;
+ mpLocalItemInfos[SDRATTR_SHADOWYDIST-SDRATTR_START]._nSID=SID_ATTR_SHADOW_YDISTANCE;
+ mpLocalItemInfos[SDRATTR_TEXT_FITTOSIZE-SDRATTR_START]._nSID=SID_ATTR_TEXT_FITTOSIZE;
+ mpLocalItemInfos[SDRATTR_GRAFCROP-SDRATTR_START]._nSID=SID_ATTR_GRAF_CROP;
+
+ mpLocalItemInfos[SDRATTR_TABLE_BORDER - SDRATTR_START ]._nSID = SID_ATTR_BORDER_OUTER;
+ mpLocalItemInfos[SDRATTR_TABLE_BORDER_INNER - SDRATTR_START ]._nSID = SID_ATTR_BORDER_INNER;
+ mpLocalItemInfos[SDRATTR_TABLE_BORDER_TLBR - SDRATTR_START ]._nSID = SID_ATTR_BORDER_DIAG_TLBR;
+ mpLocalItemInfos[SDRATTR_TABLE_BORDER_BLTR - SDRATTR_START ]._nSID = SID_ATTR_BORDER_DIAG_BLTR;
+
+ mpLocalItemInfos[SDRATTR_GLOW_RADIUS - SDRATTR_START]._nSID = SID_ATTR_GLOW_RADIUS;
+ mpLocalItemInfos[SDRATTR_GLOW_COLOR - SDRATTR_START]._nSID = SID_ATTR_GLOW_COLOR;
+ mpLocalItemInfos[SDRATTR_GLOW_TRANSPARENCY - SDRATTR_START]._nSID = SID_ATTR_GLOW_TRANSPARENCY;
+
+ mpLocalItemInfos[SDRATTR_SOFTEDGE_RADIUS - SDRATTR_START]._nSID = SID_ATTR_SOFTEDGE_RADIUS;
+
+ mpLocalItemInfos[SDRATTR_TEXTCOLUMNS_NUMBER - SDRATTR_START]._nSID = 0 /*TODO*/;
+ mpLocalItemInfos[SDRATTR_TEXTCOLUMNS_SPACING - SDRATTR_START]._nSID = 0 /*TODO*/;
+
+ // it's my own creation level, set Defaults and ItemInfos
+ SetDefaults(mpLocalPoolDefaults);
+ SetItemInfos(mpLocalItemInfos.get());
+}
+
+// copy ctor, so that static defaults are cloned
+// (Parameter 2 = sal_True)
+SdrItemPool::SdrItemPool(const SdrItemPool& rPool)
+: XOutdevItemPool(rPool)
+{
+}
+
+rtl::Reference<SfxItemPool> SdrItemPool::Clone() const
+{
+ return new SdrItemPool(*this);
+}
+
+SdrItemPool::~SdrItemPool()
+{
+ // split pools before destroying
+ SetSecondaryPool(nullptr);
+}
+
+bool SdrItemPool::GetPresentation(
+ const SfxPoolItem& rItem,
+ MapUnit ePresentationMetric, OUString& rText,
+ const IntlWrapper& rIntlWrapper) const
+{
+ if (!IsInvalidItem(&rItem)) {
+ sal_uInt16 nWhich=rItem.Which();
+ if (nWhich>=SDRATTR_SHADOW_FIRST && nWhich<=SDRATTR_END) {
+ rItem.GetPresentation(SfxItemPresentation::Nameless,
+ GetMetric(nWhich),ePresentationMetric,rText,
+ rIntlWrapper);
+ rText = GetItemName(nWhich) + " " + rText;
+
+ return true;
+ }
+ }
+ return XOutdevItemPool::GetPresentation(rItem,ePresentationMetric,rText,rIntlWrapper);
+}
+
+OUString SdrItemPool::GetItemName(sal_uInt16 nWhich)
+{
+ TranslateId pResId = SIP_UNKNOWN_ATTR;
+
+ switch (nWhich)
+ {
+ case XATTR_LINESTYLE : pResId = SIP_XA_LINESTYLE;break;
+ case XATTR_LINEDASH : pResId = SIP_XA_LINEDASH;break;
+ case XATTR_LINEWIDTH : pResId = SIP_XA_LINEWIDTH;break;
+ case XATTR_LINECOLOR : pResId = SIP_XA_LINECOLOR;break;
+ case XATTR_LINESTART : pResId = SIP_XA_LINESTART;break;
+ case XATTR_LINEEND : pResId = SIP_XA_LINEEND;break;
+ case XATTR_LINESTARTWIDTH : pResId = SIP_XA_LINESTARTWIDTH;break;
+ case XATTR_LINEENDWIDTH : pResId = SIP_XA_LINEENDWIDTH;break;
+ case XATTR_LINESTARTCENTER : pResId = SIP_XA_LINESTARTCENTER;break;
+ case XATTR_LINEENDCENTER : pResId = SIP_XA_LINEENDCENTER;break;
+ case XATTR_LINETRANSPARENCE : pResId = SIP_XA_LINETRANSPARENCE;break;
+ case XATTR_LINEJOINT : pResId = SIP_XA_LINEJOINT;break;
+ case XATTRSET_LINE : pResId = SIP_XATTRSET_LINE;break;
+
+ case XATTR_FILLSTYLE : pResId = SIP_XA_FILLSTYLE;break;
+ case XATTR_FILLCOLOR : pResId = SIP_XA_FILLCOLOR;break;
+ case XATTR_FILLGRADIENT : pResId = SIP_XA_FILLGRADIENT;break;
+ case XATTR_FILLHATCH : pResId = SIP_XA_FILLHATCH;break;
+ case XATTR_FILLBITMAP : pResId = SIP_XA_FILLBITMAP;break;
+ case XATTR_FILLTRANSPARENCE : pResId = SIP_XA_FILLTRANSPARENCE;break;
+ case XATTR_GRADIENTSTEPCOUNT : pResId = SIP_XA_GRADIENTSTEPCOUNT;break;
+ case XATTR_FILLBMP_TILE : pResId = SIP_XA_FILLBMP_TILE;break;
+ case XATTR_FILLBMP_POS : pResId = SIP_XA_FILLBMP_POS;break;
+ case XATTR_FILLBMP_SIZEX : pResId = SIP_XA_FILLBMP_SIZEX;break;
+ case XATTR_FILLBMP_SIZEY : pResId = SIP_XA_FILLBMP_SIZEY;break;
+ case XATTR_FILLFLOATTRANSPARENCE: pResId = SIP_XA_FILLFLOATTRANSPARENCE;break;
+ case XATTR_SECONDARYFILLCOLOR : pResId = SIP_XA_SECONDARYFILLCOLOR;break;
+ case XATTR_FILLBMP_SIZELOG : pResId = SIP_XA_FILLBMP_SIZELOG;break;
+ case XATTR_FILLBMP_TILEOFFSETX : pResId = SIP_XA_FILLBMP_TILEOFFSETX;break;
+ case XATTR_FILLBMP_TILEOFFSETY : pResId = SIP_XA_FILLBMP_TILEOFFSETY;break;
+ case XATTR_FILLBMP_STRETCH : pResId = SIP_XA_FILLBMP_STRETCH;break;
+ case XATTR_FILLBMP_POSOFFSETX : pResId = SIP_XA_FILLBMP_POSOFFSETX;break;
+ case XATTR_FILLBMP_POSOFFSETY : pResId = SIP_XA_FILLBMP_POSOFFSETY;break;
+ case XATTR_FILLBACKGROUND : pResId = SIP_XA_FILLBACKGROUND;break;
+ case XATTR_FILLUSESLIDEBACKGROUND: pResId = SIP_XA_FILLUSESLIDEBACKGROUND;break;
+
+ case XATTRSET_FILL : pResId = SIP_XATTRSET_FILL;break;
+
+ case XATTR_FORMTXTSTYLE : pResId = SIP_XA_FORMTXTSTYLE;break;
+ case XATTR_FORMTXTADJUST : pResId = SIP_XA_FORMTXTADJUST;break;
+ case XATTR_FORMTXTDISTANCE : pResId = SIP_XA_FORMTXTDISTANCE;break;
+ case XATTR_FORMTXTSTART : pResId = SIP_XA_FORMTXTSTART;break;
+ case XATTR_FORMTXTMIRROR : pResId = SIP_XA_FORMTXTMIRROR;break;
+ case XATTR_FORMTXTOUTLINE : pResId = SIP_XA_FORMTXTOUTLINE;break;
+ case XATTR_FORMTXTSHADOW : pResId = SIP_XA_FORMTXTSHADOW;break;
+ case XATTR_FORMTXTSHDWCOLOR : pResId = SIP_XA_FORMTXTSHDWCOLOR;break;
+ case XATTR_FORMTXTSHDWXVAL : pResId = SIP_XA_FORMTXTSHDWXVAL;break;
+ case XATTR_FORMTXTSHDWYVAL : pResId = SIP_XA_FORMTXTSHDWYVAL;break;
+ case XATTR_FORMTXTHIDEFORM : pResId = SIP_XA_FORMTXTHIDEFORM;break;
+ case XATTR_FORMTXTSHDWTRANSP: pResId = SIP_XA_FORMTXTSHDWTRANSP;break;
+
+ case SDRATTR_SHADOW : pResId = SIP_SA_SHADOW;break;
+ case SDRATTR_SHADOWCOLOR : pResId = SIP_SA_SHADOWCOLOR;break;
+ case SDRATTR_SHADOWXDIST : pResId = SIP_SA_SHADOWXDIST;break;
+ case SDRATTR_SHADOWYDIST : pResId = SIP_SA_SHADOWYDIST;break;
+ case SDRATTR_SHADOWTRANSPARENCE: pResId = SIP_SA_SHADOWTRANSPARENCE;break;
+ case SDRATTR_SHADOWBLUR : pResId = SIP_SA_SHADOWBLUR;break;
+ case SDRATTR_SHADOW3D : pResId = SIP_SA_SHADOW3D;break;
+ case SDRATTR_SHADOWPERSP : pResId = SIP_SA_SHADOWPERSP;break;
+
+ case SDRATTR_GLOW_RADIUS : pResId = SIP_SA_GLOW_RADIUS;break;
+ case SDRATTR_GLOW_COLOR : pResId = SIP_SA_GLOW_COLOR;break;
+ case SDRATTR_GLOW_TRANSPARENCY : pResId = SIP_SA_GLOW_TRANSPARENCY;break;
+
+ case SDRATTR_SOFTEDGE_RADIUS : pResId = SIP_SA_SOFTEDGE_RADIUS; break;
+
+ case SDRATTR_CAPTIONTYPE : pResId = SIP_SA_CAPTIONTYPE;break;
+ case SDRATTR_CAPTIONFIXEDANGLE: pResId = SIP_SA_CAPTIONFIXEDANGLE;break;
+ case SDRATTR_CAPTIONANGLE : pResId = SIP_SA_CAPTIONANGLE;break;
+ case SDRATTR_CAPTIONGAP : pResId = SIP_SA_CAPTIONGAP;break;
+ case SDRATTR_CAPTIONESCDIR : pResId = SIP_SA_CAPTIONESCDIR;break;
+ case SDRATTR_CAPTIONESCISREL : pResId = SIP_SA_CAPTIONESCISREL;break;
+ case SDRATTR_CAPTIONESCREL : pResId = SIP_SA_CAPTIONESCREL;break;
+ case SDRATTR_CAPTIONESCABS : pResId = SIP_SA_CAPTIONESCABS;break;
+ case SDRATTR_CAPTIONLINELEN : pResId = SIP_SA_CAPTIONLINELEN;break;
+ case SDRATTR_CAPTIONFITLINELEN: pResId = SIP_SA_CAPTIONFITLINELEN;break;
+
+ case SDRATTR_CORNER_RADIUS : pResId = SIP_SA_CORNER_RADIUS;break;
+ case SDRATTR_TEXT_MINFRAMEHEIGHT : pResId = SIP_SA_TEXT_MINFRAMEHEIGHT;break;
+ case SDRATTR_TEXT_AUTOGROWHEIGHT : pResId = SIP_SA_TEXT_AUTOGROWHEIGHT;break;
+ case SDRATTR_TEXT_FITTOSIZE : pResId = SIP_SA_TEXT_FITTOSIZE;break;
+ case SDRATTR_TEXT_LEFTDIST : pResId = SIP_SA_TEXT_LEFTDIST;break;
+ case SDRATTR_TEXT_RIGHTDIST : pResId = SIP_SA_TEXT_RIGHTDIST;break;
+ case SDRATTR_TEXT_UPPERDIST : pResId = SIP_SA_TEXT_UPPERDIST;break;
+ case SDRATTR_TEXT_LOWERDIST : pResId = SIP_SA_TEXT_LOWERDIST;break;
+ case SDRATTR_TEXT_VERTADJUST : pResId = SIP_SA_TEXT_VERTADJUST;break;
+ case SDRATTR_TEXT_MAXFRAMEHEIGHT : pResId = SIP_SA_TEXT_MAXFRAMEHEIGHT;break;
+ case SDRATTR_TEXT_MINFRAMEWIDTH : pResId = SIP_SA_TEXT_MINFRAMEWIDTH;break;
+ case SDRATTR_TEXT_MAXFRAMEWIDTH : pResId = SIP_SA_TEXT_MAXFRAMEWIDTH;break;
+ case SDRATTR_TEXT_AUTOGROWWIDTH : pResId = SIP_SA_TEXT_AUTOGROWWIDTH;break;
+ case SDRATTR_TEXT_HORZADJUST : pResId = SIP_SA_TEXT_HORZADJUST;break;
+ case SDRATTR_TEXT_ANIKIND : pResId = SIP_SA_TEXT_ANIKIND;break;
+ case SDRATTR_TEXT_ANIDIRECTION : pResId = SIP_SA_TEXT_ANIDIRECTION;break;
+ case SDRATTR_TEXT_ANISTARTINSIDE : pResId = SIP_SA_TEXT_ANISTARTINSIDE;break;
+ case SDRATTR_TEXT_ANISTOPINSIDE : pResId = SIP_SA_TEXT_ANISTOPINSIDE;break;
+ case SDRATTR_TEXT_ANICOUNT : pResId = SIP_SA_TEXT_ANICOUNT;break;
+ case SDRATTR_TEXT_ANIDELAY : pResId = SIP_SA_TEXT_ANIDELAY;break;
+ case SDRATTR_TEXT_ANIAMOUNT : pResId = SIP_SA_TEXT_ANIAMOUNT;break;
+ case SDRATTR_TEXT_CONTOURFRAME : pResId = SIP_SA_TEXT_CONTOURFRAME;break;
+ case SDRATTR_XMLATTRIBUTES : pResId = SIP_SA_XMLATTRIBUTES;break;
+ case SDRATTR_TEXT_USEFIXEDCELLHEIGHT: pResId = SIP_SA_TEXT_USEFIXEDCELLHEIGHT;break;
+ case SDRATTR_TEXT_WORDWRAP : pResId = SIP_SA_WORDWRAP;break;
+ case SDRATTR_TEXT_CHAINNEXTNAME : pResId = SIP_SA_CHAINNEXTNAME;break;
+
+ case SDRATTR_EDGEKIND : pResId = SIP_SA_EDGEKIND;break;
+ case SDRATTR_EDGENODE1HORZDIST : pResId = SIP_SA_EDGENODE1HORZDIST;break;
+ case SDRATTR_EDGENODE1VERTDIST : pResId = SIP_SA_EDGENODE1VERTDIST;break;
+ case SDRATTR_EDGENODE2HORZDIST : pResId = SIP_SA_EDGENODE2HORZDIST;break;
+ case SDRATTR_EDGENODE2VERTDIST : pResId = SIP_SA_EDGENODE2VERTDIST;break;
+ case SDRATTR_EDGENODE1GLUEDIST : pResId = SIP_SA_EDGENODE1GLUEDIST;break;
+ case SDRATTR_EDGENODE2GLUEDIST : pResId = SIP_SA_EDGENODE2GLUEDIST;break;
+ case SDRATTR_EDGELINEDELTACOUNT : pResId = SIP_SA_EDGELINEDELTACOUNT;break;
+ case SDRATTR_EDGELINE1DELTA : pResId = SIP_SA_EDGELINE1DELTA;break;
+ case SDRATTR_EDGELINE2DELTA : pResId = SIP_SA_EDGELINE2DELTA;break;
+ case SDRATTR_EDGELINE3DELTA : pResId = SIP_SA_EDGELINE3DELTA;break;
+
+ case SDRATTR_MEASUREKIND : pResId = SIP_SA_MEASUREKIND;break;
+ case SDRATTR_MEASURETEXTHPOS : pResId = SIP_SA_MEASURETEXTHPOS;break;
+ case SDRATTR_MEASURETEXTVPOS : pResId = SIP_SA_MEASURETEXTVPOS;break;
+ case SDRATTR_MEASURELINEDIST : pResId = SIP_SA_MEASURELINEDIST;break;
+ case SDRATTR_MEASUREHELPLINEOVERHANG : pResId = SIP_SA_MEASUREHELPLINEOVERHANG;break;
+ case SDRATTR_MEASUREHELPLINEDIST : pResId = SIP_SA_MEASUREHELPLINEDIST;break;
+ case SDRATTR_MEASUREHELPLINE1LEN : pResId = SIP_SA_MEASUREHELPLINE1LEN;break;
+ case SDRATTR_MEASUREHELPLINE2LEN : pResId = SIP_SA_MEASUREHELPLINE2LEN;break;
+ case SDRATTR_MEASUREBELOWREFEDGE : pResId = SIP_SA_MEASUREBELOWREFEDGE;break;
+ case SDRATTR_MEASURETEXTROTA90 : pResId = SIP_SA_MEASURETEXTROTA90;break;
+ case SDRATTR_MEASURETEXTUPSIDEDOWN : pResId = SIP_SA_MEASURETEXTUPSIDEDOWN;break;
+ case SDRATTR_MEASUREOVERHANG : pResId = SIP_SA_MEASUREOVERHANG;break;
+ case SDRATTR_MEASUREUNIT : pResId = SIP_SA_MEASUREUNIT;break;
+ case SDRATTR_MEASURESCALE : pResId = SIP_SA_MEASURESCALE;break;
+ case SDRATTR_MEASURESHOWUNIT : pResId = SIP_SA_MEASURESHOWUNIT;break;
+ case SDRATTR_MEASUREFORMATSTRING : pResId = SIP_SA_MEASUREFORMATSTRING;break;
+ case SDRATTR_MEASURETEXTAUTOANGLE : pResId = SIP_SA_MEASURETEXTAUTOANGLE;break;
+ case SDRATTR_MEASURETEXTAUTOANGLEVIEW: pResId = SIP_SA_MEASURETEXTAUTOANGLEVIEW;break;
+ case SDRATTR_MEASURETEXTISFIXEDANGLE : pResId = SIP_SA_MEASURETEXTISFIXEDANGLE;break;
+ case SDRATTR_MEASURETEXTFIXEDANGLE : pResId = SIP_SA_MEASURETEXTFIXEDANGLE;break;
+ case SDRATTR_MEASUREDECIMALPLACES : pResId = SIP_SA_MEASUREDECIMALPLACES;break;
+
+ case SDRATTR_CIRCKIND : pResId = SIP_SA_CIRCKIND;break;
+ case SDRATTR_CIRCSTARTANGLE: pResId = SIP_SA_CIRCSTARTANGLE;break;
+ case SDRATTR_CIRCENDANGLE : pResId = SIP_SA_CIRCENDANGLE;break;
+
+ case SDRATTR_OBJMOVEPROTECT : pResId = SIP_SA_OBJMOVEPROTECT;break;
+ case SDRATTR_OBJSIZEPROTECT : pResId = SIP_SA_OBJSIZEPROTECT;break;
+ case SDRATTR_OBJPRINTABLE : pResId = SIP_SA_OBJPRINTABLE;break;
+ case SDRATTR_OBJVISIBLE : pResId = SIP_SA_OBJVISIBLE;break;
+ case SDRATTR_LAYERID : pResId = SIP_SA_LAYERID;break;
+ case SDRATTR_LAYERNAME : pResId = SIP_SA_LAYERNAME;break;
+ case SDRATTR_OBJECTNAME : pResId = SIP_SA_OBJECTNAME;break;
+ case SDRATTR_ALLPOSITIONX : pResId = SIP_SA_ALLPOSITIONX;break;
+ case SDRATTR_ALLPOSITIONY : pResId = SIP_SA_ALLPOSITIONY;break;
+ case SDRATTR_ALLSIZEWIDTH : pResId = SIP_SA_ALLSIZEWIDTH;break;
+ case SDRATTR_ALLSIZEHEIGHT : pResId = SIP_SA_ALLSIZEHEIGHT;break;
+ case SDRATTR_ONEPOSITIONX : pResId = SIP_SA_ONEPOSITIONX;break;
+ case SDRATTR_ONEPOSITIONY : pResId = SIP_SA_ONEPOSITIONY;break;
+ case SDRATTR_ONESIZEWIDTH : pResId = SIP_SA_ONESIZEWIDTH;break;
+ case SDRATTR_ONESIZEHEIGHT : pResId = SIP_SA_ONESIZEHEIGHT;break;
+ case SDRATTR_LOGICSIZEWIDTH : pResId = SIP_SA_LOGICSIZEWIDTH;break;
+ case SDRATTR_LOGICSIZEHEIGHT: pResId = SIP_SA_LOGICSIZEHEIGHT;break;
+ case SDRATTR_ROTATEANGLE : pResId = SIP_SA_ROTATEANGLE;break;
+ case SDRATTR_SHEARANGLE : pResId = SIP_SA_SHEARANGLE;break;
+ case SDRATTR_MOVEX : pResId = SIP_SA_MOVEX;break;
+ case SDRATTR_MOVEY : pResId = SIP_SA_MOVEY;break;
+ case SDRATTR_RESIZEXONE : pResId = SIP_SA_RESIZEXONE;break;
+ case SDRATTR_RESIZEYONE : pResId = SIP_SA_RESIZEYONE;break;
+ case SDRATTR_ROTATEONE : pResId = SIP_SA_ROTATEONE;break;
+ case SDRATTR_HORZSHEARONE : pResId = SIP_SA_HORZSHEARONE;break;
+ case SDRATTR_VERTSHEARONE : pResId = SIP_SA_VERTSHEARONE;break;
+ case SDRATTR_RESIZEXALL : pResId = SIP_SA_RESIZEXALL;break;
+ case SDRATTR_RESIZEYALL : pResId = SIP_SA_RESIZEYALL;break;
+ case SDRATTR_ROTATEALL : pResId = SIP_SA_ROTATEALL;break;
+ case SDRATTR_HORZSHEARALL : pResId = SIP_SA_HORZSHEARALL;break;
+ case SDRATTR_VERTSHEARALL : pResId = SIP_SA_VERTSHEARALL;break;
+ case SDRATTR_TRANSFORMREF1X : pResId = SIP_SA_TRANSFORMREF1X;break;
+ case SDRATTR_TRANSFORMREF1Y : pResId = SIP_SA_TRANSFORMREF1Y;break;
+ case SDRATTR_TRANSFORMREF2X : pResId = SIP_SA_TRANSFORMREF2X;break;
+ case SDRATTR_TRANSFORMREF2Y : pResId = SIP_SA_TRANSFORMREF2Y;break;
+
+ case SDRATTR_GRAFRED : pResId = SIP_SA_GRAFRED;break;
+ case SDRATTR_GRAFGREEN : pResId = SIP_SA_GRAFGREEN;break;
+ case SDRATTR_GRAFBLUE : pResId = SIP_SA_GRAFBLUE;break;
+ case SDRATTR_GRAFLUMINANCE : pResId = SIP_SA_GRAFLUMINANCE;break;
+ case SDRATTR_GRAFCONTRAST : pResId = SIP_SA_GRAFCONTRAST;break;
+ case SDRATTR_GRAFGAMMA : pResId = SIP_SA_GRAFGAMMA;break;
+ case SDRATTR_GRAFTRANSPARENCE : pResId = SIP_SA_GRAFTRANSPARENCE;break;
+ case SDRATTR_GRAFINVERT : pResId = SIP_SA_GRAFINVERT;break;
+ case SDRATTR_GRAFMODE : pResId = SIP_SA_GRAFMODE;break;
+ case SDRATTR_GRAFCROP : pResId = SIP_SA_GRAFCROP;break;
+
+ case EE_PARA_HYPHENATE : pResId = SIP_EE_PARA_HYPHENATE;break;
+ case EE_PARA_BULLETSTATE: pResId = SIP_EE_PARA_BULLETSTATE;break;
+ case EE_PARA_OUTLLRSPACE: pResId = SIP_EE_PARA_OUTLLRSPACE;break;
+ case EE_PARA_OUTLLEVEL : pResId = SIP_EE_PARA_OUTLLEVEL;break;
+ case EE_PARA_BULLET : pResId = SIP_EE_PARA_BULLET;break;
+ case EE_PARA_LRSPACE : pResId = SIP_EE_PARA_LRSPACE;break;
+ case EE_PARA_ULSPACE : pResId = SIP_EE_PARA_ULSPACE;break;
+ case EE_PARA_SBL : pResId = SIP_EE_PARA_SBL;break;
+ case EE_PARA_JUST : pResId = SIP_EE_PARA_JUST;break;
+ case EE_PARA_TABS : pResId = SIP_EE_PARA_TABS;break;
+
+ case EE_CHAR_COLOR : pResId = SIP_EE_CHAR_COLOR;break;
+ case EE_CHAR_FONTINFO : pResId = SIP_EE_CHAR_FONTINFO;break;
+ case EE_CHAR_FONTHEIGHT : pResId = SIP_EE_CHAR_FONTHEIGHT;break;
+ case EE_CHAR_FONTWIDTH : pResId = SIP_EE_CHAR_FONTWIDTH;break;
+ case EE_CHAR_WEIGHT : pResId = SIP_EE_CHAR_WEIGHT;break;
+ case EE_CHAR_UNDERLINE : pResId = SIP_EE_CHAR_UNDERLINE;break;
+ case EE_CHAR_OVERLINE : pResId = SIP_EE_CHAR_OVERLINE;break;
+ case EE_CHAR_STRIKEOUT : pResId = SIP_EE_CHAR_STRIKEOUT;break;
+ case EE_CHAR_ITALIC : pResId = SIP_EE_CHAR_ITALIC;break;
+ case EE_CHAR_OUTLINE : pResId = SIP_EE_CHAR_OUTLINE;break;
+ case EE_CHAR_SHADOW : pResId = SIP_EE_CHAR_SHADOW;break;
+ case EE_CHAR_ESCAPEMENT : pResId = SIP_EE_CHAR_ESCAPEMENT;break;
+ case EE_CHAR_PAIRKERNING: pResId = SIP_EE_CHAR_PAIRKERNING;break;
+ case EE_CHAR_KERNING : pResId = SIP_EE_CHAR_KERNING;break;
+ case EE_CHAR_WLM : pResId = SIP_EE_CHAR_WLM;break;
+ case EE_FEATURE_TAB : pResId = SIP_EE_FEATURE_TAB;break;
+ case EE_FEATURE_LINEBR : pResId = SIP_EE_FEATURE_LINEBR;break;
+ case EE_FEATURE_NOTCONV : pResId = SIP_EE_FEATURE_NOTCONV;break;
+ case EE_FEATURE_FIELD : pResId = SIP_EE_FEATURE_FIELD;break;
+
+ case SDRATTR_TEXTCOLUMNS_NUMBER: pResId = SIP_SA_TEXTCOLUMNS_NUMBER; break;
+ case SDRATTR_TEXTCOLUMNS_SPACING: pResId = SIP_SA_TEXTCOLUMNS_SPACING; break;
+ } // switch
+
+ return SvxResId(pResId);
+}
+
+
+// FractionItem
+
+
+bool SdrFractionItem::operator==(const SfxPoolItem& rCmp) const
+{
+ return SfxPoolItem::operator==(rCmp) &&
+ static_cast<const SdrFractionItem&>(rCmp).GetValue()==nValue;
+}
+
+bool SdrFractionItem::GetPresentation(
+ SfxItemPresentation ePresentation, MapUnit /*eCoreMetric*/,
+ MapUnit /*ePresentationMetric*/, OUString &rText, const IntlWrapper&) const
+{
+ if(nValue.IsValid())
+ {
+ sal_Int32 nDiv = nValue.GetDenominator();
+ rText = OUString::number(nValue.GetNumerator());
+
+ if(nDiv != 1)
+ {
+ rText += "/" + OUString::number(nDiv);
+ }
+ }
+ else
+ {
+ rText = "?";
+ }
+
+ if(ePresentation == SfxItemPresentation::Complete)
+ {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ return true;
+ }
+ else if(ePresentation == SfxItemPresentation::Nameless)
+ return true;
+
+ return false;
+}
+
+SdrFractionItem* SdrFractionItem::Clone(SfxItemPool * /*pPool*/) const
+{
+ return new SdrFractionItem(Which(),GetValue());
+}
+
+
+// ScaleItem
+
+
+bool SdrScaleItem::GetPresentation(
+ SfxItemPresentation ePresentation, MapUnit /*eCoreMetric*/,
+ MapUnit /*ePresentationMetric*/, OUString &rText, const IntlWrapper&) const
+{
+ if(GetValue().IsValid())
+ {
+ sal_Int32 nDiv = GetValue().GetDenominator();
+
+ rText = OUString::number(GetValue().GetNumerator()) + ":" + OUString::number(nDiv);
+ }
+ else
+ {
+ rText = "?";
+ }
+
+ if(ePresentation == SfxItemPresentation::Complete)
+ {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+
+ return true;
+}
+
+SdrScaleItem* SdrScaleItem::Clone(SfxItemPool * /*pPool*/) const
+{
+ return new SdrScaleItem(Which(),GetValue());
+}
+
+
+// OnOffItem
+
+
+SdrOnOffItem* SdrOnOffItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new SdrOnOffItem(TypedWhichId<SdrOnOffItem>(Which()),GetValue());
+}
+
+OUString SdrOnOffItem::GetValueTextByVal(bool bVal) const
+{
+ if (bVal)
+ return SvxResId(STR_ItemValON);
+ return SvxResId(STR_ItemValOFF);
+}
+
+bool SdrOnOffItem::GetPresentation(SfxItemPresentation ePres,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/, OUString& rText, const IntlWrapper&) const
+{
+ rText=GetValueTextByVal(GetValue());
+ if (ePres==SfxItemPresentation::Complete) {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+ return true;
+}
+
+void SdrOnOffItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SdrOnOffItem"));
+ if (Which() == SDRATTR_SHADOW)
+ {
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST("SDRATTR_SHADOW"));
+ }
+
+ SfxBoolItem::dumpAsXml(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+SdrYesNoItem* SdrYesNoItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new SdrYesNoItem(TypedWhichId<SdrYesNoItem>(Which()),GetValue());
+}
+
+OUString SdrYesNoItem::GetValueTextByVal(bool bVal) const
+{
+ if (bVal)
+ return SvxResId(STR_ItemValYES);
+ return SvxResId(STR_ItemValNO);
+}
+
+bool SdrYesNoItem::GetPresentation(SfxItemPresentation ePres,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/, OUString& rText, const IntlWrapper&) const
+{
+ rText=GetValueTextByVal(GetValue());
+ if (ePres==SfxItemPresentation::Complete) {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+ return true;
+}
+
+SdrPercentItem* SdrPercentItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new SdrPercentItem(TypedWhichId<SdrPercentItem>(Which()),GetValue());
+}
+
+bool SdrPercentItem::GetPresentation(
+ SfxItemPresentation ePres, MapUnit /*eCoreMetric*/,
+ MapUnit /*ePresMetric*/, OUString& rText, const IntlWrapper&) const
+{
+ rText = unicode::formatPercent(GetValue(),
+ Application::GetSettings().GetUILanguageTag());
+
+ if(ePres == SfxItemPresentation::Complete)
+ {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+
+ return true;
+}
+
+void SdrPercentItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SdrPercentItem"));
+ if (Which() == SDRATTR_SHADOWTRANSPARENCE)
+ {
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"),
+ BAD_CAST("SDRATTR_SHADOWTRANSPARENCE"));
+ }
+
+ SfxUInt16Item::dumpAsXml(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+SdrAngleItem* SdrAngleItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new SdrAngleItem(TypedWhichId<SdrAngleItem>(Which()),GetValue());
+}
+
+bool SdrAngleItem::GetPresentation(
+ SfxItemPresentation ePres, MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/,
+ OUString& rText, const IntlWrapper& rIntlWrapper) const
+{
+ sal_Int32 nValue(GetValue());
+ bool bNeg(nValue < 0);
+
+ if(bNeg)
+ nValue = -nValue;
+
+ OUStringBuffer aText = OUString::number(nValue);
+
+ if(nValue)
+ {
+ sal_Unicode aUnicodeNull('0');
+ sal_Int32 nCount(2);
+
+ if(LocaleDataWrapper::isNumLeadingZero())
+ nCount++;
+
+ while(aText.getLength() < nCount)
+ aText.insert(0, aUnicodeNull);
+
+ sal_Int32 nLen = aText.getLength();
+ bool bNull1(aText[nLen-1] == aUnicodeNull);
+ bool bNull2(bNull1 && aText[nLen-2] == aUnicodeNull);
+
+ if(bNull2)
+ {
+ // no decimal place(s)
+ sal_Int32 idx = nLen-2;
+ aText.remove(idx, aText.getLength()-idx);
+ }
+ else
+ {
+ sal_Unicode cDec =
+ rIntlWrapper.getLocaleData()->getNumDecimalSep()[0];
+ aText.insert(nLen-2, cDec);
+
+ if(bNull1)
+ aText.remove(nLen, aText.getLength()-nLen);
+ }
+
+ if(bNeg)
+ aText.insert(0, '-');
+ }
+
+ aText.append(sal_Unicode(DEGREE_CHAR));
+
+ if(ePres == SfxItemPresentation::Complete)
+ {
+ OUString aStr = SdrItemPool::GetItemName(Which());
+ aText.insert(0, ' ');
+ aText.insert(0, aStr);
+ }
+
+ rText = aText.makeStringAndClear();
+ return true;
+}
+
+SdrMetricItem* SdrMetricItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new SdrMetricItem(TypedWhichId<SdrMetricItem>(Which()),GetValue());
+}
+
+bool SdrMetricItem::HasMetrics() const
+{
+ return true;
+}
+
+void SdrMetricItem::ScaleMetrics(tools::Long nMul, tools::Long nDiv)
+{
+ if (GetValue()!=0) {
+ SetValue(BigInt::Scale(GetValue(), nMul, nDiv));
+ }
+}
+
+bool SdrMetricItem::GetPresentation(SfxItemPresentation ePres,
+ MapUnit eCoreMetric, MapUnit ePresMetric, OUString& rText, const IntlWrapper&) const
+{
+ tools::Long nValue=GetValue();
+ SdrFormatter aFmt(eCoreMetric,ePresMetric);
+ rText = aFmt.GetStr(nValue);
+ rText += " " + SdrFormatter::GetUnitStr(ePresMetric);
+ if (ePres==SfxItemPresentation::Complete) {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+ return true;
+}
+
+void SdrMetricItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SdrMetricItem"));
+ if (Which() == SDRATTR_SHADOWXDIST)
+ {
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST("SDRATTR_SHADOWXDIST"));
+ }
+ else if (Which() == SDRATTR_SHADOWYDIST)
+ {
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST("SDRATTR_SHADOWYDIST"));
+ }
+ else if (Which() == SDRATTR_SHADOWSIZEX)
+ {
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST("SDRATTR_SHADOWSIZEX"));
+ }
+ else if (Which() == SDRATTR_SHADOWSIZEY)
+ {
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST("SDRATTR_SHADOWSIZEY"));
+ }
+ else if (Which() == SDRATTR_SHADOWBLUR)
+ {
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST("SDRATTR_SHADOWBLUR"));
+ }
+
+ SfxInt32Item::dumpAsXml(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+// items of the legend object
+
+
+SdrCaptionTypeItem* SdrCaptionTypeItem::Clone(SfxItemPool* /*pPool*/) const { return new SdrCaptionTypeItem(*this); }
+
+sal_uInt16 SdrCaptionTypeItem::GetValueCount() const { return 4; }
+
+OUString SdrCaptionTypeItem::GetValueTextByPos(sal_uInt16 nPos)
+{
+ static TranslateId ITEMVALCAPTIONTYPES[] =
+ {
+ STR_ItemValCAPTIONTYPE1,
+ STR_ItemValCAPTIONTYPE2,
+ STR_ItemValCAPTIONTYPE3,
+ STR_ItemValCAPTIONTYPE4
+ };
+ assert(nPos < SAL_N_ELEMENTS(ITEMVALCAPTIONTYPES) && "wrong pos!");
+ return SvxResId(ITEMVALCAPTIONTYPES[nPos]);
+}
+
+bool SdrCaptionTypeItem::GetPresentation(SfxItemPresentation ePres,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/, OUString& rText, const IntlWrapper&) const
+{
+ rText=GetValueTextByPos(sal::static_int_cast< sal_uInt16 >(GetValue()));
+ if (ePres==SfxItemPresentation::Complete) {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+ return true;
+}
+
+
+SdrCaptionEscDirItem* SdrCaptionEscDirItem::Clone(SfxItemPool* /*pPool*/) const { return new SdrCaptionEscDirItem(*this); }
+
+sal_uInt16 SdrCaptionEscDirItem::GetValueCount() const { return 3; }
+
+OUString SdrCaptionEscDirItem::GetValueTextByPos(sal_uInt16 nPos)
+{
+ static TranslateId ITEMVALCAPTIONTYPES[] =
+ {
+ STR_ItemValCAPTIONESCHORI,
+ STR_ItemValCAPTIONESCVERT,
+ STR_ItemValCAPTIONESCBESTFIT
+ };
+ assert(nPos < SAL_N_ELEMENTS(ITEMVALCAPTIONTYPES) && "wrong pos!");
+ return SvxResId(ITEMVALCAPTIONTYPES[nPos]);
+}
+
+bool SdrCaptionEscDirItem::GetPresentation(SfxItemPresentation ePres,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/, OUString& rText, const IntlWrapper&) const
+{
+ rText=GetValueTextByPos(sal::static_int_cast< sal_uInt16 >(GetValue()));
+ if (ePres==SfxItemPresentation::Complete) {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+ return true;
+}
+
+
+// MiscItems
+
+
+// FitToSize
+
+SfxPoolItem* SdrTextFitToSizeTypeItem::CreateDefault() { return new SdrTextFitToSizeTypeItem; }
+
+SdrTextFitToSizeTypeItem* SdrTextFitToSizeTypeItem::Clone(SfxItemPool* /*pPool*/) const { return new SdrTextFitToSizeTypeItem(*this); }
+
+bool SdrTextFitToSizeTypeItem::operator==(const SfxPoolItem& rItem) const
+{
+ if (!SfxEnumItem<css::drawing::TextFitToSizeType>::operator==(rItem))
+ {
+ return false;
+ }
+
+ return m_nMaxScale == static_cast<const SdrTextFitToSizeTypeItem&>(rItem).m_nMaxScale;
+}
+
+sal_uInt16 SdrTextFitToSizeTypeItem::GetValueCount() const { return 4; }
+
+OUString SdrTextFitToSizeTypeItem::GetValueTextByPos(sal_uInt16 nPos)
+{
+ static TranslateId ITEMVALFITTISIZETYPES[] =
+ {
+ STR_ItemValFITTOSIZENONE,
+ STR_ItemValFITTOSIZEPROP,
+ STR_ItemValFITTOSIZEALLLINES,
+ STR_ItemValFITTOSIZERESIZEAT
+ };
+ assert(nPos < SAL_N_ELEMENTS(ITEMVALFITTISIZETYPES) && "wrong pos!");
+ return SvxResId(ITEMVALFITTISIZETYPES[nPos]);
+}
+
+bool SdrTextFitToSizeTypeItem::GetPresentation(SfxItemPresentation ePres,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/, OUString& rText, const IntlWrapper&) const
+{
+ rText=GetValueTextByPos(sal::static_int_cast< sal_uInt16 >(GetValue()));
+ if (ePres==SfxItemPresentation::Complete) {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+ return true;
+}
+
+bool SdrTextFitToSizeTypeItem::HasBoolValue() const { return true; }
+
+bool SdrTextFitToSizeTypeItem::GetBoolValue() const { return GetValue() != drawing::TextFitToSizeType_NONE; }
+
+void SdrTextFitToSizeTypeItem::SetBoolValue(bool bVal)
+{
+ SetValue(bVal ? drawing::TextFitToSizeType_PROPORTIONAL : drawing::TextFitToSizeType_NONE);
+}
+
+bool SdrTextFitToSizeTypeItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ drawing::TextFitToSizeType eFS = GetValue();
+ rVal <<= eFS;
+
+ return true;
+}
+
+bool SdrTextFitToSizeTypeItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ drawing::TextFitToSizeType eFS;
+ if(!(rVal >>= eFS))
+ {
+ sal_Int32 nEnum = 0;
+ if(!(rVal >>= nEnum))
+ return false;
+
+ eFS = static_cast<drawing::TextFitToSizeType>(nEnum);
+ }
+
+ SetValue(eFS);
+
+ return true;
+}
+
+
+SdrTextVertAdjustItem* SdrTextVertAdjustItem::Clone(SfxItemPool* /*pPool*/) const { return new SdrTextVertAdjustItem(*this); }
+
+sal_uInt16 SdrTextVertAdjustItem::GetValueCount() const { return 5; }
+
+OUString SdrTextVertAdjustItem::GetValueTextByPos(sal_uInt16 nPos)
+{
+ static TranslateId ITEMVALTEXTVADJTYPES[] =
+ {
+ STR_ItemValTEXTVADJTOP,
+ STR_ItemValTEXTVADJCENTER,
+ STR_ItemValTEXTVADJBOTTOM,
+ STR_ItemValTEXTVADJBLOCK,
+ STR_ItemValTEXTVADJSTRETCH
+ };
+ assert(nPos < SAL_N_ELEMENTS(ITEMVALTEXTVADJTYPES) && "wrong pos!");
+ return SvxResId(ITEMVALTEXTVADJTYPES[nPos]);
+}
+
+bool SdrTextVertAdjustItem::GetPresentation(SfxItemPresentation ePres,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/, OUString& rText, const IntlWrapper&) const
+{
+ rText=GetValueTextByPos(sal::static_int_cast< sal_uInt16 >(GetValue()));
+ if (ePres==SfxItemPresentation::Complete) {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+ return true;
+}
+
+bool SdrTextVertAdjustItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= static_cast<drawing::TextVerticalAdjust>(GetValue());
+ return true;
+}
+
+bool SdrTextVertAdjustItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ drawing::TextVerticalAdjust eAdj;
+ if(!(rVal >>= eAdj))
+ {
+ sal_Int32 nEnum = 0;
+ if(!(rVal >>= nEnum))
+ return false;
+
+ eAdj = static_cast<drawing::TextVerticalAdjust>(nEnum);
+ }
+
+ SetValue( static_cast<SdrTextVertAdjust>(eAdj) );
+
+ return true;
+}
+
+void SdrTextVertAdjustItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SdrTextVertAdjustItem"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), BAD_CAST(OString::number(GetValue()).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+SdrTextHorzAdjustItem* SdrTextHorzAdjustItem::Clone(SfxItemPool* /*pPool*/) const { return new SdrTextHorzAdjustItem(*this); }
+
+sal_uInt16 SdrTextHorzAdjustItem::GetValueCount() const { return 5; }
+
+OUString SdrTextHorzAdjustItem::GetValueTextByPos(sal_uInt16 nPos)
+{
+ static TranslateId ITEMVALTEXTHADJTYPES[] =
+ {
+ STR_ItemValTEXTHADJLEFT,
+ STR_ItemValTEXTHADJCENTER,
+ STR_ItemValTEXTHADJRIGHT,
+ STR_ItemValTEXTHADJBLOCK,
+ STR_ItemValTEXTHADJSTRETCH
+ };
+ assert(nPos < SAL_N_ELEMENTS(ITEMVALTEXTHADJTYPES) && "wrong pos!");
+ return SvxResId(ITEMVALTEXTHADJTYPES[nPos]);
+}
+
+bool SdrTextHorzAdjustItem::GetPresentation(SfxItemPresentation ePres,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/, OUString& rText, const IntlWrapper&) const
+{
+ rText=GetValueTextByPos(sal::static_int_cast< sal_uInt16 >(GetValue()));
+ if (ePres==SfxItemPresentation::Complete) {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+ return true;
+}
+
+bool SdrTextHorzAdjustItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= static_cast<drawing::TextHorizontalAdjust>(GetValue());
+ return true;
+}
+
+bool SdrTextHorzAdjustItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ drawing::TextHorizontalAdjust eAdj;
+ if(!(rVal >>= eAdj))
+ {
+ sal_Int32 nEnum = 0;
+ if(!(rVal >>= nEnum))
+ return false;
+
+ eAdj = static_cast<drawing::TextHorizontalAdjust>(nEnum);
+ }
+
+ SetValue( static_cast<SdrTextHorzAdjust>(eAdj) );
+
+ return true;
+}
+
+
+SdrTextAniKindItem* SdrTextAniKindItem::Clone(SfxItemPool* /*pPool*/) const { return new SdrTextAniKindItem(*this); }
+
+sal_uInt16 SdrTextAniKindItem::GetValueCount() const { return 5; }
+
+OUString SdrTextAniKindItem::GetValueTextByPos(sal_uInt16 nPos)
+{
+ static TranslateId ITEMVALTEXTANITYPES[] =
+ {
+ STR_ItemValTEXTANI_NONE,
+ STR_ItemValTEXTANI_BLINK,
+ STR_ItemValTEXTANI_SCROLL,
+ STR_ItemValTEXTANI_ALTERNATE,
+ STR_ItemValTEXTANI_SLIDE
+ };
+ assert(nPos < SAL_N_ELEMENTS(ITEMVALTEXTANITYPES) && "wrong pos!");
+ return SvxResId(ITEMVALTEXTANITYPES[nPos]);
+}
+
+bool SdrTextAniKindItem::GetPresentation(SfxItemPresentation ePres,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/, OUString& rText, const IntlWrapper&) const
+{
+ rText=GetValueTextByPos(sal::static_int_cast< sal_uInt16 >(GetValue()));
+ if (ePres==SfxItemPresentation::Complete) {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+ return true;
+}
+
+bool SdrTextAniKindItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= static_cast<drawing::TextAnimationKind>(GetValue());
+ return true;
+}
+
+bool SdrTextAniKindItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ drawing::TextAnimationKind eKind;
+ if(!(rVal >>= eKind))
+ {
+ sal_Int32 nEnum = 0;
+ if(!(rVal >>= nEnum))
+ return false;
+ eKind = static_cast<drawing::TextAnimationKind>(nEnum);
+ }
+
+ SetValue( static_cast<SdrTextAniKind>(eKind) );
+
+ return true;
+}
+
+
+SdrTextAniDirectionItem* SdrTextAniDirectionItem::Clone(SfxItemPool* /*pPool*/) const { return new SdrTextAniDirectionItem(*this); }
+
+sal_uInt16 SdrTextAniDirectionItem::GetValueCount() const { return 4; }
+
+OUString SdrTextAniDirectionItem::GetValueTextByPos(sal_uInt16 nPos)
+{
+ static TranslateId ITEMVALTEXTANITYPES[] =
+ {
+ STR_ItemValTEXTANI_LEFT,
+ STR_ItemValTEXTANI_UP,
+ STR_ItemValTEXTANI_RIGHT,
+ STR_ItemValTEXTANI_DOWN
+ };
+ assert(nPos < SAL_N_ELEMENTS(ITEMVALTEXTANITYPES) && "wrong pos!");
+ return SvxResId(ITEMVALTEXTANITYPES[nPos]);
+}
+
+bool SdrTextAniDirectionItem::GetPresentation(SfxItemPresentation ePres,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/, OUString& rText, const IntlWrapper&) const
+{
+ rText=GetValueTextByPos(sal::static_int_cast< sal_uInt16 >(GetValue()));
+ if (ePres==SfxItemPresentation::Complete) {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+ return true;
+}
+
+bool SdrTextAniDirectionItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= static_cast<drawing::TextAnimationDirection>(GetValue());
+ return true;
+}
+
+bool SdrTextAniDirectionItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ drawing::TextAnimationDirection eDir;
+ if(!(rVal >>= eDir))
+ {
+ sal_Int32 nEnum = 0;
+ if(!(rVal >>= nEnum))
+ return false;
+
+ eDir = static_cast<drawing::TextAnimationDirection>(nEnum);
+ }
+
+ SetValue( static_cast<SdrTextAniDirection>(eDir) );
+
+ return true;
+}
+
+
+SdrTextAniDelayItem* SdrTextAniDelayItem::Clone(SfxItemPool* /*pPool*/) const { return new SdrTextAniDelayItem(*this); }
+
+bool SdrTextAniDelayItem::GetPresentation(
+ SfxItemPresentation ePres, MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/,
+ OUString& rText, const IntlWrapper&) const
+{
+ rText = OUString::number(GetValue()) + "ms";
+
+ if(ePres == SfxItemPresentation::Complete)
+ {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+
+ return true;
+}
+
+
+SdrTextAniAmountItem* SdrTextAniAmountItem::Clone(SfxItemPool* /*pPool*/) const { return new SdrTextAniAmountItem(*this); }
+
+bool SdrTextAniAmountItem::HasMetrics() const
+{
+ return GetValue()>0;
+}
+
+void SdrTextAniAmountItem::ScaleMetrics(tools::Long nMul, tools::Long nDiv)
+{
+ if (GetValue()>0) {
+ BigInt aVal(GetValue());
+ aVal*=nMul;
+ aVal+=nDiv/2; // to round accurately
+ aVal/=nDiv;
+ SetValue(short(aVal));
+ }
+}
+
+bool SdrTextAniAmountItem::GetPresentation(
+ SfxItemPresentation ePres, MapUnit eCoreMetric, MapUnit ePresMetric,
+ OUString& rText, const IntlWrapper&) const
+{
+ sal_Int32 nValue(GetValue());
+
+ if(!nValue)
+ nValue = -1;
+
+ if(nValue < 0)
+ {
+ rText = OUString::number(-nValue) + "pixel";
+ }
+ else
+ {
+ SdrFormatter aFmt(eCoreMetric, ePresMetric);
+ rText = aFmt.GetStr(nValue) +
+ SdrFormatter::GetUnitStr(ePresMetric);
+ }
+
+ if(ePres == SfxItemPresentation::Complete)
+ {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+
+ return true;
+}
+
+
+SdrTextFixedCellHeightItem::SdrTextFixedCellHeightItem( bool bUseFixedCellHeight )
+ : SfxBoolItem( SDRATTR_TEXT_USEFIXEDCELLHEIGHT, bUseFixedCellHeight )
+{
+}
+bool SdrTextFixedCellHeightItem::GetPresentation( SfxItemPresentation ePres,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresentationMetric*/,
+ OUString &rText, const IntlWrapper& ) const
+{
+ rText = GetValueTextByVal( GetValue() );
+ if (ePres==SfxItemPresentation::Complete)
+ {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+ return true;
+}
+
+SdrTextFixedCellHeightItem* SdrTextFixedCellHeightItem::Clone( SfxItemPool * /*pPool*/) const
+{
+ return new SdrTextFixedCellHeightItem( GetValue() );
+}
+
+bool SdrTextFixedCellHeightItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ bool bValue = GetValue();
+ rVal <<= bValue;
+ return true;
+}
+bool SdrTextFixedCellHeightItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ bool bValue;
+ if( !( rVal >>= bValue ) )
+ return false;
+ SetValue( bValue );
+ return true;
+}
+
+// EdgeKind
+
+SdrEdgeKindItem* SdrEdgeKindItem::Clone(SfxItemPool* /*pPool*/) const { return new SdrEdgeKindItem(*this); }
+
+sal_uInt16 SdrEdgeKindItem::GetValueCount() const { return 4; }
+
+OUString SdrEdgeKindItem::GetValueTextByPos(sal_uInt16 nPos)
+{
+ static TranslateId ITEMVALEDGES[] =
+ {
+ STR_ItemValEDGE_ORTHOLINES,
+ STR_ItemValEDGE_THREELINES,
+ STR_ItemValEDGE_ONELINE,
+ STR_ItemValEDGE_BEZIER
+ };
+ assert(nPos < SAL_N_ELEMENTS(ITEMVALEDGES) && "wrong pos!");
+ return SvxResId(ITEMVALEDGES[nPos]);
+}
+
+bool SdrEdgeKindItem::GetPresentation(SfxItemPresentation ePres,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/, OUString& rText, const IntlWrapper&) const
+{
+ rText=GetValueTextByPos(sal::static_int_cast< sal_uInt16 >(GetValue()));
+ if (ePres==SfxItemPresentation::Complete) {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+ return true;
+}
+
+bool SdrEdgeKindItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ drawing::ConnectorType eCT = drawing::ConnectorType_STANDARD;
+
+ switch( GetValue() )
+ {
+ case SdrEdgeKind::OrthoLines : eCT = drawing::ConnectorType_STANDARD; break;
+ case SdrEdgeKind::ThreeLines : eCT = drawing::ConnectorType_LINES; break;
+ case SdrEdgeKind::OneLine : eCT = drawing::ConnectorType_LINE; break;
+ case SdrEdgeKind::Bezier : eCT = drawing::ConnectorType_CURVE; break;
+ case SdrEdgeKind::Arc : eCT = drawing::ConnectorType_CURVE; break;
+ default:
+ OSL_FAIL( "SdrEdgeKindItem::QueryValue : unknown enum" );
+ }
+
+ rVal <<= eCT;
+
+ return true;
+}
+
+bool SdrEdgeKindItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ drawing::ConnectorType eCT;
+ if(!(rVal >>= eCT))
+ {
+ sal_Int32 nEnum = 0;
+ if(!(rVal >>= nEnum))
+ return false;
+
+ eCT = static_cast<drawing::ConnectorType>(nEnum);
+ }
+
+ SdrEdgeKind eEK = SdrEdgeKind::OrthoLines;
+ switch( eCT )
+ {
+ case drawing::ConnectorType_STANDARD : eEK = SdrEdgeKind::OrthoLines; break;
+ case drawing::ConnectorType_CURVE : eEK = SdrEdgeKind::Bezier; break;
+ case drawing::ConnectorType_LINE : eEK = SdrEdgeKind::OneLine; break;
+ case drawing::ConnectorType_LINES : eEK = SdrEdgeKind::ThreeLines; break;
+ default:
+ OSL_FAIL( "SdrEdgeKindItem::PuValue : unknown enum" );
+ }
+ SetValue( eEK );
+
+ return true;
+}
+
+bool SdrEdgeNode1HorzDistItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= GetValue();
+ return true;
+}
+
+bool SdrEdgeNode1HorzDistItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ sal_Int32 nValue = 0;
+ if(!(rVal >>= nValue))
+ return false;
+
+ SetValue( nValue );
+ return true;
+}
+
+SdrEdgeNode1HorzDistItem* SdrEdgeNode1HorzDistItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new SdrEdgeNode1HorzDistItem(*this);
+}
+
+bool SdrEdgeNode1VertDistItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= GetValue();
+ return true;
+}
+
+bool SdrEdgeNode1VertDistItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ sal_Int32 nValue = 0;
+ if(!(rVal >>= nValue))
+ return false;
+
+ SetValue( nValue );
+ return true;
+}
+
+SdrEdgeNode1VertDistItem* SdrEdgeNode1VertDistItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new SdrEdgeNode1VertDistItem(*this);
+}
+
+bool SdrEdgeNode2HorzDistItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= GetValue();
+ return true;
+}
+
+bool SdrEdgeNode2HorzDistItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ sal_Int32 nValue = 0;
+ if(!(rVal >>= nValue))
+ return false;
+
+ SetValue( nValue );
+ return true;
+}
+
+SdrEdgeNode2HorzDistItem* SdrEdgeNode2HorzDistItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new SdrEdgeNode2HorzDistItem(*this);
+}
+
+bool SdrEdgeNode2VertDistItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= GetValue();
+ return true;
+}
+
+bool SdrEdgeNode2VertDistItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ sal_Int32 nValue = 0;
+ if(!(rVal >>= nValue))
+ return false;
+
+ SetValue( nValue );
+ return true;
+}
+
+SdrEdgeNode2VertDistItem* SdrEdgeNode2VertDistItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new SdrEdgeNode2VertDistItem(*this);
+}
+
+SdrEdgeNode1GlueDistItem* SdrEdgeNode1GlueDistItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new SdrEdgeNode1GlueDistItem(*this);
+}
+
+SdrEdgeNode2GlueDistItem* SdrEdgeNode2GlueDistItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new SdrEdgeNode2GlueDistItem(*this);
+}
+
+SdrMeasureKindItem* SdrMeasureKindItem::Clone(SfxItemPool* /*pPool*/) const { return new SdrMeasureKindItem(*this); }
+
+sal_uInt16 SdrMeasureKindItem::GetValueCount() const { return 2; }
+
+OUString SdrMeasureKindItem::GetValueTextByPos(sal_uInt16 nPos)
+{
+ static TranslateId ITEMVALMEASURETYPES[] =
+ {
+ STR_ItemValMEASURE_STD,
+ STR_ItemValMEASURE_RADIUS
+ };
+ assert(nPos < SAL_N_ELEMENTS(ITEMVALMEASURETYPES) && "wrong pos!");
+ return SvxResId(ITEMVALMEASURETYPES[nPos]);
+}
+
+bool SdrMeasureKindItem::GetPresentation(SfxItemPresentation ePres,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/, OUString& rText, const IntlWrapper&) const
+{
+ rText=GetValueTextByPos(sal::static_int_cast< sal_uInt16 >(GetValue()));
+ if (ePres==SfxItemPresentation::Complete) {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+ return true;
+}
+
+bool SdrMeasureKindItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= static_cast<drawing::MeasureKind>(GetValue());
+ return true;
+}
+
+bool SdrMeasureKindItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ drawing::MeasureKind eKind;
+ if(!(rVal >>= eKind))
+ {
+ sal_Int32 nEnum = 0;
+ if(!(rVal >>= nEnum))
+ return false;
+
+ eKind = static_cast<drawing::MeasureKind>(nEnum);
+ }
+
+ SetValue( static_cast<SdrMeasureKind>(eKind) );
+ return true;
+}
+
+
+SdrMeasureTextHPosItem* SdrMeasureTextHPosItem::Clone(SfxItemPool* /*pPool*/) const { return new SdrMeasureTextHPosItem(*this); }
+
+sal_uInt16 SdrMeasureTextHPosItem::GetValueCount() const { return 4; }
+
+const OUString & SdrMeasureTextHPosItem::GetValueTextByPos(sal_uInt16 nPos)
+{
+ static std::array<OUString, 4> aMeasureTextHPosItem
+ {
+ "automatic",
+ "left outside",
+ "inside (centered)",
+ "right outside"
+ };
+ assert(nPos < aMeasureTextHPosItem.size() && "wrong pos!");
+ return aMeasureTextHPosItem[nPos];
+}
+
+bool SdrMeasureTextHPosItem::GetPresentation(SfxItemPresentation ePres,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/, OUString& rText, const IntlWrapper&) const
+{
+ rText=GetValueTextByPos(sal::static_int_cast< sal_uInt16 >(GetValue()));
+ if (ePres==SfxItemPresentation::Complete) {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+ return true;
+}
+
+bool SdrMeasureTextHPosItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= GetValue();
+ return true;
+}
+
+bool SdrMeasureTextHPosItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ drawing::MeasureTextHorzPos ePos;
+ if(!(rVal >>= ePos))
+ {
+ sal_Int32 nEnum = 0;
+ if(!(rVal >>= nEnum))
+ return false;
+
+ ePos = static_cast<drawing::MeasureTextHorzPos>(nEnum);
+ }
+
+ SetValue(ePos);
+ return true;
+}
+
+SdrMeasureTextVPosItem* SdrMeasureTextVPosItem::Clone(SfxItemPool* /*pPool*/) const { return new SdrMeasureTextVPosItem(*this); }
+
+sal_uInt16 SdrMeasureTextVPosItem::GetValueCount() const { return 5; }
+
+OUString SdrMeasureTextVPosItem::GetValueTextByPos(sal_uInt16 nPos)
+{
+ static TranslateId ITEMVALMEASURETEXTTYPES[] =
+ {
+ STR_ItemValMEASURE_TEXTVAUTO,
+ STR_ItemValMEASURE_ABOVE,
+ STR_ItemValMEASURETEXT_BREAKEDLINE,
+ STR_ItemValMEASURE_BELOW,
+ STR_ItemValMEASURETEXT_VERTICALCEN
+ };
+ assert(nPos < SAL_N_ELEMENTS(ITEMVALMEASURETEXTTYPES) && "wrong pos!");
+ return SvxResId(ITEMVALMEASURETEXTTYPES[nPos]);
+}
+
+bool SdrMeasureTextVPosItem::GetPresentation(SfxItemPresentation ePres,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/, OUString& rText, const IntlWrapper&) const
+{
+ rText=GetValueTextByPos(sal::static_int_cast< sal_uInt16 >(GetValue()));
+ if (ePres==SfxItemPresentation::Complete) {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+ return true;
+}
+
+bool SdrMeasureTextVPosItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= GetValue();
+ return true;
+}
+
+bool SdrMeasureTextVPosItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ drawing::MeasureTextVertPos ePos;
+ if(!(rVal >>= ePos))
+ {
+ sal_Int32 nEnum = 0;
+ if(!(rVal >>= nEnum))
+ return false;
+
+ ePos = static_cast<drawing::MeasureTextVertPos>(nEnum);
+ }
+
+ SetValue(ePos);
+ return true;
+}
+
+SdrMeasureUnitItem* SdrMeasureUnitItem::Clone(SfxItemPool* /*pPool*/) const { return new SdrMeasureUnitItem(*this); }
+
+sal_uInt16 SdrMeasureUnitItem::GetValueCount() const { return 14; }
+
+OUString SdrMeasureUnitItem::GetValueTextByPos(sal_uInt16 nPos)
+{
+ if(static_cast<FieldUnit>(nPos) == FieldUnit::NONE)
+ return "default";
+ else
+ return SdrFormatter::GetUnitStr(static_cast<FieldUnit>(nPos));
+}
+
+bool SdrMeasureUnitItem::GetPresentation(SfxItemPresentation ePres,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/, OUString& rText, const IntlWrapper&) const
+{
+ rText=GetValueTextByPos(sal::static_int_cast< sal_uInt16 >(GetValue()));
+ if (ePres==SfxItemPresentation::Complete) {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+ return true;
+}
+
+bool SdrMeasureUnitItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= static_cast<sal_Int32>(GetValue());
+ return true;
+}
+
+bool SdrMeasureUnitItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ sal_Int32 nMeasure = 0;
+ if(!(rVal >>= nMeasure))
+ return false;
+
+ SetValue( static_cast<FieldUnit>(nMeasure) );
+ return true;
+}
+
+
+SdrCircKindItem* SdrCircKindItem::Clone(SfxItemPool* /*pPool*/) const { return new SdrCircKindItem(*this); }
+
+sal_uInt16 SdrCircKindItem::GetValueCount() const { return 4; }
+
+OUString SdrCircKindItem::GetValueTextByPos(sal_uInt16 nPos)
+{
+ static TranslateId ITEMVALCIRCTYPES[] =
+ {
+ STR_ItemValCIRC_FULL,
+ STR_ItemValCIRC_SECT,
+ STR_ItemValCIRC_CUT,
+ STR_ItemValCIRC_ARC
+ };
+ assert(nPos < SAL_N_ELEMENTS(ITEMVALCIRCTYPES) && "wrong pos!");
+ return SvxResId(ITEMVALCIRCTYPES[nPos]);
+}
+
+bool SdrCircKindItem::GetPresentation(SfxItemPresentation ePres,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/, OUString& rText, const IntlWrapper&) const
+{
+ rText=GetValueTextByPos(sal::static_int_cast< sal_uInt16 >(GetValue()));
+ if (ePres==SfxItemPresentation::Complete) {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+ return true;
+}
+
+bool SdrCircKindItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= static_cast<drawing::CircleKind>(GetValue());
+ return true;
+}
+
+bool SdrCircKindItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ drawing::CircleKind eKind;
+ if(!(rVal >>= eKind))
+ {
+ sal_Int32 nEnum = 0;
+ if(!(rVal >>= nEnum))
+ return false;
+
+ eKind = static_cast<drawing::CircleKind>(nEnum);
+ }
+
+ SetValue( static_cast<SdrCircKind>(eKind) );
+ return true;
+}
+
+SdrSignedPercentItem* SdrSignedPercentItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new SdrSignedPercentItem( Which(), GetValue() );
+}
+
+bool SdrSignedPercentItem::GetPresentation(
+ SfxItemPresentation ePres, MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/,
+ OUString& rText, const IntlWrapper&) const
+{
+ rText = unicode::formatPercent(GetValue(),
+ Application::GetSettings().GetUILanguageTag());
+
+ if(ePres == SfxItemPresentation::Complete)
+ {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+
+ return true;
+}
+
+SdrGrafRedItem* SdrGrafRedItem::Clone( SfxItemPool* /*pPool*/) const
+{
+ return new SdrGrafRedItem( *this );
+}
+
+SdrGrafGreenItem* SdrGrafGreenItem::Clone( SfxItemPool* /*pPool*/) const
+{
+ return new SdrGrafGreenItem( *this );
+}
+
+SdrGrafBlueItem* SdrGrafBlueItem::Clone( SfxItemPool* /*pPool*/) const
+{
+ return new SdrGrafBlueItem( *this );
+}
+
+SdrGrafLuminanceItem* SdrGrafLuminanceItem::Clone( SfxItemPool* /*pPool*/) const
+{
+ return new SdrGrafLuminanceItem( *this );
+}
+
+SdrGrafContrastItem* SdrGrafContrastItem::Clone( SfxItemPool* /*pPool*/) const
+{
+ return new SdrGrafContrastItem( *this );
+}
+
+SdrGrafGamma100Item* SdrGrafGamma100Item::Clone( SfxItemPool* /*pPool */) const
+{
+ return new SdrGrafGamma100Item( *this );
+}
+
+bool SdrGrafGamma100Item::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= static_cast<double>(GetValue()) / 100.0;
+ return true;
+}
+
+bool SdrGrafGamma100Item::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ double nGamma = 0;
+ if(!(rVal >>= nGamma))
+ return false;
+
+ SetValue( static_cast<sal_uInt32>(nGamma * 100.0 ) );
+ return true;
+}
+
+SdrGrafInvertItem* SdrGrafInvertItem::Clone( SfxItemPool* /*pPool*/) const
+{
+ return new SdrGrafInvertItem( *this );
+}
+
+SdrGrafTransparenceItem* SdrGrafTransparenceItem::Clone( SfxItemPool* /*pPool*/) const
+{
+ return new SdrGrafTransparenceItem( *this );
+}
+
+SdrGrafModeItem* SdrGrafModeItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new SdrGrafModeItem( *this );
+}
+
+sal_uInt16 SdrGrafModeItem::GetValueCount() const
+{
+ return 4;
+}
+
+OUString SdrGrafModeItem::GetValueTextByPos(sal_uInt16 nPos)
+{
+ OUString aStr;
+
+ switch(nPos)
+ {
+ case 1:
+ {
+ aStr = "Greys";
+ break;
+ }
+ case 2:
+ {
+ aStr = "Black/White";
+ break;
+ }
+ case 3:
+ {
+ aStr = "Watermark";
+ break;
+ }
+ default:
+ {
+ aStr = "Standard";
+ break;
+ }
+ }
+
+ return aStr;
+}
+
+bool SdrGrafModeItem::GetPresentation( SfxItemPresentation ePres,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/,
+ OUString& rText, const IntlWrapper&) const
+{
+ rText = GetValueTextByPos( sal::static_int_cast< sal_uInt16 >( GetValue() ) );
+
+ if( ePres == SfxItemPresentation::Complete )
+ {
+ rText = SdrItemPool::GetItemName( Which() ) + " " + rText;
+ }
+
+ return true;
+}
+
+SdrGrafCropItem* SdrGrafCropItem::Clone( SfxItemPool* /*pPool*/) const
+{
+ return new SdrGrafCropItem( *this );
+}
+
+SdrTextAniStartInsideItem::~SdrTextAniStartInsideItem()
+{
+}
+
+SdrTextAniStartInsideItem* SdrTextAniStartInsideItem::Clone(SfxItemPool* ) const
+{
+ return new SdrTextAniStartInsideItem(*this);
+}
+
+SdrTextAniStopInsideItem::~SdrTextAniStopInsideItem()
+{
+}
+
+SdrTextAniStopInsideItem* SdrTextAniStopInsideItem::Clone(SfxItemPool* ) const
+{
+ return new SdrTextAniStopInsideItem(*this);
+}
+
+SdrCaptionEscIsRelItem::~SdrCaptionEscIsRelItem()
+{
+}
+
+SdrCaptionEscIsRelItem* SdrCaptionEscIsRelItem::Clone(SfxItemPool* ) const
+{
+ return new SdrCaptionEscIsRelItem(*this);
+}
+
+SdrCaptionEscRelItem::~SdrCaptionEscRelItem()
+{
+}
+
+SdrCaptionEscRelItem* SdrCaptionEscRelItem::Clone(SfxItemPool*) const
+{
+ return new SdrCaptionEscRelItem(*this);
+}
+
+SdrCaptionFitLineLenItem::~SdrCaptionFitLineLenItem()
+{
+}
+
+SdrCaptionFitLineLenItem* SdrCaptionFitLineLenItem::Clone(SfxItemPool* ) const
+{
+ return new SdrCaptionFitLineLenItem(*this);
+}
+
+SdrCaptionLineLenItem::~SdrCaptionLineLenItem()
+{
+}
+
+SdrCaptionLineLenItem* SdrCaptionLineLenItem::Clone(SfxItemPool*) const
+{
+ return new SdrCaptionLineLenItem(*this);
+}
+
+SdrMeasureBelowRefEdgeItem::~SdrMeasureBelowRefEdgeItem()
+{
+}
+
+SdrMeasureBelowRefEdgeItem* SdrMeasureBelowRefEdgeItem::Clone(SfxItemPool* ) const
+{
+ return new SdrMeasureBelowRefEdgeItem(*this);
+}
+
+SdrMeasureTextIsFixedAngleItem::~SdrMeasureTextIsFixedAngleItem()
+{
+}
+
+SdrMeasureTextIsFixedAngleItem* SdrMeasureTextIsFixedAngleItem::Clone(SfxItemPool* ) const
+{
+ return new SdrMeasureTextIsFixedAngleItem(*this);
+}
+
+SdrMeasureTextFixedAngleItem::~SdrMeasureTextFixedAngleItem()
+{
+}
+
+SdrMeasureTextFixedAngleItem* SdrMeasureTextFixedAngleItem::Clone(SfxItemPool* ) const
+{
+ return new SdrMeasureTextFixedAngleItem(*this);
+}
+
+SdrMeasureDecimalPlacesItem::~SdrMeasureDecimalPlacesItem()
+{
+}
+
+SdrMeasureDecimalPlacesItem* SdrMeasureDecimalPlacesItem::Clone(SfxItemPool* ) const
+{
+ return new SdrMeasureDecimalPlacesItem(*this);
+}
+
+SdrMeasureTextRota90Item::~SdrMeasureTextRota90Item()
+{
+}
+
+SdrMeasureTextRota90Item* SdrMeasureTextRota90Item::Clone(SfxItemPool* ) const
+{
+ return new SdrMeasureTextRota90Item(*this);
+}
+
+SdrMeasureTextUpsideDownItem::~SdrMeasureTextUpsideDownItem()
+{
+}
+
+SdrMeasureTextUpsideDownItem* SdrMeasureTextUpsideDownItem::Clone(SfxItemPool* ) const
+{
+ return new SdrMeasureTextUpsideDownItem(*this);
+}
+
+SdrLayerIdItem* SdrLayerIdItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new SdrLayerIdItem(*this);
+}
+
+SdrLayerNameItem* SdrLayerNameItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new SdrLayerNameItem(*this);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdcrtv.cxx b/svx/source/svdraw/svdcrtv.cxx
new file mode 100644
index 000000000..9791df7ae
--- /dev/null
+++ b/svx/source/svdraw/svdcrtv.cxx
@@ -0,0 +1,916 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <svx/svdcrtv.hxx>
+#include <svx/xlnclit.hxx>
+#include <svx/svdocapt.hxx>
+#include <svx/svdoedge.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/scene3d.hxx>
+#include <svx/view3d.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflclit.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/svdouno.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/sdr/overlay/overlaypolypolygon.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <fmobj.hxx>
+#include <svx/svdocirc.hxx>
+#include <svx/sdr/contact/viewcontact.hxx>
+#include <svx/sdr/overlay/overlayprimitive2dsequenceobject.hxx>
+#include <vcl/ptrstyle.hxx>
+
+using namespace com::sun::star;
+
+class ImplConnectMarkerOverlay
+{
+ // The OverlayObjects
+ sdr::overlay::OverlayObjectList maObjects;
+
+ // The remembered target object
+ const SdrObject& mrObject;
+
+public:
+ ImplConnectMarkerOverlay(const SdrCreateView& rView, SdrObject const & rObject);
+
+ // The OverlayObjects are cleared using the destructor of OverlayObjectList.
+ // That destructor calls clear() at the list which removes all objects from the
+ // OverlayManager and deletes them.
+
+ const SdrObject& GetTargetObject() const { return mrObject; }
+};
+
+ImplConnectMarkerOverlay::ImplConnectMarkerOverlay(const SdrCreateView& rView, SdrObject const & rObject)
+: mrObject(rObject)
+{
+ basegfx::B2DPolyPolygon aB2DPolyPolygon(rObject.TakeXorPoly());
+
+ for(sal_uInt32 a(0); a < rView.PaintWindowCount(); a++)
+ {
+ SdrPaintWindow* pCandidate = rView.GetPaintWindow(a);
+ const rtl::Reference< sdr::overlay::OverlayManager >& xTargetOverlay = pCandidate->GetOverlayManager();
+
+ if(xTargetOverlay.is())
+ {
+ float fScalingFactor = xTargetOverlay->getOutputDevice().GetDPIScaleFactor();
+ Size aHalfLogicSize(xTargetOverlay->getOutputDevice().PixelToLogic(Size(4 * fScalingFactor, 4 * fScalingFactor)));
+
+ // object
+ std::unique_ptr<sdr::overlay::OverlayPolyPolygonStripedAndFilled> pNew(new sdr::overlay::OverlayPolyPolygonStripedAndFilled(
+ aB2DPolyPolygon));
+ xTargetOverlay->add(*pNew);
+ maObjects.append(std::move(pNew));
+
+ // gluepoints
+ for(sal_uInt16 i(0); i < 4; i++)
+ {
+ SdrGluePoint aGluePoint(rObject.GetVertexGluePoint(i));
+ const Point& rPosition = aGluePoint.GetAbsolutePos(rObject);
+
+ basegfx::B2DPoint aTopLeft(rPosition.X() - aHalfLogicSize.Width(), rPosition.Y() - aHalfLogicSize.Height());
+ basegfx::B2DPoint aBottomRight(rPosition.X() + aHalfLogicSize.Width(), rPosition.Y() + aHalfLogicSize.Height());
+
+ basegfx::B2DPolygon aTempPoly;
+ aTempPoly.append(aTopLeft);
+ aTempPoly.append(basegfx::B2DPoint(aBottomRight.getX(), aTopLeft.getY()));
+ aTempPoly.append(aBottomRight);
+ aTempPoly.append(basegfx::B2DPoint(aTopLeft.getX(), aBottomRight.getY()));
+ aTempPoly.setClosed(true);
+
+ basegfx::B2DPolyPolygon aTempPolyPoly;
+ aTempPolyPoly.append(aTempPoly);
+
+ std::unique_ptr<sdr::overlay::OverlayPolyPolygonStripedAndFilled> pNew2(new sdr::overlay::OverlayPolyPolygonStripedAndFilled(
+ aTempPolyPoly));
+ xTargetOverlay->add(*pNew2);
+ maObjects.append(std::move(pNew2));
+ }
+ }
+ }
+}
+
+class ImpSdrCreateViewExtraData
+{
+ // The OverlayObjects for XOR replacement
+ sdr::overlay::OverlayObjectList maObjects;
+
+public:
+ ImpSdrCreateViewExtraData();
+ ~ImpSdrCreateViewExtraData();
+
+ void CreateAndShowOverlay(const SdrCreateView& rView, const SdrObject* pObject, const basegfx::B2DPolyPolygon& rPolyPoly);
+ void HideOverlay();
+};
+
+ImpSdrCreateViewExtraData::ImpSdrCreateViewExtraData()
+{
+}
+
+ImpSdrCreateViewExtraData::~ImpSdrCreateViewExtraData()
+{
+ HideOverlay();
+}
+
+void ImpSdrCreateViewExtraData::CreateAndShowOverlay(const SdrCreateView& rView, const SdrObject* pObject, const basegfx::B2DPolyPolygon& rPolyPoly)
+{
+ for(sal_uInt32 a(0); a < rView.PaintWindowCount(); a++)
+ {
+ SdrPaintWindow* pCandidate = rView.GetPaintWindow(a);
+ const rtl::Reference<sdr::overlay::OverlayManager>& xOverlayManager = pCandidate->GetOverlayManager();
+
+ if (xOverlayManager.is())
+ {
+ if(pObject)
+ {
+ const sdr::contact::ViewContact& rVC = pObject->GetViewContact();
+ drawinglayer::primitive2d::Primitive2DContainer aSequence;
+ rVC.getViewIndependentPrimitive2DContainer(aSequence);
+ std::unique_ptr<sdr::overlay::OverlayObject> pNew(new sdr::overlay::OverlayPrimitive2DSequenceObject(std::move(aSequence)));
+
+ xOverlayManager->add(*pNew);
+ maObjects.append(std::move(pNew));
+ }
+
+ if(rPolyPoly.count())
+ {
+ std::unique_ptr<sdr::overlay::OverlayPolyPolygonStripedAndFilled> pNew(new sdr::overlay::OverlayPolyPolygonStripedAndFilled(
+ rPolyPoly));
+ xOverlayManager->add(*pNew);
+ maObjects.append(std::move(pNew));
+ }
+ }
+ }
+}
+
+void ImpSdrCreateViewExtraData::HideOverlay()
+{
+ // the clear() call of the list removes all objects from the
+ // OverlayManager and deletes them.
+ maObjects.clear();
+}
+
+
+// CreateView
+
+
+void SdrCreateView::ImpClearConnectMarker()
+{
+ mpCoMaOverlay.reset();
+}
+
+SdrCreateView::SdrCreateView(SdrModel& rSdrModel, OutputDevice* pOut)
+ : SdrDragView(rSdrModel, pOut)
+ , mpCurrentCreate(nullptr)
+ , mpCreatePV(nullptr)
+ , mpCreateViewExtraData(new ImpSdrCreateViewExtraData())
+ , maCurrentCreatePointer(PointerStyle::Cross)
+ , mnAutoCloseDistPix(5)
+ , mnFreeHandMinDistPix(10)
+ , mnCurrentInvent(SdrInventor::Default)
+ , mnCurrentIdent(SdrObjKind::NONE)
+ , mb1stPointAsCenter(false)
+ , mbUseIncompatiblePathCreateInterface(false)
+{
+}
+
+SdrCreateView::~SdrCreateView()
+{
+ ImpClearConnectMarker();
+ mpCreateViewExtraData.reset();
+ SdrObject::Free(mpCurrentCreate);
+}
+
+bool SdrCreateView::IsAction() const
+{
+ return SdrDragView::IsAction() || mpCurrentCreate!=nullptr;
+}
+
+void SdrCreateView::MovAction(const Point& rPnt)
+{
+ SdrDragView::MovAction(rPnt);
+ if (mpCurrentCreate != nullptr) {
+ MovCreateObj(rPnt);
+ }
+}
+
+void SdrCreateView::EndAction()
+{
+ if (mpCurrentCreate != nullptr) EndCreateObj(SdrCreateCmd::ForceEnd);
+ SdrDragView::EndAction();
+}
+
+void SdrCreateView::BckAction()
+{
+ if (mpCurrentCreate != nullptr) BckCreateObj();
+ SdrDragView::BckAction();
+}
+
+void SdrCreateView::BrkAction()
+{
+ SdrDragView::BrkAction();
+ BrkCreateObj();
+}
+
+void SdrCreateView::TakeActionRect(tools::Rectangle& rRect) const
+{
+ if (mpCurrentCreate != nullptr)
+ {
+ rRect=maDragStat.GetActionRect();
+ if (rRect.IsEmpty())
+ {
+ rRect=tools::Rectangle(maDragStat.GetPrev(),maDragStat.GetNow());
+ }
+ }
+ else
+ {
+ SdrDragView::TakeActionRect(rRect);
+ }
+}
+
+bool SdrCreateView::CheckEdgeMode()
+{
+ if (mpCurrentCreate != nullptr)
+ {
+ // is managed by EdgeObj
+ if (mnCurrentInvent==SdrInventor::Default && mnCurrentIdent==SdrObjKind::Edge) return false;
+ }
+
+ if (!IsCreateMode() || mnCurrentInvent!=SdrInventor::Default || mnCurrentIdent!=SdrObjKind::Edge)
+ {
+ ImpClearConnectMarker();
+ return false;
+ }
+ else
+ {
+ // sal_True, if MouseMove should check Connect
+ return !IsAction();
+ }
+}
+
+void SdrCreateView::SetConnectMarker(const SdrObjConnection& rCon)
+{
+ SdrObject* pTargetObject = rCon.pObj;
+
+ if(pTargetObject)
+ {
+ // if target object changes, throw away overlay object to make room for changes
+ if(mpCoMaOverlay && pTargetObject != &mpCoMaOverlay->GetTargetObject())
+ {
+ ImpClearConnectMarker();
+ }
+
+ if(!mpCoMaOverlay)
+ {
+ mpCoMaOverlay.reset(new ImplConnectMarkerOverlay(*this, *pTargetObject));
+ }
+ }
+ else
+ {
+ ImpClearConnectMarker();
+ }
+}
+
+void SdrCreateView::HideConnectMarker()
+{
+ ImpClearConnectMarker();
+}
+
+bool SdrCreateView::MouseMove(const MouseEvent& rMEvt, OutputDevice* pWin)
+{
+ if(CheckEdgeMode() && pWin)
+ {
+ SdrPageView* pPV = GetSdrPageView();
+
+ if(pPV)
+ {
+ // TODO: Change default hit tolerance at IsMarkedHit() some time!
+ Point aPos(pWin->PixelToLogic(rMEvt.GetPosPixel()));
+ bool bMarkHit=PickHandle(aPos)!=nullptr || IsMarkedObjHit(aPos);
+ SdrObjConnection aCon;
+ if (!bMarkHit) SdrEdgeObj::ImpFindConnector(aPos,*pPV,aCon,nullptr,pWin);
+ SetConnectMarker(aCon);
+ }
+ }
+ return SdrDragView::MouseMove(rMEvt,pWin);
+}
+
+bool SdrCreateView::IsTextTool() const
+{
+ return meEditMode==SdrViewEditMode::Create
+ && mnCurrentInvent==SdrInventor::Default
+ && (mnCurrentIdent==SdrObjKind::Text
+ || mnCurrentIdent==SdrObjKind::TitleText
+ || mnCurrentIdent==SdrObjKind::OutlineText);
+}
+
+bool SdrCreateView::IsEdgeTool() const
+{
+ return meEditMode==SdrViewEditMode::Create && mnCurrentInvent==SdrInventor::Default && (mnCurrentIdent==SdrObjKind::Edge);
+}
+
+bool SdrCreateView::IsMeasureTool() const
+{
+ return meEditMode==SdrViewEditMode::Create && mnCurrentInvent==SdrInventor::Default && (mnCurrentIdent==SdrObjKind::Measure);
+}
+
+void SdrCreateView::SetCurrentObj(SdrObjKind nIdent, SdrInventor nInvent)
+{
+ if (mnCurrentInvent!=nInvent || mnCurrentIdent!=nIdent)
+ {
+ mnCurrentInvent=nInvent;
+ mnCurrentIdent=nIdent;
+ SdrObject * pObj = (nIdent == SdrObjKind::NONE) ? nullptr :
+ SdrObjFactory::MakeNewObject(
+ *GetModel(),
+ nInvent,
+ nIdent);
+
+ if(pObj)
+ {
+ // Using text tool, mouse cursor is usually I-Beam,
+ // crosshairs with tiny I-Beam appears only on MouseButtonDown.
+ if(IsTextTool())
+ {
+ // Here the correct pointer needs to be used
+ // if the default is set to vertical writing
+ maCurrentCreatePointer = PointerStyle::Text;
+ }
+ else
+ maCurrentCreatePointer = pObj->GetCreatePointer();
+
+ SdrObject::Free( pObj );
+ }
+ else
+ {
+ maCurrentCreatePointer = PointerStyle::Cross;
+ }
+ }
+
+ CheckEdgeMode();
+ ImpSetGlueVisible3(IsEdgeTool());
+}
+
+bool SdrCreateView::ImpBegCreateObj(SdrInventor nInvent, SdrObjKind nIdent, const Point& rPnt, OutputDevice* pOut,
+ sal_Int16 nMinMov, const tools::Rectangle& rLogRect, SdrObject* pPreparedFactoryObject)
+{
+ bool bRet=false;
+ UnmarkAllObj();
+ BrkAction();
+
+ ImpClearConnectMarker();
+
+ mpCreatePV = GetSdrPageView();
+
+ if (mpCreatePV != nullptr)
+ { // otherwise no side registered!
+ OUString aLay(maActualLayer);
+
+ if(nInvent == SdrInventor::Default && nIdent == SdrObjKind::Measure && !maMeasureLayer.isEmpty())
+ {
+ aLay = maMeasureLayer;
+ }
+
+ SdrLayerID nLayer = mpCreatePV->GetPage()->GetLayerAdmin().GetLayerID(aLay);
+ if (nLayer==SDRLAYER_NOTFOUND) nLayer = SdrLayerID(0);
+ if (!mpCreatePV->GetLockedLayers().IsSet(nLayer) && mpCreatePV->GetVisibleLayers().IsSet(nLayer))
+ {
+ if(pPreparedFactoryObject)
+ {
+ mpCurrentCreate = pPreparedFactoryObject;
+ }
+ else
+ {
+ mpCurrentCreate = SdrObjFactory::MakeNewObject(
+ *mpModel,
+ nInvent,
+ nIdent);
+ }
+
+ Point aPnt(rPnt);
+ if (mnCurrentInvent != SdrInventor::Default || (mnCurrentIdent != SdrObjKind::Edge &&
+ mnCurrentIdent != SdrObjKind::FreehandLine &&
+ mnCurrentIdent != SdrObjKind::FreehandFill )) { // no snapping for Edge and Freehand
+ aPnt=GetSnapPos(aPnt, mpCreatePV);
+ }
+ if (mpCurrentCreate!=nullptr)
+ {
+ if (mpDefaultStyleSheet!=nullptr) mpCurrentCreate->NbcSetStyleSheet(mpDefaultStyleSheet, false);
+
+ // SW uses a naked SdrObject for frame construction. Normally, such an
+ // object should not be created. Since it is possible to use it as a helper
+ // object (e.g. in letting the user define an area with the interactive
+ // construction) at least no items should be set at that object.
+ if(nInvent != SdrInventor::Default || nIdent != SdrObjKind::NONE)
+ {
+ mpCurrentCreate->SetMergedItemSet(maDefaultAttr);
+ }
+
+ if (mpModel && dynamic_cast<const SdrCaptionObj *>(mpCurrentCreate) != nullptr)
+ {
+ SfxItemSet aSet(mpModel->GetItemPool());
+ aSet.Put(XFillColorItem(OUString(),COL_WHITE)); // in case someone turns on Solid
+ aSet.Put(XFillStyleItem(drawing::FillStyle_NONE));
+
+ mpCurrentCreate->SetMergedItemSet(aSet);
+ }
+ if (mpModel && nInvent==SdrInventor::Default && (nIdent==SdrObjKind::Text ||
+ nIdent==SdrObjKind::TitleText || nIdent==SdrObjKind::OutlineText))
+ {
+ // default for all text frames: no background, no border
+ SfxItemSet aSet(mpModel->GetItemPool());
+ aSet.Put(XFillColorItem(OUString(),COL_WHITE)); // in case someone turns on Solid
+ aSet.Put(XFillStyleItem(drawing::FillStyle_NONE));
+ aSet.Put(XLineColorItem(OUString(),COL_BLACK)); // in case someone turns on Solid
+ aSet.Put(XLineStyleItem(drawing::LineStyle_NONE));
+
+ mpCurrentCreate->SetMergedItemSet(aSet);
+ }
+ if (!rLogRect.IsEmpty()) mpCurrentCreate->NbcSetLogicRect(rLogRect);
+
+ // make sure drag start point is inside WorkArea
+ const tools::Rectangle& rWorkArea = GetWorkArea();
+
+ if(!rWorkArea.IsEmpty())
+ {
+ if(aPnt.X() < rWorkArea.Left())
+ {
+ aPnt.setX( rWorkArea.Left() );
+ }
+
+ if(aPnt.X() > rWorkArea.Right())
+ {
+ aPnt.setX( rWorkArea.Right() );
+ }
+
+ if(aPnt.Y() < rWorkArea.Top())
+ {
+ aPnt.setY( rWorkArea.Top() );
+ }
+
+ if(aPnt.Y() > rWorkArea.Bottom())
+ {
+ aPnt.setY( rWorkArea.Bottom() );
+ }
+ }
+
+ maDragStat.Reset(aPnt);
+ maDragStat.SetView(static_cast<SdrView*>(this));
+ maDragStat.SetPageView(mpCreatePV);
+ maDragStat.SetMinMove(ImpGetMinMovLogic(nMinMov,pOut));
+ mpDragWin=pOut;
+ if (mpCurrentCreate->BegCreate(maDragStat))
+ {
+ ShowCreateObj(/*pOut,sal_True*/);
+ bRet=true;
+ }
+ else
+ {
+ SdrObject::Free(mpCurrentCreate);
+ mpCurrentCreate = nullptr;
+ mpCreatePV = nullptr;
+ }
+ }
+ }
+ }
+ return bRet;
+}
+
+bool SdrCreateView::BegCreateObj(const Point& rPnt, OutputDevice* pOut, short nMinMov)
+{
+ return ImpBegCreateObj(mnCurrentInvent,mnCurrentIdent,rPnt,pOut,nMinMov,tools::Rectangle(), nullptr);
+}
+
+bool SdrCreateView::BegCreatePreparedObject(const Point& rPnt, sal_Int16 nMinMov, SdrObject* pPreparedFactoryObject)
+{
+ SdrInventor nInvent(mnCurrentInvent);
+ SdrObjKind nIdent(mnCurrentIdent);
+
+ if(pPreparedFactoryObject)
+ {
+ nInvent = pPreparedFactoryObject->GetObjInventor();
+ nIdent = pPreparedFactoryObject->GetObjIdentifier();
+ }
+
+ return ImpBegCreateObj(nInvent, nIdent, rPnt, nullptr, nMinMov, tools::Rectangle(), pPreparedFactoryObject);
+}
+
+bool SdrCreateView::BegCreateCaptionObj(const Point& rPnt, const Size& rObjSiz,
+ OutputDevice* pOut, short nMinMov)
+{
+ return ImpBegCreateObj(SdrInventor::Default,SdrObjKind::Caption,rPnt,pOut,nMinMov,
+ tools::Rectangle(rPnt,Size(rObjSiz.Width()+1,rObjSiz.Height()+1)), nullptr);
+}
+
+void SdrCreateView::MovCreateObj(const Point& rPnt)
+{
+ if (mpCurrentCreate==nullptr)
+ return;
+
+ Point aPnt(rPnt);
+ if (!maDragStat.IsNoSnap())
+ {
+ aPnt=GetSnapPos(aPnt, mpCreatePV);
+ }
+ if (IsOrtho())
+ {
+ if (maDragStat.IsOrtho8Possible()) OrthoDistance8(maDragStat.GetPrev(),aPnt,IsBigOrtho());
+ else if (maDragStat.IsOrtho4Possible()) OrthoDistance4(maDragStat.GetPrev(),aPnt,IsBigOrtho());
+ }
+
+ // If the drag point was limited and Ortho is active, do
+ // the small ortho correction (reduction) -> last parameter to FALSE.
+ bool bDidLimit(ImpLimitToWorkArea(aPnt));
+ if(bDidLimit && IsOrtho())
+ {
+ if(maDragStat.IsOrtho8Possible())
+ OrthoDistance8(maDragStat.GetPrev(), aPnt, false);
+ else if(maDragStat.IsOrtho4Possible())
+ OrthoDistance4(maDragStat.GetPrev(), aPnt, false);
+ }
+
+ if (aPnt==maDragStat.GetNow()) return;
+ bool bIsMinMoved(maDragStat.IsMinMoved());
+ if (!maDragStat.CheckMinMoved(aPnt))
+ return;
+
+ if (!bIsMinMoved) maDragStat.NextPoint();
+ maDragStat.NextMove(aPnt);
+ mpCurrentCreate->MovCreate(maDragStat);
+
+ // MovCreate changes the object, so use ActionChanged() on it
+ mpCurrentCreate->ActionChanged();
+
+ // replace for DrawCreateObjDiff
+ HideCreateObj();
+ ShowCreateObj();
+}
+
+void SdrCreateView::SetupObjLayer(const SdrPageView* pPageView, const OUString& aActiveLayer, SdrObject* pObj)
+{
+ const SdrLayerAdmin& rAd = pPageView->GetPage()->GetLayerAdmin();
+ SdrLayerID nLayer(0);
+
+ // #i72535#
+ if(dynamic_cast<const FmFormObj*>( pObj) != nullptr)
+ {
+ // for FormControls, force to form layer
+ nLayer = rAd.GetLayerID(rAd.GetControlLayerName());
+ }
+ else
+ {
+ nLayer = rAd.GetLayerID(aActiveLayer);
+ }
+
+ if(SDRLAYER_NOTFOUND == nLayer)
+ {
+ nLayer = SdrLayerID(0);
+ }
+
+ pObj->SetLayer(nLayer);
+}
+
+bool SdrCreateView::EndCreateObj(SdrCreateCmd eCmd)
+{
+ bool bRet=false;
+ SdrObject* pObjCreated=mpCurrentCreate;
+
+ if (mpCurrentCreate!=nullptr)
+ {
+ sal_uInt32 nCount=maDragStat.GetPointCount();
+
+ if (nCount<=1 && eCmd==SdrCreateCmd::ForceEnd)
+ {
+ BrkCreateObj(); // objects with only a single point don't exist (at least today)
+ return false; // sal_False = event not interpreted
+ }
+
+ bool bPntsEq=nCount>1;
+ sal_uInt32 i=1;
+ Point aP0=maDragStat.GetPoint(0);
+ while (bPntsEq && i<nCount) { bPntsEq=aP0==maDragStat.GetPoint(i); i++; }
+
+ if (mpCurrentCreate->EndCreate(maDragStat,eCmd))
+ {
+ HideCreateObj();
+
+ if (!bPntsEq)
+ {
+ // otherwise Brk, because all points are equal
+ SdrObject* pObj=mpCurrentCreate;
+ mpCurrentCreate=nullptr;
+
+ SetupObjLayer(mpCreatePV, maActualLayer, pObj);
+
+ // recognize creation of a new 3D object inside a 3D scene
+ bool bSceneIntoScene(false);
+
+ E3dScene* pObjScene = dynamic_cast<E3dScene*>(pObjCreated);
+ E3dScene* pCurrentScene = pObjScene ? dynamic_cast<E3dScene*>(mpCreatePV->GetCurrentGroup()) : nullptr;
+ if (pCurrentScene)
+ {
+ bool bDidInsert = static_cast<E3dView*>(this)->ImpCloneAll3DObjectsToDestScene(
+ pObjScene, pCurrentScene, Point(0, 0));
+
+ if(bDidInsert)
+ {
+ // delete object, its content is cloned and inserted
+ SdrObject::Free( pObjCreated );
+ pObjCreated = nullptr;
+ bSceneIntoScene = true;
+ }
+ }
+
+ if(!bSceneIntoScene)
+ {
+ // Here an interactively created SdrObject gets added, so
+ // take into account that interaction created an object in
+ // model coordinates. If we have e.g. a GirdOffset, this is a
+ // little bit tricky - we have an object in model coordinates,
+ // so the fetched offset is at the wrong point in principle
+ // since we need to 'substract' the offset here to get to
+ // 'real' model coordinates. But we have nothing better here,
+ // so go for it.
+ // The 2nd a little tricky thing is that this will early-create
+ // a ViewObjectContact for the new SdrObject, but these VOCs
+ // are anyways layouted for being create-on-demand. This will
+ // be adapted/replaced correctly later on.
+ // This *should* be the right place for getting all interactively
+ // created objects, see InsertObjectAtView below that calls
+ // CreateUndoNewObject.
+ basegfx::B2DVector aGridOffset(0.0, 0.0);
+ if(getPossibleGridOffsetForSdrObject(aGridOffset, pObj, mpCreatePV))
+ {
+ const Size aOffset(
+ basegfx::fround(-aGridOffset.getX()),
+ basegfx::fround(-aGridOffset.getY()));
+
+ pObj->NbcMove(aOffset);
+ }
+
+ // do the same as before
+ InsertObjectAtView(pObj, *mpCreatePV);
+ }
+
+ mpCreatePV = nullptr;
+ bRet=true; // sal_True = event interpreted
+ }
+ else
+ {
+ BrkCreateObj();
+ }
+ }
+ else
+ { // more points
+ if (eCmd==SdrCreateCmd::ForceEnd || // nothing there -- force ending
+ nCount==0 || // no existing points (should never happen)
+ (nCount<=1 && !maDragStat.IsMinMoved())) { // MinMove not met
+ BrkCreateObj();
+ }
+ else
+ {
+ // replace for DrawCreateObjDiff
+ HideCreateObj();
+ ShowCreateObj();
+ maDragStat.ResetMinMoved(); // NextPoint is at MovCreateObj()
+ bRet=true;
+ }
+ }
+ }
+ return bRet;
+}
+
+void SdrCreateView::BckCreateObj()
+{
+ if (mpCurrentCreate==nullptr)
+ return;
+
+ if (maDragStat.GetPointCount()<=2 )
+ {
+ BrkCreateObj();
+ }
+ else
+ {
+ HideCreateObj();
+ maDragStat.PrevPoint();
+ if (mpCurrentCreate->BckCreate(maDragStat))
+ {
+ ShowCreateObj();
+ }
+ else
+ {
+ BrkCreateObj();
+ }
+ }
+}
+
+void SdrCreateView::BrkCreateObj()
+{
+ if (mpCurrentCreate!=nullptr)
+ {
+ HideCreateObj();
+ mpCurrentCreate->BrkCreate(maDragStat);
+ SdrObject::Free( mpCurrentCreate );
+ mpCurrentCreate = nullptr;
+ mpCreatePV = nullptr;
+ }
+}
+
+void SdrCreateView::ShowCreateObj(/*OutputDevice* pOut, sal_Bool bFull*/)
+{
+ if(!IsCreateObj() || maDragStat.IsShown())
+ return;
+
+ if (mpCurrentCreate)
+ {
+ // for migration from XOR, replace DrawDragObj here to create
+ // overlay objects instead.
+ bool bUseSolidDragging(IsSolidDragging());
+
+ // #i101648# check if dragged object is a naked SdrObject (not
+ // a derivation). This is e.g. used in SW Frame construction
+ // as placeholder. Do not use SolidDragging for naked SdrObjects,
+ // they cannot have a valid optical representation
+ if(bUseSolidDragging && SdrObjKind::NONE == mpCurrentCreate->GetObjIdentifier())
+ {
+ bUseSolidDragging = false;
+ }
+
+ // check for objects with no fill and no line
+ if(bUseSolidDragging)
+ {
+ const SfxItemSet& rSet = mpCurrentCreate->GetMergedItemSet();
+ const drawing::FillStyle eFill(rSet.Get(XATTR_FILLSTYLE).GetValue());
+ const drawing::LineStyle eLine(rSet.Get(XATTR_LINESTYLE).GetValue());
+
+ if(drawing::LineStyle_NONE == eLine && drawing::FillStyle_NONE == eFill)
+ {
+ bUseSolidDragging = false;
+ }
+ }
+
+ // check for form controls
+ if(bUseSolidDragging)
+ {
+ if (dynamic_cast<const SdrUnoObj*>(mpCurrentCreate) != nullptr)
+ {
+ bUseSolidDragging = false;
+ }
+ }
+
+ // #i101781# force to non-solid dragging when not creating a full circle
+ if(bUseSolidDragging)
+ {
+ SdrCircObj* pCircObj = dynamic_cast<SdrCircObj*>(mpCurrentCreate);
+
+ if(pCircObj && SdrObjKind::CircleOrEllipse != pCircObj->GetObjIdentifier())
+ {
+ // #i103058# Allow SolidDragging with four points
+ if(maDragStat.GetPointCount() < 4)
+ {
+ bUseSolidDragging = false;
+ }
+ }
+ }
+
+ if(bUseSolidDragging)
+ {
+ basegfx::B2DPolyPolygon aDragPolyPolygon;
+
+ if (dynamic_cast<const SdrRectObj*>(mpCurrentCreate) != nullptr)
+ {
+ // ensure object has some size, necessary for SdrTextObj because
+ // there are still untested divisions by that sizes
+ tools::Rectangle aCurrentSnapRect(mpCurrentCreate->GetSnapRect());
+
+ if(aCurrentSnapRect.GetWidth() <= 1 || aCurrentSnapRect.GetHeight() <= 1)
+ {
+ tools::Rectangle aNewRect(maDragStat.GetStart(), maDragStat.GetStart() + Point(2, 2));
+ mpCurrentCreate->NbcSetSnapRect(aNewRect);
+ }
+ }
+
+ if (auto pPathObj = dynamic_cast<SdrPathObj*>(mpCurrentCreate))
+ {
+ // The up-to-now created path needs to be set at the object to have something
+ // that can be visualized
+ const basegfx::B2DPolyPolygon aCurrentPolyPolygon(pPathObj->getObjectPolyPolygon(maDragStat));
+
+ if(aCurrentPolyPolygon.count())
+ {
+ pPathObj->NbcSetPathPoly(aCurrentPolyPolygon);
+ }
+
+ aDragPolyPolygon = pPathObj->getDragPolyPolygon(maDragStat);
+ }
+
+ // use the SdrObject directly for overlay
+ mpCreateViewExtraData->CreateAndShowOverlay(*this, mpCurrentCreate, aDragPolyPolygon);
+ }
+ else
+ {
+ const ::basegfx::B2DPolyPolygon aPoly(mpCurrentCreate->TakeCreatePoly(maDragStat));
+
+ mpCreateViewExtraData->CreateAndShowOverlay(*this, nullptr, aPoly);
+ }
+
+ // #i101679# Force changed overlay to be shown
+ for(sal_uInt32 a(0); a < PaintWindowCount(); a++)
+ {
+ SdrPaintWindow* pCandidate = GetPaintWindow(a);
+ const rtl::Reference<sdr::overlay::OverlayManager>& xOverlayManager = pCandidate->GetOverlayManager();
+
+ if (xOverlayManager.is())
+ {
+ xOverlayManager->flush();
+ }
+ }
+ }
+
+ maDragStat.SetShown(true);
+}
+
+void SdrCreateView::HideCreateObj()
+{
+ if(IsCreateObj() && maDragStat.IsShown())
+ {
+ // for migration from XOR, replace DrawDragObj here to create
+ // overlay objects instead.
+ mpCreateViewExtraData->HideOverlay();
+
+ //DrawCreateObj(pOut,bFull);
+ maDragStat.SetShown(false);
+ }
+}
+
+
+void SdrCreateView::GetAttributes(SfxItemSet& rTargetSet, bool bOnlyHardAttr) const
+{
+ if (mpCurrentCreate)
+ {
+ rTargetSet.Put(mpCurrentCreate->GetMergedItemSet());
+ }
+ else
+ {
+ SdrDragView::GetAttributes(rTargetSet, bOnlyHardAttr);
+ }
+}
+
+bool SdrCreateView::SetAttributes(const SfxItemSet& rSet, bool bReplaceAll)
+{
+ if (mpCurrentCreate)
+ {
+ mpCurrentCreate->SetMergedItemSetAndBroadcast(rSet, bReplaceAll);
+
+ return true;
+ }
+ else
+ {
+ return SdrDragView::SetAttributes(rSet,bReplaceAll);
+ }
+}
+
+SfxStyleSheet* SdrCreateView::GetStyleSheet() const
+{
+ if (mpCurrentCreate != nullptr)
+ {
+ return mpCurrentCreate->GetStyleSheet();
+ }
+ else
+ {
+ return SdrDragView::GetStyleSheet();
+ }
+}
+
+void SdrCreateView::SetStyleSheet(SfxStyleSheet* pStyleSheet, bool bDontRemoveHardAttr)
+{
+ if (mpCurrentCreate != nullptr)
+ {
+ mpCurrentCreate->SetStyleSheet(pStyleSheet,bDontRemoveHardAttr);
+ }
+ else
+ {
+ SdrDragView::SetStyleSheet(pStyleSheet,bDontRemoveHardAttr);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svddrag.cxx b/svx/source/svdraw/svddrag.cxx
new file mode 100644
index 000000000..0c785dc67
--- /dev/null
+++ b/svx/source/svdraw/svddrag.cxx
@@ -0,0 +1,129 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/svdview.hxx>
+#include <svx/svddrag.hxx>
+
+SdrDragStatUserData::~SdrDragStatUserData() = default;
+
+SdrDragStat::~SdrDragStat()
+{
+}
+
+void SdrDragStat::Clear()
+{
+ mpUserData.reset();
+ mvPnts.clear();
+ mvPnts.emplace_back();
+}
+
+void SdrDragStat::Reset()
+{
+ pView=nullptr;
+ pPageView=nullptr;
+ bShown=false;
+ nMinMov=1;
+ bMinMoved=false;
+ bHorFixed=false;
+ bVerFixed=false;
+ bWantNoSnap=false;
+ pHdl=nullptr;
+ bOrtho4=false;
+ bOrtho8=false;
+ pDragMethod=nullptr;
+ bEndDragChangesAttributes=false;
+ bEndDragChangesGeoAndAttributes=false;
+ mbEndDragChangesLayout=false;
+ bMouseIsUp=false;
+ Clear();
+ aActionRect=tools::Rectangle();
+}
+
+void SdrDragStat::Reset(const Point& rPnt)
+{
+ Reset();
+ mvPnts[0]=rPnt;
+ aPos0=rPnt;
+ aRealNow=rPnt;
+}
+
+void SdrDragStat::NextMove(const Point& rPnt)
+{
+ aPos0=mvPnts.back();
+ aRealNow=rPnt;
+ mvPnts.back()=rPnt;
+}
+
+void SdrDragStat::NextPoint()
+{
+ mvPnts.emplace_back(aRealNow);
+}
+
+void SdrDragStat::PrevPoint()
+{
+ if (mvPnts.size()>1) { // one has to remain at all times
+ mvPnts.erase(mvPnts.begin()+mvPnts.size()-2);
+ mvPnts.back() = aRealNow;
+ }
+}
+
+bool SdrDragStat::CheckMinMoved(const Point& rPnt)
+{
+ if (!bMinMoved) {
+ tools::Long dx=rPnt.X()-GetPrev().X(); if (dx<0) dx=-dx;
+ tools::Long dy=rPnt.Y()-GetPrev().Y(); if (dy<0) dy=-dy;
+ if (dx>=tools::Long(nMinMov) || dy>=tools::Long(nMinMov))
+ bMinMoved=true;
+ }
+ return bMinMoved;
+}
+
+Fraction SdrDragStat::GetXFact() const
+{
+ tools::Long nMul=mvPnts.back().X()-aRef1.X();
+ tools::Long nDiv=GetPrev().X()-aRef1.X();
+ if (nDiv==0) nDiv=1;
+ if (bHorFixed) { nMul=1; nDiv=1; }
+ return Fraction(nMul,nDiv);
+}
+
+Fraction SdrDragStat::GetYFact() const
+{
+ tools::Long nMul=mvPnts.back().Y()-aRef1.Y();
+ tools::Long nDiv=GetPrev().Y()-aRef1.Y();
+ if (nDiv==0) nDiv=1;
+ if (bVerFixed) { nMul=1; nDiv=1; }
+ return Fraction(nMul,nDiv);
+}
+
+void SdrDragStat::TakeCreateRect(tools::Rectangle& rRect) const
+{
+ rRect=tools::Rectangle(mvPnts[0], mvPnts.back());
+ if (mvPnts.size()>1) {
+ Point aBtmRgt(mvPnts[1]);
+ rRect.SetRight(aBtmRgt.X() );
+ rRect.SetBottom(aBtmRgt.Y() );
+ }
+ if (pView!=nullptr && pView->IsCreate1stPointAsCenter()) {
+ rRect.AdjustTop(rRect.Top()-rRect.Bottom() );
+ rRect.AdjustLeft(rRect.Left()-rRect.Right() );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svddrgm1.hxx b/svx/source/svdraw/svddrgm1.hxx
new file mode 100644
index 000000000..54214b956
--- /dev/null
+++ b/svx/source/svdraw/svddrgm1.hxx
@@ -0,0 +1,232 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SVX_SOURCE_SVDRAW_SVDDRGM1_HXX
+#define INCLUDED_SVX_SOURCE_SVDRAW_SVDDRGM1_HXX
+
+#include <svx/xpoly.hxx>
+#include <svx/svdhdl.hxx>
+#include <svx/svddrgv.hxx>
+#include <svx/svddrgmt.hxx>
+
+class SdrDragView;
+
+class SdrDragMovHdl : public SdrDragMethod
+{
+protected:
+ // define nothing, override to do so
+ virtual void createSdrDragEntries() override;
+
+public:
+ explicit SdrDragMovHdl(SdrDragView& rNewView);
+
+ virtual OUString GetSdrDragComment() const override;
+ virtual bool BeginSdrDrag() override;
+ virtual void MoveSdrDrag(const Point& rPnt) override;
+ virtual bool EndSdrDrag(bool bCopy) override;
+ virtual void CancelSdrDrag() override;
+ virtual PointerStyle GetSdrDragPointer() const override;
+};
+
+class SdrDragRotate : public SdrDragMethod
+{
+private:
+ double nSin;
+ double nCos;
+ Degree100 nAngle0;
+ Degree100 nAngle;
+ bool bRight;
+
+public:
+ explicit SdrDragRotate(SdrDragView& rNewView);
+
+ virtual OUString GetSdrDragComment() const override;
+ virtual bool BeginSdrDrag() override;
+ virtual void MoveSdrDrag(const Point& rPnt) override;
+ virtual bool EndSdrDrag(bool bCopy) override;
+ virtual PointerStyle GetSdrDragPointer() const override;
+
+ virtual basegfx::B2DHomMatrix getCurrentTransformation() const override;
+ virtual void applyCurrentTransformationToSdrObject(SdrObject& rTarget) override;
+};
+
+class SdrDragShear : public SdrDragMethod
+{
+private:
+ Fraction aFact;
+ Degree100 nAngle0;
+ Degree100 nAngle;
+ double nTan;
+ bool bVertical; // contort vertically
+ bool bResize; // shear and resize
+ bool bUpSideDown; // mirror and shear/slant
+ bool bSlant;
+
+public:
+ SdrDragShear(SdrDragView& rNewView,bool bSlant1);
+
+ virtual OUString GetSdrDragComment() const override;
+ virtual bool BeginSdrDrag() override;
+ virtual void MoveSdrDrag(const Point& rPnt) override;
+ virtual bool EndSdrDrag(bool bCopy) override;
+ virtual PointerStyle GetSdrDragPointer() const override;
+
+ virtual basegfx::B2DHomMatrix getCurrentTransformation() const override;
+ virtual void applyCurrentTransformationToSdrObject(SdrObject& rTarget) override;
+};
+
+class SdrDragMirror : public SdrDragMethod
+{
+private:
+ Point aDif;
+ Degree100 nAngle;
+ bool bMirrored;
+ bool bSide0;
+
+ bool ImpCheckSide(const Point& rPnt) const;
+
+public:
+ explicit SdrDragMirror(SdrDragView& rNewView);
+
+ virtual OUString GetSdrDragComment() const override;
+ virtual bool BeginSdrDrag() override;
+ virtual void MoveSdrDrag(const Point& rPnt) override;
+ virtual bool EndSdrDrag(bool bCopy) override;
+ virtual PointerStyle GetSdrDragPointer() const override;
+
+ virtual basegfx::B2DHomMatrix getCurrentTransformation() const override;
+ virtual void applyCurrentTransformationToSdrObject(SdrObject& rTarget) override;
+};
+
+class SdrDragGradient : public SdrDragMethod
+{
+private:
+ // Handles to work on
+ SdrHdlGradient* pIAOHandle;
+
+ // is this for gradient (or for transparency)?
+ bool bIsGradient : 1;
+
+public:
+ SdrDragGradient(SdrDragView& rNewView, bool bGrad = true);
+
+ bool IsGradient() const { return bIsGradient; }
+
+ virtual OUString GetSdrDragComment() const override;
+ virtual bool BeginSdrDrag() override;
+ virtual void MoveSdrDrag(const Point& rPnt) override;
+ virtual bool EndSdrDrag(bool bCopy) override;
+ virtual PointerStyle GetSdrDragPointer() const override;
+ virtual void CancelSdrDrag() override;
+};
+
+class SdrDragCrook : public SdrDragMethod
+{
+private:
+ tools::Rectangle aMarkRect;
+ Point aMarkCenter;
+ Point aCenter;
+ Point aStart;
+ Fraction aFact;
+ Point aRad;
+ bool bContortionAllowed;
+ bool bNoContortionAllowed;
+ bool bContortion;
+ bool bResizeAllowed;
+ bool bResize;
+ bool bRotateAllowed;
+ bool bRotate;
+ bool bVertical;
+ bool bValid;
+ bool bLft;
+ bool bRgt;
+ bool bUpr;
+ bool bLwr;
+ bool bAtCenter;
+ Degree100 nAngle;
+ tools::Long nMarkSize;
+ SdrCrookMode eMode;
+
+ // helpers for applyCurrentTransformationToPolyPolygon
+ void MovAllPoints(basegfx::B2DPolyPolygon& rTarget);
+ void MovCrookPoint(Point& rPnt, Point* pC1, Point* pC2);
+
+protected:
+ // needs to add drag geometry to the default
+ virtual void createSdrDragEntries() override;
+
+public:
+ explicit SdrDragCrook(SdrDragView& rNewView);
+
+ virtual OUString GetSdrDragComment() const override;
+ virtual bool BeginSdrDrag() override;
+ virtual void MoveSdrDrag(const Point& rPnt) override;
+ virtual bool EndSdrDrag(bool bCopy) override;
+ virtual PointerStyle GetSdrDragPointer() const override;
+
+ virtual void applyCurrentTransformationToSdrObject(SdrObject& rTarget) override;
+ virtual void applyCurrentTransformationToPolyPolygon(basegfx::B2DPolyPolygon& rTarget) override;
+};
+
+class SdrDragDistort : public SdrDragMethod
+{
+private:
+ tools::Rectangle aMarkRect;
+ XPolygon aDistortedRect;
+ sal_uInt16 nPolyPt;
+ bool bContortionAllowed;
+ bool bNoContortionAllowed;
+ bool bContortion;
+
+ // helper for applyCurrentTransformationToPolyPolygon
+ void MovAllPoints(basegfx::B2DPolyPolygon& rTarget);
+
+protected:
+ // needs to add drag geometry to the default
+ virtual void createSdrDragEntries() override;
+
+public:
+ explicit SdrDragDistort(SdrDragView& rNewView);
+
+ virtual OUString GetSdrDragComment() const override;
+ virtual bool BeginSdrDrag() override;
+ virtual void MoveSdrDrag(const Point& rPnt) override;
+ virtual bool EndSdrDrag(bool bCopy) override;
+ virtual PointerStyle GetSdrDragPointer() const override;
+
+ virtual void applyCurrentTransformationToSdrObject(SdrObject& rTarget) override;
+ virtual void applyCurrentTransformationToPolyPolygon(basegfx::B2DPolyPolygon& rTarget) override;
+};
+
+// derive from SdrDragObjOwn to have handles aligned to object when it
+// is sheared or rotated
+class SdrDragCrop : public SdrDragObjOwn
+{
+public:
+ explicit SdrDragCrop(SdrDragView& rNewView);
+
+ virtual OUString GetSdrDragComment() const override;
+ virtual bool BeginSdrDrag() override;
+ virtual bool EndSdrDrag(bool bCopy) override;
+ virtual PointerStyle GetSdrDragPointer() const override;
+};
+
+#endif // INCLUDED_SVX_SOURCE_SVDRAW_SVDDRGM1_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svddrgmt.cxx b/svx/source/svdraw/svddrgmt.cxx
new file mode 100644
index 000000000..40fd8df27
--- /dev/null
+++ b/svx/source/svdraw/svddrgmt.cxx
@@ -0,0 +1,3860 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "svddrgm1.hxx"
+#include <math.h>
+
+#include <o3tl/numeric.hxx>
+#include <osl/diagnose.h>
+#include <vcl/canvastools.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <svx/xpoly.hxx>
+#include <svx/svdtrans.hxx>
+#include <svx/svdundo.hxx>
+#include <svx/svdmark.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svddrgv.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include <svx/sdgcpitm.hxx>
+#include <svx/sdooitm.hxx>
+#include <svx/sdtagitm.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <sdr/overlay/overlayrollingrectangle.hxx>
+#include <svx/sdrpagewindow.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <svx/sdr/contact/viewcontact.hxx>
+#include <svx/sdr/overlay/overlayprimitive2dsequenceobject.hxx>
+#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
+#include <svx/sdr/contact/objectcontact.hxx>
+#include <svx/svditer.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/polypolygoneditor.hxx>
+#include <drawinglayer/primitive2d/PolygonMarkerPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonSelectionPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonMarkerPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/primitive2d/markerarrayprimitive2d.hxx>
+#include <sdr/primitive2d/sdrattributecreator.hxx>
+#include <sdr/primitive2d/sdrdecompositiontools.hxx>
+#include <sdr/primitive2d/sdrprimitivetools.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <drawinglayer/attribute/sdrlineattribute.hxx>
+#include <drawinglayer/attribute/sdrlinestartendattribute.hxx>
+#include <svl/itempool.hxx>
+#include <svtools/optionsdrawinglayer.hxx>
+#include <comphelper/lok.hxx>
+#include <map>
+#include <vector>
+
+
+SdrDragEntry::SdrDragEntry()
+: mbAddToTransparent(false)
+{
+}
+
+SdrDragEntry::~SdrDragEntry()
+{
+}
+
+
+SdrDragEntryPolyPolygon::SdrDragEntryPolyPolygon(const basegfx::B2DPolyPolygon& rOriginalPolyPolygon)
+: maOriginalPolyPolygon(rOriginalPolyPolygon)
+{
+}
+
+SdrDragEntryPolyPolygon::~SdrDragEntryPolyPolygon()
+{
+}
+
+drawinglayer::primitive2d::Primitive2DContainer SdrDragEntryPolyPolygon::createPrimitive2DSequenceInCurrentState(SdrDragMethod& rDragMethod)
+{
+ drawinglayer::primitive2d::Primitive2DContainer aRetval;
+
+ if(maOriginalPolyPolygon.count())
+ {
+ basegfx::B2DPolyPolygon aCopy(maOriginalPolyPolygon);
+
+ rDragMethod.applyCurrentTransformationToPolyPolygon(aCopy);
+ basegfx::BColor aColA(SvtOptionsDrawinglayer::GetStripeColorA().getBColor());
+ basegfx::BColor aColB(SvtOptionsDrawinglayer::GetStripeColorB().getBColor());
+ const double fStripeLength(SvtOptionsDrawinglayer::GetStripeLength());
+
+ if(Application::GetSettings().GetStyleSettings().GetHighContrastMode())
+ {
+ aColA = aColB = Application::GetSettings().GetStyleSettings().GetHighlightColor().getBColor();
+ aColB.invert();
+ }
+
+ aRetval.resize(2);
+ aRetval[0] = new drawinglayer::primitive2d::PolyPolygonMarkerPrimitive2D(
+ aCopy,
+ aColA,
+ aColB,
+ fStripeLength);
+
+ const basegfx::BColor aHilightColor(SvtOptionsDrawinglayer::getHilightColor().getBColor());
+ const double fTransparence(SvtOptionsDrawinglayer::GetTransparentSelectionPercent() * 0.01);
+
+ aRetval[1] = new drawinglayer::primitive2d::PolyPolygonSelectionPrimitive2D(
+ aCopy,
+ aHilightColor,
+ fTransparence,
+ 3.0,
+ false);
+ }
+
+ return aRetval;
+}
+
+
+SdrDragEntrySdrObject::SdrDragEntrySdrObject(
+ const SdrObject& rOriginal,
+ bool bModify)
+: maOriginal(rOriginal),
+ mbModify(bModify)
+{
+ // add SdrObject parts to transparent overlay stuff
+ setAddToTransparent(true);
+}
+
+SdrDragEntrySdrObject::~SdrDragEntrySdrObject()
+{
+}
+
+void SdrDragEntrySdrObject::prepareCurrentState(SdrDragMethod& rDragMethod)
+{
+ // for the moment, i need to re-create the clone in all cases. I need to figure
+ // out when clone and original have the same class, so that i can use operator=
+ // in those cases
+
+ mxClone.reset();
+
+ if(mbModify)
+ {
+ mxClone = maOriginal.getFullDragClone();
+
+ // apply original transformation, implemented at the DragMethods
+ rDragMethod.applyCurrentTransformationToSdrObject(*mxClone);
+ }
+}
+
+drawinglayer::primitive2d::Primitive2DContainer SdrDragEntrySdrObject::createPrimitive2DSequenceInCurrentState(SdrDragMethod&)
+{
+ const SdrObject* pSource = &maOriginal;
+
+ if(mbModify && mxClone)
+ {
+ // choose source for geometry data
+ pSource = mxClone.get();
+ }
+
+ // use the view-independent primitive representation (without
+ // evtl. GridOffset, that may be applied to the DragEntry individually)
+ drawinglayer::primitive2d::Primitive2DContainer xRetval;
+ pSource->GetViewContact().getViewIndependentPrimitive2DContainer(xRetval);
+ return xRetval;
+}
+
+
+SdrDragEntryPrimitive2DSequence::SdrDragEntryPrimitive2DSequence(
+ drawinglayer::primitive2d::Primitive2DContainer&& rSequence)
+: maPrimitive2DSequence(std::move(rSequence))
+{
+ // add parts to transparent overlay stuff if necessary
+ setAddToTransparent(true);
+}
+
+SdrDragEntryPrimitive2DSequence::~SdrDragEntryPrimitive2DSequence()
+{
+}
+
+drawinglayer::primitive2d::Primitive2DContainer SdrDragEntryPrimitive2DSequence::createPrimitive2DSequenceInCurrentState(SdrDragMethod& rDragMethod)
+{
+ drawinglayer::primitive2d::Primitive2DReference aTransformPrimitive2D(
+ new drawinglayer::primitive2d::TransformPrimitive2D(
+ rDragMethod.getCurrentTransformation(),
+ drawinglayer::primitive2d::Primitive2DContainer(maPrimitive2DSequence)));
+
+ return drawinglayer::primitive2d::Primitive2DContainer { aTransformPrimitive2D };
+}
+
+
+SdrDragEntryPointGlueDrag::SdrDragEntryPointGlueDrag(std::vector< basegfx::B2DPoint >&& rPositions, bool bIsPointDrag)
+: maPositions(std::move(rPositions)),
+ mbIsPointDrag(bIsPointDrag)
+{
+ // add SdrObject parts to transparent overlay stuff
+ setAddToTransparent(true);
+}
+
+SdrDragEntryPointGlueDrag::~SdrDragEntryPointGlueDrag()
+{
+}
+
+drawinglayer::primitive2d::Primitive2DContainer SdrDragEntryPointGlueDrag::createPrimitive2DSequenceInCurrentState(SdrDragMethod& rDragMethod)
+{
+ drawinglayer::primitive2d::Primitive2DContainer aRetval;
+
+ if(!maPositions.empty())
+ {
+ basegfx::B2DPolygon aPolygon;
+
+ for(auto const & a: maPositions)
+ {
+ aPolygon.append(a);
+ }
+
+ basegfx::B2DPolyPolygon aPolyPolygon(aPolygon);
+
+ rDragMethod.applyCurrentTransformationToPolyPolygon(aPolyPolygon);
+
+ const basegfx::B2DPolygon aTransformed(aPolyPolygon.getB2DPolygon(0));
+ std::vector< basegfx::B2DPoint > aTransformedPositions;
+
+ aTransformedPositions.reserve(aTransformed.count());
+
+ for(sal_uInt32 a = 0; a < aTransformed.count(); a++)
+ {
+ aTransformedPositions.push_back(aTransformed.getB2DPoint(a));
+ }
+
+ if(mbIsPointDrag)
+ {
+ basegfx::BColor aColor(SvtOptionsDrawinglayer::GetStripeColorA().getBColor());
+
+ if(Application::GetSettings().GetStyleSettings().GetHighContrastMode())
+ {
+ aColor = Application::GetSettings().GetStyleSettings().GetHighlightColor().getBColor();
+ }
+
+ drawinglayer::primitive2d::Primitive2DReference aMarkerArrayPrimitive2D(
+ new drawinglayer::primitive2d::MarkerArrayPrimitive2D(std::move(aTransformedPositions),
+ drawinglayer::primitive2d::createDefaultCross_3x3(aColor)));
+
+ aRetval = drawinglayer::primitive2d::Primitive2DContainer { aMarkerArrayPrimitive2D };
+ }
+ else
+ {
+ drawinglayer::primitive2d::Primitive2DReference aMarkerArrayPrimitive2D(
+ new drawinglayer::primitive2d::MarkerArrayPrimitive2D(std::move(aTransformedPositions),
+ SdrHdl::createGluePointBitmap()));
+ aRetval = drawinglayer::primitive2d::Primitive2DContainer { aMarkerArrayPrimitive2D };
+ }
+ }
+
+ return aRetval;
+}
+
+
+void SdrDragMethod::resetSdrDragEntries()
+{
+ // clear entries; creation is on demand
+ clearSdrDragEntries();
+}
+
+basegfx::B2DRange SdrDragMethod::getCurrentRange() const
+{
+ return maOverlayObjectList.getBaseRange();
+}
+
+void SdrDragMethod::clearSdrDragEntries()
+{
+ maSdrDragEntries.clear();
+}
+
+void SdrDragMethod::addSdrDragEntry(std::unique_ptr<SdrDragEntry> pNew)
+{
+ assert(pNew);
+ maSdrDragEntries.push_back(std::move(pNew));
+}
+
+void SdrDragMethod::createSdrDragEntries()
+{
+ if(!(getSdrDragView().GetSdrPageView() && getSdrDragView().GetSdrPageView()->HasMarkedObjPageView()))
+ return;
+
+ if(getSdrDragView().IsDraggingPoints())
+ {
+ createSdrDragEntries_PointDrag();
+ }
+ else if(getSdrDragView().IsDraggingGluePoints())
+ {
+ createSdrDragEntries_GlueDrag();
+ }
+ else
+ {
+ if(getSolidDraggingActive())
+ {
+ createSdrDragEntries_SolidDrag();
+ }
+ else
+ {
+ createSdrDragEntries_PolygonDrag();
+ }
+ }
+}
+
+void SdrDragMethod::createSdrDragEntryForSdrObject(const SdrObject& rOriginal)
+{
+ // add full object drag; Clone() at the object has to work
+ // for this
+ addSdrDragEntry(std::unique_ptr<SdrDragEntry>(new SdrDragEntrySdrObject(rOriginal, true/*bModify*/)));
+}
+
+void SdrDragMethod::insertNewlyCreatedOverlayObjectForSdrDragMethod(
+ std::unique_ptr<sdr::overlay::OverlayObject> pOverlayObject,
+ const sdr::contact::ObjectContact& rObjectContact,
+ sdr::overlay::OverlayManager& rOverlayManager)
+{
+ // check if we have an OverlayObject
+ if(!pOverlayObject)
+ {
+ return;
+ }
+
+ // add to OverlayManager
+ rOverlayManager.add(*pOverlayObject);
+
+ // Add GridOffset for non-linear ViewToDevice transformation (calc)
+ if(rObjectContact.supportsGridOffsets())
+ {
+ const basegfx::B2DRange& rNewRange(pOverlayObject->getBaseRange());
+
+ if(!rNewRange.isEmpty())
+ {
+ basegfx::B2DVector aOffset(0.0, 0.0);
+ rObjectContact.calculateGridOffsetForB2DRange(aOffset, rNewRange);
+
+ if(!aOffset.equalZero())
+ {
+ pOverlayObject->setOffset(aOffset);
+ }
+ }
+ }
+
+ // add to local OverlayObjectList - ownership change (!)
+ maOverlayObjectList.append(std::move(pOverlayObject));
+}
+
+void SdrDragMethod::createSdrDragEntries_SolidDrag()
+{
+ const size_t nMarkCount(getSdrDragView().GetMarkedObjectCount());
+ SdrPageView* pPV = getSdrDragView().GetSdrPageView();
+
+ if(!pPV)
+ return;
+
+ for(size_t a = 0; a < nMarkCount; ++a)
+ {
+ SdrMark* pM = getSdrDragView().GetSdrMarkByIndex(a);
+
+ if(pM->GetPageView() == pPV)
+ {
+ const SdrObject* pObject = pM->GetMarkedSdrObj();
+
+ if(pObject)
+ {
+ if(pPV->PageWindowCount())
+ {
+ SdrObjListIter aIter(*pObject);
+
+ while(aIter.IsMore())
+ {
+ SdrObject* pCandidate = aIter.Next();
+
+ if(pCandidate)
+ {
+ const bool bSuppressFullDrag(!pCandidate->supportsFullDrag());
+ bool bAddWireframe(bSuppressFullDrag);
+
+ if(!bAddWireframe && !pCandidate->HasLineStyle())
+ {
+ // add wireframe for objects without outline
+ bAddWireframe = true;
+ }
+
+ if(!bSuppressFullDrag)
+ {
+ // add full object drag; Clone() at the object has to work
+ // for this
+ createSdrDragEntryForSdrObject(*pCandidate);
+ }
+
+ if(bAddWireframe)
+ {
+ // when dragging a 50% transparent copy of a filled or not filled object without
+ // outline, this is normally hard to see. Add extra wireframe in that case. This
+ // works nice e.g. with text frames etc.
+ addSdrDragEntry(std::unique_ptr<SdrDragEntry>(new SdrDragEntryPolyPolygon(pCandidate->TakeXorPoly())));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void SdrDragMethod::createSdrDragEntries_PolygonDrag()
+{
+ const size_t nMarkCount(getSdrDragView().GetMarkedObjectCount());
+ bool bNoPolygons(getSdrDragView().IsNoDragXorPolys() || nMarkCount > SdrDragView::GetDragXorPolyLimit());
+ basegfx::B2DPolyPolygon aResult;
+ sal_uInt32 nPointCount(0);
+
+ for(size_t a = 0; !bNoPolygons && a < nMarkCount; ++a)
+ {
+ SdrMark* pM = getSdrDragView().GetSdrMarkByIndex(a);
+
+ if(pM->GetPageView() == getSdrDragView().GetSdrPageView())
+ {
+ const basegfx::B2DPolyPolygon aNewPolyPolygon(pM->GetMarkedSdrObj()->TakeXorPoly());
+
+ for(auto const& rPolygon : aNewPolyPolygon)
+ {
+ nPointCount += rPolygon.count();
+ }
+
+ if(nPointCount > SdrDragView::GetDragXorPointLimit())
+ {
+ bNoPolygons = true;
+ }
+
+ if(!bNoPolygons)
+ {
+ aResult.append(aNewPolyPolygon);
+ }
+ }
+ }
+
+ if(bNoPolygons)
+ {
+ const tools::Rectangle aR(getSdrDragView().GetSdrPageView()->MarkSnap());
+ const basegfx::B2DRange aNewRectangle = vcl::unotools::b2DRectangleFromRectangle(aR);
+ basegfx::B2DPolygon aNewPolygon(basegfx::utils::createPolygonFromRect(aNewRectangle));
+
+ aResult = basegfx::B2DPolyPolygon(basegfx::utils::expandToCurve(aNewPolygon));
+ }
+
+ if(aResult.count())
+ {
+ addSdrDragEntry(std::unique_ptr<SdrDragEntry>(new SdrDragEntryPolyPolygon(aResult)));
+ }
+}
+
+void SdrDragMethod::createSdrDragEntries_PointDrag()
+{
+ const size_t nMarkCount(getSdrDragView().GetMarkedObjectCount());
+ std::vector< basegfx::B2DPoint > aPositions;
+
+ for(size_t nm = 0; nm < nMarkCount; ++nm)
+ {
+ SdrMark* pM = getSdrDragView().GetSdrMarkByIndex(nm);
+
+ if(pM->GetPageView() == getSdrDragView().GetSdrPageView())
+ {
+ const SdrUShortCont& rPts = pM->GetMarkedPoints();
+
+ if (!rPts.empty())
+ {
+ const SdrObject* pObj = pM->GetMarkedSdrObj();
+ const SdrPathObj* pPath = dynamic_cast< const SdrPathObj* >(pObj);
+
+ if(pPath)
+ {
+ const basegfx::B2DPolyPolygon& aPathXPP = pPath->GetPathPoly();
+
+ if(aPathXPP.count())
+ {
+ for(const sal_uInt16 nObjPt : rPts)
+ {
+ sal_uInt32 nPolyNum, nPointNum;
+
+ if(sdr::PolyPolygonEditor::GetRelativePolyPoint(aPathXPP, nObjPt, nPolyNum, nPointNum))
+ {
+ aPositions.push_back(aPathXPP.getB2DPolygon(nPolyNum).getB2DPoint(nPointNum));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if(!aPositions.empty())
+ {
+ addSdrDragEntry(std::unique_ptr<SdrDragEntry>(new SdrDragEntryPointGlueDrag(std::move(aPositions), true)));
+ }
+}
+
+void SdrDragMethod::createSdrDragEntries_GlueDrag()
+{
+ const size_t nMarkCount(getSdrDragView().GetMarkedObjectCount());
+ std::vector< basegfx::B2DPoint > aPositions;
+
+ for(size_t nm = 0; nm < nMarkCount; ++nm)
+ {
+ SdrMark* pM = getSdrDragView().GetSdrMarkByIndex(nm);
+
+ if(pM->GetPageView() == getSdrDragView().GetSdrPageView())
+ {
+ const SdrUShortCont& rPts = pM->GetMarkedGluePoints();
+
+ if (!rPts.empty())
+ {
+ const SdrObject* pObj = pM->GetMarkedSdrObj();
+ const SdrGluePointList* pGPL = pObj->GetGluePointList();
+
+ if (pGPL)
+ {
+ for(const sal_uInt16 nObjPt : rPts)
+ {
+ const sal_uInt16 nGlueNum(pGPL->FindGluePoint(nObjPt));
+
+ if(SDRGLUEPOINT_NOTFOUND != nGlueNum)
+ {
+ const Point aPoint((*pGPL)[nGlueNum].GetAbsolutePos(*pObj));
+ aPositions.emplace_back(aPoint.X(), aPoint.Y());
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if(!aPositions.empty())
+ {
+ addSdrDragEntry(std::unique_ptr<SdrDragEntry>(new SdrDragEntryPointGlueDrag(std::move(aPositions), false)));
+ }
+}
+
+OUString SdrDragMethod::ImpGetDescriptionStr(TranslateId pStrCacheID) const
+{
+ ImpGetDescriptionOptions nOpt=ImpGetDescriptionOptions::NONE;
+ if (IsDraggingPoints()) {
+ nOpt=ImpGetDescriptionOptions::POINTS;
+ } else if (IsDraggingGluePoints()) {
+ nOpt=ImpGetDescriptionOptions::GLUEPOINTS;
+ }
+ return getSdrDragView().ImpGetDescriptionString(pStrCacheID, nOpt);
+}
+
+SdrObject* SdrDragMethod::GetDragObj() const
+{
+ SdrObject* pObj=nullptr;
+ if (getSdrDragView().mpDragHdl!=nullptr) pObj=getSdrDragView().mpDragHdl->GetObj();
+ if (pObj==nullptr) pObj=getSdrDragView().mpMarkedObj;
+ return pObj;
+}
+
+SdrPageView* SdrDragMethod::GetDragPV() const
+{
+ SdrPageView* pPV=nullptr;
+ if (getSdrDragView().mpDragHdl!=nullptr) pPV=getSdrDragView().mpDragHdl->GetPageView();
+ if (pPV==nullptr) pPV=getSdrDragView().mpMarkedPV;
+ return pPV;
+}
+
+void SdrDragMethod::applyCurrentTransformationToSdrObject(SdrObject& rTarget)
+{
+ // the original applies the transformation using TRGetBaseGeometry/TRSetBaseGeometry.
+ // Later this should be the only needed one for linear transforms (not for SdrDragCrook and
+ // SdrDragDistort, those are NOT linear). Currently, this can not yet be used since the
+ // special handling of rotate/mirror due to the not-being-able to handle it in the old
+ // drawinglayer stuff. Text would currently not correctly be mirrored in the preview.
+ basegfx::B2DHomMatrix aObjectTransform;
+ basegfx::B2DPolyPolygon aObjectPolyPolygon;
+ bool bPolyUsed(rTarget.TRGetBaseGeometry(aObjectTransform, aObjectPolyPolygon));
+
+ // apply transform to object transform
+ aObjectTransform *= getCurrentTransformation();
+
+ if(bPolyUsed)
+ {
+ // do something special since the object size is in the polygon
+ // break up matrix to get the scale
+ const basegfx::utils::B2DHomMatrixBufferedDecompose aTmpDecomp(aObjectTransform);
+
+ // get polygon's position and size
+ const basegfx::B2DRange aPolyRange(aObjectPolyPolygon.getB2DRange());
+
+ // get the scaling factors (do not mirror, this is in the object transformation)
+ const double fScaleX(fabs(aTmpDecomp.getScale().getX()) / (basegfx::fTools::equalZero(aPolyRange.getWidth()) ? 1.0 : aPolyRange.getWidth()));
+ const double fScaleY(fabs(aTmpDecomp.getScale().getY()) / (basegfx::fTools::equalZero(aPolyRange.getHeight()) ? 1.0 : aPolyRange.getHeight()));
+
+ // prepare transform matrix for polygon
+ basegfx::B2DHomMatrix aPolyTransform(
+ basegfx::utils::createTranslateB2DHomMatrix(
+ -aPolyRange.getMinX(),
+ -aPolyRange.getMinY()));
+ aPolyTransform.scale(fScaleX, fScaleY);
+
+ // transform the polygon
+ aObjectPolyPolygon.transform(aPolyTransform);
+ }
+
+ rTarget.TRSetBaseGeometry(getCurrentTransformation() * aObjectTransform, aObjectPolyPolygon);
+}
+
+void SdrDragMethod::applyCurrentTransformationToPolyPolygon(basegfx::B2DPolyPolygon& rTarget)
+{
+ // original uses CurrentTransformation
+ rTarget.transform(getCurrentTransformation());
+}
+
+SdrDragMethod::SdrDragMethod(SdrDragView& rNewView)
+: mrSdrDragView(rNewView),
+ mbMoveOnly(false),
+ mbSolidDraggingActive(getSdrDragView().IsSolidDragging()),
+ mbShiftPressed(false)
+{
+ if(mbSolidDraggingActive && Application::GetSettings().GetStyleSettings().GetHighContrastMode())
+ {
+ // fallback to wireframe when high contrast is used
+ mbSolidDraggingActive = false;
+ }
+}
+
+SdrDragMethod::~SdrDragMethod()
+{
+ clearSdrDragEntries();
+}
+
+void SdrDragMethod::Show()
+{
+ getSdrDragView().ShowDragObj();
+}
+
+void SdrDragMethod::Hide()
+{
+ getSdrDragView().HideDragObj();
+}
+
+basegfx::B2DHomMatrix SdrDragMethod::getCurrentTransformation() const
+{
+ return basegfx::B2DHomMatrix();
+}
+
+void SdrDragMethod::CancelSdrDrag()
+{
+ Hide();
+}
+
+typedef std::map< const SdrObject*, SdrObject* > SdrObjectAndCloneMap;
+
+void SdrDragMethod::CreateOverlayGeometry(
+ sdr::overlay::OverlayManager& rOverlayManager,
+ const sdr::contact::ObjectContact& rObjectContact)
+{
+ // We do client-side object manipulation with the Kit API
+ if (comphelper::LibreOfficeKit::isActive())
+ return;
+
+ // create SdrDragEntries on demand
+ if(maSdrDragEntries.empty())
+ {
+ createSdrDragEntries();
+ }
+
+ // if there are entries, derive OverlayObjects from the entries, including
+ // modification from current interactive state
+ if(!maSdrDragEntries.empty())
+ {
+ // #i54102# SdrDragEntrySdrObject creates clones of SdrObjects as base for creating the needed
+ // primitives, holding the original and the clone. If connectors (Edges) are involved,
+ // the cloned connectors need to be connected to the cloned SdrObjects (after cloning
+ // they are connected to the original SdrObjects). To do so, trigger the preparation
+ // steps for SdrDragEntrySdrObject, save an association of (orig, clone) in a helper
+ // and evtl. remember if it was an edge
+ SdrObjectAndCloneMap aOriginalAndClones;
+ std::vector< SdrEdgeObj* > aEdges;
+
+ // #i54102# execute prepareCurrentState for all SdrDragEntrySdrObject, register pair of original and
+ // clone, remember edges
+ for(auto const & a: maSdrDragEntries)
+ {
+ SdrDragEntrySdrObject* pSdrDragEntrySdrObject = dynamic_cast< SdrDragEntrySdrObject*>(a.get());
+
+ if(pSdrDragEntrySdrObject)
+ {
+ pSdrDragEntrySdrObject->prepareCurrentState(*this);
+
+ SdrEdgeObj* pSdrEdgeObj = dynamic_cast< SdrEdgeObj* >(pSdrDragEntrySdrObject->getClone());
+
+ if(pSdrEdgeObj)
+ {
+ aEdges.push_back(pSdrEdgeObj);
+ }
+
+ if(pSdrDragEntrySdrObject->getClone())
+ {
+ aOriginalAndClones[&pSdrDragEntrySdrObject->getOriginal()] = pSdrDragEntrySdrObject->getClone();
+ }
+ }
+ }
+
+ // #i54102# if there are edges, reconnect their ends to the corresponding clones (if found)
+ for(SdrEdgeObj* pSdrEdgeObj: aEdges)
+ {
+ SdrObject* pConnectedTo = pSdrEdgeObj->GetConnectedNode(true);
+
+ if(pConnectedTo)
+ {
+ SdrObjectAndCloneMap::iterator aEntry = aOriginalAndClones.find(pConnectedTo);
+
+ if(aEntry != aOriginalAndClones.end())
+ {
+ pSdrEdgeObj->ConnectToNode(true, aEntry->second);
+ }
+ }
+
+ pConnectedTo = pSdrEdgeObj->GetConnectedNode(false);
+
+ if(pConnectedTo)
+ {
+ SdrObjectAndCloneMap::iterator aEntry = aOriginalAndClones.find(pConnectedTo);
+
+ if(aEntry != aOriginalAndClones.end())
+ {
+ pSdrEdgeObj->ConnectToNode(false, aEntry->second);
+ }
+ }
+ }
+
+ // collect primitives for visualisation
+ drawinglayer::primitive2d::Primitive2DContainer aResult;
+ drawinglayer::primitive2d::Primitive2DContainer aResultTransparent;
+
+ for(auto & pCandidate: maSdrDragEntries)
+ {
+ const drawinglayer::primitive2d::Primitive2DContainer aCandidateResult(pCandidate->createPrimitive2DSequenceInCurrentState(*this));
+
+ if(!aCandidateResult.empty())
+ {
+ if(pCandidate->getAddToTransparent())
+ {
+ aResultTransparent.append(aCandidateResult);
+ }
+ else
+ {
+ aResult.append(aCandidateResult);
+ }
+ }
+ }
+
+ if(DoAddConnectorOverlays())
+ {
+ const drawinglayer::primitive2d::Primitive2DContainer aConnectorOverlays(AddConnectorOverlays());
+
+ if(!aConnectorOverlays.empty())
+ {
+ // add connector overlays to transparent part
+ aResultTransparent.append(aConnectorOverlays);
+ }
+ }
+
+ if(!aResult.empty())
+ {
+ std::unique_ptr<sdr::overlay::OverlayObject> pNewOverlayObject(
+ new sdr::overlay::OverlayPrimitive2DSequenceObject(
+ std::move(aResult)));
+
+ insertNewlyCreatedOverlayObjectForSdrDragMethod(
+ std::move(pNewOverlayObject),
+ rObjectContact,
+ rOverlayManager);
+ }
+
+ if(!aResultTransparent.empty())
+ {
+ drawinglayer::primitive2d::Primitive2DReference aUnifiedTransparencePrimitive2D(new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(std::move(aResultTransparent), 0.5));
+ aResultTransparent = drawinglayer::primitive2d::Primitive2DContainer { aUnifiedTransparencePrimitive2D };
+
+ std::unique_ptr<sdr::overlay::OverlayObject> pNewOverlayObject(
+ new sdr::overlay::OverlayPrimitive2DSequenceObject(
+ std::move(aResultTransparent)));
+
+ insertNewlyCreatedOverlayObjectForSdrDragMethod(
+ std::move(pNewOverlayObject),
+ rObjectContact,
+ rOverlayManager);
+ }
+ }
+
+ // add DragStripes if necessary (help lines cross the page when dragging)
+ if(!getSdrDragView().IsDragStripes())
+ return;
+
+ tools::Rectangle aActionRectangle;
+ getSdrDragView().TakeActionRect(aActionRectangle);
+
+ const basegfx::B2DPoint aTopLeft(aActionRectangle.Left(), aActionRectangle.Top());
+ const basegfx::B2DPoint aBottomRight(aActionRectangle.Right(), aActionRectangle.Bottom());
+ std::unique_ptr<sdr::overlay::OverlayRollingRectangleStriped> pNew(
+ new sdr::overlay::OverlayRollingRectangleStriped(
+ aTopLeft,
+ aBottomRight,
+ true,
+ false));
+
+ insertNewlyCreatedOverlayObjectForSdrDragMethod(
+ std::move(pNew),
+ rObjectContact,
+ rOverlayManager);
+}
+
+void SdrDragMethod::destroyOverlayGeometry()
+{
+ maOverlayObjectList.clear();
+}
+
+bool SdrDragMethod::DoAddConnectorOverlays()
+{
+ // these conditions are translated from SdrDragView::ImpDrawEdgeXor
+ const SdrMarkList& rMarkedNodes = getSdrDragView().GetEdgesOfMarkedNodes();
+
+ if(!rMarkedNodes.GetMarkCount())
+ {
+ return false;
+ }
+
+ if(getSdrDragView().IsDraggingPoints() || getSdrDragView().IsDraggingGluePoints())
+ {
+ return false;
+ }
+
+ if(!getMoveOnly() && !(
+ dynamic_cast<const SdrDragMove*>(this) != nullptr || dynamic_cast<const SdrDragResize*>(this) != nullptr ||
+ dynamic_cast<const SdrDragRotate*>(this) != nullptr || dynamic_cast<const SdrDragMirror*>(this) != nullptr ))
+ {
+ return false;
+ }
+
+ // one more migrated from SdrEdgeObj::NspToggleEdgeXor
+ if( dynamic_cast< const SdrDragObjOwn* >(this) != nullptr || dynamic_cast< const SdrDragMovHdl* >(this) != nullptr )
+ {
+ return false;
+ }
+
+ return true;
+}
+
+drawinglayer::primitive2d::Primitive2DContainer SdrDragMethod::AddConnectorOverlays()
+{
+ drawinglayer::primitive2d::Primitive2DContainer aRetval;
+ const bool bDetail(getMoveOnly());
+ const SdrMarkList& rMarkedNodes = getSdrDragView().GetEdgesOfMarkedNodes();
+
+ for(size_t a = 0; a < rMarkedNodes.GetMarkCount(); ++a)
+ {
+ SdrMark* pEM = rMarkedNodes.GetMark(a);
+
+ if(pEM && pEM->GetMarkedSdrObj())
+ {
+ SdrEdgeObj* pEdge = dynamic_cast< SdrEdgeObj* >(pEM->GetMarkedSdrObj());
+
+ if(pEdge)
+ {
+ const basegfx::B2DPolygon aEdgePolygon(pEdge->ImplAddConnectorOverlay(*this, pEM->IsCon1(), pEM->IsCon2(), bDetail));
+
+ if(aEdgePolygon.count())
+ {
+ // this polygon is a temporary calculated connector path, so it is not possible to fetch
+ // the needed primitives directly from the pEdge object which does not get changed. If full
+ // drag is on, use the SdrObjects ItemSet to create an adequate representation
+ bool bUseSolidDragging(getSolidDraggingActive());
+
+ if(bUseSolidDragging)
+ {
+ // switch off solid dragging if connector is not visible
+ if(!pEdge->HasLineStyle())
+ {
+ bUseSolidDragging = false;
+ }
+ }
+
+ if(bUseSolidDragging)
+ {
+ const SfxItemSet& rItemSet = pEdge->GetMergedItemSet();
+ const drawinglayer::attribute::SdrLineAttribute aLine(
+ drawinglayer::primitive2d::createNewSdrLineAttribute(rItemSet));
+
+ if(!aLine.isDefault())
+ {
+ const drawinglayer::attribute::SdrLineStartEndAttribute aLineStartEnd(
+ drawinglayer::primitive2d::createNewSdrLineStartEndAttribute(
+ rItemSet,
+ aLine.getWidth()));
+
+ aRetval.push_back(drawinglayer::primitive2d::createPolygonLinePrimitive(
+ aEdgePolygon,
+ aLine,
+ aLineStartEnd));
+ }
+ }
+ else
+ {
+ basegfx::BColor aColA(SvtOptionsDrawinglayer::GetStripeColorA().getBColor());
+ basegfx::BColor aColB(SvtOptionsDrawinglayer::GetStripeColorB().getBColor());
+ const double fStripeLength(SvtOptionsDrawinglayer::GetStripeLength());
+
+ if(Application::GetSettings().GetStyleSettings().GetHighContrastMode())
+ {
+ aColA = aColB = Application::GetSettings().GetStyleSettings().GetHighlightColor().getBColor();
+ aColB.invert();
+ }
+
+ drawinglayer::primitive2d::Primitive2DReference aPolyPolygonMarkerPrimitive2D(
+ new drawinglayer::primitive2d::PolygonMarkerPrimitive2D(
+ aEdgePolygon, aColA, aColB, fStripeLength));
+ aRetval.push_back(aPolyPolygonMarkerPrimitive2D);
+ }
+ }
+ }
+ }
+ }
+
+ return aRetval;
+}
+
+
+SdrDragMovHdl::SdrDragMovHdl(SdrDragView& rNewView)
+: SdrDragMethod(rNewView)
+{
+}
+
+void SdrDragMovHdl::createSdrDragEntries()
+{
+ // SdrDragMovHdl does not use the default drags,
+ // but creates nothing
+}
+
+OUString SdrDragMovHdl::GetSdrDragComment() const
+{
+ OUString aStr=SvxResId(STR_DragMethMovHdl);
+ if (getSdrDragView().IsDragWithCopy()) aStr+=SvxResId(STR_EditWithCopy);
+ return aStr;
+}
+
+bool SdrDragMovHdl::BeginSdrDrag()
+{
+ if( !GetDragHdl() )
+ return false;
+
+ DragStat().SetRef1(GetDragHdl()->GetPos());
+ DragStat().SetShown(!DragStat().IsShown());
+ SdrHdlKind eKind=GetDragHdl()->GetKind();
+ SdrHdl* pH1=GetHdlList().GetHdl(SdrHdlKind::Ref1);
+ SdrHdl* pH2=GetHdlList().GetHdl(SdrHdlKind::Ref2);
+
+ if (eKind==SdrHdlKind::MirrorAxis)
+ {
+ if (pH1==nullptr || pH2==nullptr)
+ {
+ OSL_FAIL("SdrDragMovHdl::BeginSdrDrag(): Moving the axis of reflection: reference handles not found.");
+ return false;
+ }
+
+ DragStat().SetActionRect(tools::Rectangle(pH1->GetPos(),pH2->GetPos()));
+ }
+ else
+ {
+ Point aPt(GetDragHdl()->GetPos());
+ DragStat().SetActionRect(tools::Rectangle(aPt,aPt));
+ }
+
+ return true;
+}
+
+void SdrDragMovHdl::MoveSdrDrag(const Point& rNoSnapPnt)
+{
+ Point aPnt(rNoSnapPnt);
+
+ if ( !(GetDragHdl() && DragStat().CheckMinMoved(rNoSnapPnt)))
+ return;
+
+ if (GetDragHdl()->GetKind()==SdrHdlKind::MirrorAxis)
+ {
+ SdrHdl* pH1=GetHdlList().GetHdl(SdrHdlKind::Ref1);
+ SdrHdl* pH2=GetHdlList().GetHdl(SdrHdlKind::Ref2);
+
+ if (pH1==nullptr || pH2==nullptr)
+ return;
+
+ if (!DragStat().IsNoSnap())
+ {
+ tools::Long nBestXSnap=0;
+ tools::Long nBestYSnap=0;
+ bool bXSnapped=false;
+ bool bYSnapped=false;
+ Point aDif(aPnt-DragStat().GetStart());
+ getSdrDragView().CheckSnap(Ref1()+aDif,nBestXSnap,nBestYSnap,bXSnapped,bYSnapped);
+ getSdrDragView().CheckSnap(Ref2()+aDif,nBestXSnap,nBestYSnap,bXSnapped,bYSnapped);
+ aPnt.AdjustX(nBestXSnap );
+ aPnt.AdjustY(nBestYSnap );
+ }
+
+ if (aPnt!=DragStat().GetNow())
+ {
+ Hide();
+ DragStat().NextMove(aPnt);
+ Point aDif(DragStat().GetNow()-DragStat().GetStart());
+ pH1->SetPos(Ref1()+aDif);
+ pH2->SetPos(Ref2()+aDif);
+
+ SdrHdl* pHM = GetHdlList().GetHdl(SdrHdlKind::MirrorAxis);
+
+ if(pHM)
+ pHM->Touch();
+
+ Show();
+ DragStat().SetActionRect(tools::Rectangle(pH1->GetPos(),pH2->GetPos()));
+ }
+ }
+ else
+ {
+ if (!DragStat().IsNoSnap()) SnapPos(aPnt);
+ Degree100 nSA(0);
+
+ if (getSdrDragView().IsAngleSnapEnabled())
+ nSA=getSdrDragView().GetSnapAngle();
+
+ if (getSdrDragView().IsMirrorAllowed(true,true))
+ { // limited
+ if (!getSdrDragView().IsMirrorAllowed()) nSA=4500_deg100;
+ if (!getSdrDragView().IsMirrorAllowed(true)) nSA=9000_deg100;
+ }
+
+ if (getSdrDragView().IsOrtho() && nSA!=9000_deg100)
+ nSA=4500_deg100;
+
+ if (nSA)
+ { // angle snapping
+ SdrHdlKind eRef=SdrHdlKind::Ref1;
+
+ if (GetDragHdl()->GetKind()==SdrHdlKind::Ref1)
+ eRef=SdrHdlKind::Ref2;
+
+ SdrHdl* pH=GetHdlList().GetHdl(eRef);
+
+ if (pH!=nullptr)
+ {
+ Point aRef(pH->GetPos());
+ Degree100 nAngle=NormAngle36000(GetAngle(aPnt-aRef));
+ Degree100 nNewAngle=nAngle;
+ nNewAngle+=nSA/2_deg100;
+ nNewAngle/=nSA;
+ nNewAngle*=nSA;
+ nNewAngle=NormAngle36000(nNewAngle);
+ double a=toRadians(nNewAngle-nAngle);
+ double nSin=sin(a);
+ double nCos=cos(a);
+ RotatePoint(aPnt,aRef,nSin,nCos);
+
+ // eliminate rounding errors for certain values
+ if (nSA==9000_deg100)
+ {
+ if (nNewAngle==0_deg100 || nNewAngle==18000_deg100) aPnt.setY(aRef.Y() );
+ if (nNewAngle==9000_deg100 || nNewAngle==27000_deg100) aPnt.setX(aRef.X() );
+ }
+
+ if (nSA==4500_deg100)
+ OrthoDistance8(aRef,aPnt,true);
+ }
+ }
+
+ if (aPnt!=DragStat().GetNow())
+ {
+ Hide();
+ DragStat().NextMove(aPnt);
+ GetDragHdl()->SetPos(DragStat().GetNow());
+ SdrHdl* pHM = GetHdlList().GetHdl(SdrHdlKind::MirrorAxis);
+
+ if(pHM)
+ pHM->Touch();
+
+ Show();
+ DragStat().SetActionRect(tools::Rectangle(aPnt,aPnt));
+ }
+ }
+}
+
+bool SdrDragMovHdl::EndSdrDrag(bool /*bCopy*/)
+{
+ if( GetDragHdl() )
+ {
+ switch (GetDragHdl()->GetKind())
+ {
+ case SdrHdlKind::Ref1:
+ Ref1()=DragStat().GetNow();
+ break;
+
+ case SdrHdlKind::Ref2:
+ Ref2()=DragStat().GetNow();
+ break;
+
+ case SdrHdlKind::MirrorAxis:
+ Ref1()+=DragStat().GetNow()-DragStat().GetStart();
+ Ref2()+=DragStat().GetNow()-DragStat().GetStart();
+ break;
+
+ default: break;
+ }
+ }
+
+ return true;
+}
+
+void SdrDragMovHdl::CancelSdrDrag()
+{
+ Hide();
+
+ SdrHdl* pHdl = GetDragHdl();
+ if( pHdl )
+ pHdl->SetPos(DragStat().GetRef1());
+
+ SdrHdl* pHM = GetHdlList().GetHdl(SdrHdlKind::MirrorAxis);
+
+ if(pHM)
+ pHM->Touch();
+}
+
+PointerStyle SdrDragMovHdl::GetSdrDragPointer() const
+{
+ const SdrHdl* pHdl = GetDragHdl();
+
+ if (pHdl!=nullptr)
+ {
+ return pHdl->GetPointer();
+ }
+
+ return PointerStyle::RefHand;
+}
+
+
+SdrDragObjOwn::SdrDragObjOwn(SdrDragView& rNewView)
+: SdrDragMethod(rNewView)
+{
+ const SdrObject* pObj = GetDragObj();
+
+ if(pObj)
+ {
+ // suppress full drag for some object types
+ setSolidDraggingActive(pObj->supportsFullDrag());
+ }
+}
+
+SdrDragObjOwn::~SdrDragObjOwn()
+{
+}
+
+void SdrDragObjOwn::createSdrDragEntries()
+{
+ if(!mxClone)
+ return;
+
+ basegfx::B2DPolyPolygon aDragPolyPolygon;
+ bool bAddWireframe(true);
+
+ if(getSolidDraggingActive())
+ {
+ SdrPageView* pPV = getSdrDragView().GetSdrPageView();
+
+ if(pPV && pPV->PageWindowCount())
+ {
+ addSdrDragEntry(std::unique_ptr<SdrDragEntry>(new SdrDragEntrySdrObject(*mxClone, false)));
+
+ // potentially no wireframe needed, full drag works
+ bAddWireframe = false;
+ }
+ }
+
+ if(!bAddWireframe)
+ {
+ // check for extra conditions for wireframe, e.g. no border at
+ // objects
+ if(!mxClone->HasLineStyle())
+ {
+ bAddWireframe = true;
+ }
+ }
+
+ if(bAddWireframe)
+ {
+ // use wireframe poly when full drag is off or did not work
+ aDragPolyPolygon = mxClone->TakeXorPoly();
+ }
+
+ // add evtl. extra DragPolyPolygon
+ const basegfx::B2DPolyPolygon aSpecialDragPolyPolygon(mxClone->getSpecialDragPoly(DragStat()));
+
+ if(aSpecialDragPolyPolygon.count())
+ {
+ aDragPolyPolygon.append(aSpecialDragPolyPolygon);
+ }
+
+ if(aDragPolyPolygon.count())
+ {
+ addSdrDragEntry(std::unique_ptr<SdrDragEntry>(new SdrDragEntryPolyPolygon(aDragPolyPolygon)));
+ }
+}
+
+OUString SdrDragObjOwn::GetSdrDragComment() const
+{
+ OUString aStr;
+ // #i103058# get info string from the clone preferred, the original will
+ // not be changed. For security, use original as fallback
+ if(mxClone)
+ {
+ aStr = mxClone->getSpecialDragComment(DragStat());
+ }
+ else
+ {
+ const SdrObject* pObj = GetDragObj();
+
+ if(pObj)
+ {
+ aStr = pObj->getSpecialDragComment(DragStat());
+ }
+ }
+ return aStr;
+}
+
+bool SdrDragObjOwn::BeginSdrDrag()
+{
+ if(!mxClone)
+ {
+ const SdrObject* pObj = GetDragObj();
+
+ if(pObj && !pObj->IsResizeProtect())
+ {
+ if(pObj->beginSpecialDrag(DragStat()))
+ {
+ // create initial clone to have a start visualization
+ mxClone = pObj->getFullDragClone();
+ mxClone->applySpecialDrag(DragStat());
+
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+void SdrDragObjOwn::MoveSdrDrag(const Point& rNoSnapPnt)
+{
+ const SdrObject* pObj = GetDragObj();
+
+ if (!pObj)
+ // No object to drag. Bail out.
+ return;
+
+ Point aPnt(rNoSnapPnt);
+ SdrPageView* pPV = GetDragPV();
+
+ if (!pPV)
+ // No page view available. Bail out.
+ return;
+
+ if(!DragStat().IsNoSnap())
+ {
+ SnapPos(aPnt);
+ }
+ if(getSdrDragView().IsOrtho())
+ {
+ if (DragStat().IsOrtho8Possible())
+ {
+ OrthoDistance8(DragStat().GetStart(),aPnt,getSdrDragView().IsBigOrtho());
+ }
+ else if (DragStat().IsOrtho4Possible())
+ {
+ OrthoDistance4(DragStat().GetStart(),aPnt,getSdrDragView().IsBigOrtho());
+ }
+ }
+
+ if (!DragStat().CheckMinMoved(rNoSnapPnt))
+ // Not moved by the minimum threshold. Nothing to do.
+ return;
+
+ Hide();
+ DragStat().NextMove(aPnt);
+
+ // since SdrDragObjOwn currently supports no transformation of
+ // existing SdrDragEntries but only their recreation, a recreation
+ // after every move is needed in this mode. Delete existing
+ // SdrDragEntries here to force their recreation in the following Show().
+ clearSdrDragEntries();
+
+ // delete current clone (after the last reference to it is deleted above)
+ mxClone.reset();
+
+ // create a new clone and modify to current drag state
+ mxClone = pObj->getFullDragClone();
+ mxClone->applySpecialDrag(DragStat());
+
+ // AutoGrowWidth may change for SdrTextObj due to the automatism used
+ // with bDisableAutoWidthOnDragging, so not only geometry changes but
+ // also this (pretty indirect) property change is possible. If it gets
+ // changed, it needs to be copied to the original since nothing will
+ // happen when it only changes in the drag clone
+ const bool bOldAutoGrowWidth(pObj->GetMergedItem(SDRATTR_TEXT_AUTOGROWWIDTH).GetValue());
+ const bool bNewAutoGrowWidth(mxClone->GetMergedItem(SDRATTR_TEXT_AUTOGROWWIDTH).GetValue());
+
+ if (bOldAutoGrowWidth != bNewAutoGrowWidth)
+ {
+ GetDragObj()->SetMergedItem(makeSdrTextAutoGrowWidthItem(bNewAutoGrowWidth));
+ }
+
+ Show();
+}
+
+bool SdrDragObjOwn::EndSdrDrag(bool /*bCopy*/)
+{
+ Hide();
+ std::vector< std::unique_ptr<SdrUndoAction> > vConnectorUndoActions;
+ bool bRet = false;
+ SdrObject* pObj = GetDragObj();
+
+ if(pObj)
+ {
+ std::unique_ptr<SdrUndoAction> pUndo;
+ std::unique_ptr<SdrUndoAction> pUndo2;
+ const bool bUndo = getSdrDragView().IsUndoEnabled();
+
+ if( bUndo )
+ {
+ getSdrDragView().EndTextEditCurrentView();
+ if(!getSdrDragView().IsInsObjPoint() && pObj->IsInserted() )
+ {
+ if (DragStat().IsEndDragChangesAttributes())
+ {
+ pUndo=getSdrDragView().GetModel()->GetSdrUndoFactory().CreateUndoAttrObject(*pObj);
+
+ if (DragStat().IsEndDragChangesGeoAndAttributes())
+ {
+ vConnectorUndoActions = getSdrDragView().CreateConnectorUndo( *pObj );
+ pUndo2 = getSdrDragView().GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pObj);
+ }
+ }
+ else
+ {
+ vConnectorUndoActions = getSdrDragView().CreateConnectorUndo( *pObj );
+ pUndo= getSdrDragView().GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pObj);
+ }
+ }
+
+ if( pUndo )
+ {
+ getSdrDragView().BegUndo( pUndo->GetComment() );
+ }
+ else
+ {
+ getSdrDragView().BegUndo();
+ }
+ }
+
+ // Maybe use operator = for setting changed object data (do not change selection in
+ // view, this will destroy the interactor). This is possible since a clone is now
+ // directly modified by the modifiers. Only SdrTableObj is adding own UNDOs
+ // in its SdrTableObj::endSpecialDrag, so currently not possible. OTOH it uses
+ // a CreateUndoGeoObject(), so maybe setting SetEndDragChangesAttributes is okay. I
+ // will test this now
+ tools::Rectangle aBoundRect0;
+
+ if(pObj->GetUserCall())
+ {
+ aBoundRect0 = pObj->GetLastBoundRect();
+ }
+
+ bRet = pObj->applySpecialDrag(DragStat());
+ if (DragStat().IsEndDragChangesLayout())
+ {
+ auto pGeoUndo = dynamic_cast<SdrUndoGeoObj*>(pUndo.get());
+ if (pGeoUndo)
+ pGeoUndo->SetSkipChangeLayout(true);
+ }
+
+ if(bRet)
+ {
+ pObj->SetChanged();
+ pObj->BroadcastObjectChange();
+ pObj->SendUserCall( SdrUserCallType::Resize, aBoundRect0 );
+ }
+
+ if(bRet && bUndo )
+ {
+ getSdrDragView().AddUndoActions( std::move(vConnectorUndoActions) );
+
+ if ( pUndo )
+ {
+ getSdrDragView().AddUndo(std::move(pUndo));
+ }
+
+ if ( pUndo2 )
+ {
+ getSdrDragView().AddUndo(std::move(pUndo2));
+ }
+ }
+
+ if( bUndo )
+ getSdrDragView().EndUndo();
+ }
+
+ return bRet;
+}
+
+PointerStyle SdrDragObjOwn::GetSdrDragPointer() const
+{
+ const SdrHdl* pHdl=GetDragHdl();
+
+ if (pHdl)
+ {
+ return pHdl->GetPointer();
+ }
+
+ return PointerStyle::Move;
+}
+
+
+void SdrDragMove::createSdrDragEntryForSdrObject(const SdrObject& rOriginal)
+{
+ // use the view-independent primitive representation (without
+ // evtl. GridOffset, that may be applied to the DragEntry individually)
+ drawinglayer::primitive2d::Primitive2DContainer xRetval;
+ rOriginal.GetViewContact().getViewIndependentPrimitive2DContainer(xRetval);
+ addSdrDragEntry(
+ std::unique_ptr<SdrDragEntry>(
+ new SdrDragEntryPrimitive2DSequence(
+ std::move(xRetval))));
+
+}
+
+void SdrDragMove::applyCurrentTransformationToSdrObject(SdrObject& rTarget)
+{
+ rTarget.Move(Size(DragStat().GetDX(), DragStat().GetDY()));
+}
+
+SdrDragMove::SdrDragMove(SdrDragView& rNewView)
+ : SdrDragMethod(rNewView)
+ , nBestXSnap(0)
+ , nBestYSnap(0)
+ , bXSnapped(false)
+ , bYSnapped(false)
+{
+ setMoveOnly(true);
+}
+
+OUString SdrDragMove::GetSdrDragComment() const
+{
+ OUString aStr = ImpGetDescriptionStr(STR_DragMethMove)
+ + " (x="
+ + getSdrDragView().GetModel()->GetMetricString(DragStat().GetDX())
+ + " y="
+ + getSdrDragView().GetModel()->GetMetricString(DragStat().GetDY())
+ + ")";
+
+ if(getSdrDragView().IsDragWithCopy())
+ {
+ if(!getSdrDragView().IsInsObjPoint() && !getSdrDragView().IsInsGluePoint())
+ {
+ aStr += SvxResId(STR_EditWithCopy);
+ }
+ }
+ return aStr;
+}
+
+bool SdrDragMove::BeginSdrDrag()
+{
+ DragStat().SetActionRect(GetMarkedRect());
+ Show();
+
+ return true;
+}
+
+basegfx::B2DHomMatrix SdrDragMove::getCurrentTransformation() const
+{
+ return basegfx::utils::createTranslateB2DHomMatrix(DragStat().GetDX(), DragStat().GetDY());
+}
+
+void SdrDragMove::ImpCheckSnap(const Point& rPt)
+{
+ Point aPt(rPt);
+ SdrSnap nRet=SnapPos(aPt);
+ aPt-=rPt;
+
+ if (nRet & SdrSnap::XSNAPPED)
+ {
+ if (bXSnapped)
+ {
+ if (std::abs(aPt.X())<std::abs(nBestXSnap))
+ {
+ nBestXSnap=aPt.X();
+ }
+ }
+ else
+ {
+ nBestXSnap=aPt.X();
+ bXSnapped=true;
+ }
+ }
+
+ if (!(nRet & SdrSnap::YSNAPPED))
+ return;
+
+ if (bYSnapped)
+ {
+ if (std::abs(aPt.Y())<std::abs(nBestYSnap))
+ {
+ nBestYSnap=aPt.Y();
+ }
+ }
+ else
+ {
+ nBestYSnap=aPt.Y();
+ bYSnapped=true;
+ }
+}
+
+void SdrDragMove::MoveSdrDrag(const Point& rNoSnapPnt_)
+{
+ nBestXSnap=0;
+ nBestYSnap=0;
+ bXSnapped=false;
+ bYSnapped=false;
+ Point aNoSnapPnt(rNoSnapPnt_);
+ const tools::Rectangle& aSR=GetMarkedRect();
+ tools::Long nMovedx=aNoSnapPnt.X()-DragStat().GetStart().X();
+ tools::Long nMovedy=aNoSnapPnt.Y()-DragStat().GetStart().Y();
+ Point aLO(aSR.TopLeft()); aLO.AdjustX(nMovedx ); aLO.AdjustY(nMovedy );
+ Point aRU(aSR.BottomRight()); aRU.AdjustX(nMovedx ); aRU.AdjustY(nMovedy );
+ Point aLU(aLO.X(),aRU.Y());
+ Point aRO(aRU.X(),aLO.Y());
+ ImpCheckSnap(aLO);
+
+ if (!getSdrDragView().IsMoveSnapOnlyTopLeft())
+ {
+ ImpCheckSnap(aRO);
+ ImpCheckSnap(aLU);
+ ImpCheckSnap(aRU);
+ }
+
+ Point aPnt(aNoSnapPnt.X()+nBestXSnap,aNoSnapPnt.Y()+nBestYSnap);
+ bool bOrtho=getSdrDragView().IsOrtho();
+
+ if (bOrtho)
+ OrthoDistance8(DragStat().GetStart(),aPnt,getSdrDragView().IsBigOrtho());
+
+ if (!DragStat().CheckMinMoved(aNoSnapPnt))
+ return;
+
+ Point aPt1(aPnt);
+ tools::Rectangle aLR(getSdrDragView().GetWorkArea());
+ bool bWorkArea=!aLR.IsEmpty();
+ bool bDragLimit=IsDragLimit();
+
+ if (bDragLimit || bWorkArea)
+ {
+ tools::Rectangle aSR2(GetMarkedRect());
+ Point aD(aPt1-DragStat().GetStart());
+
+ if (bDragLimit)
+ {
+ tools::Rectangle aR2(GetDragLimitRect());
+
+ if (bWorkArea)
+ aLR.Intersection(aR2);
+ else
+ aLR=aR2;
+ }
+
+ if (aSR2.Left()>aLR.Left() || aSR2.Right()<aLR.Right())
+ { // any space to move to?
+ aSR2.Move(aD.X(),0);
+
+ if (aSR2.Left()<aLR.Left())
+ {
+ aPt1.AdjustX( -(aSR2.Left()-aLR.Left()) );
+ }
+ else if (aSR2.Right()>aLR.Right())
+ {
+ aPt1.AdjustX( -(aSR2.Right()-aLR.Right()) );
+ }
+ }
+ else
+ aPt1.setX(DragStat().GetStart().X() ); // no space to move to
+
+ if (aSR2.Top()>aLR.Top() || aSR2.Bottom()<aLR.Bottom())
+ { // any space to move to?
+ aSR2.Move(0,aD.Y());
+
+ if (aSR2.Top()<aLR.Top())
+ {
+ aPt1.AdjustY( -(aSR2.Top()-aLR.Top()) );
+ }
+ else if (aSR2.Bottom()>aLR.Bottom())
+ {
+ aPt1.AdjustY( -(aSR2.Bottom()-aLR.Bottom()) );
+ }
+ }
+ else
+ aPt1.setY(DragStat().GetStart().Y() ); // no space to move to
+ }
+
+ if (getSdrDragView().IsDraggingGluePoints())
+ { // restrict gluepoints to the BoundRect of the Obj
+ aPt1-=DragStat().GetStart();
+ const SdrMarkList& rML=GetMarkedObjectList();
+ const size_t nMarkCount=rML.GetMarkCount();
+
+ for (size_t nMarkNum=0; nMarkNum<nMarkCount; ++nMarkNum)
+ {
+ const SdrMark* pM=rML.GetMark(nMarkNum);
+ const SdrUShortCont& rPts = pM->GetMarkedGluePoints();
+
+ if (!rPts.empty())
+ {
+ const SdrObject* pObj=pM->GetMarkedSdrObj();
+ const SdrGluePointList* pGPL=pObj->GetGluePointList();
+ tools::Rectangle aBound(pObj->GetCurrentBoundRect());
+
+ for (sal_uInt16 nId : rPts)
+ {
+ sal_uInt16 nGlueNum=pGPL->FindGluePoint(nId);
+
+ if (nGlueNum!=SDRGLUEPOINT_NOTFOUND)
+ {
+ Point aPt((*pGPL)[nGlueNum].GetAbsolutePos(*pObj));
+ aPt+=aPt1; // move by this much
+ if (aPt.X()<aBound.Left() ) aPt1.AdjustX( -(aPt.X()-aBound.Left()) ) ;
+ if (aPt.X()>aBound.Right() ) aPt1.AdjustX( -(aPt.X()-aBound.Right()) ) ;
+ if (aPt.Y()<aBound.Top() ) aPt1.AdjustY( -(aPt.Y()-aBound.Top()) ) ;
+ if (aPt.Y()>aBound.Bottom()) aPt1.AdjustY( -(aPt.Y()-aBound.Bottom()) );
+ }
+ }
+ }
+ }
+
+ aPt1+=DragStat().GetStart();
+ }
+
+ if (bOrtho)
+ OrthoDistance8(DragStat().GetStart(),aPt1,false);
+
+ if (aPt1!=DragStat().GetNow())
+ {
+ Hide();
+ DragStat().NextMove(aPt1);
+ tools::Rectangle aAction(GetMarkedRect());
+ aAction.Move(DragStat().GetDX(),DragStat().GetDY());
+ DragStat().SetActionRect(aAction);
+ Show();
+ }
+}
+
+bool SdrDragMove::EndSdrDrag(bool bCopy)
+{
+ Hide();
+
+ if (getSdrDragView().IsInsObjPoint() || getSdrDragView().IsInsGluePoint())
+ bCopy=false;
+
+ if (IsDraggingPoints())
+ {
+ getSdrDragView().MoveMarkedPoints(Size(DragStat().GetDX(),DragStat().GetDY()));
+ }
+ else if (IsDraggingGluePoints())
+ {
+ getSdrDragView().MoveMarkedGluePoints(Size(DragStat().GetDX(),DragStat().GetDY()),bCopy);
+ }
+ else
+ {
+ getSdrDragView().MoveMarkedObj(Size(DragStat().GetDX(),DragStat().GetDY()),bCopy);
+ }
+
+ return true;
+}
+
+PointerStyle SdrDragMove::GetSdrDragPointer() const
+{
+ if (IsDraggingPoints() || IsDraggingGluePoints())
+ {
+ return PointerStyle::MovePoint;
+ }
+ else
+ {
+ return PointerStyle::Move;
+ }
+}
+
+
+SdrDragResize::SdrDragResize(SdrDragView& rNewView)
+: SdrDragMethod(rNewView),
+ aXFact(1,1),
+ aYFact(1,1)
+{
+}
+
+OUString SdrDragResize::GetSdrDragComment() const
+{
+ OUString aStr = ImpGetDescriptionStr(STR_DragMethResize);
+ Fraction aFact1(1,1);
+ Point aStart(DragStat().GetStart());
+ Point aRef(DragStat().GetRef1());
+ sal_Int32 nXDiv(aStart.X() - aRef.X());
+
+ if(!nXDiv)
+ nXDiv = 1;
+
+ sal_Int32 nYDiv(aStart.Y() - aRef.Y());
+
+ if(!nYDiv)
+ nYDiv = 1;
+
+ bool bX(aXFact != aFact1 && std::abs(nXDiv) > 1);
+ bool bY(aYFact != aFact1 && std::abs(nYDiv) > 1);
+
+ if(bX || bY)
+ {
+ aStr += " (";
+
+ bool bEqual(aXFact == aYFact);
+ if(bX)
+ {
+ if(!bEqual)
+ aStr += "x=";
+
+ aStr += SdrModel::GetPercentString(aXFact);
+ }
+
+ if(bY && !bEqual)
+ {
+ if(bX)
+ aStr += " ";
+
+ aStr += "y=" + SdrModel::GetPercentString(aYFact);
+ }
+
+ aStr += ")";
+ }
+
+ if(getSdrDragView().IsDragWithCopy())
+ aStr += SvxResId(STR_EditWithCopy);
+ return aStr;
+}
+
+bool SdrDragResize::BeginSdrDrag()
+{
+ SdrHdlKind eRefHdl=SdrHdlKind::Move;
+ SdrHdl* pRefHdl=nullptr;
+
+ switch (GetDragHdlKind())
+ {
+ case SdrHdlKind::UpperLeft: eRefHdl=SdrHdlKind::LowerRight; break;
+ case SdrHdlKind::Upper: eRefHdl=SdrHdlKind::Lower; DragStat().SetHorFixed(true); break;
+ case SdrHdlKind::UpperRight: eRefHdl=SdrHdlKind::LowerLeft; break;
+ case SdrHdlKind::Left : eRefHdl=SdrHdlKind::Right; DragStat().SetVerFixed(true); break;
+ case SdrHdlKind::Right: eRefHdl=SdrHdlKind::Left ; DragStat().SetVerFixed(true); break;
+ case SdrHdlKind::LowerLeft: eRefHdl=SdrHdlKind::UpperRight; break;
+ case SdrHdlKind::Lower: eRefHdl=SdrHdlKind::Upper; DragStat().SetHorFixed(true); break;
+ case SdrHdlKind::LowerRight: eRefHdl=SdrHdlKind::UpperLeft; break;
+ default: break;
+ }
+
+ if (eRefHdl!=SdrHdlKind::Move)
+ pRefHdl=GetHdlList().GetHdl(eRefHdl);
+
+ if (pRefHdl!=nullptr && !getSdrDragView().IsResizeAtCenter())
+ {
+ DragStat().SetRef1(pRefHdl->GetPos());
+ }
+ else
+ {
+ SdrHdl* pRef1=GetHdlList().GetHdl(SdrHdlKind::UpperLeft);
+ SdrHdl* pRef2=GetHdlList().GetHdl(SdrHdlKind::LowerRight);
+
+ if (pRef1!=nullptr && pRef2!=nullptr)
+ {
+ DragStat().SetRef1(tools::Rectangle(pRef1->GetPos(),pRef2->GetPos()).Center());
+ }
+ else
+ {
+ DragStat().SetRef1(GetMarkedRect().Center());
+ }
+ }
+
+ Show();
+
+ return true;
+}
+
+basegfx::B2DHomMatrix SdrDragResize::getCurrentTransformation() const
+{
+ basegfx::B2DHomMatrix aRetval(basegfx::utils::createTranslateB2DHomMatrix(
+ -DragStat().GetRef1().X(), -DragStat().GetRef1().Y()));
+ aRetval.scale(double(aXFact), double(aYFact));
+ aRetval.translate(DragStat().GetRef1().X(), DragStat().GetRef1().Y());
+
+ return aRetval;
+}
+
+void SdrDragResize::MoveSdrDrag(const Point& rNoSnapPnt)
+{
+ Point aPnt(GetSnapPos(rNoSnapPnt));
+ Point aStart(DragStat().GetStart());
+ Point aRef(DragStat().GetRef1());
+ Fraction aMaxFact(0x7FFFFFFF,1);
+ tools::Rectangle aLR(getSdrDragView().GetWorkArea());
+ bool bWorkArea=!aLR.IsEmpty();
+ bool bDragLimit=IsDragLimit();
+
+ if (bDragLimit || bWorkArea)
+ {
+ tools::Rectangle aSR(GetMarkedRect());
+
+ if (bDragLimit)
+ {
+ tools::Rectangle aR2(GetDragLimitRect());
+
+ if (bWorkArea)
+ aLR.Intersection(aR2);
+ else
+ aLR=aR2;
+ }
+
+ if (aPnt.X()<aLR.Left())
+ aPnt.setX(aLR.Left() );
+ else if (aPnt.X()>aLR.Right())
+ aPnt.setX(aLR.Right() );
+
+ if (aPnt.Y()<aLR.Top())
+ aPnt.setY(aLR.Top() );
+ else if (aPnt.Y()>aLR.Bottom())
+ aPnt.setY(aLR.Bottom() );
+
+ if (aRef.X()>aSR.Left())
+ {
+ Fraction aMax(aRef.X()-aLR.Left(),aRef.X()-aSR.Left());
+
+ if (aMax<aMaxFact)
+ aMaxFact=aMax;
+ }
+
+ if (aRef.X()<aSR.Right())
+ {
+ Fraction aMax(aLR.Right()-aRef.X(),aSR.Right()-aRef.X());
+
+ if (aMax<aMaxFact)
+ aMaxFact=aMax;
+ }
+
+ if (aRef.Y()>aSR.Top())
+ {
+ Fraction aMax(aRef.Y()-aLR.Top(),aRef.Y()-aSR.Top());
+
+ if (aMax<aMaxFact)
+ aMaxFact=aMax;
+ }
+
+ if (aRef.Y()<aSR.Bottom())
+ {
+ Fraction aMax(aLR.Bottom()-aRef.Y(),aSR.Bottom()-aRef.Y());
+
+ if (aMax<aMaxFact)
+ aMaxFact=aMax;
+ }
+ }
+
+ tools::Long nXDiv=aStart.X()-aRef.X(); if (nXDiv==0) nXDiv=1;
+ tools::Long nYDiv=aStart.Y()-aRef.Y(); if (nYDiv==0) nYDiv=1;
+ tools::Long nXMul=aPnt.X()-aRef.X();
+ tools::Long nYMul=aPnt.Y()-aRef.Y();
+
+ if (nXDiv<0)
+ {
+ nXDiv=-nXDiv;
+ nXMul=-nXMul;
+ }
+
+ if (nYDiv<0)
+ {
+ nYDiv=-nYDiv;
+ nYMul=-nYMul;
+ }
+
+ bool bXNeg=nXMul<0; if (bXNeg) nXMul=-nXMul;
+ bool bYNeg=nYMul<0; if (bYNeg) nYMul=-nYMul;
+ bool bOrtho=getSdrDragView().IsOrtho() || !getSdrDragView().IsResizeAllowed();
+
+ if (!DragStat().IsHorFixed() && !DragStat().IsVerFixed())
+ {
+ if (std::abs(nXDiv)<=1 || std::abs(nYDiv)<=1)
+ bOrtho=false;
+
+ if (bOrtho)
+ {
+ if ((Fraction(nXMul,nXDiv)>Fraction(nYMul,nYDiv)) !=getSdrDragView().IsBigOrtho())
+ {
+ nXMul=nYMul;
+ nXDiv=nYDiv;
+ }
+ else
+ {
+ nYMul=nXMul;
+ nYDiv=nXDiv;
+ }
+ }
+ }
+ else
+ {
+ if (bOrtho)
+ {
+ if (DragStat().IsHorFixed())
+ {
+ bXNeg=false;
+ nXMul=nYMul;
+ nXDiv=nYDiv;
+ }
+
+ if (DragStat().IsVerFixed())
+ {
+ bYNeg=false;
+ nYMul=nXMul;
+ nYDiv=nXDiv;
+ }
+ }
+ else
+ {
+ if (DragStat().IsHorFixed())
+ {
+ bXNeg=false;
+ nXMul=1;
+ nXDiv=1;
+ }
+
+ if (DragStat().IsVerFixed())
+ {
+ bYNeg=false;
+ nYMul=1;
+ nYDiv=1;
+ }
+ }
+ }
+
+ Fraction aNewXFact(nXMul,nXDiv);
+ Fraction aNewYFact(nYMul,nYDiv);
+
+ if (bOrtho)
+ {
+ if (aNewXFact>aMaxFact)
+ {
+ aNewXFact=aMaxFact;
+ aNewYFact=aMaxFact;
+ }
+
+ if (aNewYFact>aMaxFact)
+ {
+ aNewXFact=aMaxFact;
+ aNewYFact=aMaxFact;
+ }
+ }
+
+ if (bXNeg)
+ aNewXFact=Fraction(-aNewXFact.GetNumerator(),aNewXFact.GetDenominator());
+
+ if (bYNeg)
+ aNewYFact=Fraction(-aNewYFact.GetNumerator(),aNewYFact.GetDenominator());
+
+ if (DragStat().CheckMinMoved(aPnt))
+ {
+ if ((!DragStat().IsHorFixed() && aPnt.X()!=DragStat().GetNow().X()) ||
+ (!DragStat().IsVerFixed() && aPnt.Y()!=DragStat().GetNow().Y()))
+ {
+ Hide();
+ DragStat().NextMove(aPnt);
+ aXFact=aNewXFact;
+ aYFact=aNewYFact;
+ Show();
+ }
+ }
+}
+
+void SdrDragResize::applyCurrentTransformationToSdrObject(SdrObject& rTarget)
+{
+ rTarget.Resize(DragStat().GetRef1(),aXFact,aYFact);
+}
+
+bool SdrDragResize::EndSdrDrag(bool bCopy)
+{
+ Hide();
+
+ if (IsDraggingPoints())
+ {
+ getSdrDragView().ResizeMarkedPoints(DragStat().GetRef1(),aXFact,aYFact);
+ }
+ else if (IsDraggingGluePoints())
+ {
+ getSdrDragView().ResizeMarkedGluePoints(DragStat().GetRef1(),aXFact,aYFact,bCopy);
+ }
+ else
+ {
+ getSdrDragView().ResizeMarkedObj(DragStat().GetRef1(),aXFact,aYFact,bCopy);
+ }
+
+ return true;
+}
+
+PointerStyle SdrDragResize::GetSdrDragPointer() const
+{
+ const SdrHdl* pHdl=GetDragHdl();
+
+ if (pHdl!=nullptr)
+ {
+ return pHdl->GetPointer();
+ }
+
+ return PointerStyle::Move;
+}
+
+
+void SdrDragRotate::applyCurrentTransformationToSdrObject(SdrObject& rTarget)
+{
+ rTarget.Rotate(DragStat().GetRef1(), nAngle, nSin, nCos);
+}
+
+SdrDragRotate::SdrDragRotate(SdrDragView& rNewView)
+: SdrDragMethod(rNewView),
+ nSin(0.0),
+ nCos(1.0),
+ nAngle0(0),
+ nAngle(0),
+ bRight(false)
+{
+}
+
+OUString SdrDragRotate::GetSdrDragComment() const
+{
+ OUString aStr = ImpGetDescriptionStr(STR_DragMethRotate) +
+ " (";
+ Degree100 nTmpAngle(NormAngle36000(nAngle));
+
+ if(bRight && nAngle)
+ {
+ nTmpAngle -= 36000_deg100;
+ }
+
+ aStr += SdrModel::GetAngleString(nTmpAngle) + ")";
+
+ if(getSdrDragView().IsDragWithCopy())
+ aStr += SvxResId(STR_EditWithCopy);
+ return aStr;
+}
+
+bool SdrDragRotate::BeginSdrDrag()
+{
+ SdrHdl* pH=GetHdlList().GetHdl(SdrHdlKind::Ref1);
+
+ if (nullptr != pH)
+ {
+ Show();
+ DragStat().SetRef1(pH->GetPos());
+ nAngle0=GetAngle(DragStat().GetStart()-DragStat().GetRef1());
+ return true;
+ }
+
+ // RotGrfFlyFrame: Support rotation around center *without* Ref1 (normally
+ // the rotation point)
+ const tools::Rectangle aLocalMarkRect(getSdrDragView().GetMarkedObjRect());
+
+ if(!aLocalMarkRect.IsEmpty())
+ {
+ Show();
+ DragStat().SetRef1(aLocalMarkRect.Center());
+ nAngle0=GetAngle(DragStat().GetStart()-DragStat().GetRef1());
+ return true;
+ }
+
+ OSL_FAIL("SdrDragRotate::BeginSdrDrag(): No reference point handle found.");
+ return false;
+}
+
+basegfx::B2DHomMatrix SdrDragRotate::getCurrentTransformation() const
+{
+ return basegfx::utils::createRotateAroundPoint(
+ DragStat().GetRef1().X(), DragStat().GetRef1().Y(),
+ -atan2(nSin, nCos));
+}
+
+void SdrDragRotate::MoveSdrDrag(const Point& rPnt_)
+{
+ Point aPnt(rPnt_);
+ if (!DragStat().CheckMinMoved(aPnt))
+ return;
+
+ Degree100 nNewAngle=NormAngle36000(GetAngle(aPnt-DragStat().GetRef1())-nAngle0);
+ Degree100 nSA(0);
+
+ if (getSdrDragView().IsAngleSnapEnabled())
+ nSA=getSdrDragView().GetSnapAngle();
+
+ if (!getSdrDragView().IsRotateAllowed())
+ nSA=9000_deg100;
+
+ if (nSA)
+ { // angle snapping
+ nNewAngle += nSA / 2_deg100;
+ nNewAngle /= nSA;
+ nNewAngle *= nSA;
+ }
+
+ nNewAngle=NormAngle18000(nNewAngle);
+
+ if (nAngle==nNewAngle)
+ return;
+
+ sal_uInt16 nSekt0=GetAngleSector(nAngle);
+ sal_uInt16 nSekt1=GetAngleSector(nNewAngle);
+
+ if (nSekt0==0 && nSekt1==3)
+ bRight=true;
+
+ if (nSekt0==3 && nSekt1==0)
+ bRight=false;
+
+ nAngle=nNewAngle;
+ double a = toRadians(nAngle);
+ double nSin1=sin(a); // calculate now, so as little time as possible
+ double nCos1=cos(a); // passes between Hide() and Show()
+ Hide();
+ nSin=nSin1;
+ nCos=nCos1;
+ DragStat().NextMove(aPnt);
+ Show();
+}
+
+bool SdrDragRotate::EndSdrDrag(bool bCopy)
+{
+ Hide();
+
+ if (nAngle!=0_deg100)
+ {
+ if (IsDraggingPoints())
+ {
+ getSdrDragView().RotateMarkedPoints(DragStat().GetRef1(),nAngle);
+ }
+ else if (IsDraggingGluePoints())
+ {
+ getSdrDragView().RotateMarkedGluePoints(DragStat().GetRef1(),nAngle,bCopy);
+ }
+ else
+ {
+ getSdrDragView().RotateMarkedObj(DragStat().GetRef1(),nAngle,bCopy);
+ }
+ }
+ return true;
+}
+
+PointerStyle SdrDragRotate::GetSdrDragPointer() const
+{
+ return PointerStyle::Rotate;
+}
+
+
+SdrDragShear::SdrDragShear(SdrDragView& rNewView, bool bSlant1)
+: SdrDragMethod(rNewView),
+ aFact(1,1),
+ nAngle0(0),
+ nAngle(0),
+ nTan(0.0),
+ bVertical(false),
+ bResize(false),
+ bUpSideDown(false),
+ bSlant(bSlant1)
+{
+}
+
+OUString SdrDragShear::GetSdrDragComment() const
+{
+ OUString aStr = ImpGetDescriptionStr(STR_DragMethShear) +
+ " (";
+
+ Degree100 nTmpAngle(nAngle);
+
+ if(bUpSideDown)
+ nTmpAngle += 18000_deg100;
+
+ nTmpAngle = NormAngle18000(nTmpAngle);
+
+ aStr += SdrModel::GetAngleString(nTmpAngle) + ")";
+
+ if(getSdrDragView().IsDragWithCopy())
+ aStr += SvxResId(STR_EditWithCopy);
+ return aStr;
+}
+
+bool SdrDragShear::BeginSdrDrag()
+{
+ SdrHdlKind eRefHdl=SdrHdlKind::Move;
+ SdrHdl* pRefHdl=nullptr;
+
+ switch (GetDragHdlKind())
+ {
+ case SdrHdlKind::Upper: eRefHdl=SdrHdlKind::Lower; break;
+ case SdrHdlKind::Lower: eRefHdl=SdrHdlKind::Upper; break;
+ case SdrHdlKind::Left : eRefHdl=SdrHdlKind::Right; bVertical=true; break;
+ case SdrHdlKind::Right: eRefHdl=SdrHdlKind::Left ; bVertical=true; break;
+ default: break;
+ }
+
+ if (eRefHdl!=SdrHdlKind::Move)
+ pRefHdl=GetHdlList().GetHdl(eRefHdl);
+
+ if (pRefHdl!=nullptr)
+ {
+ DragStat().SetRef1(pRefHdl->GetPos());
+ nAngle0=GetAngle(DragStat().GetStart()-DragStat().GetRef1());
+ }
+ else
+ {
+ OSL_FAIL("SdrDragShear::BeginSdrDrag(): No reference point handle for shearing found.");
+ return false;
+ }
+
+ Show();
+ return true;
+}
+
+basegfx::B2DHomMatrix SdrDragShear::getCurrentTransformation() const
+{
+ basegfx::B2DHomMatrix aRetval(basegfx::utils::createTranslateB2DHomMatrix(
+ -DragStat().GetRef1().X(), -DragStat().GetRef1().Y()));
+
+ if (bResize)
+ {
+ if (bVertical)
+ {
+ aRetval.scale(double(aFact), 1.0);
+ aRetval.shearY(-nTan);
+ }
+ else
+ {
+ aRetval.scale(1.0, double(aFact));
+ aRetval.shearX(-nTan);
+ }
+ }
+
+ aRetval.translate(DragStat().GetRef1().X(), DragStat().GetRef1().Y());
+
+ return aRetval;
+}
+
+void SdrDragShear::MoveSdrDrag(const Point& rPnt)
+{
+ if (!DragStat().CheckMinMoved(rPnt))
+ return;
+
+ bResize=!getSdrDragView().IsOrtho();
+ Degree100 nSA(0);
+
+ if (getSdrDragView().IsAngleSnapEnabled())
+ nSA=getSdrDragView().GetSnapAngle();
+
+ Point aP0(DragStat().GetStart());
+ Point aPnt(rPnt);
+ Fraction aNewFract(1,1);
+
+ // if angle snapping not activated, snap to raster (except when using slant)
+ if (nSA==0_deg100 && !bSlant)
+ aPnt=GetSnapPos(aPnt);
+
+ if (!bSlant && !bResize)
+ { // shear, but no resize
+ if (bVertical)
+ aPnt.setX(aP0.X() );
+ else
+ aPnt.setY(aP0.Y() );
+ }
+
+ Point aRef(DragStat().GetRef1());
+ Point aDif(aPnt-aRef);
+
+ Degree100 nNewAngle(0);
+
+ if (bSlant)
+ {
+ nNewAngle=NormAngle18000(-(GetAngle(aDif)-nAngle0));
+
+ if (bVertical)
+ nNewAngle=NormAngle18000(-nNewAngle);
+ }
+ else
+ {
+ if (bVertical)
+ nNewAngle=NormAngle18000(GetAngle(aDif));
+ else
+ nNewAngle=NormAngle18000(-(GetAngle(aDif)-9000_deg100));
+
+ if (nNewAngle<Degree100(-9000) || nNewAngle>9000_deg100)
+ nNewAngle=NormAngle18000(nNewAngle+18000_deg100);
+
+ if (bResize)
+ {
+ Point aPt2(aPnt);
+
+ if (nSA!=0_deg100)
+ aPt2=GetSnapPos(aPnt); // snap this one in any case
+
+ if (bVertical)
+ {
+ aNewFract=Fraction(aPt2.X()-aRef.X(),aP0.X()-aRef.X());
+ }
+ else
+ {
+ aNewFract=Fraction(aPt2.Y()-aRef.Y(),aP0.Y()-aRef.Y());
+ }
+ }
+ }
+
+ bool bNeg=nNewAngle<0_deg100;
+
+ if (bNeg)
+ nNewAngle=-nNewAngle;
+
+ if (nSA)
+ { // angle snapping
+ nNewAngle += nSA / 2_deg100;
+ nNewAngle /= nSA;
+ nNewAngle *= nSA;
+ }
+
+ nNewAngle=NormAngle36000(nNewAngle);
+ bUpSideDown=nNewAngle>9000_deg100 && nNewAngle<27000_deg100;
+
+ if (bSlant)
+ { // calculate resize for slant
+ // when angle snapping is activated, disable 89 degree limit
+ Degree100 nTmpAngle=nNewAngle;
+ if (bUpSideDown) nNewAngle -= 18000_deg100;
+ if (bNeg) nTmpAngle=-nTmpAngle;
+ bResize=true;
+ aNewFract = cos(toRadians(nTmpAngle));
+ aFact.ReduceInaccurate(10); // three decimals should be enough
+ }
+
+ if (nNewAngle > 8900_deg100)
+ nNewAngle = 8900_deg100;
+
+ if (bNeg)
+ nNewAngle=-nNewAngle;
+
+ if (nAngle!=nNewAngle || aFact!=aNewFract)
+ {
+ nAngle=nNewAngle;
+ aFact=aNewFract;
+ double a = toRadians(nAngle);
+ double nTan1=tan(a); // calculate now, so as little time as possible passes between Hide() and Show()
+ Hide();
+ nTan=nTan1;
+ DragStat().NextMove(rPnt);
+ Show();
+ }
+}
+
+void SdrDragShear::applyCurrentTransformationToSdrObject(SdrObject& rTarget)
+{
+ if (bResize)
+ {
+ if (bVertical)
+ {
+ rTarget.Resize(DragStat().GetRef1(),aFact,Fraction(1,1));
+ }
+ else
+ {
+ rTarget.Resize(DragStat().GetRef1(),Fraction(1,1),aFact);
+ }
+ }
+
+ if (nAngle)
+ {
+ rTarget.Shear(DragStat().GetRef1(), nAngle, nTan, bVertical);
+ }
+}
+
+bool SdrDragShear::EndSdrDrag(bool bCopy)
+{
+ Hide();
+
+ if (bResize && aFact==Fraction(1,1))
+ bResize=false;
+
+ if (nAngle || bResize)
+ {
+ if (nAngle && bResize)
+ {
+ OUString aStr = ImpGetDescriptionStr(STR_EditShear);
+
+ if (bCopy)
+ aStr += SvxResId(STR_EditWithCopy);
+
+ getSdrDragView().BegUndo(aStr);
+ }
+
+ if (bResize)
+ {
+ if (bVertical)
+ {
+ getSdrDragView().ResizeMarkedObj(DragStat().GetRef1(),aFact,Fraction(1,1),bCopy);
+ }
+ else
+ {
+ getSdrDragView().ResizeMarkedObj(DragStat().GetRef1(),Fraction(1,1),aFact,bCopy);
+ }
+
+ bCopy=false;
+ }
+
+ if (nAngle)
+ {
+ getSdrDragView().ShearMarkedObj(DragStat().GetRef1(),nAngle,bVertical,bCopy);
+ }
+
+ if (nAngle && bResize)
+ getSdrDragView().EndUndo();
+
+ return true;
+ }
+
+ return false;
+}
+
+PointerStyle SdrDragShear::GetSdrDragPointer() const
+{
+ if (bVertical)
+ return PointerStyle::VShear;
+ else
+ return PointerStyle::HShear;
+}
+
+
+void SdrDragMirror::applyCurrentTransformationToSdrObject(SdrObject& rTarget)
+{
+ if(bMirrored)
+ {
+ rTarget.Mirror(DragStat().GetRef1(), DragStat().GetRef2());
+ }
+}
+
+SdrDragMirror::SdrDragMirror(SdrDragView& rNewView)
+: SdrDragMethod(rNewView),
+ nAngle(0),
+ bMirrored(false),
+ bSide0(false)
+{
+}
+
+bool SdrDragMirror::ImpCheckSide(const Point& rPnt) const
+{
+ Degree100 nAngle1=GetAngle(rPnt-DragStat().GetRef1());
+ nAngle1-=nAngle;
+ nAngle1=NormAngle36000(nAngle1);
+
+ return nAngle1<18000_deg100;
+}
+
+OUString SdrDragMirror::GetSdrDragComment() const
+{
+ OUString aStr;
+ if (aDif.X()==0)
+ aStr = ImpGetDescriptionStr(STR_DragMethMirrorHori);
+ else if (aDif.Y()==0)
+ aStr = ImpGetDescriptionStr(STR_DragMethMirrorVert);
+ else if (std::abs(aDif.X()) == std::abs(aDif.Y()))
+ aStr = ImpGetDescriptionStr(STR_DragMethMirrorDiag);
+ else
+ aStr = ImpGetDescriptionStr(STR_DragMethMirrorFree);
+
+ if (getSdrDragView().IsDragWithCopy())
+ aStr+=SvxResId(STR_EditWithCopy);
+ return aStr;
+}
+
+bool SdrDragMirror::BeginSdrDrag()
+{
+ SdrHdl* pH1=GetHdlList().GetHdl(SdrHdlKind::Ref1);
+ SdrHdl* pH2=GetHdlList().GetHdl(SdrHdlKind::Ref2);
+
+ if (pH1!=nullptr && pH2!=nullptr)
+ {
+ DragStat().SetRef1(pH1->GetPos());
+ DragStat().SetRef2(pH2->GetPos());
+ Ref1()=pH1->GetPos();
+ Ref2()=pH2->GetPos();
+ aDif=pH2->GetPos()-pH1->GetPos();
+ bool b90=(aDif.X()==0) || aDif.Y()==0;
+ bool b45=b90 || (std::abs(aDif.X()) == std::abs(aDif.Y()));
+ nAngle=NormAngle36000(GetAngle(aDif));
+
+ if (!getSdrDragView().IsMirrorAllowed() && !b45)
+ return false; // free choice of axis angle not allowed
+
+ if (!getSdrDragView().IsMirrorAllowed() && !b90)
+ return false; // 45 degrees not allowed either
+
+ bSide0=ImpCheckSide(DragStat().GetStart());
+ Show();
+ return true;
+ }
+ else
+ {
+ OSL_FAIL("SdrDragMirror::BeginSdrDrag(): Axis of reflection not found.");
+ return false;
+ }
+}
+
+basegfx::B2DHomMatrix SdrDragMirror::getCurrentTransformation() const
+{
+ basegfx::B2DHomMatrix aRetval;
+
+ if (bMirrored)
+ {
+ const double fDeltaX(DragStat().GetRef2().X() - DragStat().GetRef1().X());
+ const double fDeltaY(DragStat().GetRef2().Y() - DragStat().GetRef1().Y());
+ const double fRotation(atan2(fDeltaY, fDeltaX));
+
+ aRetval = basegfx::utils::createTranslateB2DHomMatrix(-DragStat().GetRef1().X(), -DragStat().GetRef1().Y());
+ aRetval.rotate(-fRotation);
+ aRetval.scale(1.0, -1.0);
+ aRetval.rotate(fRotation);
+ aRetval.translate(DragStat().GetRef1().X(), DragStat().GetRef1().Y());
+ }
+
+ return aRetval;
+}
+
+void SdrDragMirror::MoveSdrDrag(const Point& rPnt)
+{
+ if (!DragStat().CheckMinMoved(rPnt))
+ return;
+
+ bool bNewSide=ImpCheckSide(rPnt);
+ bool bNewMirrored=bSide0!=bNewSide;
+
+ if (bMirrored!=bNewMirrored)
+ {
+ Hide();
+ bMirrored=bNewMirrored;
+ DragStat().NextMove(rPnt);
+ Show();
+ }
+}
+
+bool SdrDragMirror::EndSdrDrag(bool bCopy)
+{
+ Hide();
+
+ if (bMirrored)
+ {
+ getSdrDragView().MirrorMarkedObj(DragStat().GetRef1(),DragStat().GetRef2(),bCopy);
+ }
+
+ return true;
+}
+
+PointerStyle SdrDragMirror::GetSdrDragPointer() const
+{
+ return PointerStyle::Mirror;
+}
+
+
+SdrDragGradient::SdrDragGradient(SdrDragView& rNewView, bool bGrad)
+: SdrDragMethod(rNewView),
+ pIAOHandle(nullptr),
+ bIsGradient(bGrad)
+{
+}
+
+OUString SdrDragGradient::GetSdrDragComment() const
+{
+ if(IsGradient())
+ return ImpGetDescriptionStr(STR_DragMethGradient);
+ else
+ return ImpGetDescriptionStr(STR_DragMethTransparence);
+}
+
+bool SdrDragGradient::BeginSdrDrag()
+{
+ bool bRetval(false);
+
+ pIAOHandle = static_cast<SdrHdlGradient*>(GetHdlList().GetHdl(IsGradient() ? SdrHdlKind::Gradient : SdrHdlKind::Transparence));
+
+ if(pIAOHandle)
+ {
+ // save old values
+ DragStat().SetRef1( pIAOHandle->GetPos() );
+ DragStat().SetRef2( pIAOHandle->Get2ndPos() );
+
+ // what was hit?
+ bool bHit(false);
+ SdrHdlColor* pColHdl = pIAOHandle->GetColorHdl1();
+
+ // init handling flags
+ pIAOHandle->SetMoveSingleHandle(false);
+ pIAOHandle->SetMoveFirstHandle(false);
+
+ // test first color handle
+ if(pColHdl)
+ {
+ basegfx::B2DPoint aPosition(DragStat().GetStart().X(), DragStat().GetStart().Y());
+
+ if(pColHdl->getOverlayObjectList().isHitLogic(aPosition))
+ {
+ bHit = true;
+ pIAOHandle->SetMoveSingleHandle(true);
+ pIAOHandle->SetMoveFirstHandle(true);
+ }
+ }
+
+ // test second color handle
+ pColHdl = pIAOHandle->GetColorHdl2();
+
+ if(!bHit && pColHdl)
+ {
+ basegfx::B2DPoint aPosition(DragStat().GetStart().X(), DragStat().GetStart().Y());
+
+ if(pColHdl->getOverlayObjectList().isHitLogic(aPosition))
+ {
+ bHit = true;
+ pIAOHandle->SetMoveSingleHandle(true);
+ }
+ }
+
+ // test gradient handle itself
+ if(!bHit)
+ {
+ basegfx::B2DPoint aPosition(DragStat().GetStart().X(), DragStat().GetStart().Y());
+
+ if(pIAOHandle->getOverlayObjectList().isHitLogic(aPosition))
+ {
+ bHit = true;
+ }
+ }
+
+ // everything up and running :o}
+ bRetval = bHit;
+ }
+ else
+ {
+ OSL_FAIL("SdrDragGradient::BeginSdrDrag(): IAOGradient not found.");
+ }
+
+ return bRetval;
+}
+
+void SdrDragGradient::MoveSdrDrag(const Point& rPnt)
+{
+ if(!(pIAOHandle && DragStat().CheckMinMoved(rPnt)))
+ return;
+
+ DragStat().NextMove(rPnt);
+
+ // Do the Move here!!! DragStat().GetStart()
+ Point aMoveDiff = rPnt - DragStat().GetStart();
+
+ if(pIAOHandle->IsMoveSingleHandle())
+ {
+ if(pIAOHandle->IsMoveFirstHandle())
+ {
+ pIAOHandle->SetPos(DragStat().GetRef1() + aMoveDiff);
+ if(pIAOHandle->GetColorHdl1())
+ pIAOHandle->GetColorHdl1()->SetPos(DragStat().GetRef1() + aMoveDiff);
+ }
+ else
+ {
+ pIAOHandle->Set2ndPos(DragStat().GetRef2() + aMoveDiff);
+ if(pIAOHandle->GetColorHdl2())
+ pIAOHandle->GetColorHdl2()->SetPos(DragStat().GetRef2() + aMoveDiff);
+ }
+ }
+ else
+ {
+ pIAOHandle->SetPos(DragStat().GetRef1() + aMoveDiff);
+ pIAOHandle->Set2ndPos(DragStat().GetRef2() + aMoveDiff);
+
+ if(pIAOHandle->GetColorHdl1())
+ pIAOHandle->GetColorHdl1()->SetPos(DragStat().GetRef1() + aMoveDiff);
+
+ if(pIAOHandle->GetColorHdl2())
+ pIAOHandle->GetColorHdl2()->SetPos(DragStat().GetRef2() + aMoveDiff);
+ }
+
+ // new state
+ pIAOHandle->FromIAOToItem(getSdrDragView().GetMarkedObjectList().GetMark(0)->GetMarkedSdrObj(), false, false);
+}
+
+bool SdrDragGradient::EndSdrDrag(bool /*bCopy*/)
+{
+ Ref1() = pIAOHandle->GetPos();
+ Ref2() = pIAOHandle->Get2ndPos();
+
+ // new state
+ pIAOHandle->FromIAOToItem(getSdrDragView().GetMarkedObjectList().GetMark(0)->GetMarkedSdrObj(), true, true);
+
+ return true;
+}
+
+void SdrDragGradient::CancelSdrDrag()
+{
+ // restore old values
+ pIAOHandle->SetPos(DragStat().GetRef1());
+ pIAOHandle->Set2ndPos(DragStat().GetRef2());
+
+ if(pIAOHandle->GetColorHdl1())
+ pIAOHandle->GetColorHdl1()->SetPos(DragStat().GetRef1());
+
+ if(pIAOHandle->GetColorHdl2())
+ pIAOHandle->GetColorHdl2()->SetPos(DragStat().GetRef2());
+
+ // new state
+ pIAOHandle->FromIAOToItem(getSdrDragView().GetMarkedObjectList().GetMark(0)->GetMarkedSdrObj(), true, false);
+}
+
+PointerStyle SdrDragGradient::GetSdrDragPointer() const
+{
+ return PointerStyle::RefHand;
+}
+
+
+SdrDragCrook::SdrDragCrook(SdrDragView& rNewView)
+: SdrDragMethod(rNewView),
+ aFact(1,1),
+ bContortionAllowed(false),
+ bNoContortionAllowed(false),
+ bContortion(false),
+ bResizeAllowed(false),
+ bResize(false),
+ bRotateAllowed(false),
+ bRotate(false),
+ bVertical(false),
+ bValid(false),
+ bLft(false),
+ bRgt(false),
+ bUpr(false),
+ bLwr(false),
+ bAtCenter(false),
+ nAngle(0),
+ nMarkSize(0),
+ eMode(SdrCrookMode::Rotate)
+{
+}
+
+OUString SdrDragCrook::GetSdrDragComment() const
+{
+ OUString aStr = ImpGetDescriptionStr(!bContortion ? STR_DragMethCrook : STR_DragMethCrookContortion);
+
+ if(bValid)
+ {
+ aStr += " (";
+
+ sal_Int32 nVal(nAngle);
+
+ if(bAtCenter)
+ nVal *= 2;
+
+ nVal = std::abs(nVal);
+ aStr += SdrModel::GetAngleString(Degree100(nVal)) + ")";
+ }
+
+ if(getSdrDragView().IsDragWithCopy())
+ aStr += SvxResId(STR_EditWithCopy);
+ return aStr;
+}
+
+// These defines parametrize the created raster
+// for interactions
+#define DRAG_CROOK_RASTER_MINIMUM (4)
+#define DRAG_CROOK_RASTER_MAXIMUM (15)
+#define DRAG_CROOK_RASTER_DISTANCE (30)
+
+static basegfx::B2DPolyPolygon impCreateDragRaster(SdrPageView const & rPageView, const tools::Rectangle& rMarkRect)
+{
+ basegfx::B2DPolyPolygon aRetval;
+
+ if(rPageView.PageWindowCount())
+ {
+ OutputDevice& rOut = rPageView.GetPageWindow(0)->GetPaintWindow().GetOutputDevice();
+ tools::Rectangle aPixelSize = rOut.LogicToPixel(rMarkRect);
+ sal_uInt32 nHorDiv(aPixelSize.GetWidth() / DRAG_CROOK_RASTER_DISTANCE);
+ sal_uInt32 nVerDiv(aPixelSize.GetHeight() / DRAG_CROOK_RASTER_DISTANCE);
+
+ if(nHorDiv > DRAG_CROOK_RASTER_MAXIMUM)
+ nHorDiv = DRAG_CROOK_RASTER_MAXIMUM;
+ if(nHorDiv < DRAG_CROOK_RASTER_MINIMUM)
+ nHorDiv = DRAG_CROOK_RASTER_MINIMUM;
+
+ if(nVerDiv > DRAG_CROOK_RASTER_MAXIMUM)
+ nVerDiv = DRAG_CROOK_RASTER_MAXIMUM;
+ if(nVerDiv < DRAG_CROOK_RASTER_MINIMUM)
+ nVerDiv = DRAG_CROOK_RASTER_MINIMUM;
+
+ const double fXLen(rMarkRect.GetWidth() / static_cast<double>(nHorDiv));
+ const double fYLen(rMarkRect.GetHeight() / static_cast<double>(nVerDiv));
+ double fYPos(rMarkRect.Top());
+ sal_uInt32 a, b;
+
+ for(a = 0; a <= nVerDiv; a++)
+ {
+ // horizontal lines
+ for(b = 0; b < nHorDiv; b++)
+ {
+ basegfx::B2DPolygon aHorLineSegment;
+
+ const double fNewX(rMarkRect.Left() + (b * fXLen));
+ aHorLineSegment.append(basegfx::B2DPoint(fNewX, fYPos));
+ aHorLineSegment.appendBezierSegment(
+ basegfx::B2DPoint(fNewX + (fXLen * (1.0 / 3.0)), fYPos),
+ basegfx::B2DPoint(fNewX + (fXLen * (2.0 / 3.0)), fYPos),
+ basegfx::B2DPoint(fNewX + fXLen, fYPos));
+ aRetval.append(aHorLineSegment);
+ }
+
+ // increments
+ fYPos += fYLen;
+ }
+
+ double fXPos(rMarkRect.Left());
+
+ for(a = 0; a <= nHorDiv; a++)
+ {
+ // vertical lines
+ for(b = 0; b < nVerDiv; b++)
+ {
+ basegfx::B2DPolygon aVerLineSegment;
+
+ const double fNewY(rMarkRect.Top() + (b * fYLen));
+ aVerLineSegment.append(basegfx::B2DPoint(fXPos, fNewY));
+ aVerLineSegment.appendBezierSegment(
+ basegfx::B2DPoint(fXPos, fNewY + (fYLen * (1.0 / 3.0))),
+ basegfx::B2DPoint(fXPos, fNewY + (fYLen * (2.0 / 3.0))),
+ basegfx::B2DPoint(fXPos, fNewY + fYLen));
+ aRetval.append(aVerLineSegment);
+ }
+
+ // increments
+ fXPos += fXLen;
+ }
+ }
+
+ return aRetval;
+}
+
+void SdrDragCrook::createSdrDragEntries()
+{
+ // Add extended frame raster first, so it will be behind objects
+ if(getSdrDragView().GetSdrPageView())
+ {
+ const basegfx::B2DPolyPolygon aDragRaster(impCreateDragRaster(*getSdrDragView().GetSdrPageView(), GetMarkedRect()));
+
+ if(aDragRaster.count())
+ {
+ addSdrDragEntry(std::unique_ptr<SdrDragEntry>(new SdrDragEntryPolyPolygon(aDragRaster)));
+ }
+ }
+
+ // call parent
+ SdrDragMethod::createSdrDragEntries();
+}
+
+bool SdrDragCrook::BeginSdrDrag()
+{
+ bContortionAllowed=getSdrDragView().IsCrookAllowed();
+ bNoContortionAllowed=getSdrDragView().IsCrookAllowed(true);
+ bResizeAllowed=getSdrDragView().IsResizeAllowed();
+ bRotateAllowed=getSdrDragView().IsRotateAllowed();
+
+ if (bContortionAllowed || bNoContortionAllowed)
+ {
+ bVertical=(GetDragHdlKind()==SdrHdlKind::Lower || GetDragHdlKind()==SdrHdlKind::Upper);
+ aMarkRect=GetMarkedRect();
+ aMarkCenter=aMarkRect.Center();
+ nMarkSize=bVertical ? (aMarkRect.GetHeight()-1) : (aMarkRect.GetWidth()-1);
+ aCenter=aMarkCenter;
+ aStart=DragStat().GetStart();
+ Show();
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+void SdrDragCrook::MovAllPoints(basegfx::B2DPolyPolygon& rTarget)
+{
+ SdrPageView* pPV = getSdrDragView().GetSdrPageView();
+
+ if(!pPV)
+ return;
+
+ XPolyPolygon aTempPolyPoly(rTarget);
+
+ if (pPV->HasMarkedObjPageView())
+ {
+ sal_uInt16 nPolyCount=aTempPolyPoly.Count();
+
+ if (!bContortion && !getSdrDragView().IsNoDragXorPolys())
+ {
+ sal_uInt16 n1st=0,nLast=0;
+ Point aC(aCenter);
+
+ while (n1st<nPolyCount)
+ {
+ nLast=n1st;
+ while (nLast<nPolyCount && aTempPolyPoly[nLast].GetPointCount()!=0) nLast++;
+ tools::Rectangle aBound(aTempPolyPoly[n1st].GetBoundRect());
+ sal_uInt16 i;
+
+ for (i=n1st+1; i<nLast; i++)
+ {
+ aBound.Union(aTempPolyPoly[n1st].GetBoundRect());
+ }
+
+ Point aCtr0(aBound.Center());
+ Point aCtr1(aCtr0);
+
+ if (bResize)
+ {
+ Fraction aFact1(1,1);
+
+ if (bVertical)
+ {
+ ResizePoint(aCtr1,aC,aFact1,aFact);
+ }
+ else
+ {
+ ResizePoint(aCtr1,aC,aFact,aFact1);
+ }
+ }
+
+ bool bRotOk=false;
+ double nSin=0,nCos=0;
+
+ if (aRad.X()!=0 && aRad.Y()!=0)
+ {
+ bRotOk=bRotate;
+
+ switch (eMode)
+ {
+ case SdrCrookMode::Rotate : CrookRotateXPoint (aCtr1,nullptr,nullptr,aC,aRad,nSin,nCos,bVertical); break;
+ case SdrCrookMode::Slant : CrookSlantXPoint (aCtr1,nullptr,nullptr,aC,aRad,nSin,nCos,bVertical); break;
+ case SdrCrookMode::Stretch: CrookStretchXPoint(aCtr1,nullptr,nullptr,aC,aRad,nSin,nCos,bVertical,aMarkRect); break;
+ } // switch
+ }
+
+ aCtr1-=aCtr0;
+
+ for (i=n1st; i<nLast; i++)
+ {
+ if (bRotOk)
+ {
+ RotateXPoly(aTempPolyPoly[i],aCtr0,nSin,nCos);
+ }
+
+ aTempPolyPoly[i].Move(aCtr1.X(),aCtr1.Y());
+ }
+
+ n1st=nLast+1;
+ }
+ }
+ else
+ {
+ sal_uInt16 i,j;
+
+ for (j=0; j<nPolyCount; j++)
+ {
+ XPolygon& aPol=aTempPolyPoly[j];
+ sal_uInt16 nPointCount=aPol.GetPointCount();
+ i=0;
+
+ while (i<nPointCount)
+ {
+ Point* pPnt=&aPol[i];
+ Point* pC1=nullptr;
+ Point* pC2=nullptr;
+
+ if (i+1<nPointCount && aPol.IsControl(i))
+ { // control point on the left
+ pC1=pPnt;
+ i++;
+ pPnt=&aPol[i];
+ }
+
+ i++;
+
+ if (i<nPointCount && aPol.IsControl(i))
+ { // control point on the right
+ pC2=&aPol[i];
+ i++;
+ }
+
+ MovCrookPoint(*pPnt,pC1,pC2);
+ }
+ }
+ }
+ }
+
+ rTarget = aTempPolyPoly.getB2DPolyPolygon();
+}
+
+void SdrDragCrook::MovCrookPoint(Point& rPnt, Point* pC1, Point* pC2)
+{
+ bool bVert=bVertical;
+ bool bC1=pC1!=nullptr;
+ bool bC2=pC2!=nullptr;
+ Point aC(aCenter);
+
+ if (bResize)
+ {
+ Fraction aFact1(1,1);
+
+ if (bVert)
+ {
+ ResizePoint(rPnt,aC,aFact1,aFact);
+
+ if (bC1)
+ ResizePoint(*pC1,aC,aFact1,aFact);
+
+ if (bC2)
+ ResizePoint(*pC2,aC,aFact1,aFact);
+ }
+ else
+ {
+ ResizePoint(rPnt,aC,aFact,aFact1);
+
+ if (bC1)
+ ResizePoint(*pC1,aC,aFact,aFact1);
+
+ if (bC2)
+ ResizePoint(*pC2,aC,aFact,aFact1);
+ }
+ }
+
+ if (aRad.X()!=0 && aRad.Y()!=0)
+ {
+ double nSin,nCos;
+
+ switch (eMode)
+ {
+ case SdrCrookMode::Rotate : CrookRotateXPoint (rPnt,pC1,pC2,aC,aRad,nSin,nCos,bVert); break;
+ case SdrCrookMode::Slant : CrookSlantXPoint (rPnt,pC1,pC2,aC,aRad,nSin,nCos,bVert); break;
+ case SdrCrookMode::Stretch: CrookStretchXPoint(rPnt,pC1,pC2,aC,aRad,nSin,nCos,bVert,aMarkRect); break;
+ } // switch
+ }
+}
+
+void SdrDragCrook::MoveSdrDrag(const Point& rPnt)
+{
+ if (!DragStat().CheckMinMoved(rPnt))
+ return;
+
+ bool bNewMoveOnly=getSdrDragView().IsMoveOnlyDragging();
+ bAtCenter=false;
+ SdrCrookMode eNewMode=getSdrDragView().GetCrookMode();
+ bool bNewContortion=!bNewMoveOnly && ((bContortionAllowed && !getSdrDragView().IsCrookNoContortion()) || !bNoContortionAllowed);
+ bResize=!getSdrDragView().IsOrtho() && bResizeAllowed && !bNewMoveOnly;
+ bool bNewRotate=bRotateAllowed && !bNewContortion && !bNewMoveOnly && eNewMode==SdrCrookMode::Rotate;
+
+ Point aPnt(GetSnapPos(rPnt));
+
+ Point aNewCenter(aMarkCenter.X(),aStart.Y());
+
+ if (bVertical)
+ {
+ aNewCenter.setX(aStart.X() );
+ aNewCenter.setY(aMarkCenter.Y() );
+ }
+
+ if (!getSdrDragView().IsCrookAtCenter())
+ {
+ switch (GetDragHdlKind())
+ {
+ case SdrHdlKind::UpperLeft: aNewCenter.setX(aMarkRect.Right() ); bLft=true; break;
+ case SdrHdlKind::Upper: aNewCenter.setY(aMarkRect.Bottom() ); bUpr=true; break;
+ case SdrHdlKind::UpperRight: aNewCenter.setX(aMarkRect.Left() ); bRgt=true; break;
+ case SdrHdlKind::Left : aNewCenter.setX(aMarkRect.Right() ); bLft=true; break;
+ case SdrHdlKind::Right: aNewCenter.setX(aMarkRect.Left() ); bRgt=true; break;
+ case SdrHdlKind::LowerLeft: aNewCenter.setX(aMarkRect.Right() ); bLft=true; break;
+ case SdrHdlKind::Lower: aNewCenter.setY(aMarkRect.Top() ); bLwr=true; break;
+ case SdrHdlKind::LowerRight: aNewCenter.setX(aMarkRect.Left() ); bRgt=true; break;
+ default: bAtCenter=true;
+ }
+ }
+ else
+ bAtCenter=true;
+
+ Fraction aNewFract(1,1);
+ tools::Long dx1=aPnt.X()-aNewCenter.X();
+ tools::Long dy1=aPnt.Y()-aNewCenter.Y();
+ bValid=bVertical ? dx1!=0 : dy1!=0;
+
+ if (bValid)
+ {
+ if (bVertical)
+ bValid = std::abs(dx1)*100>std::abs(dy1);
+ else
+ bValid = std::abs(dy1)*100>std::abs(dx1);
+ }
+
+ tools::Long nNewRad=0;
+ nAngle=0_deg100;
+
+ if (bValid)
+ {
+ double a=0; // slope of the radius
+ Degree100 nPntAngle(0);
+
+ if (bVertical)
+ {
+ a=static_cast<double>(dy1)/static_cast<double>(dx1); // slope of the radius
+ nNewRad=(static_cast<tools::Long>(dy1*a)+dx1) /2;
+ aNewCenter.AdjustX(nNewRad );
+ nPntAngle=GetAngle(aPnt-aNewCenter);
+ }
+ else
+ {
+ a=static_cast<double>(dx1)/static_cast<double>(dy1); // slope of the radius
+ nNewRad=(static_cast<tools::Long>(dx1*a)+dy1) /2;
+ aNewCenter.AdjustY(nNewRad );
+ nPntAngle=GetAngle(aPnt-aNewCenter)-9000_deg100;
+ }
+
+ if (!bAtCenter)
+ {
+ if (nNewRad<0)
+ {
+ if (bRgt) nPntAngle += 18000_deg100;
+ if (bLft) nPntAngle = 18000_deg100 - nPntAngle;
+ if (bLwr) nPntAngle =- nPntAngle;
+ }
+ else
+ {
+ if (bRgt) nPntAngle = -nPntAngle;
+ if (bUpr) nPntAngle = 18000_deg100 - nPntAngle;
+ if (bLwr) nPntAngle += 18000_deg100;
+ }
+
+ nPntAngle=NormAngle36000(nPntAngle);
+ }
+ else
+ {
+ if (nNewRad<0) nPntAngle += 18000_deg100;
+ if (bVertical) nPntAngle = 18000_deg100 - nPntAngle;
+ nPntAngle = NormAngle18000(nPntAngle);
+ nPntAngle = abs(nPntAngle);
+ }
+
+ double nCircumference = 2 * std::abs(nNewRad) * M_PI;
+
+ if (bResize)
+ {
+ tools::Long nMul=static_cast<tools::Long>(nCircumference * NormAngle36000(nPntAngle).get() / 36000.0);
+
+ if (bAtCenter)
+ nMul*=2;
+
+ aNewFract=Fraction(nMul,nMarkSize);
+ nAngle=nPntAngle;
+ }
+ else
+ {
+ nAngle = Degree100(static_cast<tools::Long>((nMarkSize*360/nCircumference)*100)/2);
+
+ if (nAngle==0_deg100)
+ bValid=false;
+ }
+ }
+
+ if (nAngle==0_deg100 || nNewRad==0)
+ bValid=false;
+
+ if (!bValid)
+ nNewRad=0;
+
+ if (!bValid && bResize)
+ {
+ tools::Long nMul=bVertical ? dy1 : dx1;
+
+ if (bLft || bUpr)
+ nMul=-nMul;
+
+ tools::Long nDiv=nMarkSize;
+
+ if (bAtCenter)
+ {
+ nMul*=2;
+ nMul = std::abs(nMul);
+ }
+
+ aNewFract=Fraction(nMul,nDiv);
+ }
+
+ if (aNewCenter==aCenter && bNewContortion==bContortion && aNewFract==aFact &&
+ bNewMoveOnly == getMoveOnly() && bNewRotate==bRotate && eNewMode==eMode)
+ return;
+
+ Hide();
+ setMoveOnly(bNewMoveOnly);
+ bRotate=bNewRotate;
+ eMode=eNewMode;
+ bContortion=bNewContortion;
+ aCenter=aNewCenter;
+ aFact=aNewFract;
+ aRad=Point(nNewRad,nNewRad);
+ bResize=aFact!=Fraction(1,1) && aFact.GetDenominator()!=0 && aFact.IsValid();
+ DragStat().NextMove(aPnt);
+ Show();
+}
+
+void SdrDragCrook::applyCurrentTransformationToSdrObject(SdrObject& rTarget)
+{
+ const bool bDoResize(aFact!=Fraction(1,1));
+ const bool bDoCrook(aCenter!=aMarkCenter && aRad.X()!=0 && aRad.Y()!=0);
+
+ if (!(bDoCrook || bDoResize))
+ return;
+
+ if (bDoResize)
+ {
+ Fraction aFact1(1,1);
+
+ if (bContortion)
+ {
+ if (bVertical)
+ {
+ rTarget.Resize(aCenter,aFact1,aFact);
+ }
+ else
+ {
+ rTarget.Resize(aCenter,aFact,aFact1);
+ }
+ }
+ else
+ {
+ Point aCtr0(rTarget.GetSnapRect().Center());
+ Point aCtr1(aCtr0);
+
+ if (bVertical)
+ {
+ ResizePoint(aCtr1,aCenter,aFact1,aFact);
+ }
+ else
+ {
+ ResizePoint(aCtr1,aCenter,aFact,aFact1);
+ }
+
+ Size aSiz(aCtr1.X()-aCtr0.X(),aCtr1.Y()-aCtr0.Y());
+
+ rTarget.Move(aSiz);
+ }
+ }
+
+ if (bDoCrook)
+ {
+ const tools::Rectangle aLocalMarkRect(getSdrDragView().GetMarkedObjRect());
+ const bool bLocalRotate(!bContortion && eMode == SdrCrookMode::Rotate && getSdrDragView().IsRotateAllowed());
+
+ SdrEditView::ImpCrookObj(&rTarget,aCenter,aRad,eMode,bVertical,!bContortion,bLocalRotate,aLocalMarkRect);
+ }
+}
+
+void SdrDragCrook::applyCurrentTransformationToPolyPolygon(basegfx::B2DPolyPolygon& rTarget)
+{
+ // use helper derived from old stuff
+ MovAllPoints(rTarget);
+}
+
+bool SdrDragCrook::EndSdrDrag(bool bCopy)
+{
+ Hide();
+
+ if (bResize && aFact==Fraction(1,1))
+ bResize=false;
+
+ const bool bUndo = getSdrDragView().IsUndoEnabled();
+
+ bool bDoCrook=aCenter!=aMarkCenter && aRad.X()!=0 && aRad.Y()!=0;
+
+ if (bDoCrook || bResize)
+ {
+ if (bResize && bUndo)
+ {
+ OUString aStr = ImpGetDescriptionStr(!bContortion?STR_EditCrook:STR_EditCrookContortion);
+
+ if (bCopy)
+ aStr += SvxResId(STR_EditWithCopy);
+
+ getSdrDragView().BegUndo(aStr);
+ }
+
+ if (bResize)
+ {
+ Fraction aFact1(1,1);
+
+ if (bContortion)
+ {
+ if (bVertical)
+ getSdrDragView().ResizeMarkedObj(aCenter,aFact1,aFact,bCopy);
+ else
+ getSdrDragView().ResizeMarkedObj(aCenter,aFact,aFact1,bCopy);
+ }
+ else
+ {
+ if (bCopy)
+ getSdrDragView().CopyMarkedObj();
+
+ const size_t nMarkCount=getSdrDragView().GetMarkedObjectList().GetMarkCount();
+
+ for (size_t nm=0; nm<nMarkCount; ++nm)
+ {
+ SdrMark* pM=getSdrDragView().GetMarkedObjectList().GetMark(nm);
+ SdrObject* pO=pM->GetMarkedSdrObj();
+ Point aCtr0(pO->GetSnapRect().Center());
+ Point aCtr1(aCtr0);
+
+ if (bVertical)
+ ResizePoint(aCtr1,aCenter,aFact1,aFact);
+ else
+ ResizePoint(aCtr1,aCenter,aFact,aFact1);
+
+ Size aSiz(aCtr1.X()-aCtr0.X(),aCtr1.Y()-aCtr0.Y());
+ if( bUndo )
+ AddUndo(getSdrDragView().GetModel()->GetSdrUndoFactory().CreateUndoMoveObject(*pO,aSiz));
+ pO->Move(aSiz);
+ }
+ }
+
+ bCopy=false;
+ }
+
+ if (bDoCrook)
+ {
+ getSdrDragView().CrookMarkedObj(aCenter,aRad,eMode,bVertical,!bContortion,bCopy);
+ }
+
+ if (bResize && bUndo)
+ getSdrDragView().EndUndo();
+
+ return true;
+ }
+
+ return false;
+}
+
+PointerStyle SdrDragCrook::GetSdrDragPointer() const
+{
+ return PointerStyle::Crook;
+}
+
+
+SdrDragDistort::SdrDragDistort(SdrDragView& rNewView)
+: SdrDragMethod(rNewView),
+ nPolyPt(0),
+ bContortionAllowed(false),
+ bNoContortionAllowed(false),
+ bContortion(false)
+{
+}
+
+OUString SdrDragDistort::GetSdrDragComment() const
+{
+ OUString aStr = ImpGetDescriptionStr(STR_DragMethDistort)
+ + " (x="
+ + getSdrDragView().GetModel()->GetMetricString(DragStat().GetDX())
+ + " y="
+ + getSdrDragView().GetModel()->GetMetricString(DragStat().GetDY())
+ + ")";
+
+ if(getSdrDragView().IsDragWithCopy())
+ aStr += SvxResId(STR_EditWithCopy);
+ return aStr;
+}
+
+void SdrDragDistort::createSdrDragEntries()
+{
+ // Add extended frame raster first, so it will be behind objects
+ if(getSdrDragView().GetSdrPageView())
+ {
+ const basegfx::B2DPolyPolygon aDragRaster(impCreateDragRaster(*getSdrDragView().GetSdrPageView(), GetMarkedRect()));
+
+ if(aDragRaster.count())
+ {
+ addSdrDragEntry(std::unique_ptr<SdrDragEntry>(new SdrDragEntryPolyPolygon(aDragRaster)));
+ }
+ }
+
+ // call parent
+ SdrDragMethod::createSdrDragEntries();
+}
+
+bool SdrDragDistort::BeginSdrDrag()
+{
+ bContortionAllowed=getSdrDragView().IsDistortAllowed();
+ bNoContortionAllowed=getSdrDragView().IsDistortAllowed(true);
+
+ if (bContortionAllowed || bNoContortionAllowed)
+ {
+ SdrHdlKind eKind=GetDragHdlKind();
+ nPolyPt=0xFFFF;
+
+ if (eKind==SdrHdlKind::UpperLeft) nPolyPt=0;
+ if (eKind==SdrHdlKind::UpperRight) nPolyPt=1;
+ if (eKind==SdrHdlKind::LowerRight) nPolyPt=2;
+ if (eKind==SdrHdlKind::LowerLeft) nPolyPt=3;
+ if (nPolyPt>3) return false;
+
+ aMarkRect=GetMarkedRect();
+ aDistortedRect=XPolygon(aMarkRect);
+ Show();
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+void SdrDragDistort::MovAllPoints(basegfx::B2DPolyPolygon& rTarget)
+{
+ if (!bContortion)
+ return;
+
+ SdrPageView* pPV = getSdrDragView().GetSdrPageView();
+
+ if(pPV && pPV->HasMarkedObjPageView())
+ {
+ basegfx::B2DPolyPolygon aDragPolygon(rTarget);
+ const basegfx::B2DRange aOriginalRange = vcl::unotools::b2DRectangleFromRectangle(aMarkRect);
+ const basegfx::B2DPoint aTopLeft(aDistortedRect[0].X(), aDistortedRect[0].Y());
+ const basegfx::B2DPoint aTopRight(aDistortedRect[1].X(), aDistortedRect[1].Y());
+ const basegfx::B2DPoint aBottomLeft(aDistortedRect[3].X(), aDistortedRect[3].Y());
+ const basegfx::B2DPoint aBottomRight(aDistortedRect[2].X(), aDistortedRect[2].Y());
+
+ aDragPolygon = basegfx::utils::distort(aDragPolygon, aOriginalRange, aTopLeft, aTopRight, aBottomLeft, aBottomRight);
+ rTarget = aDragPolygon;
+ }
+}
+
+void SdrDragDistort::MoveSdrDrag(const Point& rPnt)
+{
+ if (!DragStat().CheckMinMoved(rPnt))
+ return;
+
+ Point aPnt(GetSnapPos(rPnt));
+
+ if (getSdrDragView().IsOrtho())
+ OrthoDistance8(DragStat().GetStart(),aPnt,getSdrDragView().IsBigOrtho());
+
+ bool bNewContortion=(bContortionAllowed && !getSdrDragView().IsCrookNoContortion()) || !bNoContortionAllowed;
+
+ if (bNewContortion!=bContortion || aDistortedRect[nPolyPt]!=aPnt)
+ {
+ Hide();
+ aDistortedRect[nPolyPt]=aPnt;
+ bContortion=bNewContortion;
+ DragStat().NextMove(aPnt);
+ Show();
+ }
+}
+
+bool SdrDragDistort::EndSdrDrag(bool bCopy)
+{
+ Hide();
+ bool bDoDistort=DragStat().GetDX()!=0 || DragStat().GetDY()!=0;
+
+ if (bDoDistort)
+ {
+ getSdrDragView().DistortMarkedObj(aMarkRect,aDistortedRect,!bContortion,bCopy);
+ return true;
+ }
+
+ return false;
+}
+
+PointerStyle SdrDragDistort::GetSdrDragPointer() const
+{
+ return PointerStyle::RefHand;
+}
+
+void SdrDragDistort::applyCurrentTransformationToSdrObject(SdrObject& rTarget)
+{
+ const bool bDoDistort(DragStat().GetDX()!=0 || DragStat().GetDY()!=0);
+
+ if (bDoDistort)
+ {
+ SdrEditView::ImpDistortObj(&rTarget, aMarkRect, aDistortedRect, !bContortion);
+ }
+}
+
+void SdrDragDistort::applyCurrentTransformationToPolyPolygon(basegfx::B2DPolyPolygon& rTarget)
+{
+ // use helper derived from old stuff
+ MovAllPoints(rTarget);
+}
+
+
+SdrDragCrop::SdrDragCrop(SdrDragView& rNewView)
+: SdrDragObjOwn(rNewView)
+{
+ // switch off solid dragging for crop; it just makes no sense since showing
+ // a 50% transparent object above the original will not be visible
+ setSolidDraggingActive(false);
+}
+
+OUString SdrDragCrop::GetSdrDragComment() const
+{
+ OUString aStr = ImpGetDescriptionStr(STR_DragMethCrop)
+ + " (x="
+ + getSdrDragView().GetModel()->GetMetricString(DragStat().GetDX())
+ + " y="
+ + getSdrDragView().GetModel()->GetMetricString(DragStat().GetDY())
+ + ")";
+
+ if(getSdrDragView().IsDragWithCopy())
+ aStr += SvxResId(STR_EditWithCopy);
+ return aStr;
+}
+
+bool SdrDragCrop::BeginSdrDrag()
+{
+ // call parent
+ bool bRetval(SdrDragObjOwn::BeginSdrDrag());
+
+ if(!GetDragHdl())
+ {
+ // we need the DragHdl, break if not there
+ bRetval = false;
+ }
+
+ return bRetval;
+}
+
+bool SdrDragCrop::EndSdrDrag(bool /*bCopy*/)
+{
+ Hide();
+
+ if(0 == DragStat().GetDX() && 0 == DragStat().GetDY())
+ {
+ // no change, done
+ return false;
+ }
+
+ const SdrMarkList& rMarkList = getSdrDragView().GetMarkedObjectList();
+
+ if(1 != rMarkList.GetMarkCount())
+ {
+ // Crop only with single Object selected
+ return false;
+ }
+
+ // prepare for SdrGrafObj or others. This code has to work with usual
+ // SdrGrafObj's from Draw/Impress/Calc, but also with SdrObjects from
+ // Writer. It would be better to handle this in Writer directly, but
+ // there are currently no easy mechanisms to plug an alternative interaction
+ // from there
+ SdrObject* pSdrObject = rMarkList.GetMark(0)->GetMarkedSdrObj();
+ SdrObjectUniquePtr pFullDragClone;
+ bool bExternal(false);
+ SdrObject* pExternalSdrObject(nullptr);
+
+ // RotGrfFlyFrame: Crop decision for DrawingLayer/Writer now
+ // locally, no two-in-one methods any more
+ if (nullptr != pSdrObject && dynamic_cast< const SdrGrafObj* >(pSdrObject) == nullptr)
+ {
+ // If Writer, get the already offered for interaction SdrGrafObj
+ // and set up for using that replacement object that contains the
+ // real transformation. That SdrObject is owned and has to be deleted,
+ // so use a std::unique_ptr with special handling for the protected
+ // SDrObject destructor
+ pFullDragClone = pSdrObject->getFullDragClone();
+
+ if(dynamic_cast< SdrGrafObj* >(pFullDragClone.get()))
+ {
+ bExternal = true;
+ pExternalSdrObject = pSdrObject;
+ pSdrObject = pFullDragClone.get();
+ }
+ }
+
+ // get and check for SdrGrafObj now
+ SdrGrafObj* pObj = dynamic_cast<SdrGrafObj*>( pSdrObject );
+
+ if(!pObj)
+ {
+ return false;
+ }
+
+ // no undo for external needed, done there
+ const bool bUndo(!bExternal && getSdrDragView().IsUndoEnabled());
+
+ if(bUndo)
+ {
+ OUString aUndoStr = ImpGetDescriptionStr(STR_DragMethCrop);
+
+ getSdrDragView().BegUndo( aUndoStr );
+ getSdrDragView().AddUndo( getSdrDragView().GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pObj));
+ // also need attr undo, the SdrGrafCropItem will be changed
+ getSdrDragView().AddUndo( getSdrDragView().GetModel()->GetSdrUndoFactory().CreateUndoAttrObject(*pObj));
+ }
+
+ // get the original objects transformation
+ basegfx::B2DHomMatrix aOriginalMatrix;
+ basegfx::B2DPolyPolygon aPolyPolygon;
+ bool bShearCorrected(false);
+ pObj->TRGetBaseGeometry(aOriginalMatrix, aPolyPolygon);
+
+ { // correct shear, it comes currently mirrored from TRGetBaseGeometry, can be removed with aw080
+ const basegfx::utils::B2DHomMatrixBufferedDecompose aTmpDecomp(aOriginalMatrix);
+
+ if(!basegfx::fTools::equalZero(aTmpDecomp.getShearX()))
+ {
+ bShearCorrected = true;
+ aOriginalMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ aTmpDecomp.getScale(),
+ -aTmpDecomp.getShearX(),
+ aTmpDecomp.getRotate(),
+ aTmpDecomp.getTranslate());
+ }
+ }
+
+ // generate start point of original drag vector in unit coordinates (the
+ // vis-a-vis of the drag point)
+ basegfx::B2DPoint aLocalStart(0.0, 0.0);
+ bool bOnAxis(false);
+
+ switch(GetDragHdlKind())
+ {
+ case SdrHdlKind::UpperLeft: aLocalStart.setX(1.0); aLocalStart.setY(1.0); break;
+ case SdrHdlKind::Upper: aLocalStart.setX(0.5); aLocalStart.setY(1.0); bOnAxis = true; break;
+ case SdrHdlKind::UpperRight: aLocalStart.setX(0.0); aLocalStart.setY(1.0); break;
+ case SdrHdlKind::Left : aLocalStart.setX(1.0); aLocalStart.setY(0.5); bOnAxis = true; break;
+ case SdrHdlKind::Right: aLocalStart.setX(0.0); aLocalStart.setY(0.5); bOnAxis = true; break;
+ case SdrHdlKind::LowerLeft: aLocalStart.setX(1.0); aLocalStart.setY(0.0); break;
+ case SdrHdlKind::Lower: aLocalStart.setX(0.5); aLocalStart.setY(0.0); bOnAxis = true; break;
+ case SdrHdlKind::LowerRight: aLocalStart.setX(0.0); aLocalStart.setY(0.0); break;
+ default: break;
+ }
+
+ // create the current drag position in unit coordinates. To get there,
+ // transform back the DragPoint to UnitCoordinates
+ basegfx::B2DHomMatrix aInverse(aOriginalMatrix);
+ aInverse.invert();
+ basegfx::B2DPoint aLocalCurrent(aInverse * basegfx::B2DPoint(DragStat().GetNow().X(), DragStat().GetNow().Y()));
+
+ // if one of the edge handles is used, limit to X or Y drag only
+ if(bOnAxis)
+ {
+ if(basegfx::fTools::equal(aLocalStart.getX(), 0.5))
+ {
+ aLocalCurrent.setX(aLocalStart.getX());
+ }
+ else
+ {
+ aLocalCurrent.setY(aLocalStart.getY());
+ }
+ }
+
+ // create internal change in unit coordinates
+ basegfx::B2DHomMatrix aDiscreteChangeMatrix;
+
+ if(!basegfx::fTools::equal(aLocalCurrent.getX(), aLocalStart.getX()))
+ {
+ if(aLocalStart.getX() < 0.5)
+ {
+ aDiscreteChangeMatrix.scale(aLocalCurrent.getX(), 1.0);
+ }
+ else
+ {
+ aDiscreteChangeMatrix.scale(1.0 - aLocalCurrent.getX(), 1.0);
+ aDiscreteChangeMatrix.translate(aLocalCurrent.getX(), 0.0);
+ }
+ }
+
+ if(!basegfx::fTools::equal(aLocalCurrent.getY(), aLocalStart.getY()))
+ {
+ if(aLocalStart.getY() < 0.5)
+ {
+ aDiscreteChangeMatrix.scale(1.0, aLocalCurrent.getY());
+ }
+ else
+ {
+ aDiscreteChangeMatrix.scale(1.0, 1.0 - aLocalCurrent.getY());
+ aDiscreteChangeMatrix.translate(0.0, aLocalCurrent.getY());
+ }
+ }
+
+ // We now have the whole executed Crop in UnitCoordinates in
+ // aDiscreteChangeMatrix, go to concrete sizes now.
+ // Create the unrotated original rectangle and the unrotated modified
+ // rectangle as Ranges
+ const basegfx::utils::B2DHomMatrixBufferedDecompose aOriginalMatrixDecomp(aOriginalMatrix);
+
+ // prepare unsheared/unrotated versions of the old and new transformation
+ const basegfx::B2DHomMatrix aOriginalMatrixNoShearNoRotate(
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ basegfx::absolute(aOriginalMatrixDecomp.getScale()),
+ aOriginalMatrixDecomp.getTranslate()));
+
+ // create the ranges for these
+ basegfx::B2DRange aRangeOriginalNoShearNoRotate(0.0, 0.0, 1.0, 1.0);
+ basegfx::B2DRange aRangeNewNoShearNoRotate(0.0, 0.0, 1.0, 1.0);
+ aRangeOriginalNoShearNoRotate.transform(aOriginalMatrixNoShearNoRotate);
+ aRangeNewNoShearNoRotate.transform(aOriginalMatrixNoShearNoRotate * aDiscreteChangeMatrix);
+
+ if(bExternal)
+ {
+ // With aLocalStart point (opposed to dragged point), X scale and Y scale,
+ // we call crop (virtual method) on pSdrObject which calls VirtFlyDrawObj
+ // crop. Use aLocalStart unchanged, so being relative to the Crop-Action,
+ // the called instance knows best how to use it
+ const double fScaleX(aRangeNewNoShearNoRotate.getWidth() / aRangeOriginalNoShearNoRotate.getWidth());
+ const double fScaleY(aRangeNewNoShearNoRotate.getHeight() / aRangeOriginalNoShearNoRotate.getHeight());
+
+ pExternalSdrObject->Crop(
+ aLocalStart,
+ fScaleX,
+ fScaleY);
+ }
+ else
+ {
+ // prepare matrix to apply to object; evtl. back-correct shear
+ basegfx::B2DHomMatrix aNewObjectMatrix(aOriginalMatrix * aDiscreteChangeMatrix);
+
+ if(bShearCorrected)
+ {
+ // back-correct shear
+ const basegfx::utils::B2DHomMatrixBufferedDecompose aTmpDecomp(aNewObjectMatrix);
+
+ aNewObjectMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ aTmpDecomp.getScale(),
+ -aTmpDecomp.getShearX(),
+ aTmpDecomp.getRotate(),
+ aTmpDecomp.getTranslate());
+ }
+
+ // apply change to object by applying the unit coordinate change followed
+ // by the original change
+ pObj->TRSetBaseGeometry(aNewObjectMatrix, aPolyPolygon);
+
+ // extract the old Rectangle structures
+ tools::Rectangle aOldRect(
+ basegfx::fround(aRangeOriginalNoShearNoRotate.getMinX()),
+ basegfx::fround(aRangeOriginalNoShearNoRotate.getMinY()),
+ basegfx::fround(aRangeOriginalNoShearNoRotate.getMaxX()),
+ basegfx::fround(aRangeOriginalNoShearNoRotate.getMaxY()));
+ tools::Rectangle aNewRect(
+ basegfx::fround(aRangeNewNoShearNoRotate.getMinX()),
+ basegfx::fround(aRangeNewNoShearNoRotate.getMinY()),
+ basegfx::fround(aRangeNewNoShearNoRotate.getMaxX()),
+ basegfx::fround(aRangeNewNoShearNoRotate.getMaxY()));
+
+ // continue with the old original stuff
+ if (!aOldRect.GetWidth() || !aOldRect.GetHeight())
+ {
+ throw o3tl::divide_by_zero();
+ }
+
+ if((pObj->GetGraphicType() == GraphicType::NONE) || (pObj->GetGraphicType() == GraphicType::Default))
+ {
+ return false;
+ }
+
+ const GraphicObject& rGraphicObject(pObj->GetGraphicObject());
+ // tdf#117145 Usually Writer will go the bExternal path (see above), but more correct for
+ // the future is to use the MapMode from the SdrModel/SfxItemPool if the Writer's current
+ // special handling should be unified to this path in the future. Usually it *should* be
+ // MapUnit::Map100thMM, but better do not mix up Units.
+ // Checked now what SwVirtFlyDrawObj::NbcCrop is doing - it calculates everything forced
+ // to MapUnit::Map100thMM, but extracts/packs Twips to the used SdrGrafCropItem in Writer.
+ const MapMode aMapModePool(pObj->getSdrModelFromSdrObject().GetItemPool().GetMetric(0));
+ Size aGraphicSize(rGraphicObject.GetPrefSize());
+
+ if(MapUnit::MapPixel == rGraphicObject.GetPrefMapMode().GetMapUnit())
+ {
+ aGraphicSize = Application::GetDefaultDevice()->PixelToLogic(aGraphicSize, aMapModePool);
+ }
+ else
+ {
+ aGraphicSize = OutputDevice::LogicToLogic(aGraphicSize, rGraphicObject.GetPrefMapMode(), aMapModePool);
+ }
+
+ if(0 == aGraphicSize.Width() || 0 == aGraphicSize.Height())
+ {
+ return false;
+ }
+
+ const SdrGrafCropItem& rOldCrop = pObj->GetMergedItem(SDRATTR_GRAFCROP);
+ double fScaleX = ( aGraphicSize.Width() - rOldCrop.GetLeft() - rOldCrop.GetRight() ) / static_cast<double>(aOldRect.GetWidth());
+ double fScaleY = ( aGraphicSize.Height() - rOldCrop.GetTop() - rOldCrop.GetBottom() ) / static_cast<double>(aOldRect.GetHeight());
+
+ sal_Int32 nDiffLeft = aNewRect.Left() - aOldRect.Left();
+ sal_Int32 nDiffTop = aNewRect.Top() - aOldRect.Top();
+ sal_Int32 nDiffRight = aNewRect.Right() - aOldRect.Right();
+ sal_Int32 nDiffBottom = aNewRect.Bottom() - aOldRect.Bottom();
+
+ if(pObj->IsMirrored())
+ {
+ // mirrored X or Y, for old stuff, exchange X
+ // check for aw080
+ sal_Int32 nTmp(nDiffLeft);
+ nDiffLeft = -nDiffRight;
+ nDiffRight = -nTmp;
+ }
+
+ sal_Int32 nLeftCrop = static_cast<sal_Int32>( rOldCrop.GetLeft() + nDiffLeft * fScaleX );
+ sal_Int32 nTopCrop = static_cast<sal_Int32>( rOldCrop.GetTop() + nDiffTop * fScaleY );
+ sal_Int32 nRightCrop = static_cast<sal_Int32>( rOldCrop.GetRight() - nDiffRight * fScaleX );
+ sal_Int32 nBottomCrop = static_cast<sal_Int32>( rOldCrop.GetBottom() - nDiffBottom * fScaleY );
+
+ SfxItemPool& rPool = getSdrDragView().GetModel()->GetItemPool();
+ SfxItemSetFixed<SDRATTR_GRAFCROP, SDRATTR_GRAFCROP> aSet( rPool );
+ aSet.Put( SdrGrafCropItem( nLeftCrop, nTopCrop, nRightCrop, nBottomCrop ) );
+ getSdrDragView().SetAttributes( aSet, false );
+ }
+
+ if(bUndo)
+ {
+ getSdrDragView().EndUndo();
+ }
+
+ return true;
+}
+
+PointerStyle SdrDragCrop::GetSdrDragPointer() const
+{
+ return PointerStyle::Crop;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svddrgv.cxx b/svx/source/svdraw/svddrgv.cxx
new file mode 100644
index 000000000..b6ac05cef
--- /dev/null
+++ b/svx/source/svdraw/svddrgv.cxx
@@ -0,0 +1,919 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <osl/diagnose.h>
+#include <tools/debug.hxx>
+#include <svx/svddrgv.hxx>
+#include <svx/svdview.hxx>
+#include <svx/svdundo.hxx>
+#include <svx/svdocapt.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/svdoedge.hxx>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include "svddrgm1.hxx"
+#include <svx/obj3d.hxx>
+#include <svx/svdoashp.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <svtools/optionsdrawinglayer.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <svx/sdrpagewindow.hxx>
+#include <unotools/configmgr.hxx>
+#include <comphelper/lok.hxx>
+
+using namespace sdr;
+
+SdrDragView::SdrDragView(SdrModel& rSdrModel, OutputDevice* pOut)
+ : SdrExchangeView(rSdrModel, pOut)
+ , mpDragHdl(nullptr)
+ , mpInsPointUndo(nullptr)
+ , meDragHdl(SdrHdlKind::Move)
+ , mbFramDrag(false)
+ , mbMarkedHitMovesAlways(false)
+ , mbDragLimit(false)
+ , mbDragHdl(false)
+ , mbDragStripes(false)
+ , mbSolidDragging(utl::ConfigManager::IsFuzzing() || SvtOptionsDrawinglayer::IsSolidDragCreate())
+ , mbResizeAtCenter(false)
+ , mbCrookAtCenter(false)
+ , mbDragWithCopy(false)
+ , mbInsGluePoint(false)
+ , mbInsObjPointMode(false)
+ , mbInsGluePointMode(false)
+ , mbNoDragXorPolys(false)
+{
+ meDragMode = SdrDragMode::Move;
+}
+
+SdrDragView::~SdrDragView()
+{
+}
+
+bool SdrDragView::IsAction() const
+{
+ return (mpCurrentSdrDragMethod || SdrExchangeView::IsAction());
+}
+
+void SdrDragView::MovAction(const Point& rPnt)
+{
+ SdrExchangeView::MovAction(rPnt);
+ if (mpCurrentSdrDragMethod)
+ {
+ MovDragObj(rPnt);
+ }
+}
+
+void SdrDragView::EndAction()
+{
+ if (mpCurrentSdrDragMethod)
+ {
+ EndDragObj();
+ }
+ SdrExchangeView::EndAction();
+}
+
+void SdrDragView::BckAction()
+{
+ SdrExchangeView::BckAction();
+ BrkDragObj();
+}
+
+void SdrDragView::BrkAction()
+{
+ SdrExchangeView::BrkAction();
+ BrkDragObj();
+}
+
+void SdrDragView::TakeActionRect(tools::Rectangle& rRect) const
+{
+ if (mpCurrentSdrDragMethod)
+ {
+ rRect=maDragStat.GetActionRect();
+ if (rRect.IsEmpty())
+ {
+ SdrPageView* pPV = GetSdrPageView();
+
+ if(pPV&& pPV->HasMarkedObjPageView())
+ {
+ // #i95646# is this used..?
+ const basegfx::B2DRange aBoundRange(mpCurrentSdrDragMethod->getCurrentRange());
+ if (aBoundRange.isEmpty())
+ {
+ rRect.SetEmpty();
+ }
+ else
+ {
+ rRect = tools::Rectangle(
+ basegfx::fround(aBoundRange.getMinX()), basegfx::fround(aBoundRange.getMinY()),
+ basegfx::fround(aBoundRange.getMaxX()), basegfx::fround(aBoundRange.getMaxY()));
+ }
+ }
+ }
+ if (rRect.IsEmpty())
+ {
+ rRect=tools::Rectangle(maDragStat.GetNow(),maDragStat.GetNow());
+ }
+ }
+ else
+ {
+ SdrExchangeView::TakeActionRect(rRect);
+ }
+}
+
+bool SdrDragView::TakeDragObjAnchorPos(Point& rPos, bool bTR ) const
+{
+ tools::Rectangle aR;
+ TakeActionRect(aR);
+ rPos = bTR ? aR.TopRight() : aR.TopLeft();
+ if (GetMarkedObjectCount()==1 && IsDragObj() && // only on single selection
+ !IsDraggingPoints() && !IsDraggingGluePoints() && // not when moving points
+ dynamic_cast<const SdrDragMovHdl*>( mpCurrentSdrDragMethod.get() ) == nullptr) // not when moving handles
+ {
+ SdrObject* pObj=GetMarkedObjectByIndex(0);
+ if (auto pCaptionObj = dynamic_cast<SdrCaptionObj*>(pObj))
+ {
+ Point aPt(pCaptionObj->GetTailPos());
+ bool bTail=meDragHdl==SdrHdlKind::Poly; // drag tail
+ bool bOwn=dynamic_cast<const SdrDragObjOwn*>( mpCurrentSdrDragMethod.get() ) != nullptr; // specific to object
+ if (!bTail)
+ { // for bTail, TakeActionRect already does the right thing
+ if (bOwn)
+ { // bOwn may be MoveTextFrame, ResizeTextFrame, but may not (any more) be DragTail
+ rPos=aPt;
+ }
+ else
+ {
+ // drag the whole Object (Move, Resize, ...)
+ const basegfx::B2DPoint aTransformed(mpCurrentSdrDragMethod->getCurrentTransformation() * basegfx::B2DPoint(aPt.X(), aPt.Y()));
+ rPos.setX( basegfx::fround(aTransformed.getX()) );
+ rPos.setY( basegfx::fround(aTransformed.getY()) );
+ }
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+
+bool SdrDragView::TakeDragLimit(SdrDragMode /*eMode*/, tools::Rectangle& /*rRect*/) const
+{
+ return false;
+}
+
+bool SdrDragView::BegDragObj(const Point& rPnt, OutputDevice* pOut, SdrHdl* pHdl, short nMinMov, SdrDragMethod* _pForcedMeth)
+{
+ BrkAction();
+
+ // so we don't leak the object on early return
+ std::unique_ptr<SdrDragMethod> pForcedMeth(_pForcedMeth);
+
+ bool bRet=false;
+ {
+ SetDragWithCopy(false);
+ //TODO: aAni.Reset();
+ mpCurrentSdrDragMethod=nullptr;
+ SdrDragMode eTmpMode=meDragMode;
+ if (eTmpMode==SdrDragMode::Move && pHdl!=nullptr && pHdl->GetKind()!=SdrHdlKind::Move) {
+ eTmpMode=SdrDragMode::Resize;
+ }
+ mbDragLimit=TakeDragLimit(eTmpMode,maDragLimit);
+ mbFramDrag=ImpIsFrameHandles();
+ if (!mbFramDrag &&
+ (mpMarkedObj==nullptr || !mpMarkedObj->hasSpecialDrag()) &&
+ (pHdl==nullptr || pHdl->GetObj()==nullptr)) {
+ mbFramDrag=true;
+ }
+
+ Point aPnt(rPnt);
+ basegfx::B2DVector aGridOffset(0.0, 0.0);
+
+ // Coordinate maybe affected by GridOffset, so we may need to
+ // adapt to Model-coordinates here
+ if((comphelper::LibreOfficeKit::isActive() && mpMarkedObj
+ && getPossibleGridOffsetForSdrObject(aGridOffset, GetMarkedObjectByIndex(0), GetSdrPageView()))
+ || (getPossibleGridOffsetForPosition(
+ aGridOffset,
+ basegfx::B2DPoint(aPnt.X(), aPnt.Y()),
+ GetSdrPageView())))
+ {
+ aPnt.AdjustX(basegfx::fround(-aGridOffset.getX()));
+ aPnt.AdjustY(basegfx::fround(-aGridOffset.getY()));
+ }
+
+ if(pHdl == nullptr
+ || pHdl->GetKind() == SdrHdlKind::Move
+ || pHdl->GetKind() == SdrHdlKind::MirrorAxis
+ || pHdl->GetKind() == SdrHdlKind::Transparence
+ || pHdl->GetKind() == SdrHdlKind::Gradient)
+ {
+ maDragStat.Reset(aPnt);
+ }
+ else
+ {
+ maDragStat.Reset(pHdl->GetPos());
+ }
+
+ maDragStat.SetView(static_cast<SdrView*>(this));
+ maDragStat.SetPageView(mpMarkedPV); // <<-- DragPV has to go here!!!
+ maDragStat.SetMinMove(ImpGetMinMovLogic(nMinMov,pOut));
+ maDragStat.SetHdl(pHdl);
+ maDragStat.NextPoint();
+ mpDragWin=pOut;
+ mpDragHdl=pHdl;
+ meDragHdl= pHdl==nullptr ? SdrHdlKind::Move : pHdl->GetKind();
+ mbDragHdl=meDragHdl==SdrHdlKind::Ref1 || meDragHdl==SdrHdlKind::Ref2 || meDragHdl==SdrHdlKind::MirrorAxis;
+
+ // Expand test for SdrHdlKind::Anchor_TR
+ bool bNotDraggable = (SdrHdlKind::Anchor == meDragHdl || SdrHdlKind::Anchor_TR == meDragHdl);
+
+ if(pHdl && (pHdl->GetKind() == SdrHdlKind::SmartTag) && pForcedMeth )
+ {
+ // just use the forced method for smart tags
+ }
+ else if(mbDragHdl)
+ {
+ mpCurrentSdrDragMethod.reset(new SdrDragMovHdl(*this));
+ }
+ else if(!bNotDraggable)
+ {
+ switch (meDragMode)
+ {
+ case SdrDragMode::Rotate: case SdrDragMode::Shear:
+ {
+ switch (meDragHdl)
+ {
+ case SdrHdlKind::Left: case SdrHdlKind::Right:
+ case SdrHdlKind::Upper: case SdrHdlKind::Lower:
+ {
+ // are 3D objects selected?
+ bool b3DObjSelected = false;
+ for(size_t a=0; !b3DObjSelected && a<GetMarkedObjectCount(); ++a)
+ {
+ SdrObject* pObj = GetMarkedObjectByIndex(a);
+ if(dynamic_cast< const E3dObject* >(pObj))
+ b3DObjSelected = true;
+ }
+ // If yes, allow shear even when !IsShearAllowed,
+ // because 3D objects are limited rotations
+ if (!b3DObjSelected && !IsShearAllowed())
+ return false;
+ mpCurrentSdrDragMethod.reset(new SdrDragShear(*this,meDragMode==SdrDragMode::Rotate));
+ } break;
+ case SdrHdlKind::UpperLeft: case SdrHdlKind::UpperRight:
+ case SdrHdlKind::LowerLeft: case SdrHdlKind::LowerRight:
+ {
+ if (meDragMode==SdrDragMode::Shear)
+ {
+ if (!IsDistortAllowed(true) && !IsDistortAllowed()) return false;
+ mpCurrentSdrDragMethod.reset(new SdrDragDistort(*this));
+ }
+ else
+ {
+ if (!IsRotateAllowed(true)) return false;
+ mpCurrentSdrDragMethod.reset(new SdrDragRotate(*this));
+ }
+ } break;
+ default:
+ {
+ if (IsMarkedHitMovesAlways() && meDragHdl==SdrHdlKind::Move)
+ { // SdrHdlKind::Move is true, even if Obj is hit directly
+ if (!IsMoveAllowed()) return false;
+ mpCurrentSdrDragMethod.reset(new SdrDragMove(*this));
+ }
+ else
+ {
+ if (!IsRotateAllowed(true)) return false;
+ mpCurrentSdrDragMethod.reset(new SdrDragRotate(*this));
+ }
+ }
+ }
+ } break;
+ case SdrDragMode::Mirror:
+ {
+ if (meDragHdl==SdrHdlKind::Move && IsMarkedHitMovesAlways())
+ {
+ if (!IsMoveAllowed()) return false;
+ mpCurrentSdrDragMethod.reset(new SdrDragMove(*this));
+ }
+ else
+ {
+ if (!IsMirrorAllowed(true,true)) return false;
+ mpCurrentSdrDragMethod.reset(new SdrDragMirror(*this));
+ }
+ } break;
+
+ case SdrDragMode::Crop:
+ {
+ if (meDragHdl==SdrHdlKind::Move && IsMarkedHitMovesAlways())
+ {
+ if (!IsMoveAllowed())
+ return false;
+ mpCurrentSdrDragMethod.reset(new SdrDragMove(*this));
+ }
+ else
+ {
+ if (!IsCropAllowed())
+ return false;
+ mpCurrentSdrDragMethod.reset(new SdrDragCrop(*this));
+ }
+ }
+ break;
+
+ case SdrDragMode::Transparence:
+ {
+ if(meDragHdl == SdrHdlKind::Move && IsMarkedHitMovesAlways())
+ {
+ if(!IsMoveAllowed())
+ return false;
+ mpCurrentSdrDragMethod.reset(new SdrDragMove(*this));
+ }
+ else
+ {
+ if(!IsTransparenceAllowed())
+ return false;
+
+ mpCurrentSdrDragMethod.reset(new SdrDragGradient(*this, false));
+ }
+ break;
+ }
+ case SdrDragMode::Gradient:
+ {
+ if(meDragHdl == SdrHdlKind::Move && IsMarkedHitMovesAlways())
+ {
+ if(!IsMoveAllowed())
+ return false;
+ mpCurrentSdrDragMethod.reset(new SdrDragMove(*this));
+ }
+ else
+ {
+ if(!IsGradientAllowed())
+ return false;
+
+ mpCurrentSdrDragMethod.reset(new SdrDragGradient(*this));
+ }
+ break;
+ }
+
+ case SdrDragMode::Crook :
+ {
+ if (meDragHdl==SdrHdlKind::Move && IsMarkedHitMovesAlways())
+ {
+ if (!IsMoveAllowed()) return false;
+ mpCurrentSdrDragMethod.reset( new SdrDragMove(*this) );
+ }
+ else
+ {
+ if (!IsCrookAllowed(true) && !IsCrookAllowed()) return false;
+ mpCurrentSdrDragMethod.reset( new SdrDragCrook(*this) );
+ }
+ } break;
+
+ default:
+ {
+ // SdrDragMode::Move
+ if((meDragHdl == SdrHdlKind::Move) && !IsMoveAllowed())
+ {
+ return false;
+ }
+ else if(meDragHdl == SdrHdlKind::Glue)
+ {
+ mpCurrentSdrDragMethod.reset( new SdrDragMove(*this) );
+ }
+ else
+ {
+ if(mbFramDrag)
+ {
+ if(meDragHdl == SdrHdlKind::Move)
+ {
+ mpCurrentSdrDragMethod.reset( new SdrDragMove(*this) );
+ }
+ else
+ {
+ if(!IsResizeAllowed(true))
+ {
+ return false;
+ }
+
+ bool bSingleTextObjMark = false; // SJ: #i100490#
+ if ( GetMarkedObjectCount() == 1 )
+ {
+ mpMarkedObj=GetMarkedObjectByIndex(0);
+ if ( mpMarkedObj &&
+ dynamic_cast<const SdrTextObj*>( mpMarkedObj) != nullptr &&
+ static_cast<SdrTextObj*>(mpMarkedObj)->IsTextFrame() )
+ bSingleTextObjMark = true;
+ }
+ if ( bSingleTextObjMark )
+ mpCurrentSdrDragMethod.reset( new SdrDragObjOwn(*this) );
+ else
+ mpCurrentSdrDragMethod.reset( new SdrDragResize(*this) );
+ }
+ }
+ else
+ {
+ if(SdrHdlKind::Move == meDragHdl)
+ {
+ const bool bCustomShapeSelected(1 == GetMarkedObjectCount() && dynamic_cast<const SdrObjCustomShape*>(GetMarkedObjectByIndex(0)) != nullptr);
+
+ if(bCustomShapeSelected)
+ {
+ mpCurrentSdrDragMethod.reset( new SdrDragMove( *this ) );
+ }
+ }
+ else if(SdrHdlKind::Poly == meDragHdl)
+ {
+ const bool bConnectorSelected(1 == GetMarkedObjectCount() && dynamic_cast<const SdrEdgeObj*>(GetMarkedObjectByIndex(0)) != nullptr);
+
+ if(bConnectorSelected)
+ {
+ // #i97784#
+ // fallback to old behaviour for connectors (see
+ // text in task description for more details)
+ }
+ else if(!IsMoveAllowed() || !IsResizeAllowed())
+ {
+ // #i77187#
+ // do not allow move of polygon points if object is move or size protected
+ return false;
+ }
+ }
+
+ if(!mpCurrentSdrDragMethod)
+ {
+ // fallback to DragSpecial if no interaction defined
+ mpCurrentSdrDragMethod.reset( new SdrDragObjOwn(*this) );
+ }
+ }
+ }
+ }
+ }
+ }
+ if (pForcedMeth)
+ {
+ mpCurrentSdrDragMethod = std::move(pForcedMeth);
+ }
+ maDragStat.SetDragMethod(mpCurrentSdrDragMethod.get());
+ if (mpCurrentSdrDragMethod)
+ {
+ bRet = mpCurrentSdrDragMethod->BeginSdrDrag();
+ if (!bRet)
+ {
+ if (pHdl==nullptr && dynamic_cast< const SdrDragObjOwn* >(mpCurrentSdrDragMethod.get()) != nullptr)
+ {
+ // Obj may not Move SpecialDrag, so try with MoveFrameDrag
+ mpCurrentSdrDragMethod.reset();
+
+ if (!IsMoveAllowed())
+ return false;
+
+ mbFramDrag=true;
+ mpCurrentSdrDragMethod.reset( new SdrDragMove(*this) );
+ maDragStat.SetDragMethod(mpCurrentSdrDragMethod.get());
+ bRet = mpCurrentSdrDragMethod->BeginSdrDrag();
+ }
+ }
+ if (!bRet)
+ {
+ mpCurrentSdrDragMethod.reset();
+ maDragStat.SetDragMethod(mpCurrentSdrDragMethod.get());
+ }
+ }
+ }
+
+ return bRet;
+}
+
+void SdrDragView::MovDragObj(const Point& rPnt)
+{
+ if (!mpCurrentSdrDragMethod)
+ return;
+
+ Point aPnt(rPnt);
+ basegfx::B2DVector aGridOffset(0.0, 0.0);
+
+ // Coordinate maybe affected by GridOffset, so we may need to
+ // adapt to Model-coordinates here
+ if((comphelper::LibreOfficeKit::isActive() && mpMarkedObj
+ && getPossibleGridOffsetForSdrObject(aGridOffset, GetMarkedObjectByIndex(0), GetSdrPageView()))
+ || (getPossibleGridOffsetForPosition(
+ aGridOffset,
+ basegfx::B2DPoint(aPnt.X(), aPnt.Y()),
+ GetSdrPageView())))
+ {
+ aPnt.AdjustX(basegfx::fround(-aGridOffset.getX()));
+ aPnt.AdjustY(basegfx::fround(-aGridOffset.getY()));
+ }
+
+ ImpLimitToWorkArea(aPnt);
+ mpCurrentSdrDragMethod->MoveSdrDrag(aPnt); // this call already makes a Hide()/Show combination
+}
+
+bool SdrDragView::EndDragObj(bool bCopy)
+{
+ bool bRet(false);
+
+ // #i73341# If inserting GluePoint, do not insist on last points being different
+ if(mpCurrentSdrDragMethod && maDragStat.IsMinMoved() && (IsInsertGluePoint() || maDragStat.GetNow() != maDragStat.GetPrev()))
+ {
+ sal_Int32 nSavedHdlCount=0;
+
+ if (bEliminatePolyPoints)
+ {
+ nSavedHdlCount=GetMarkablePointCount();
+ }
+
+ const bool bUndo = IsUndoEnabled();
+ if (IsInsertGluePoint() && bUndo)
+ {
+ BegUndo(maInsPointUndoStr);
+ AddUndo(std::unique_ptr<SdrUndoAction>(mpInsPointUndo));
+ }
+
+ bRet = mpCurrentSdrDragMethod->EndSdrDrag(bCopy);
+
+ if( IsInsertGluePoint() && bUndo)
+ EndUndo();
+
+ mpCurrentSdrDragMethod.reset();
+
+ if (bEliminatePolyPoints)
+ {
+ if (nSavedHdlCount!=GetMarkablePointCount())
+ {
+ UnmarkAllPoints();
+ }
+ }
+
+ if (mbInsPolyPoint)
+ {
+ SetMarkHandles(nullptr);
+ mbInsPolyPoint=false;
+ if( bUndo )
+ {
+ BegUndo(maInsPointUndoStr);
+ AddUndo(std::unique_ptr<SdrUndoAction>(mpInsPointUndo));
+ EndUndo();
+ }
+ }
+
+ meDragHdl=SdrHdlKind::Move;
+ mpDragHdl=nullptr;
+
+ if (!mbSomeObjChgdFlag)
+ {
+ // Obj did not broadcast (e. g. Writer FlyFrames)
+ if(!mbDragHdl)
+ {
+ AdjustMarkHdl();
+ }
+ }
+ }
+ else
+ {
+ BrkDragObj();
+ }
+
+ mbInsPolyPoint=false;
+ SetInsertGluePoint(false);
+
+ return bRet;
+}
+
+void SdrDragView::BrkDragObj()
+{
+ if (!mpCurrentSdrDragMethod)
+ return;
+
+ mpCurrentSdrDragMethod->CancelSdrDrag();
+
+ mpCurrentSdrDragMethod.reset();
+
+ if (mbInsPolyPoint)
+ {
+ mpInsPointUndo->Undo(); // delete inserted point again
+ delete mpInsPointUndo;
+ mpInsPointUndo=nullptr;
+ SetMarkHandles(nullptr);
+ mbInsPolyPoint=false;
+ }
+
+ if (IsInsertGluePoint())
+ {
+ mpInsPointUndo->Undo(); // delete inserted gluepoint again
+ delete mpInsPointUndo;
+ mpInsPointUndo=nullptr;
+ SetInsertGluePoint(false);
+ }
+
+ meDragHdl=SdrHdlKind::Move;
+ mpDragHdl=nullptr;
+}
+
+bool SdrDragView::IsInsObjPointPossible() const
+{
+ return mpMarkedObj!=nullptr && mpMarkedObj->IsPolyObj();
+}
+
+bool SdrDragView::ImpBegInsObjPoint(bool bIdxZwang, const Point& rPnt, bool bNewObj, OutputDevice* pOut)
+{
+ bool bRet(false);
+
+ if(auto pMarkedPath = dynamic_cast<SdrPathObj*>( mpMarkedObj))
+ {
+ BrkAction();
+ mpInsPointUndo = dynamic_cast< SdrUndoGeoObj* >( GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*mpMarkedObj).release() );
+ DBG_ASSERT( mpInsPointUndo, "svx::SdrDragView::BegInsObjPoint(), could not create correct undo object!" );
+
+ OUString aStr(SvxResId(STR_DragInsertPoint));
+
+ maInsPointUndoStr = aStr.replaceFirst("%1", mpMarkedObj->TakeObjNameSingul() );
+
+ Point aPt(rPnt);
+
+ if(bNewObj)
+ aPt = GetSnapPos(aPt,mpMarkedPV);
+
+ bool bClosed0 = pMarkedPath->IsClosedObj();
+
+ const sal_uInt32 nInsPointNum { bIdxZwang
+ ? pMarkedPath->NbcInsPoint(aPt, bNewObj)
+ : pMarkedPath->NbcInsPointOld(aPt, bNewObj)
+ };
+
+ if(bClosed0 != pMarkedPath->IsClosedObj())
+ {
+ // Obj was closed implicitly
+ // object changed
+ pMarkedPath->SetChanged();
+ pMarkedPath->BroadcastObjectChange();
+ }
+
+ if (nInsPointNum != SAL_MAX_UINT32)
+ {
+ mbInsPolyPoint = true;
+ UnmarkAllPoints();
+ AdjustMarkHdl();
+
+ bRet = BegDragObj(rPnt, pOut, maHdlList.GetHdl(nInsPointNum), 0);
+
+ if (bRet)
+ {
+ maDragStat.SetMinMoved();
+ MovDragObj(rPnt);
+ }
+ }
+ else
+ {
+ delete mpInsPointUndo;
+ mpInsPointUndo = nullptr;
+ }
+ }
+
+ return bRet;
+}
+
+bool SdrDragView::EndInsObjPoint(SdrCreateCmd eCmd)
+{
+ if(IsInsObjPoint())
+ {
+ Point aPnt(maDragStat.GetNow());
+ bool bOk=EndDragObj();
+ if (bOk && eCmd!=SdrCreateCmd::ForceEnd)
+ {
+ // Ret=True means: Action is over.
+ bOk = ! ImpBegInsObjPoint(true, aPnt, eCmd == SdrCreateCmd::NextObject, mpDragWin);
+ }
+
+ return bOk;
+ } else return false;
+}
+
+bool SdrDragView::IsInsGluePointPossible() const
+{
+ bool bRet=false;
+ if (IsInsGluePointMode() && AreObjectsMarked())
+ {
+ if (GetMarkedObjectCount()==1)
+ {
+ // return sal_False, if only 1 object which is a connector.
+ const SdrObject* pObj=GetMarkedObjectByIndex(0);
+ if (dynamic_cast<const SdrEdgeObj *>(pObj) == nullptr)
+ {
+ bRet=true;
+ }
+ }
+ else
+ {
+ bRet=true;
+ }
+ }
+ return bRet;
+}
+
+bool SdrDragView::BegInsGluePoint(const Point& rPnt)
+{
+ bool bRet=false;
+ SdrObject* pObj;
+ SdrPageView* pPV;
+ if (PickMarkedObj(rPnt,pObj,pPV,SdrSearchOptions::PASS2BOUND))
+ {
+ BrkAction();
+ UnmarkAllGluePoints();
+ mpInsPointUndo= dynamic_cast< SdrUndoGeoObj* >( GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pObj).release() );
+ DBG_ASSERT( mpInsPointUndo, "svx::SdrDragView::BegInsObjPoint(), could not create correct undo object!" );
+ OUString aStr(SvxResId(STR_DragInsertGluePoint));
+
+ maInsPointUndoStr = aStr.replaceFirst("%1", pObj->TakeObjNameSingul() );
+
+ SdrGluePointList* pGPL=pObj->ForceGluePointList();
+ if (pGPL!=nullptr)
+ {
+ sal_uInt16 nGlueIdx=pGPL->Insert(SdrGluePoint());
+ SdrGluePoint& rGP=(*pGPL)[nGlueIdx];
+ sal_uInt16 nGlueId=rGP.GetId();
+ rGP.SetAbsolutePos(rPnt,*pObj);
+
+ SdrHdl* pHdl=nullptr;
+ if (MarkGluePoint(pObj,nGlueId,false))
+ {
+ pHdl=GetGluePointHdl(pObj,nGlueId);
+ }
+ if (pHdl!=nullptr && pHdl->GetKind()==SdrHdlKind::Glue && pHdl->GetObj()==pObj && pHdl->GetObjHdlNum()==nGlueId)
+ {
+ SetInsertGluePoint(true);
+ bRet=BegDragObj(rPnt,nullptr,pHdl,0);
+ if (bRet)
+ {
+ maDragStat.SetMinMoved();
+ MovDragObj(rPnt);
+ }
+ else
+ {
+ SetInsertGluePoint(false);
+ delete mpInsPointUndo;
+ mpInsPointUndo=nullptr;
+ }
+ }
+ else
+ {
+ OSL_FAIL("BegInsGluePoint(): GluePoint handle not found.");
+ }
+ }
+ else
+ {
+ // no gluepoints possible for this object (e. g. Edge)
+ SetInsertGluePoint(false);
+ delete mpInsPointUndo;
+ mpInsPointUndo=nullptr;
+ }
+ }
+
+ return bRet;
+}
+
+void SdrDragView::ShowDragObj()
+{
+ if(!mpCurrentSdrDragMethod || maDragStat.IsShown())
+ return;
+
+ // Changed for the GridOffset stuff: No longer iterate over
+ // SdrPaintWindow(s), but now over SdrPageWindow(s), so doing the
+ // same as the SdrHdl visualizations (see ::CreateB2dIAObject) do.
+ // This is needed to get access to an ObjectContact which is needed
+ // to evtl. process that GridOffset in CreateOverlayGeometry
+ SdrPageView* pPageView(GetSdrPageView());
+
+ if(nullptr != pPageView)
+ {
+ for(sal_uInt32 a(0); a < pPageView->PageWindowCount(); a++)
+ {
+ const SdrPageWindow& rPageWindow(*pPageView->GetPageWindow(a));
+ const SdrPaintWindow& rPaintWindow(rPageWindow.GetPaintWindow());
+
+ if(rPaintWindow.OutputToWindow())
+ {
+ const rtl::Reference<sdr::overlay::OverlayManager>& xOverlayManager(
+ rPaintWindow.GetOverlayManager());
+
+ if(xOverlayManager.is())
+ {
+ mpCurrentSdrDragMethod->CreateOverlayGeometry(
+ *xOverlayManager,
+ rPageWindow.GetObjectContact());
+
+ // #i101679# Force changed overlay to be shown
+ xOverlayManager->flush();
+ }
+ }
+ }
+ }
+
+ maDragStat.SetShown(true);
+}
+
+void SdrDragView::HideDragObj()
+{
+ if(mpCurrentSdrDragMethod && maDragStat.IsShown())
+ {
+ mpCurrentSdrDragMethod->destroyOverlayGeometry();
+ maDragStat.SetShown(false);
+ }
+}
+
+
+void SdrDragView::SetNoDragXorPolys(bool bOn)
+{
+ if (IsNoDragXorPolys()==bOn)
+ return;
+
+ const bool bDragging(mpCurrentSdrDragMethod);
+ const bool bShown(bDragging && maDragStat.IsShown());
+
+ if(bShown)
+ {
+ HideDragObj();
+ }
+
+ mbNoDragXorPolys = bOn;
+
+ if(bDragging)
+ {
+ // force recreation of drag content
+ mpCurrentSdrDragMethod->resetSdrDragEntries();
+ }
+
+ if(bShown)
+ {
+ ShowDragObj();
+ }
+}
+
+void SdrDragView::SetDragStripes(bool bOn)
+{
+ if (mpCurrentSdrDragMethod && maDragStat.IsShown())
+ {
+ HideDragObj();
+ mbDragStripes=bOn;
+ ShowDragObj();
+ }
+ else
+ {
+ mbDragStripes=bOn;
+ }
+}
+
+bool SdrDragView::IsOrthoDesired() const
+{
+ if( dynamic_cast< const SdrDragObjOwn* >( mpCurrentSdrDragMethod.get() )
+ || dynamic_cast< const SdrDragResize* >(mpCurrentSdrDragMethod.get() ))
+ {
+ return m_bOrthoDesiredOnMarked;
+ }
+
+ return false;
+}
+
+void SdrDragView::SetMarkHandles(SfxViewShell* pOtherShell)
+{
+ if( mpDragHdl )
+ mpDragHdl = nullptr;
+
+ SdrExchangeView::SetMarkHandles(pOtherShell);
+}
+
+void SdrDragView::SetSolidDragging(bool bOn)
+{
+ if(mbSolidDragging != bOn)
+ {
+ mbSolidDragging = bOn;
+ }
+}
+
+bool SdrDragView::IsSolidDragging() const
+{
+ // allow each user to disable by having a local setting, but using AND for
+ // checking allowance
+ return mbSolidDragging && SvtOptionsDrawinglayer::IsSolidDragCreate();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdedtv.cxx b/svx/source/svdraw/svdedtv.cxx
new file mode 100644
index 000000000..b019f26ae
--- /dev/null
+++ b/svx/source/svdraw/svdedtv.cxx
@@ -0,0 +1,1103 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/svdedtv.hxx>
+#include <svx/svdundo.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdoedge.hxx>
+#include <svx/svdlayer.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdpoev.hxx>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include <svx/e3dsceneupdater.hxx>
+#include <rtl/strbuf.hxx>
+#include <svx/svdview.hxx>
+#include <clonelist.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/scene3d.hxx>
+#include <svx/xfillit0.hxx>
+#include <osl/diagnose.h>
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+using namespace com::sun::star;
+
+void SdrEditView::ImpResetPossibilityFlags()
+{
+ m_bReadOnly =false;
+
+ m_bGroupPossible =false;
+ m_bUnGroupPossible =false;
+ m_bGrpEnterPossible =false;
+ m_bToTopPossible =false;
+ m_bToBtmPossible =false;
+ m_bReverseOrderPossible =false;
+
+ m_bImportMtfPossible =false;
+ m_bCombinePossible =false;
+ m_bDismantlePossible =false;
+ m_bCombineNoPolyPolyPossible =false;
+ m_bDismantleMakeLinesPossible=false;
+ m_bOrthoDesiredOnMarked =false;
+
+ m_bOneOrMoreMovable =false;
+ m_bMoreThanOneNoMovRot =false;
+ m_bContortionPossible =false;
+ m_bMoveAllowed =false;
+ m_bResizeFreeAllowed =false;
+ m_bResizePropAllowed =false;
+ m_bRotateFreeAllowed =false;
+ m_bRotate90Allowed =false;
+ m_bMirrorFreeAllowed =false;
+ m_bMirror45Allowed =false;
+ m_bMirror90Allowed =false;
+ m_bTransparenceAllowed =false;
+ m_bCropAllowed =false;
+ m_bGradientAllowed =false;
+ m_bShearAllowed =false;
+ m_bEdgeRadiusAllowed =false;
+ m_bCanConvToPath =false;
+ m_bCanConvToPoly =false;
+ m_bCanConvToContour =false;
+ m_bMoveProtect =false;
+ m_bResizeProtect =false;
+}
+
+SdrEditView::SdrEditView(SdrModel& rSdrModel, OutputDevice* pOut)
+ : SdrMarkView(rSdrModel, pOut)
+ , m_bPossibilitiesDirty(true)
+ , m_bReadOnly(false)
+ , m_bGroupPossible(false)
+ , m_bUnGroupPossible(false)
+ , m_bGrpEnterPossible(false)
+ , m_bToTopPossible(false)
+ , m_bToBtmPossible(false)
+ , m_bReverseOrderPossible(false)
+ , m_bImportMtfPossible(false)
+ , m_bCombinePossible(false)
+ , m_bDismantlePossible(false)
+ , m_bCombineNoPolyPolyPossible(false)
+ , m_bDismantleMakeLinesPossible(false)
+ , m_bOrthoDesiredOnMarked(false)
+ , m_bOneOrMoreMovable(false)
+ , m_bMoreThanOneNoMovRot(false)
+ , m_bContortionPossible(false)
+ , m_bMoveAllowed(false)
+ , m_bResizeFreeAllowed(false)
+ , m_bResizePropAllowed(false)
+ , m_bRotateFreeAllowed(false)
+ , m_bRotate90Allowed(false)
+ , m_bMirrorFreeAllowed(false)
+ , m_bMirror45Allowed(false)
+ , m_bMirror90Allowed(false)
+ , m_bShearAllowed(false)
+ , m_bEdgeRadiusAllowed(false)
+ , m_bTransparenceAllowed(false)
+ , m_bCropAllowed(false)
+ , m_bGradientAllowed(false)
+ , m_bCanConvToPath(false)
+ , m_bCanConvToPoly(false)
+ , m_bCanConvToContour(false)
+ , m_bMoveProtect(false)
+ , m_bResizeProtect(false)
+{
+}
+
+SdrEditView::~SdrEditView()
+{
+}
+
+void SdrEditView::InsertNewLayer(const OUString& rName, sal_uInt16 nPos)
+{
+ SdrLayerAdmin& rLA=mpModel->GetLayerAdmin();
+ sal_uInt16 nMax=rLA.GetLayerCount();
+ if (nPos>nMax) nPos=nMax;
+ rLA.NewLayer(rName,nPos);
+
+ if( GetModel()->IsUndoEnabled() )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewLayer(nPos,rLA,*mpModel));
+
+ mpModel->SetChanged();
+}
+
+bool SdrEditView::ImpDelLayerCheck(SdrObjList const * pOL, SdrLayerID nDelID) const
+{
+ bool bDelAll(true);
+
+ for(size_t nObjNum = pOL->GetObjCount(); nObjNum > 0 && bDelAll;)
+ {
+ nObjNum--;
+ SdrObject* pObj = pOL->GetObj(nObjNum);
+ SdrObjList* pSubOL = pObj->GetSubList();
+
+ // explicitly test for group objects and 3d scenes
+ if(pSubOL && (dynamic_cast<const SdrObjGroup*>(pObj) != nullptr || dynamic_cast< const E3dScene* >(pObj) != nullptr))
+ {
+ if(!ImpDelLayerCheck(pSubOL, nDelID))
+ {
+ bDelAll = false;
+ }
+ }
+ else
+ {
+ if(pObj->GetLayer() != nDelID)
+ {
+ bDelAll = false;
+ }
+ }
+ }
+
+ return bDelAll;
+}
+
+void SdrEditView::ImpDelLayerDelObjs(SdrObjList* pOL, SdrLayerID nDelID)
+{
+ const size_t nObjCount(pOL->GetObjCount());
+ // make sure OrdNums are correct
+ pOL->GetObj(0)->GetOrdNum();
+
+ const bool bUndo = GetModel()->IsUndoEnabled();
+
+ for(size_t nObjNum = nObjCount; nObjNum > 0;)
+ {
+ nObjNum--;
+ SdrObject* pObj = pOL->GetObj(nObjNum);
+ SdrObjList* pSubOL = pObj->GetSubList();
+
+
+ // explicitly test for group objects and 3d scenes
+ if(pSubOL && (dynamic_cast<const SdrObjGroup*>( pObj) != nullptr || dynamic_cast<const E3dScene* >(pObj) != nullptr))
+ {
+ if(ImpDelLayerCheck(pSubOL, nDelID))
+ {
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoDeleteObject(*pObj, true));
+ pOL->RemoveObject(nObjNum);
+
+ if( !bUndo )
+ SdrObject::Free( pObj );
+ }
+ else
+ {
+ ImpDelLayerDelObjs(pSubOL, nDelID);
+ }
+ }
+ else
+ {
+ if(pObj->GetLayer() == nDelID)
+ {
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoDeleteObject(*pObj, true));
+ pOL->RemoveObject(nObjNum);
+ if( !bUndo )
+ SdrObject::Free( pObj );
+ }
+ }
+ }
+}
+
+void SdrEditView::DeleteLayer(const OUString& rName)
+{
+ SdrLayerAdmin& rLA = mpModel->GetLayerAdmin();
+ SdrLayer* pLayer = rLA.GetLayer(rName);
+
+ if(!pLayer)
+ return;
+
+ sal_uInt16 nLayerNum(rLA.GetLayerPos(pLayer));
+ SdrLayerID nDelID = pLayer->GetID();
+
+ const bool bUndo = IsUndoEnabled();
+ if( bUndo )
+ BegUndo(SvxResId(STR_UndoDelLayer));
+
+ bool bMaPg(true);
+
+ for(sal_uInt16 nPageKind(0); nPageKind < 2; nPageKind++)
+ {
+ // MasterPages and DrawPages
+ sal_uInt16 nPgCount(bMaPg ? mpModel->GetMasterPageCount() : mpModel->GetPageCount());
+
+ for(sal_uInt16 nPgNum(0); nPgNum < nPgCount; nPgNum++)
+ {
+ // over all pages
+ SdrPage* pPage = bMaPg ? mpModel->GetMasterPage(nPgNum) : mpModel->GetPage(nPgNum);
+ const size_t nObjCount(pPage->GetObjCount());
+
+ // make sure OrdNums are correct
+ if(nObjCount)
+ pPage->GetObj(0)->GetOrdNum();
+
+ for(size_t nObjNum(nObjCount); nObjNum > 0;)
+ {
+ nObjNum--;
+ SdrObject* pObj = pPage->GetObj(nObjNum);
+ SdrObjList* pSubOL = pObj->GetSubList();
+
+ // explicitly test for group objects and 3d scenes
+ if(pSubOL && (dynamic_cast<const SdrObjGroup*>(pObj) != nullptr || dynamic_cast<const E3dScene* >(pObj) != nullptr))
+ {
+ if(ImpDelLayerCheck(pSubOL, nDelID))
+ {
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoDeleteObject(*pObj, true));
+ pPage->RemoveObject(nObjNum);
+ if( !bUndo )
+ SdrObject::Free(pObj);
+ }
+ else
+ {
+ ImpDelLayerDelObjs(pSubOL, nDelID);
+ }
+ }
+ else
+ {
+ if(pObj->GetLayer() == nDelID)
+ {
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoDeleteObject(*pObj, true));
+ pPage->RemoveObject(nObjNum);
+ if( !bUndo )
+ SdrObject::Free(pObj);
+ }
+ }
+ }
+ }
+ bMaPg = false;
+ }
+
+ if( bUndo )
+ {
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoDeleteLayer(nLayerNum, rLA, *mpModel));
+ rLA.RemoveLayer(nLayerNum).release();
+ EndUndo();
+ }
+ else
+ {
+ rLA.RemoveLayer(nLayerNum);
+ }
+
+ mpModel->SetChanged();
+}
+
+
+void SdrEditView::EndUndo()
+{
+ // #i13033#
+ // Comparison changed to 1L since EndUndo() is called later now
+ // and EndUndo WILL change count to count-1
+ if(1 == mpModel->GetUndoBracketLevel())
+ {
+ ImpBroadcastEdgesOfMarkedNodes();
+ }
+
+ // #i13033#
+ // moved to bottom to still have access to UNDOs inside of
+ // ImpBroadcastEdgesOfMarkedNodes()
+ mpModel->EndUndo();
+}
+
+void SdrEditView::ImpBroadcastEdgesOfMarkedNodes()
+{
+ std::vector<SdrObject*>::const_iterator iterPos;
+ const std::vector<SdrObject*>& rAllMarkedObjects = GetTransitiveHullOfMarkedObjects();
+
+ // #i13033#
+ // New mechanism to search for necessary disconnections for
+ // changed connectors inside the transitive hull of all at
+ // the beginning of UNDO selected objects
+ for(size_t a(0); a < rAllMarkedObjects.size(); a++)
+ {
+ SdrEdgeObj* pEdge = dynamic_cast<SdrEdgeObj*>( rAllMarkedObjects[a] );
+
+ if(pEdge)
+ {
+ SdrObject* pObj1 = pEdge->GetConnectedNode(false);
+ SdrObject* pObj2 = pEdge->GetConnectedNode(true);
+
+ if(pObj1 && !pEdge->CheckNodeConnection(false))
+ {
+ iterPos = std::find(rAllMarkedObjects.begin(),rAllMarkedObjects.end(),pObj1);
+
+ if (iterPos == rAllMarkedObjects.end())
+ {
+ if( IsUndoEnabled() )
+ AddUndo( GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pEdge));
+ pEdge->DisconnectFromNode(false);
+ }
+ }
+
+ if(pObj2 && !pEdge->CheckNodeConnection(true))
+ {
+ iterPos = std::find(rAllMarkedObjects.begin(),rAllMarkedObjects.end(),pObj2);
+
+ if (iterPos == rAllMarkedObjects.end())
+ {
+ if( IsUndoEnabled() )
+ AddUndo( GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pEdge));
+ pEdge->DisconnectFromNode(true);
+ }
+ }
+ }
+ }
+
+ const size_t nMarkedEdgeCnt = GetMarkedEdgesOfMarkedNodes().GetMarkCount();
+
+ for (size_t i=0; i<nMarkedEdgeCnt; ++i) {
+ SdrMark* pEM = GetMarkedEdgesOfMarkedNodes().GetMark(i);
+ SdrObject* pEdgeTmp=pEM->GetMarkedSdrObj();
+ SdrEdgeObj* pEdge=dynamic_cast<SdrEdgeObj*>( pEdgeTmp );
+ if (pEdge!=nullptr) {
+ pEdge->SetEdgeTrackDirty();
+ }
+ }
+}
+
+
+// Possibilities
+
+
+void SdrEditView::MarkListHasChanged()
+{
+ SdrMarkView::MarkListHasChanged();
+ m_bPossibilitiesDirty=true;
+}
+
+void SdrEditView::ModelHasChanged()
+{
+ SdrMarkView::ModelHasChanged();
+ m_bPossibilitiesDirty=true;
+}
+
+bool SdrEditView::IsResizeAllowed(bool bProp) const
+{
+ ForcePossibilities();
+ if (m_bResizeProtect) return false;
+ if (bProp) return m_bResizePropAllowed;
+ return m_bResizeFreeAllowed;
+}
+
+bool SdrEditView::IsRotateAllowed(bool b90Deg) const
+{
+ ForcePossibilities();
+ if (m_bMoveProtect) return false;
+ if (b90Deg) return m_bRotate90Allowed;
+ return m_bRotateFreeAllowed;
+}
+
+bool SdrEditView::IsMirrorAllowed(bool b45Deg, bool b90Deg) const
+{
+ ForcePossibilities();
+ if (m_bMoveProtect) return false;
+ if (b90Deg) return m_bMirror90Allowed;
+ if (b45Deg) return m_bMirror45Allowed;
+ return m_bMirrorFreeAllowed;
+}
+
+bool SdrEditView::IsTransparenceAllowed() const
+{
+ ForcePossibilities();
+ return m_bTransparenceAllowed;
+}
+
+bool SdrEditView::IsCropAllowed() const
+{
+ ForcePossibilities();
+ return m_bCropAllowed;
+}
+
+bool SdrEditView::IsGradientAllowed() const
+{
+ ForcePossibilities();
+ return m_bGradientAllowed;
+}
+
+bool SdrEditView::IsShearAllowed() const
+{
+ ForcePossibilities();
+ if (m_bResizeProtect) return false;
+ return m_bShearAllowed;
+}
+
+bool SdrEditView::IsEdgeRadiusAllowed() const
+{
+ ForcePossibilities();
+ return m_bEdgeRadiusAllowed;
+}
+
+bool SdrEditView::IsCrookAllowed(bool bNoContortion) const
+{
+ // CrookMode missing here (no rotations allowed when shearing ...)
+ ForcePossibilities();
+ if (bNoContortion) {
+ if (!m_bRotateFreeAllowed) return false;
+ return !m_bMoveProtect && m_bMoveAllowed;
+ } else {
+ return !m_bResizeProtect && m_bContortionPossible;
+ }
+}
+
+bool SdrEditView::IsDistortAllowed(bool bNoContortion) const
+{
+ ForcePossibilities();
+ if (bNoContortion) {
+ return false;
+ } else {
+ return !m_bResizeProtect && m_bContortionPossible;
+ }
+}
+
+bool SdrEditView::IsCombinePossible(bool bNoPolyPoly) const
+{
+ ForcePossibilities();
+ if (bNoPolyPoly) return m_bCombineNoPolyPolyPossible;
+ else return m_bCombinePossible;
+}
+
+bool SdrEditView::IsDismantlePossible(bool bMakeLines) const
+{
+ ForcePossibilities();
+ if (bMakeLines) return m_bDismantleMakeLinesPossible;
+ else return m_bDismantlePossible;
+}
+
+void SdrEditView::CheckPossibilities()
+{
+ if (mbSomeObjChgdFlag)
+ {
+ m_bPossibilitiesDirty = true;
+
+ // This call IS necessary to correct the MarkList, in which
+ // no longer to the model belonging objects still can reside.
+ // These ones need to be removed.
+ CheckMarked();
+ }
+
+ if (!m_bPossibilitiesDirty)
+ return;
+
+ ImpResetPossibilityFlags();
+ SortMarkedObjects();
+ const size_t nMarkCount = GetMarkedObjectCount();
+ if (nMarkCount != 0)
+ {
+ m_bReverseOrderPossible = (nMarkCount >= 2);
+
+ size_t nMovableCount=0;
+ m_bGroupPossible=nMarkCount>=2;
+ m_bCombinePossible=nMarkCount>=2;
+ if (nMarkCount==1)
+ {
+ // check bCombinePossible more thoroughly
+ // still missing ...
+ const SdrObject* pObj=GetMarkedObjectByIndex(0);
+ //const SdrPathObj* pPath=dynamic_cast<SdrPathObj*>( pObj );
+ bool bGroup=pObj->GetSubList()!=nullptr;
+ bool bHasText=pObj->GetOutlinerParaObject()!=nullptr;
+ if (bGroup || bHasText) {
+ m_bCombinePossible=true;
+ }
+ }
+ m_bCombineNoPolyPolyPossible=m_bCombinePossible;
+ // accept transformations for now
+ m_bMoveAllowed =true;
+ m_bResizeFreeAllowed=true;
+ m_bResizePropAllowed=true;
+ m_bRotateFreeAllowed=true;
+ m_bRotate90Allowed =true;
+ m_bMirrorFreeAllowed=true;
+ m_bMirror45Allowed =true;
+ m_bMirror90Allowed =true;
+ m_bShearAllowed =true;
+ m_bEdgeRadiusAllowed=false;
+ m_bContortionPossible=true;
+ m_bCanConvToContour = true;
+
+ // these ones are only allowed when single object is selected
+ m_bTransparenceAllowed = (nMarkCount == 1);
+ m_bGradientAllowed = (nMarkCount == 1);
+ m_bCropAllowed = (nMarkCount == 1);
+ if(m_bGradientAllowed)
+ {
+ // gradient depends on fill style
+ const SdrMark* pM = GetSdrMarkByIndex(0);
+ const SdrObject* pObj = pM->GetMarkedSdrObj();
+
+ // may be group object, so get merged ItemSet
+ const SfxItemSet& rSet = pObj->GetMergedItemSet();
+ SfxItemState eState = rSet.GetItemState(XATTR_FILLSTYLE, false);
+
+ if(SfxItemState::DONTCARE != eState)
+ {
+ // If state is not DONTCARE, test the item
+ drawing::FillStyle eFillStyle = rSet.Get(XATTR_FILLSTYLE).GetValue();
+
+ if(eFillStyle != drawing::FillStyle_GRADIENT)
+ {
+ m_bGradientAllowed = false;
+ }
+ }
+ }
+
+ bool bNoMovRotFound=false;
+ const SdrPageView* pPV0=nullptr;
+
+ for (size_t nm=0; nm<nMarkCount; ++nm) {
+ const SdrMark* pM=GetSdrMarkByIndex(nm);
+ const SdrObject* pObj=pM->GetMarkedSdrObj();
+ const SdrPageView* pPV=pM->GetPageView();
+ if (pPV!=pPV0) {
+ if (pPV->IsReadOnly()) m_bReadOnly=true;
+ pPV0=pPV;
+ }
+
+ SdrObjTransformInfoRec aInfo;
+ pObj->TakeObjInfo(aInfo);
+ bool bMovPrt=pObj->IsMoveProtect();
+ bool bSizPrt=pObj->IsResizeProtect();
+ if (!bMovPrt && aInfo.bMoveAllowed) nMovableCount++; // count MovableObjs
+ if (bMovPrt) m_bMoveProtect=true;
+ if (bSizPrt) m_bResizeProtect=true;
+
+ // not allowed when not allowed at one object
+ if(!aInfo.bTransparenceAllowed)
+ m_bTransparenceAllowed = false;
+
+ // If one of these can't do something, none can
+ if (!aInfo.bMoveAllowed ) m_bMoveAllowed =false;
+ if (!aInfo.bResizeFreeAllowed) m_bResizeFreeAllowed=false;
+ if (!aInfo.bResizePropAllowed) m_bResizePropAllowed=false;
+ if (!aInfo.bRotateFreeAllowed) m_bRotateFreeAllowed=false;
+ if (!aInfo.bRotate90Allowed ) m_bRotate90Allowed =false;
+ if (!aInfo.bMirrorFreeAllowed) m_bMirrorFreeAllowed=false;
+ if (!aInfo.bMirror45Allowed ) m_bMirror45Allowed =false;
+ if (!aInfo.bMirror90Allowed ) m_bMirror90Allowed =false;
+ if (!aInfo.bShearAllowed ) m_bShearAllowed =false;
+ if (aInfo.bEdgeRadiusAllowed) m_bEdgeRadiusAllowed=true;
+ if (aInfo.bNoContortion ) m_bContortionPossible=false;
+ // For Crook with Contortion: all objects have to be
+ // Movable and Rotatable, except for a maximum of 1 of them
+ if (!m_bMoreThanOneNoMovRot) {
+ if (!aInfo.bMoveAllowed || !aInfo.bResizeFreeAllowed) {
+ m_bMoreThanOneNoMovRot=bNoMovRotFound;
+ bNoMovRotFound=true;
+ }
+ }
+
+ // Must be resizable to allow cropping
+ if (!aInfo.bResizeFreeAllowed && !aInfo.bResizePropAllowed)
+ m_bCropAllowed = false;
+
+ // if one member cannot be converted, no conversion is possible
+ if(!aInfo.bCanConvToContour)
+ m_bCanConvToContour = false;
+
+ // Ungroup
+ if (!m_bUnGroupPossible) m_bUnGroupPossible=pObj->GetSubList()!=nullptr;
+ // ConvertToCurve: If at least one can be converted, that is fine.
+ if (aInfo.bCanConvToPath ) m_bCanConvToPath =true;
+ if (aInfo.bCanConvToPoly ) m_bCanConvToPoly =true;
+
+ // Combine/Dismantle
+ if(m_bCombinePossible)
+ {
+ m_bCombinePossible = ImpCanConvertForCombine(pObj);
+ m_bCombineNoPolyPolyPossible = m_bCombinePossible;
+ }
+
+ if (!m_bDismantlePossible) m_bDismantlePossible = ImpCanDismantle(pObj, false);
+ if (!m_bDismantleMakeLinesPossible) m_bDismantleMakeLinesPossible = ImpCanDismantle(pObj, true);
+ // check OrthoDesiredOnMarked
+ if (!m_bOrthoDesiredOnMarked && !aInfo.bNoOrthoDesired) m_bOrthoDesiredOnMarked=true;
+ // check ImportMtf
+
+ if (!m_bImportMtfPossible)
+ {
+ const SdrGrafObj* pSdrGrafObj = dynamic_cast< const SdrGrafObj* >(pObj);
+ if (pSdrGrafObj != nullptr)
+ {
+ if ((pSdrGrafObj->HasGDIMetaFile() && !pSdrGrafObj->IsEPS()) ||
+ pSdrGrafObj->isEmbeddedVectorGraphicData())
+ {
+ m_bImportMtfPossible = true;
+ }
+ }
+
+ const SdrOle2Obj* pSdrOle2Obj = dynamic_cast< const SdrOle2Obj* >(pObj);
+ if (pSdrOle2Obj)
+ {
+ m_bImportMtfPossible = pSdrOle2Obj->GetObjRef().is();
+ }
+ }
+ }
+
+ m_bOneOrMoreMovable=nMovableCount!=0;
+ m_bGrpEnterPossible=m_bUnGroupPossible;
+ }
+ ImpCheckToTopBtmPossible();
+ static_cast<SdrPolyEditView*>(this)->ImpCheckPolyPossibilities();
+ m_bPossibilitiesDirty=false;
+
+ if (m_bReadOnly) {
+ bool bTemp=m_bGrpEnterPossible;
+ ImpResetPossibilityFlags();
+ m_bReadOnly=true;
+ m_bGrpEnterPossible=bTemp;
+ }
+ if (!m_bMoveAllowed) return;
+
+ // Don't allow moving glued connectors.
+ // Currently only implemented for single selection.
+ if (nMarkCount==1) {
+ SdrObject* pObj=GetMarkedObjectByIndex(0);
+ SdrEdgeObj* pEdge=dynamic_cast<SdrEdgeObj*>( pObj );
+ if (pEdge!=nullptr) {
+ SdrObject* pNode1=pEdge->GetConnectedNode(true);
+ SdrObject* pNode2=pEdge->GetConnectedNode(false);
+ if (pNode1!=nullptr || pNode2!=nullptr) m_bMoveAllowed=false;
+ }
+ }
+
+ // Don't allow enter Diagrams
+ if (1 == nMarkCount && m_bGrpEnterPossible)
+ {
+ SdrObject* pCandidate(GetMarkedObjectByIndex(0));
+
+ if(nullptr != pCandidate && pCandidate->isDiagram())
+ m_bGrpEnterPossible = false;
+ }
+}
+
+
+void SdrEditView::ForceMarkedObjToAnotherPage()
+{
+ bool bFlg=false;
+ for (size_t nm=0; nm<GetMarkedObjectCount(); ++nm) {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ tools::Rectangle aObjRect(pObj->GetCurrentBoundRect());
+ tools::Rectangle aPgRect(pM->GetPageView()->GetPageRect());
+ if (!aObjRect.Overlaps(aPgRect)) {
+ bool bFnd=false;
+ SdrPageView* pPV = GetSdrPageView();
+
+ if(pPV)
+ {
+ bFnd = aObjRect.Overlaps(pPV->GetPageRect());
+ }
+
+ if(bFnd)
+ {
+ pM->GetPageView()->GetObjList()->RemoveObject(pObj->GetOrdNum());
+ pPV->GetObjList()->InsertObject(pObj, SAL_MAX_SIZE);
+ pM->SetPageView(pPV);
+ InvalidateAllWin(aObjRect);
+ bFlg=true;
+ }
+ }
+ }
+ if (bFlg) {
+ MarkListHasChanged();
+ }
+}
+
+std::vector<SdrObject*> SdrEditView::DeleteMarkedList(SdrMarkList const& rMark)
+{
+ std::vector<SdrObject*> ret;
+ if (rMark.GetMarkCount()!=0)
+ {
+ rMark.ForceSort();
+
+ const bool bUndo = IsUndoEnabled();
+ if( bUndo )
+ BegUndo();
+ const size_t nMarkCount(rMark.GetMarkCount());
+
+ if(nMarkCount)
+ {
+ std::vector< E3DModifySceneSnapRectUpdater* > aUpdaters;
+
+ if( bUndo )
+ {
+ for(size_t nm = nMarkCount; nm > 0;)
+ {
+ --nm;
+ SdrMark* pM = rMark.GetMark(nm);
+ SdrObject* pObj = pM->GetMarkedSdrObj();
+
+ // extra undo actions for changed connector which now may hold its laid out path (SJ)
+ AddUndoActions(CreateConnectorUndo( *pObj ));
+
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoDeleteObject(*pObj));
+ }
+ }
+
+ // make sure, OrderNums are correct:
+ rMark.GetMark(0)->GetMarkedSdrObj()->GetOrdNum();
+
+ for(size_t nm = nMarkCount; nm > 0;)
+ {
+ --nm;
+ SdrMark* pM = rMark.GetMark(nm);
+ SdrObject* pObj = pM->GetMarkedSdrObj();
+ SdrObjList* pOL = pObj->getParentSdrObjListFromSdrObject();
+ const size_t nOrdNum(pObj->GetOrdNumDirect());
+
+ bool bIs3D = dynamic_cast< E3dObject* >(pObj);
+ // set up a scene updater if object is a 3d object
+ if(bIs3D)
+ {
+ aUpdaters.push_back(new E3DModifySceneSnapRectUpdater(pObj));
+ }
+
+ pOL->RemoveObject(nOrdNum);
+
+ if( !bUndo )
+ {
+ // tdf#108863 don't delete objects before EndUndo()
+ ret.push_back(pObj);
+ }
+ }
+
+ // fire scene updaters
+ while(!aUpdaters.empty())
+ {
+ delete aUpdaters.back();
+ aUpdaters.pop_back();
+ }
+ }
+
+ if( bUndo )
+ EndUndo();
+ }
+ return ret;
+}
+
+static void lcl_LazyDelete(std::vector<SdrObject*> & rLazyDelete)
+{
+ // now delete removed scene objects
+ while (!rLazyDelete.empty())
+ {
+ SdrObject::Free( rLazyDelete.back() );
+ rLazyDelete.pop_back();
+ }
+}
+
+void SdrEditView::DeleteMarkedObj()
+{
+ // #i110981# return when nothing is to be done at all
+ if(!GetMarkedObjectCount())
+ {
+ return;
+ }
+
+ // moved breaking action and undo start outside loop
+ BrkAction();
+ BegUndo(SvxResId(STR_EditDelete),GetDescriptionOfMarkedObjects(),SdrRepeatFunc::Delete);
+
+ std::vector<SdrObject*> lazyDeleteObjects;
+ // remove as long as something is selected. This allows to schedule objects for
+ // removal for a next run as needed
+ while(GetMarkedObjectCount())
+ {
+ // vector to remember the parents which may be empty after object removal
+ std::vector< SdrObject* > aParents;
+
+ {
+ const SdrMarkList& rMarkList = GetMarkedObjectList();
+ const size_t nCount(rMarkList.GetMarkCount());
+
+ for(size_t a = 0; a < nCount; ++a)
+ {
+ // in the first run, add all found parents, but only once
+ SdrMark* pMark(rMarkList.GetMark(a));
+ SdrObject* pObject(pMark->GetMarkedSdrObj());
+ SdrObject* pParent(pObject->getParentSdrObjectFromSdrObject());
+
+ if(pParent)
+ {
+ if(!aParents.empty())
+ {
+ std::vector< SdrObject* >::iterator aFindResult =
+ std::find(aParents.begin(), aParents.end(), pParent);
+
+ if(aFindResult == aParents.end())
+ {
+ aParents.push_back(pParent);
+ }
+ }
+ else
+ {
+ aParents.push_back(pParent);
+ }
+ }
+ }
+
+ if(!aParents.empty())
+ {
+ // in a 2nd run, remove all objects which may already be scheduled for
+ // removal. I am not sure if this can happen, but theoretically
+ // a to-be-removed object may already be the group/3DScene itself
+ for(size_t a = 0; a < nCount; ++a)
+ {
+ SdrMark* pMark = rMarkList.GetMark(a);
+ SdrObject* pObject = pMark->GetMarkedSdrObj();
+
+ std::vector< SdrObject* >::iterator aFindResult =
+ std::find(aParents.begin(), aParents.end(), pObject);
+
+ if(aFindResult != aParents.end())
+ {
+ aParents.erase(aFindResult);
+ }
+ }
+ }
+ }
+
+ // original stuff: remove selected objects. Handle clear will
+ // do something only once
+ auto temp(DeleteMarkedList(GetMarkedObjectList()));
+ for (auto p : temp)
+ {
+ lazyDeleteObjects.push_back(p);
+ }
+ GetMarkedObjectListWriteAccess().Clear();
+ maHdlList.Clear();
+
+ while(!aParents.empty() && !GetMarkedObjectCount())
+ {
+ // iterate over remembered parents
+ SdrObject* pParent = aParents.back();
+ aParents.pop_back();
+
+ if(pParent->GetSubList() && 0 == pParent->GetSubList()->GetObjCount())
+ {
+ // we detected an empty parent, a candidate to leave group/3DScene
+ // if entered
+ if(GetSdrPageView()->GetCurrentGroup()
+ && GetSdrPageView()->GetCurrentGroup() == pParent)
+ {
+ GetSdrPageView()->LeaveOneGroup();
+ }
+
+ // schedule empty parent for removal
+ GetMarkedObjectListWriteAccess().InsertEntry(
+ SdrMark(pParent, GetSdrPageView()));
+ }
+ }
+ }
+
+ // end undo and change messaging moved at the end
+ EndUndo();
+ MarkListHasChanged();
+
+ lcl_LazyDelete(lazyDeleteObjects);
+}
+
+void SdrEditView::CopyMarkedObj()
+{
+ SortMarkedObjects();
+
+ SdrMarkList aSourceObjectsForCopy(GetMarkedObjectList());
+ // The following loop is used instead of MarkList::Merge(), to be
+ // able to flag the MarkEntries.
+ const size_t nEdgeCnt = GetEdgesOfMarkedNodes().GetMarkCount();
+ for (size_t nEdgeNum=0; nEdgeNum<nEdgeCnt; ++nEdgeNum) {
+ SdrMark aM(*GetEdgesOfMarkedNodes().GetMark(nEdgeNum));
+ aM.SetUser(1);
+ aSourceObjectsForCopy.InsertEntry(aM);
+ }
+ aSourceObjectsForCopy.ForceSort();
+
+ // #i13033#
+ // New mechanism to re-create the connections of cloned connectors
+ CloneList aCloneList;
+
+ const bool bUndo = IsUndoEnabled();
+
+ GetMarkedObjectListWriteAccess().Clear();
+ size_t nCloneErrCnt=0;
+ std::unordered_set<rtl::OUString> aNameSet;
+ const size_t nMarkCount=aSourceObjectsForCopy.GetMarkCount();
+ for (size_t nm=0; nm<nMarkCount; ++nm) {
+ SdrMark* pM=aSourceObjectsForCopy.GetMark(nm);
+ SdrObject* pSource(pM->GetMarkedSdrObj());
+ SdrObject* pO(pSource->CloneSdrObject(pSource->getSdrModelFromSdrObject()));
+ if (pO!=nullptr) {
+ pM->GetPageView()->GetObjList()->InsertObjectThenMakeNameUnique(pO, aNameSet);
+
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoCopyObject(*pO));
+
+ SdrMark aME(*pM);
+ aME.SetMarkedSdrObj(pO);
+ aCloneList.AddPair(pM->GetMarkedSdrObj(), pO);
+
+ if (pM->GetUser()==0)
+ {
+ // otherwise it is only an Edge we have to copy as well
+ GetMarkedObjectListWriteAccess().InsertEntry(aME);
+ }
+ } else {
+ nCloneErrCnt++;
+ }
+ }
+
+ // #i13033#
+ // New mechanism to re-create the connections of cloned connectors
+ aCloneList.CopyConnections();
+
+ if(nCloneErrCnt)
+ {
+#ifdef DBG_UTIL
+ OStringBuffer aStr("SdrEditView::CopyMarkedObj(): Error when cloning ");
+
+ if(nCloneErrCnt == 1)
+ {
+ aStr.append("a drawing object.");
+ }
+ else
+ {
+ aStr.append(static_cast<sal_Int32>(nCloneErrCnt));
+ aStr.append(" drawing objects.");
+ }
+
+ aStr.append(" This object's/These objects's connections will not be copied.");
+ OSL_FAIL(aStr.getStr());
+#endif
+ }
+ MarkListHasChanged();
+}
+
+
+bool SdrEditView::InsertObjectAtView(SdrObject* pObj, SdrPageView& rPV, SdrInsertFlags nOptions)
+{
+ if (nOptions & SdrInsertFlags::SETDEFLAYER) {
+ SdrLayerID nLayer=rPV.GetPage()->GetLayerAdmin().GetLayerID(maActualLayer);
+ if (nLayer==SDRLAYER_NOTFOUND) nLayer=SdrLayerID(0);
+ if (rPV.GetLockedLayers().IsSet(nLayer) || !rPV.GetVisibleLayers().IsSet(nLayer)) {
+ SdrObject::Free( pObj ); // Layer locked or invisible
+ return false;
+ }
+ pObj->NbcSetLayer(nLayer);
+ }
+ if (nOptions & SdrInsertFlags::SETDEFATTR) {
+ if (mpDefaultStyleSheet!=nullptr) pObj->NbcSetStyleSheet(mpDefaultStyleSheet, false);
+ pObj->SetMergedItemSet(maDefaultAttr);
+ }
+ if (!pObj->IsInserted()) {
+ rPV.GetObjList()->InsertObject(pObj, SAL_MAX_SIZE);
+ }
+ if( IsUndoEnabled())
+ {
+ bool bDontDeleteReally = true;
+ EndTextEditCurrentView(bDontDeleteReally);
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pObj));
+ }
+
+ css::uno::Reference<lang::XServiceInfo> xServices(GetModel()->getUnoModel(),
+ css::uno::UNO_QUERY);
+ if (xServices.is() && (xServices->supportsService("com.sun.star.sheet.SpreadsheetDocument") ||
+ xServices->supportsService("com.sun.star.text.TextDocument")))
+ {
+ const bool bUndo(IsUndoEnabled());
+ GetModel()->EnableUndo(false);
+ pObj->MakeNameUnique();
+ GetModel()->EnableUndo(bUndo);
+ }
+
+ if (!(nOptions & SdrInsertFlags::DONTMARK)) {
+ if (!(nOptions & SdrInsertFlags::ADDMARK)) UnmarkAllObj();
+ MarkObj(pObj,&rPV);
+ }
+ return true;
+}
+
+void SdrEditView::ReplaceObjectAtView(SdrObject* pOldObj, SdrPageView& rPV, SdrObject* pNewObj, bool bMark)
+{
+ if(IsTextEdit())
+ {
+#ifdef DBG_UTIL
+ if(auto pTextObj = dynamic_cast< SdrTextObj* >(pOldObj))
+ if (pTextObj->IsTextEditActive())
+ OSL_ENSURE(false, "OldObject is in TextEdit mode, this has to be ended before replacing it using SdrEndTextEdit (!)");
+
+ if(auto pTextObj = dynamic_cast< SdrTextObj* >(pNewObj))
+ if (pTextObj->IsTextEditActive())
+ OSL_ENSURE(false, "NewObject is in TextEdit mode, this has to be ended before replacing it using SdrEndTextEdit (!)");
+#endif
+
+ // #i123468# emergency repair situation, needs to cast up to a class derived from
+ // this one; (aw080 has a mechanism for that and the view hierarchy is secured to
+ // always be a SdrView)
+ SdrView *pSdrView = dynamic_cast<SdrView*>(this);
+ if (pSdrView)
+ pSdrView->SdrEndTextEdit();
+ }
+
+ SdrObjList* pOL=pOldObj->getParentSdrObjListFromSdrObject();
+ const bool bUndo = IsUndoEnabled();
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoReplaceObject(*pOldObj,*pNewObj));
+
+ if( IsObjMarked( pOldObj ) )
+ MarkObj( pOldObj, &rPV, true /*unmark!*/ );
+
+ pOL->ReplaceObject(pNewObj,pOldObj->GetOrdNum());
+
+ if( !bUndo )
+ SdrObject::Free( pOldObj );
+
+ if (bMark) MarkObj(pNewObj,&rPV);
+}
+
+
+bool SdrEditView::IsUndoEnabled() const
+{
+ return mpModel->IsUndoEnabled();
+}
+
+void SdrEditView::EndTextEditAllViews() const
+{
+ size_t nViews = mpModel->GetListenerCount();
+ for (size_t nView = 0; nView < nViews; ++nView)
+ {
+ SdrObjEditView* pView = dynamic_cast<SdrObjEditView*>(mpModel->GetListener(nView));
+ if (!pView)
+ continue;
+
+ if (pView->IsTextEdit())
+ pView->SdrEndTextEdit();
+ }
+}
+
+void SdrEditView::EndTextEditCurrentView(bool bDontDeleteReally)
+{
+ if (IsTextEdit())
+ {
+ SdrView* pSdrView = dynamic_cast<SdrView*>(this);
+ if (pSdrView)
+ pSdrView->SdrEndTextEdit(bDontDeleteReally);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdedtv1.cxx b/svx/source/svdraw/svdedtv1.cxx
new file mode 100644
index 000000000..371584bd8
--- /dev/null
+++ b/svx/source/svdraw/svdedtv1.cxx
@@ -0,0 +1,2019 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <editeng/eeitem.hxx>
+#include <math.h>
+#include <svl/itemiter.hxx>
+#include <svl/whiter.hxx>
+#include <tools/bigint.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+
+#include <getallcharpropids.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/svditer.hxx>
+#include <svx/strings.hrc>
+
+#include <AffineMatrixItem.hxx>
+#include <svx/e3dsceneupdater.hxx>
+#include <svx/obj3d.hxx>
+#include <svx/rectenum.hxx>
+#include <svx/sdr/contact/viewcontact.hxx>
+#include <svx/sdooitm.hxx>
+#include <svx/sderitm.hxx>
+#include <svx/sdtagitm.hxx>
+#include <svx/svdedtv.hxx>
+#include <svx/svdetc.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdtrans.hxx>
+#include <svx/svdundo.hxx>
+#include <svx/svxids.hrc>
+#include <sxallitm.hxx>
+#include <sxmovitm.hxx>
+#include <sxreaitm.hxx>
+#include <sxreoitm.hxx>
+#include <sxroaitm.hxx>
+#include <sxrooitm.hxx>
+#include <sxsalitm.hxx>
+#include <sxsoitm.hxx>
+#include <sxtraitm.hxx>
+#include <svx/xlnedwit.hxx>
+#include <svx/xlnstwit.hxx>
+#include <svx/xlnwtit.hxx>
+#include <svx/xlnclit.hxx>
+#include <svx/xflclit.hxx>
+#include <svx/xlntrit.hxx>
+#include <svx/xfltrit.hxx>
+#include <svx/sdprcitm.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <rtl/ustring.hxx>
+#include <sfx2/viewsh.hxx>
+#include <comphelper/lok.hxx>
+#include <osl/diagnose.h>
+
+// EditView
+
+
+void SdrEditView::SetMarkedObjRect(const tools::Rectangle& rRect)
+{
+ DBG_ASSERT(!rRect.IsEmpty(),"SetMarkedObjRect() with an empty Rect does not make sense.");
+ if (rRect.IsEmpty()) return;
+ const size_t nCount=GetMarkedObjectCount();
+ if (nCount==0) return;
+ tools::Rectangle aR0(GetMarkedObjRect());
+ DBG_ASSERT(!aR0.IsEmpty(),"SetMarkedObjRect(): GetMarkedObjRect() is empty.");
+ if (aR0.IsEmpty()) return;
+ tools::Long x0=aR0.Left();
+ tools::Long y0=aR0.Top();
+ tools::Long w0=aR0.Right()-x0;
+ tools::Long h0=aR0.Bottom()-y0;
+ tools::Long x1=rRect.Left();
+ tools::Long y1=rRect.Top();
+ tools::Long w1=rRect.Right()-x1;
+ tools::Long h1=rRect.Bottom()-y1;
+
+ const bool bUndo = IsUndoEnabled();
+ if( bUndo )
+ {
+ EndTextEditCurrentView();
+ BegUndo(ImpGetDescriptionString(STR_EditPosSize));
+ }
+
+ for (size_t nm=0; nm<nCount; ++nm)
+ {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pO=pM->GetMarkedSdrObj();
+ if( bUndo )
+ AddUndo( GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pO));
+
+ tools::Rectangle aR1(pO->GetSnapRect());
+ if (!aR1.IsEmpty())
+ {
+ if (aR1==aR0)
+ {
+ aR1=rRect;
+ }
+ else
+ { // transform aR1 to aR0 after rRect
+ aR1.Move(-x0,-y0);
+ BigInt l(aR1.Left());
+ BigInt r(aR1.Right());
+ BigInt t(aR1.Top());
+ BigInt b(aR1.Bottom());
+ if (w0!=0) {
+ l*=w1; l/=w0;
+ r*=w1; r/=w0;
+ } else {
+ l=0; r=w1;
+ }
+ if (h0!=0) {
+ t*=h1; t/=h0;
+ b*=h1; b/=h0;
+ } else {
+ t=0; b=h1;
+ }
+ aR1.SetLeft(tools::Long(l) );
+ aR1.SetRight(tools::Long(r) );
+ aR1.SetTop(tools::Long(t) );
+ aR1.SetBottom(tools::Long(b) );
+ aR1.Move(x1,y1);
+ }
+ pO->SetSnapRect(aR1);
+ } else {
+ OSL_FAIL("SetMarkedObjRect(): pObj->GetSnapRect() returns empty Rect");
+ }
+ }
+ if( bUndo )
+ EndUndo();
+}
+
+std::vector< std::unique_ptr<SdrUndoAction> > SdrEditView::CreateConnectorUndo( const SdrObject& rO )
+{
+ std::vector< std::unique_ptr<SdrUndoAction> > vUndoActions;
+
+ if ( rO.GetBroadcaster() )
+ {
+ const SdrPage* pPage = rO.getSdrPageFromSdrObject();
+ if ( pPage )
+ {
+ SdrObjListIter aIter(pPage, SdrIterMode::DeepWithGroups);
+ while( aIter.IsMore() )
+ {
+ SdrObject* pPartObj = aIter.Next();
+ if ( dynamic_cast<const SdrEdgeObj*>( pPartObj) != nullptr )
+ {
+ if ( ( pPartObj->GetConnectedNode( false ) == &rO ) ||
+ ( pPartObj->GetConnectedNode( true ) == &rO ) )
+ {
+ vUndoActions.push_back( GetModel()->GetSdrUndoFactory().CreateUndoGeoObject( *pPartObj ) );
+ }
+ }
+ }
+ }
+ }
+ return vUndoActions;
+}
+
+void SdrEditView::AddUndoActions( std::vector< std::unique_ptr<SdrUndoAction> > aUndoActions )
+{
+ for (auto & rAction : aUndoActions)
+ AddUndo( std::move(rAction) );
+}
+
+void SdrEditView::MoveMarkedObj(const Size& rSiz, bool bCopy)
+{
+ const bool bUndo = IsUndoEnabled();
+
+ if( bUndo )
+ {
+ EndTextEditCurrentView();
+ OUString aStr(SvxResId(STR_EditMove));
+ if (bCopy)
+ aStr += SvxResId(STR_EditWithCopy);
+ // needs its own UndoGroup because of its parameters
+ BegUndo(aStr,GetDescriptionOfMarkedObjects(),SdrRepeatFunc::Move);
+ }
+
+ if (bCopy)
+ CopyMarkedObj();
+
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nm=0; nm<nMarkCount; ++nm)
+ {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pO=pM->GetMarkedSdrObj();
+ if( bUndo )
+ {
+ AddUndoActions( CreateConnectorUndo( *pO ) );
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoMoveObject(*pO,rSiz));
+ }
+ pO->Move(rSiz);
+ }
+
+ if( bUndo )
+ EndUndo();
+}
+
+void SdrEditView::ResizeMarkedObj(const Point& rRef, const Fraction& xFact, const Fraction& yFact, bool bCopy)
+{
+ const bool bUndo = IsUndoEnabled();
+ if( bUndo )
+ {
+ EndTextEditCurrentView();
+ OUString aStr {ImpGetDescriptionString(STR_EditResize)};
+ if (bCopy)
+ aStr+=SvxResId(STR_EditWithCopy);
+ BegUndo(aStr);
+ }
+
+ if (bCopy)
+ CopyMarkedObj();
+
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nm=0; nm<nMarkCount; ++nm)
+ {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pO=pM->GetMarkedSdrObj();
+ if( bUndo )
+ {
+ AddUndoActions( CreateConnectorUndo( *pO ) );
+ AddUndo( GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pO));
+ }
+ pO->Resize(rRef,xFact,yFact);
+ }
+
+ if( bUndo )
+ EndUndo();
+}
+void SdrEditView::ResizeMultMarkedObj(const Point& rRef,
+ const Fraction& xFact,
+ const Fraction& yFact,
+ const bool bWdh,
+ const bool bHgt)
+{
+ const bool bUndo = IsUndoEnabled();
+ if( bUndo )
+ {
+ EndTextEditCurrentView();
+ BegUndo(ImpGetDescriptionString(STR_EditResize));
+ }
+
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nm=0; nm<nMarkCount; ++nm)
+ {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pO=pM->GetMarkedSdrObj();
+ if( bUndo )
+ {
+ AddUndoActions( CreateConnectorUndo( *pO ) );
+ AddUndo( GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pO));
+ }
+
+ Fraction aFrac(1,1);
+ if (bWdh && xFact.IsValid() && bHgt && yFact.IsValid())
+ pO->Resize(rRef, xFact, yFact);
+ else if (bWdh && xFact.IsValid())
+ pO->Resize(rRef, xFact, aFrac);
+ else if (bHgt && yFact.IsValid())
+ pO->Resize(rRef, aFrac, yFact);
+ }
+ if( bUndo )
+ EndUndo();
+}
+
+Degree100 SdrEditView::GetMarkedObjRotate() const
+{
+ Degree100 nRetval(0);
+
+ if(GetMarkedObjectCount())
+ {
+ SdrMark* pM = GetSdrMarkByIndex(0);
+ SdrObject* pO = pM->GetMarkedSdrObj();
+
+ nRetval = pO->GetRotateAngle();
+ }
+
+ return nRetval;
+}
+
+void SdrEditView::RotateMarkedObj(const Point& rRef, Degree100 nAngle, bool bCopy)
+{
+ const bool bUndo = IsUndoEnabled();
+ if( bUndo )
+ {
+ EndTextEditCurrentView();
+ OUString aStr {ImpGetDescriptionString(STR_EditRotate)};
+ if (bCopy) aStr+=SvxResId(STR_EditWithCopy);
+ BegUndo(aStr);
+ }
+
+ if (bCopy)
+ CopyMarkedObj();
+
+ double nSin = sin(toRadians(nAngle));
+ double nCos = cos(toRadians(nAngle));
+ const size_t nMarkCount(GetMarkedObjectCount());
+
+ if(nMarkCount)
+ {
+ std::vector< E3DModifySceneSnapRectUpdater* > aUpdaters;
+
+ for(size_t nm = 0; nm < nMarkCount; ++nm)
+ {
+ SdrMark* pM = GetSdrMarkByIndex(nm);
+ SdrObject* pO = pM->GetMarkedSdrObj();
+
+ if( bUndo )
+ {
+ // extra undo actions for changed connector which now may hold its laid out path (SJ)
+ AddUndoActions( CreateConnectorUndo( *pO ) );
+
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pO));
+ }
+
+ // set up a scene updater if object is a 3d object
+ if(dynamic_cast< E3dObject* >(pO))
+ {
+ aUpdaters.push_back(new E3DModifySceneSnapRectUpdater(pO));
+ }
+
+ pO->Rotate(rRef,nAngle,nSin,nCos);
+ }
+
+ // fire scene updaters
+ while(!aUpdaters.empty())
+ {
+ delete aUpdaters.back();
+ aUpdaters.pop_back();
+ }
+ }
+
+ if( bUndo )
+ EndUndo();
+}
+
+void SdrEditView::MirrorMarkedObj(const Point& rRef1, const Point& rRef2, bool bCopy)
+{
+ const bool bUndo = IsUndoEnabled();
+
+ if( bUndo )
+ {
+ EndTextEditCurrentView();
+ OUString aStr;
+ Point aDif(rRef2-rRef1);
+ if (aDif.X()==0)
+ aStr = ImpGetDescriptionString(STR_EditMirrorHori);
+ else if (aDif.Y()==0)
+ aStr = ImpGetDescriptionString(STR_EditMirrorVert);
+ else if (std::abs(aDif.X()) == std::abs(aDif.Y()))
+ aStr = ImpGetDescriptionString(STR_EditMirrorDiag);
+ else
+ aStr = ImpGetDescriptionString(STR_EditMirrorFree);
+ if (bCopy) aStr+=SvxResId(STR_EditWithCopy);
+ BegUndo(aStr);
+ }
+
+ if (bCopy)
+ CopyMarkedObj();
+
+ const size_t nMarkCount(GetMarkedObjectCount());
+
+ if(nMarkCount)
+ {
+ std::vector< E3DModifySceneSnapRectUpdater* > aUpdaters;
+
+ for(size_t nm = 0; nm < nMarkCount; ++nm)
+ {
+ SdrMark* pM = GetSdrMarkByIndex(nm);
+ SdrObject* pO = pM->GetMarkedSdrObj();
+
+ if( bUndo )
+ {
+ // extra undo actions for changed connector which now may hold its laid out path (SJ)
+ AddUndoActions( CreateConnectorUndo( *pO ) );
+
+ AddUndo( GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pO));
+ }
+
+ // set up a scene updater if object is a 3d object
+ if(dynamic_cast< E3dObject* >(pO))
+ {
+ aUpdaters.push_back(new E3DModifySceneSnapRectUpdater(pO));
+ }
+
+ pO->Mirror(rRef1,rRef2);
+ }
+
+ // fire scene updaters
+ while(!aUpdaters.empty())
+ {
+ delete aUpdaters.back();
+ aUpdaters.pop_back();
+ }
+ }
+
+ if( bUndo )
+ EndUndo();
+}
+
+void SdrEditView::MirrorMarkedObjHorizontal()
+{
+ Point aCenter(GetMarkedObjRect().Center());
+ Point aPt2(aCenter);
+ aPt2.AdjustY( 1 );
+ MirrorMarkedObj(aCenter,aPt2);
+}
+
+void SdrEditView::MirrorMarkedObjVertical()
+{
+ Point aCenter(GetMarkedObjRect().Center());
+ Point aPt2(aCenter);
+ aPt2.AdjustX( 1 );
+ MirrorMarkedObj(aCenter,aPt2);
+}
+
+Degree100 SdrEditView::GetMarkedObjShear() const
+{
+ bool b1st=true;
+ bool bOk=true;
+ Degree100 nAngle(0);
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nm=0; nm<nMarkCount && bOk; ++nm) {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pO=pM->GetMarkedSdrObj();
+ Degree100 nAngle2=pO->GetShearAngle();
+ if (b1st) nAngle=nAngle2;
+ else if (nAngle2!=nAngle) bOk=false;
+ b1st=false;
+ }
+ if (nAngle>SDRMAXSHEAR) nAngle=SDRMAXSHEAR;
+ if (nAngle<-SDRMAXSHEAR) nAngle=-SDRMAXSHEAR;
+ if (!bOk) nAngle=0_deg100;
+ return nAngle;
+}
+
+void SdrEditView::ShearMarkedObj(const Point& rRef, Degree100 nAngle, bool bVShear, bool bCopy)
+{
+ const bool bUndo = IsUndoEnabled();
+
+ if( bUndo )
+ {
+ EndTextEditCurrentView();
+ OUString aStr {ImpGetDescriptionString(STR_EditShear)};
+ if (bCopy)
+ aStr+=SvxResId(STR_EditWithCopy);
+ BegUndo(aStr);
+ }
+
+ if (bCopy)
+ CopyMarkedObj();
+
+ double nTan = tan(toRadians(nAngle));
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nm=0; nm<nMarkCount; ++nm)
+ {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pO=pM->GetMarkedSdrObj();
+ if( bUndo )
+ {
+ AddUndoActions( CreateConnectorUndo( *pO ) );
+ AddUndo( GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pO));
+ }
+ pO->Shear(rRef,nAngle,nTan,bVShear);
+ }
+
+ if( bUndo )
+ EndUndo();
+}
+
+void SdrEditView::ImpCrookObj(SdrObject* pO, const Point& rRef, const Point& rRad,
+ SdrCrookMode eMode, bool bVertical, bool bNoContortion, bool bRotate, const tools::Rectangle& rMarkRect)
+{
+ SdrPathObj* pPath=dynamic_cast<SdrPathObj*>( pO );
+ bool bDone = false;
+
+ if(pPath!=nullptr && !bNoContortion)
+ {
+ XPolyPolygon aXPP(pPath->GetPathPoly());
+ switch (eMode) {
+ case SdrCrookMode::Rotate : CrookRotatePoly (aXPP,rRef,rRad,bVertical); break;
+ case SdrCrookMode::Slant : CrookSlantPoly (aXPP,rRef,rRad,bVertical); break;
+ case SdrCrookMode::Stretch: CrookStretchPoly(aXPP,rRef,rRad,bVertical,rMarkRect); break;
+ } // switch
+ pPath->SetPathPoly(aXPP.getB2DPolyPolygon());
+ bDone = true;
+ }
+
+ if(!bDone && !pPath && pO->IsPolyObj() && 0 != pO->GetPointCount())
+ {
+ // for PolyObj's, but NOT for SdrPathObj's, e.g. the measurement object
+ sal_uInt32 nPointCount(pO->GetPointCount());
+ XPolygon aXP(static_cast<sal_uInt16>(nPointCount));
+ sal_uInt32 nPtNum;
+
+ for(nPtNum = 0; nPtNum < nPointCount; nPtNum++)
+ {
+ Point aPt(pO->GetPoint(nPtNum));
+ aXP[static_cast<sal_uInt16>(nPtNum)]=aPt;
+ }
+
+ switch (eMode)
+ {
+ case SdrCrookMode::Rotate : CrookRotatePoly (aXP,rRef,rRad,bVertical); break;
+ case SdrCrookMode::Slant : CrookSlantPoly (aXP,rRef,rRad,bVertical); break;
+ case SdrCrookMode::Stretch: CrookStretchPoly(aXP,rRef,rRad,bVertical,rMarkRect); break;
+ }
+
+ for(nPtNum = 0; nPtNum < nPointCount; nPtNum++)
+ {
+ // broadcasting could be optimized here, but for the
+ // current two points of the measurement object, it's fine
+ pO->SetPoint(aXP[static_cast<sal_uInt16>(nPtNum)],nPtNum);
+ }
+
+ bDone = true;
+ }
+
+ if(bDone)
+ return;
+
+ // for all others or if bNoContortion
+ Point aCtr0(pO->GetSnapRect().Center());
+ Point aCtr1(aCtr0);
+ bool bRotOk(false);
+ double nSin(0.0), nCos(1.0);
+ double nAngle(0.0);
+
+ if(0 != rRad.X() && 0 != rRad.Y())
+ {
+ bRotOk = bRotate;
+
+ switch (eMode)
+ {
+ case SdrCrookMode::Rotate : nAngle=CrookRotateXPoint (aCtr1,nullptr,nullptr,rRef,rRad,nSin,nCos,bVertical); bRotOk=bRotate; break;
+ case SdrCrookMode::Slant : nAngle=CrookSlantXPoint (aCtr1,nullptr,nullptr,rRef,rRad,nSin,nCos,bVertical); break;
+ case SdrCrookMode::Stretch: nAngle=CrookStretchXPoint(aCtr1,nullptr,nullptr,rRef,rRad,nSin,nCos,bVertical,rMarkRect); break;
+ }
+ }
+
+ aCtr1 -= aCtr0;
+
+ if(bRotOk)
+ pO->Rotate(aCtr0, Degree100(FRound(basegfx::rad2deg<100>(nAngle))), nSin, nCos);
+
+ pO->Move(Size(aCtr1.X(),aCtr1.Y()));
+}
+
+void SdrEditView::CrookMarkedObj(const Point& rRef, const Point& rRad, SdrCrookMode eMode,
+ bool bVertical, bool bNoContortion, bool bCopy)
+{
+ tools::Rectangle aMarkRect(GetMarkedObjRect());
+ const bool bUndo = IsUndoEnabled();
+
+ bool bRotate=bNoContortion && eMode==SdrCrookMode::Rotate && IsRotateAllowed();
+
+ if( bUndo )
+ {
+ EndTextEditCurrentView();
+ OUString aStr {ImpGetDescriptionString(bNoContortion ? STR_EditCrook : STR_EditCrookContortion)};
+ if (bCopy)
+ aStr+=SvxResId(STR_EditWithCopy);
+ BegUndo(aStr);
+ }
+
+ if (bCopy)
+ CopyMarkedObj();
+
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nm=0; nm<nMarkCount; ++nm)
+ {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pO=pM->GetMarkedSdrObj();
+ if( bUndo )
+ AddUndo( GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pO));
+
+ const SdrObjList* pOL=pO->GetSubList();
+ if (bNoContortion || pOL==nullptr) {
+ ImpCrookObj(pO,rRef,rRad,eMode,bVertical,bNoContortion,bRotate,aMarkRect);
+ } else {
+ SdrObjListIter aIter(pOL,SdrIterMode::DeepNoGroups);
+ while (aIter.IsMore()) {
+ SdrObject* pO1=aIter.Next();
+ ImpCrookObj(pO1,rRef,rRad,eMode,bVertical,bNoContortion,bRotate,aMarkRect);
+ }
+ }
+ }
+
+ if( bUndo )
+ EndUndo();
+}
+
+void SdrEditView::ImpDistortObj(SdrObject* pO, const tools::Rectangle& rRef, const XPolygon& rDistortedRect, bool bNoContortion)
+{
+ SdrPathObj* pPath = dynamic_cast<SdrPathObj*>( pO );
+
+ if(!bNoContortion && pPath)
+ {
+ XPolyPolygon aXPP(pPath->GetPathPoly());
+ aXPP.Distort(rRef, rDistortedRect);
+ pPath->SetPathPoly(aXPP.getB2DPolyPolygon());
+ }
+ else if(pO->IsPolyObj())
+ {
+ // e. g. for the measurement object
+ sal_uInt32 nPointCount(pO->GetPointCount());
+ XPolygon aXP(static_cast<sal_uInt16>(nPointCount));
+ sal_uInt32 nPtNum;
+
+ for(nPtNum = 0; nPtNum < nPointCount; nPtNum++)
+ {
+ Point aPt(pO->GetPoint(nPtNum));
+ aXP[static_cast<sal_uInt16>(nPtNum)]=aPt;
+ }
+
+ aXP.Distort(rRef, rDistortedRect);
+
+ for(nPtNum = 0; nPtNum < nPointCount; nPtNum++)
+ {
+ // broadcasting could be optimized here, but for the
+ // current two points of the measurement object it's fine
+ pO->SetPoint(aXP[static_cast<sal_uInt16>(nPtNum)],nPtNum);
+ }
+ }
+}
+
+void SdrEditView::DistortMarkedObj(const tools::Rectangle& rRef, const XPolygon& rDistortedRect, bool bNoContortion, bool bCopy)
+{
+ const bool bUndo = IsUndoEnabled();
+
+ if( bUndo )
+ {
+ EndTextEditCurrentView();
+ OUString aStr {ImpGetDescriptionString(STR_EditDistort)};
+ if (bCopy)
+ aStr+=SvxResId(STR_EditWithCopy);
+ BegUndo(aStr);
+ }
+
+ if (bCopy)
+ CopyMarkedObj();
+
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nm=0; nm<nMarkCount; ++nm)
+ {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pO=pM->GetMarkedSdrObj();
+ if( bUndo )
+ AddUndo( GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pO));
+
+ tools::Rectangle aRefRect(rRef);
+ const SdrObjList* pOL=pO->GetSubList();
+ if (bNoContortion || pOL==nullptr) {
+ ImpDistortObj(pO,aRefRect,rDistortedRect,bNoContortion);
+ } else {
+ SdrObjListIter aIter(pOL,SdrIterMode::DeepNoGroups);
+ while (aIter.IsMore()) {
+ SdrObject* pO1=aIter.Next();
+ ImpDistortObj(pO1,aRefRect,rDistortedRect,bNoContortion);
+ }
+ }
+ }
+ if( bUndo )
+ EndUndo();
+}
+
+
+void SdrEditView::SetNotPersistAttrToMarked(const SfxItemSet& rAttr)
+{
+ // bReplaceAll has no effect here
+ tools::Rectangle aAllSnapRect(GetMarkedObjRect());
+ if (const SdrTransformRef1XItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_TRANSFORMREF1X))
+ {
+ tools::Long n = pPoolItem->GetValue();
+ SetRef1(Point(n,GetRef1().Y()));
+ }
+ if (const SdrTransformRef1YItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_TRANSFORMREF1Y))
+ {
+ tools::Long n = pPoolItem->GetValue();
+ SetRef1(Point(GetRef1().X(),n));
+ }
+ if (const SdrTransformRef2XItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_TRANSFORMREF2X))
+ {
+ tools::Long n = pPoolItem->GetValue();
+ SetRef2(Point(n,GetRef2().Y()));
+ }
+ if (const SdrTransformRef2YItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_TRANSFORMREF2Y))
+ {
+ tools::Long n = pPoolItem->GetValue();
+ SetRef2(Point(GetRef2().X(),n));
+ }
+ tools::Long nAllPosX=0; bool bAllPosX=false;
+ tools::Long nAllPosY=0; bool bAllPosY=false;
+ tools::Long nAllWdt=0; bool bAllWdt=false;
+ tools::Long nAllHgt=0; bool bAllHgt=false;
+ bool bDoIt=false;
+ if (const SdrAllPositionXItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_ALLPOSITIONX))
+ {
+ nAllPosX = pPoolItem->GetValue();
+ bAllPosX=true; bDoIt=true;
+ }
+ if (const SdrAllPositionYItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_ALLPOSITIONY))
+ {
+ nAllPosY = pPoolItem->GetValue();
+ bAllPosY=true; bDoIt=true;
+ }
+ if (const SdrAllSizeWidthItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_ALLSIZEWIDTH))
+ {
+ nAllWdt = pPoolItem->GetValue();
+ bAllWdt=true; bDoIt=true;
+ }
+ if (const SdrAllSizeHeightItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_ALLSIZEHEIGHT))
+ {
+ nAllHgt = pPoolItem->GetValue();
+ bAllHgt=true; bDoIt=true;
+ }
+ if (bDoIt) {
+ tools::Rectangle aRect(aAllSnapRect); // TODO: change this for PolyPt's and GluePt's!!!
+ if (bAllPosX) aRect.Move(nAllPosX-aRect.Left(),0);
+ if (bAllPosY) aRect.Move(0,nAllPosY-aRect.Top());
+ if (bAllWdt) aRect.SetRight(aAllSnapRect.Left()+nAllWdt );
+ if (bAllHgt) aRect.SetBottom(aAllSnapRect.Top()+nAllHgt );
+ SetMarkedObjRect(aRect);
+ }
+ if (const SdrResizeXAllItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_RESIZEXALL))
+ {
+ Fraction aXFact = pPoolItem->GetValue();
+ ResizeMarkedObj(aAllSnapRect.TopLeft(),aXFact,Fraction(1,1));
+ }
+ if (const SdrResizeYAllItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_RESIZEYALL))
+ {
+ Fraction aYFact = pPoolItem->GetValue();
+ ResizeMarkedObj(aAllSnapRect.TopLeft(),Fraction(1,1),aYFact);
+ }
+ if (const SdrRotateAllItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_ROTATEALL))
+ {
+ Degree100 nAngle = pPoolItem->GetValue();
+ RotateMarkedObj(aAllSnapRect.Center(),nAngle);
+ }
+ if (const SdrHorzShearAllItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_HORZSHEARALL))
+ {
+ Degree100 nAngle = pPoolItem->GetValue();
+ ShearMarkedObj(aAllSnapRect.Center(),nAngle);
+ }
+ if (const SdrVertShearAllItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_VERTSHEARALL))
+ {
+ Degree100 nAngle = pPoolItem->GetValue();
+ ShearMarkedObj(aAllSnapRect.Center(),nAngle,true);
+ }
+
+ const bool bUndo = IsUndoEnabled();
+
+ // TODO: check if WhichRange is necessary.
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nm=0; nm<nMarkCount; ++nm)
+ {
+ const SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ if( bUndo )
+ AddUndo( GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pObj));
+
+ pObj->ApplyNotPersistAttr(rAttr);
+ }
+}
+
+void SdrEditView::MergeNotPersistAttrFromMarked(SfxItemSet& rAttr) const
+{
+ // TODO: Take into account the origin and PvPos.
+ tools::Rectangle aAllSnapRect(GetMarkedObjRect()); // TODO: change this for PolyPt's and GluePt's!!!
+ tools::Long nAllSnapPosX=aAllSnapRect.Left();
+ tools::Long nAllSnapPosY=aAllSnapRect.Top();
+ tools::Long nAllSnapWdt=aAllSnapRect.GetWidth()-1;
+ tools::Long nAllSnapHgt=aAllSnapRect.GetHeight()-1;
+ // TODO: could go into CheckPossibilities
+ bool bMovProtect = false, bMovProtectDC = false;
+ bool bSizProtect = false, bSizProtectDC = false;
+ bool bPrintable = true, bPrintableDC = false;
+ bool bVisible = true, bVisibleDC = false;
+ SdrLayerID nLayerId(0);
+ bool bLayerDC=false;
+ tools::Long nSnapPosX=0; bool bSnapPosXDC=false;
+ tools::Long nSnapPosY=0; bool bSnapPosYDC=false;
+ tools::Long nSnapWdt=0; bool bSnapWdtDC=false;
+ tools::Long nSnapHgt=0; bool bSnapHgtDC=false;
+ tools::Long nLogicWdt=0; bool bLogicWdtDC=false,bLogicWdtDiff=false;
+ tools::Long nLogicHgt=0; bool bLogicHgtDC=false,bLogicHgtDiff=false;
+ Degree100 nRotAngle(0); bool bRotAngleDC=false;
+ Degree100 nShrAngle(0); bool bShrAngleDC=false;
+ tools::Rectangle aSnapRect;
+ tools::Rectangle aLogicRect;
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nm=0; nm<nMarkCount; ++nm) {
+ const SdrMark* pM=GetSdrMarkByIndex(nm);
+ const SdrObject* pObj=pM->GetMarkedSdrObj();
+ if (nm==0) {
+ nLayerId=pObj->GetLayer();
+ bMovProtect=pObj->IsMoveProtect();
+ bSizProtect=pObj->IsResizeProtect();
+ bPrintable =pObj->IsPrintable();
+ bVisible = pObj->IsVisible();
+ tools::Rectangle aSnapRect2(pObj->GetSnapRect());
+ tools::Rectangle aLogicRect2(pObj->GetLogicRect());
+ nSnapPosX=aSnapRect2.Left();
+ nSnapPosY=aSnapRect2.Top();
+ nSnapWdt=aSnapRect2.GetWidth()-1;
+ nSnapHgt=aSnapRect2.GetHeight()-1;
+ nLogicWdt=aLogicRect2.GetWidth()-1;
+ nLogicHgt=aLogicRect2.GetHeight()-1;
+ bLogicWdtDiff=nLogicWdt!=nSnapWdt;
+ bLogicHgtDiff=nLogicHgt!=nSnapHgt;
+ nRotAngle=pObj->GetRotateAngle();
+ nShrAngle=pObj->GetShearAngle();
+ } else {
+ if (!bLayerDC && nLayerId !=pObj->GetLayer()) bLayerDC = true;
+ if (!bMovProtectDC && bMovProtect!=pObj->IsMoveProtect()) bMovProtectDC = true;
+ if (!bSizProtectDC && bSizProtect!=pObj->IsResizeProtect()) bSizProtectDC = true;
+ if (!bPrintableDC && bPrintable !=pObj->IsPrintable()) bPrintableDC = true;
+ if (!bVisibleDC && bVisible !=pObj->IsVisible()) bVisibleDC=true;
+ if (!bRotAngleDC && nRotAngle !=pObj->GetRotateAngle()) bRotAngleDC=true;
+ if (!bShrAngleDC && nShrAngle !=pObj->GetShearAngle()) bShrAngleDC=true;
+ if (!bSnapWdtDC || !bSnapHgtDC || !bSnapPosXDC || !bSnapPosYDC || !bLogicWdtDiff || !bLogicHgtDiff) {
+ aSnapRect=pObj->GetSnapRect();
+ if (nSnapPosX!=aSnapRect.Left()) bSnapPosXDC=true;
+ if (nSnapPosY!=aSnapRect.Top()) bSnapPosYDC=true;
+ if (nSnapWdt!=aSnapRect.GetWidth()-1) bSnapWdtDC=true;
+ if (nSnapHgt!=aSnapRect.GetHeight()-1) bSnapHgtDC=true;
+ }
+ if (!bLogicWdtDC || !bLogicHgtDC || !bLogicWdtDiff || !bLogicHgtDiff) {
+ aLogicRect=pObj->GetLogicRect();
+ if (nLogicWdt!=aLogicRect.GetWidth()-1) bLogicWdtDC=true;
+ if (nLogicHgt!=aLogicRect.GetHeight()-1) bLogicHgtDC=true;
+ if (!bLogicWdtDiff && aSnapRect.GetWidth()!=aLogicRect.GetWidth()) bLogicWdtDiff=true;
+ if (!bLogicHgtDiff && aSnapRect.GetHeight()!=aLogicRect.GetHeight()) bLogicHgtDiff=true;
+ }
+ }
+ }
+
+ if (bSnapPosXDC || nAllSnapPosX!=nSnapPosX) rAttr.Put(SdrAllPositionXItem(nAllSnapPosX));
+ if (bSnapPosYDC || nAllSnapPosY!=nSnapPosY) rAttr.Put(SdrAllPositionYItem(nAllSnapPosY));
+ if (bSnapWdtDC || nAllSnapWdt !=nSnapWdt ) rAttr.Put(SdrAllSizeWidthItem(nAllSnapWdt));
+ if (bSnapHgtDC || nAllSnapHgt !=nSnapHgt ) rAttr.Put(SdrAllSizeHeightItem(nAllSnapHgt));
+
+ // items for pure transformations
+ rAttr.Put(SdrMoveXItem());
+ rAttr.Put(SdrMoveYItem());
+ rAttr.Put(SdrResizeXOneItem());
+ rAttr.Put(SdrResizeYOneItem());
+ rAttr.Put(SdrRotateOneItem());
+ rAttr.Put(SdrHorzShearOneItem());
+ rAttr.Put(SdrVertShearOneItem());
+
+ if (nMarkCount>1) {
+ rAttr.Put(SdrResizeXAllItem());
+ rAttr.Put(SdrResizeYAllItem());
+ rAttr.Put(SdrRotateAllItem());
+ rAttr.Put(SdrHorzShearAllItem());
+ rAttr.Put(SdrVertShearAllItem());
+ }
+
+ if(meDragMode == SdrDragMode::Rotate || meDragMode == SdrDragMode::Mirror)
+ {
+ rAttr.Put(SdrTransformRef1XItem(GetRef1().X()));
+ rAttr.Put(SdrTransformRef1YItem(GetRef1().Y()));
+ }
+
+ if(meDragMode == SdrDragMode::Mirror)
+ {
+ rAttr.Put(SdrTransformRef2XItem(GetRef2().X()));
+ rAttr.Put(SdrTransformRef2YItem(GetRef2().Y()));
+ }
+}
+
+SfxItemSet SdrEditView::GetAttrFromMarked(bool bOnlyHardAttr) const
+{
+ SfxItemSet aSet(mpModel->GetItemPool());
+ MergeAttrFromMarked(aSet,bOnlyHardAttr);
+ //the EE_FEATURE items should not be set with SetAttrToMarked (see error message there)
+ //so we do not set them here
+ // #i32448#
+ // Do not disable, but clear the items.
+ aSet.ClearItem(EE_FEATURE_TAB);
+ aSet.ClearItem(EE_FEATURE_LINEBR);
+ aSet.ClearItem(EE_FEATURE_NOTCONV);
+ aSet.ClearItem(EE_FEATURE_FIELD);
+
+ return aSet;
+}
+
+void SdrEditView::MergeAttrFromMarked(SfxItemSet& rAttr, bool bOnlyHardAttr) const
+{
+ const size_t nMarkCount(GetMarkedObjectCount());
+
+ for(size_t a = 0; a < nMarkCount; ++a)
+ {
+ // #80277# merging was done wrong in the prev version
+ SdrObject *pObj = GetMarkedObjectByIndex(a);
+ if (!pObj)
+ {
+ continue;
+ }
+
+ const SfxItemSet& rSet = pObj->GetMergedItemSet();
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich(aIter.FirstWhich());
+
+ while(nWhich)
+ {
+ if(!bOnlyHardAttr)
+ {
+ if(SfxItemState::DONTCARE == aIter.GetItemState(false))
+ rAttr.InvalidateItem(nWhich);
+ else
+ rAttr.MergeValue(rSet.Get(nWhich), true);
+ }
+ else if(SfxItemState::SET == aIter.GetItemState(false))
+ {
+ const SfxPoolItem& rItem = rSet.Get(nWhich);
+ rAttr.MergeValue(rItem, true);
+ }
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ OUString sPayload;
+ switch(nWhich)
+ {
+ case XATTR_LINECOLOR:
+ {
+ const SfxPoolItem* pItem = rSet.GetItem(XATTR_LINECOLOR);
+ if (pItem)
+ {
+ Color aColor = static_cast<const XLineColorItem*>(pItem)->GetColorValue();
+ sPayload = OUString::number(static_cast<sal_uInt32>(aColor));
+
+ sPayload = ".uno:XLineColor=" + sPayload;
+ }
+ break;
+ }
+
+ case XATTR_FILLCOLOR:
+ {
+ const SfxPoolItem* pItem = rSet.GetItem(XATTR_FILLCOLOR);
+ if (pItem)
+ {
+ Color aColor = static_cast<const XFillColorItem*>(pItem)->GetColorValue();
+ sPayload = OUString::number(static_cast<sal_uInt32>(aColor));
+
+ sPayload = ".uno:FillColor=" + sPayload;
+ }
+ break;
+ }
+
+ case XATTR_FILLTRANSPARENCE:
+ {
+ const SfxPoolItem* pItem = rSet.GetItem(XATTR_FILLTRANSPARENCE);
+ if (pItem)
+ {
+ sal_uInt16 nTransparency = static_cast<const SfxUInt16Item*>(pItem)->GetValue();
+ sPayload = OUString::number(nTransparency);
+
+ sPayload = ".uno:FillTransparence=" + sPayload;
+ }
+ break;
+ }
+
+ case XATTR_LINETRANSPARENCE:
+ {
+ const SfxPoolItem* pItem = rSet.GetItem(XATTR_LINETRANSPARENCE);
+ if (pItem)
+ {
+ sal_uInt16 nTransparency = static_cast<const SfxUInt16Item*>(pItem)->GetValue();
+ sPayload = OUString::number(nTransparency);
+
+ sPayload = ".uno:LineTransparence=" + sPayload;
+ }
+ break;
+ }
+
+ case XATTR_LINEWIDTH:
+ {
+ const SfxPoolItem* pItem = rSet.GetItem(XATTR_LINEWIDTH);
+ if (pItem)
+ {
+ sal_uInt32 nWidth = static_cast<const XLineWidthItem*>(pItem)->GetValue();
+ sPayload = OUString::number(nWidth);
+
+ sPayload = ".uno:LineWidth=" + sPayload;
+ }
+ break;
+ }
+
+ case SDRATTR_SHADOWTRANSPARENCE:
+ {
+ const SfxPoolItem* pItem = rSet.GetItem(SDRATTR_SHADOWTRANSPARENCE);
+ if (pItem)
+ {
+ sal_uInt16 nWidth = static_cast<const SfxUInt16Item*>(pItem)->GetValue();
+ sPayload = OUString::number(nWidth);
+
+ sPayload = ".uno:FillShadowTransparency=" + sPayload;
+ }
+ break;
+ }
+ }
+
+ if (!sPayload.isEmpty())
+ GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED,
+ OUStringToOString(sPayload, RTL_TEXTENCODING_ASCII_US).getStr());
+ }
+
+ nWhich = aIter.NextWhich();
+ }
+ }
+}
+
+std::vector<sal_uInt16> GetAllCharPropIds(const SfxItemSet& rSet)
+{
+ std::vector<sal_uInt16> aCharWhichIds;
+ {
+ SfxItemIter aIter(rSet);
+ for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ {
+ if (!IsInvalidItem(pItem))
+ {
+ sal_uInt16 nWhich = pItem->Which();
+ if (nWhich>=EE_CHAR_START && nWhich<=EE_CHAR_END)
+ aCharWhichIds.push_back( nWhich );
+ }
+ }
+ }
+ return aCharWhichIds;
+}
+
+std::vector<sal_uInt16> GetAllCharPropIds(o3tl::span< const SfxPoolItem* const > aChangedItems)
+{
+ std::vector<sal_uInt16> aCharWhichIds;
+ for (const SfxPoolItem* pItem : aChangedItems)
+ {
+ sal_uInt16 nWhich = pItem->Which();
+ if (nWhich>=EE_CHAR_START && nWhich<=EE_CHAR_END)
+ aCharWhichIds.push_back( nWhich );
+ }
+ return aCharWhichIds;
+}
+
+void SdrEditView::SetAttrToMarked(const SfxItemSet& rAttr, bool bReplaceAll)
+{
+ if (!AreObjectsMarked())
+ return;
+
+#ifdef DBG_UTIL
+ {
+ bool bHasEEFeatureItems=false;
+ SfxItemIter aIter(rAttr);
+ for (const SfxPoolItem* pItem = aIter.GetCurItem(); !bHasEEFeatureItems && pItem;
+ pItem = aIter.NextItem())
+ {
+ if (!IsInvalidItem(pItem)) {
+ sal_uInt16 nW=pItem->Which();
+ if (nW>=EE_FEATURE_START && nW<=EE_FEATURE_END) bHasEEFeatureItems=true;
+ }
+ }
+ if(bHasEEFeatureItems)
+ {
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Info, VclButtonsType::Ok,
+ "SdrEditView::SetAttrToMarked(): Setting EE_FEATURE items at the SdrView does not make sense! It only leads to overhead and unreadable documents."));
+ xInfoBox->run();
+ }
+ }
+#endif
+
+ // #103836# if the user sets character attributes to the complete shape,
+ // we want to remove all hard set character attributes with same
+ // which ids from the text. We do that later but here we remember
+ // all character attribute which id's that are set.
+ std::vector<sal_uInt16> aCharWhichIds(GetAllCharPropIds(rAttr));
+
+ // To make Undo reconstruct text attributes correctly after Format.Standard
+ bool bHasEEItems=SearchOutlinerItems(rAttr,bReplaceAll);
+
+ // save additional geometry information when paragraph or character attributes
+ // are changed and the geometrical shape of the text object might be changed
+ bool bPossibleGeomChange(false);
+ SfxWhichIter aIter(rAttr);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while(!bPossibleGeomChange && nWhich)
+ {
+ SfxItemState eState = aIter.GetItemState();
+ if(eState == SfxItemState::SET)
+ {
+ if((nWhich >= SDRATTR_TEXT_MINFRAMEHEIGHT && nWhich <= SDRATTR_TEXT_CONTOURFRAME)
+ || nWhich == SDRATTR_3DOBJ_PERCENT_DIAGONAL
+ || nWhich == SDRATTR_3DOBJ_BACKSCALE
+ || nWhich == SDRATTR_3DOBJ_DEPTH
+ || nWhich == SDRATTR_3DOBJ_END_ANGLE
+ || nWhich == SDRATTR_3DSCENE_DISTANCE)
+ {
+ bPossibleGeomChange = true;
+ }
+ }
+ nWhich = aIter.NextWhich();
+ }
+
+ const bool bUndo = IsUndoEnabled();
+ if( bUndo )
+ {
+ EndTextEditCurrentView();
+ BegUndo(ImpGetDescriptionString(STR_EditSetAttributes));
+ }
+
+ const size_t nMarkCount(GetMarkedObjectCount());
+ std::vector< E3DModifySceneSnapRectUpdater* > aUpdaters;
+
+ // create ItemSet without SfxItemState::DONTCARE. Put()
+ // uses its second parameter (bInvalidAsDefault) to
+ // remove all such items to set them to default.
+ SfxItemSet aAttr(*rAttr.GetPool(), rAttr.GetRanges());
+ aAttr.Put(rAttr);
+
+ // #i38135#
+ bool bResetAnimationTimer(false);
+
+ const bool bLineStartWidthExplicitChange(SfxItemState::SET
+ == aAttr.GetItemState(XATTR_LINESTARTWIDTH));
+ const bool bLineEndWidthExplicitChange(SfxItemState::SET
+ == aAttr.GetItemState(XATTR_LINEENDWIDTH));
+ // check if LineWidth is part of the change
+ const bool bAdaptStartEndWidths(!(bLineStartWidthExplicitChange && bLineEndWidthExplicitChange)
+ && SfxItemState::SET == aAttr.GetItemState(XATTR_LINEWIDTH));
+ sal_Int32 nNewLineWidth(0);
+
+ if(bAdaptStartEndWidths)
+ {
+ nNewLineWidth = aAttr.Get(XATTR_LINEWIDTH).GetValue();
+ }
+
+ for (size_t nm=0; nm<nMarkCount; ++nm)
+ {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pObj = pM->GetMarkedSdrObj();
+
+ if( bUndo )
+ {
+ SdrEdgeObj* pEdgeObj = dynamic_cast< SdrEdgeObj* >( pObj );
+ if ( pEdgeObj )
+ bPossibleGeomChange = true;
+ else
+ AddUndoActions( CreateConnectorUndo( *pObj ) );
+ }
+
+ // new geometry undo
+ if(bPossibleGeomChange && bUndo)
+ {
+ // save position and size of object, too
+ AddUndo( GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pObj));
+ }
+
+ if( bUndo )
+ {
+ // #i8508#
+ // If this is a text object also rescue the OutlinerParaObject since
+ // applying attributes to the object may change text layout when
+ // multiple portions exist with multiple formats. If an OutlinerParaObject
+ // really exists and needs to be rescued is evaluated in the undo
+ // implementation itself.
+ const bool bRescueText = dynamic_cast< SdrTextObj* >(pObj) != nullptr;
+
+ // add attribute undo
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoAttrObject(*pObj,false,bHasEEItems || bPossibleGeomChange || bRescueText));
+ }
+
+ // set up a scene updater if object is a 3d object
+ if(dynamic_cast< E3dObject* >(pObj))
+ {
+ aUpdaters.push_back(new E3DModifySceneSnapRectUpdater(pObj));
+ }
+
+ sal_Int32 nOldLineWidth(0);
+ if (bAdaptStartEndWidths)
+ {
+ nOldLineWidth = pObj->GetMergedItem(XATTR_LINEWIDTH).GetValue();
+ }
+
+ // set attributes at object
+ pObj->SetMergedItemSetAndBroadcast(aAttr, bReplaceAll);
+
+ if(bAdaptStartEndWidths)
+ {
+ const SfxItemSet& rSet = pObj->GetMergedItemSet();
+
+ if(nOldLineWidth != nNewLineWidth)
+ {
+ if(SfxItemState::DONTCARE != rSet.GetItemState(XATTR_LINESTARTWIDTH))
+ {
+ const sal_Int32 nValAct(rSet.Get(XATTR_LINESTARTWIDTH).GetValue());
+ const sal_Int32 nValNewStart(std::max(sal_Int32(0), nValAct + (((nNewLineWidth - nOldLineWidth) * 15) / 10)));
+
+ pObj->SetMergedItem(XLineStartWidthItem(nValNewStart));
+ }
+
+ if(SfxItemState::DONTCARE != rSet.GetItemState(XATTR_LINEENDWIDTH))
+ {
+ const sal_Int32 nValAct(rSet.Get(XATTR_LINEENDWIDTH).GetValue());
+ const sal_Int32 nValNewEnd(std::max(sal_Int32(0), nValAct + (((nNewLineWidth - nOldLineWidth) * 15) / 10)));
+
+ pObj->SetMergedItem(XLineEndWidthItem(nValNewEnd));
+ }
+ }
+ }
+
+ if(auto pTextObj = dynamic_cast<SdrTextObj*>( pObj))
+ {
+ if(!aCharWhichIds.empty())
+ {
+ tools::Rectangle aOldBoundRect = pTextObj->GetLastBoundRect();
+
+ // #110094#-14 pTextObj->SendRepaintBroadcast(pTextObj->GetBoundRect());
+ pTextObj->RemoveOutlinerCharacterAttribs( aCharWhichIds );
+
+ // object has changed, should be called from
+ // RemoveOutlinerCharacterAttribs. This will change when the text
+ // object implementation changes.
+ pTextObj->SetChanged();
+
+ pTextObj->BroadcastObjectChange();
+ pTextObj->SendUserCall(SdrUserCallType::ChangeAttr, aOldBoundRect);
+ }
+ }
+
+ // #i38495#
+ if(!bResetAnimationTimer)
+ {
+ if(pObj->GetViewContact().isAnimatedInAnyViewObjectContact())
+ {
+ bResetAnimationTimer = true;
+ }
+ }
+ }
+
+ // fire scene updaters
+ while(!aUpdaters.empty())
+ {
+ delete aUpdaters.back();
+ aUpdaters.pop_back();
+ }
+
+ // #i38135#
+ if(bResetAnimationTimer)
+ {
+ SetAnimationTimer(0);
+ }
+
+ // better check before what to do:
+ // pObj->SetAttr() or SetNotPersistAttr()
+ // TODO: missing implementation!
+ SetNotPersistAttrToMarked(rAttr);
+
+ if( bUndo )
+ EndUndo();
+}
+
+SfxStyleSheet* SdrEditView::GetStyleSheetFromMarked() const
+{
+ SfxStyleSheet* pRet=nullptr;
+ bool b1st=true;
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nm=0; nm<nMarkCount; ++nm) {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SfxStyleSheet* pSS=pM->GetMarkedSdrObj()->GetStyleSheet();
+ if (b1st) pRet=pSS;
+ else if (pRet!=pSS) return nullptr; // different stylesheets
+ b1st=false;
+ }
+ return pRet;
+}
+
+void SdrEditView::SetStyleSheetToMarked(SfxStyleSheet* pStyleSheet, bool bDontRemoveHardAttr)
+{
+ if (!AreObjectsMarked())
+ return;
+
+ const bool bUndo = IsUndoEnabled();
+
+ if( bUndo )
+ {
+ EndTextEditCurrentView();
+ OUString aStr;
+ if (pStyleSheet!=nullptr)
+ aStr = ImpGetDescriptionString(STR_EditSetStylesheet);
+ else
+ aStr = ImpGetDescriptionString(STR_EditDelStylesheet);
+ BegUndo(aStr);
+ }
+
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nm=0; nm<nMarkCount; ++nm)
+ {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ if( bUndo )
+ {
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pM->GetMarkedSdrObj()));
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoAttrObject(*pM->GetMarkedSdrObj(),true,true));
+ }
+ pM->GetMarkedSdrObj()->SetStyleSheet(pStyleSheet,bDontRemoveHardAttr);
+ }
+
+ if( bUndo )
+ EndUndo();
+}
+
+
+void SdrEditView::GetAttributes(SfxItemSet& rTargetSet, bool bOnlyHardAttr) const
+{
+ if(GetMarkedObjectCount())
+ {
+ rTargetSet.Put(GetAttrFromMarked(bOnlyHardAttr), false);
+ }
+ else
+ {
+ SdrMarkView::GetAttributes(rTargetSet, bOnlyHardAttr);
+ }
+}
+
+void SdrEditView::SetAttributes(const SfxItemSet& rSet, bool bReplaceAll)
+{
+ if (GetMarkedObjectCount()!=0) {
+ SetAttrToMarked(rSet,bReplaceAll);
+ } else {
+ SdrMarkView::SetAttributes(rSet,bReplaceAll);
+ }
+}
+
+SfxStyleSheet* SdrEditView::GetStyleSheet() const
+{
+ if (GetMarkedObjectCount()!=0) {
+ return GetStyleSheetFromMarked();
+ } else {
+ return SdrMarkView::GetStyleSheet();
+ }
+}
+
+void SdrEditView::SetStyleSheet(SfxStyleSheet* pStyleSheet, bool bDontRemoveHardAttr)
+{
+ if (GetMarkedObjectCount()!=0) {
+ SetStyleSheetToMarked(pStyleSheet,bDontRemoveHardAttr);
+ } else {
+ SdrMarkView::SetStyleSheet(pStyleSheet,bDontRemoveHardAttr);
+ }
+}
+
+
+SfxItemSet SdrEditView::GetGeoAttrFromMarked() const
+{
+ SfxItemSet aRetSet(
+ mpModel->GetItemPool(),
+ svl::Items< // SID_ATTR_TRANSFORM_... from s:svxids.hrc
+ SDRATTR_CORNER_RADIUS, SDRATTR_CORNER_RADIUS,
+ SID_ATTR_TRANSFORM_POS_X, SID_ATTR_TRANSFORM_ANGLE,
+ SID_ATTR_TRANSFORM_PROTECT_POS, SID_ATTR_TRANSFORM_AUTOHEIGHT>);
+
+ if (AreObjectsMarked())
+ {
+ SfxItemSet aMarkAttr(GetAttrFromMarked(false)); // because of AutoGrowHeight and corner radius
+ tools::Rectangle aRect(GetMarkedObjRect());
+
+ if(GetSdrPageView())
+ {
+ GetSdrPageView()->LogicToPagePos(aRect);
+ }
+
+ // position
+ aRetSet.Put(SfxInt32Item(SID_ATTR_TRANSFORM_POS_X,aRect.Left()));
+ aRetSet.Put(SfxInt32Item(SID_ATTR_TRANSFORM_POS_Y,aRect.Top()));
+
+ // size
+ tools::Long nResizeRefX=aRect.Left();
+ tools::Long nResizeRefY=aRect.Top();
+ if (meDragMode==SdrDragMode::Rotate) { // use rotation axis as a reference for resizing, too
+ nResizeRefX=maRef1.X();
+ nResizeRefY=maRef1.Y();
+ }
+ aRetSet.Put(SfxUInt32Item(SID_ATTR_TRANSFORM_WIDTH,aRect.Right()-aRect.Left()));
+ aRetSet.Put(SfxUInt32Item(SID_ATTR_TRANSFORM_HEIGHT,aRect.Bottom()-aRect.Top()));
+ aRetSet.Put(SfxInt32Item(SID_ATTR_TRANSFORM_RESIZE_REF_X,nResizeRefX));
+ aRetSet.Put(SfxInt32Item(SID_ATTR_TRANSFORM_RESIZE_REF_Y,nResizeRefY));
+
+ Point aRotateAxe(maRef1);
+
+ if(GetSdrPageView())
+ {
+ GetSdrPageView()->LogicToPagePos(aRotateAxe);
+ }
+
+ // rotation
+ tools::Long nRotateRefX=aRect.Center().X();
+ tools::Long nRotateRefY=aRect.Center().Y();
+ if (meDragMode==SdrDragMode::Rotate) {
+ nRotateRefX=aRotateAxe.X();
+ nRotateRefY=aRotateAxe.Y();
+ }
+ aRetSet.Put(SdrAngleItem(SID_ATTR_TRANSFORM_ANGLE,GetMarkedObjRotate()));
+ aRetSet.Put(SfxInt32Item(SID_ATTR_TRANSFORM_ROT_X,nRotateRefX));
+ aRetSet.Put(SfxInt32Item(SID_ATTR_TRANSFORM_ROT_Y,nRotateRefY));
+
+ // shearing
+ tools::Long nShearRefX=aRect.Left();
+ tools::Long nShearRefY=aRect.Bottom();
+ if (meDragMode==SdrDragMode::Rotate) { // use rotation axis as a reference for shearing, too
+ nShearRefX=aRotateAxe.X();
+ nShearRefY=aRotateAxe.Y();
+ }
+ aRetSet.Put(SdrAngleItem(SID_ATTR_TRANSFORM_SHEAR,GetMarkedObjShear()));
+ aRetSet.Put(SfxInt32Item(SID_ATTR_TRANSFORM_SHEAR_X,nShearRefX));
+ aRetSet.Put(SfxInt32Item(SID_ATTR_TRANSFORM_SHEAR_Y,nShearRefY));
+
+ // check every object whether it is protected
+ const SdrMarkList& rMarkList=GetMarkedObjectList();
+ const size_t nMarkCount=rMarkList.GetMarkCount();
+ SdrObject* pObj=rMarkList.GetMark(0)->GetMarkedSdrObj();
+ bool bPosProt=pObj->IsMoveProtect();
+ bool bSizProt=pObj->IsResizeProtect();
+ bool bPosProtDontCare=false;
+ bool bSizProtDontCare=false;
+ for (size_t i=1; i<nMarkCount && (!bPosProtDontCare || !bSizProtDontCare); ++i)
+ {
+ pObj=rMarkList.GetMark(i)->GetMarkedSdrObj();
+ if (bPosProt!=pObj->IsMoveProtect()) bPosProtDontCare=true;
+ if (bSizProt!=pObj->IsResizeProtect()) bSizProtDontCare=true;
+ }
+
+ // InvalidateItem sets item to DONT_CARE
+ if (bPosProtDontCare) {
+ aRetSet.InvalidateItem(SID_ATTR_TRANSFORM_PROTECT_POS);
+ } else {
+ aRetSet.Put(SfxBoolItem(SID_ATTR_TRANSFORM_PROTECT_POS,bPosProt));
+ }
+ if (bSizProtDontCare) {
+ aRetSet.InvalidateItem(SID_ATTR_TRANSFORM_PROTECT_SIZE);
+ } else {
+ aRetSet.Put(SfxBoolItem(SID_ATTR_TRANSFORM_PROTECT_SIZE,bSizProt));
+ }
+
+ SfxItemState eState=aMarkAttr.GetItemState(SDRATTR_TEXT_AUTOGROWWIDTH);
+ bool bAutoGrow=aMarkAttr.Get(SDRATTR_TEXT_AUTOGROWWIDTH).GetValue();
+ if (eState==SfxItemState::DONTCARE) {
+ aRetSet.InvalidateItem(SID_ATTR_TRANSFORM_AUTOWIDTH);
+ } else if (eState==SfxItemState::SET) {
+ aRetSet.Put(SfxBoolItem(SID_ATTR_TRANSFORM_AUTOWIDTH,bAutoGrow));
+ }
+
+ eState=aMarkAttr.GetItemState(SDRATTR_TEXT_AUTOGROWHEIGHT);
+ bAutoGrow=aMarkAttr.Get(SDRATTR_TEXT_AUTOGROWHEIGHT).GetValue();
+ if (eState==SfxItemState::DONTCARE) {
+ aRetSet.InvalidateItem(SID_ATTR_TRANSFORM_AUTOHEIGHT);
+ } else if (eState==SfxItemState::SET) {
+ aRetSet.Put(SfxBoolItem(SID_ATTR_TRANSFORM_AUTOHEIGHT,bAutoGrow));
+ }
+
+ eState=aMarkAttr.GetItemState(SDRATTR_CORNER_RADIUS);
+ tools::Long nRadius=aMarkAttr.Get(SDRATTR_CORNER_RADIUS).GetValue();
+ if (eState==SfxItemState::DONTCARE) {
+ aRetSet.InvalidateItem(SDRATTR_CORNER_RADIUS);
+ } else if (eState==SfxItemState::SET) {
+ aRetSet.Put(makeSdrEckenradiusItem(nRadius));
+ }
+
+ basegfx::B2DHomMatrix aTransformation;
+
+ if(nMarkCount > 1)
+ {
+ // multiple objects, range is collected in aRect
+ aTransformation = basegfx::utils::createScaleTranslateB2DHomMatrix(
+ aRect.Left(), aRect.Top(),
+ aRect.getWidth(), aRect.getHeight());
+ }
+ else
+ {
+ // single object, get homogen transformation
+ basegfx::B2DPolyPolygon aPolyPolygon;
+
+ pObj->TRGetBaseGeometry(aTransformation, aPolyPolygon);
+ }
+
+ if(aTransformation.isIdentity())
+ {
+ aRetSet.InvalidateItem(SID_ATTR_TRANSFORM_MATRIX);
+ }
+ else
+ {
+ css::geometry::AffineMatrix2D aAffineMatrix2D;
+ Point aPageOffset(0, 0);
+
+ if(GetSdrPageView())
+ {
+ aPageOffset = GetSdrPageView()->GetPageOrigin();
+ }
+
+ aAffineMatrix2D.m00 = aTransformation.get(0, 0);
+ aAffineMatrix2D.m01 = aTransformation.get(0, 1);
+ aAffineMatrix2D.m02 = aTransformation.get(0, 2) - aPageOffset.X();
+ aAffineMatrix2D.m10 = aTransformation.get(1, 0);
+ aAffineMatrix2D.m11 = aTransformation.get(1, 1);
+ aAffineMatrix2D.m12 = aTransformation.get(1, 2) - aPageOffset.Y();
+
+ aRetSet.Put(AffineMatrixItem(&aAffineMatrix2D));
+ }
+ }
+
+ return aRetSet;
+}
+
+static Point ImpGetPoint(const tools::Rectangle& rRect, RectPoint eRP)
+{
+ switch(eRP) {
+ case RectPoint::LT: return rRect.TopLeft();
+ case RectPoint::MT: return rRect.TopCenter();
+ case RectPoint::RT: return rRect.TopRight();
+ case RectPoint::LM: return rRect.LeftCenter();
+ case RectPoint::MM: return rRect.Center();
+ case RectPoint::RM: return rRect.RightCenter();
+ case RectPoint::LB: return rRect.BottomLeft();
+ case RectPoint::MB: return rRect.BottomCenter();
+ case RectPoint::RB: return rRect.BottomRight();
+ }
+ return Point(); // Should not happen!
+}
+
+void SdrEditView::SetGeoAttrToMarked(const SfxItemSet& rAttr, bool addPageMargin)
+{
+ const bool bTiledRendering = comphelper::LibreOfficeKit::isActive();
+
+ tools::Rectangle aRect(GetMarkedObjRect());
+
+ if(GetSdrPageView())
+ {
+ if (addPageMargin)
+ {
+ SdrPage * pPage = GetSdrPageView()->GetPage();
+ Point upperLeft(pPage->GetLeftBorder(), pPage->GetUpperBorder());
+ aRect.Move(upperLeft.getX(), upperLeft.getY());
+ }
+ GetSdrPageView()->LogicToPagePos(aRect);
+ }
+
+ Degree100 nOldRotateAngle=GetMarkedObjRotate();
+ Degree100 nOldShearAngle=GetMarkedObjShear();
+ const SdrMarkList& rMarkList=GetMarkedObjectList();
+ SdrObject* pObj=nullptr;
+
+ RectPoint eSizePoint=RectPoint::MM;
+ tools::Long nPosDX=0;
+ tools::Long nPosDY=0;
+ tools::Long nSizX=0;
+ tools::Long nSizY=0;
+ Degree100 nRotateAngle(0);
+
+ bool bModeIsRotate(meDragMode == SdrDragMode::Rotate);
+ tools::Long nRotateX(0);
+ tools::Long nRotateY(0);
+ tools::Long nOldRotateX(0);
+ tools::Long nOldRotateY(0);
+ if(bModeIsRotate)
+ {
+ Point aRotateAxe(maRef1);
+
+ if(GetSdrPageView())
+ {
+ GetSdrPageView()->LogicToPagePos(aRotateAxe);
+ }
+
+ nRotateX = nOldRotateX = aRotateAxe.X();
+ nRotateY = nOldRotateY = aRotateAxe.Y();
+ }
+
+ Degree100 nShearAngle(0);
+ tools::Long nShearX=0;
+ tools::Long nShearY=0;
+ bool bShearVert=false;
+
+ bool bChgPos=false;
+ bool bChgSiz=false;
+ bool bChgWdh=false;
+ bool bChgHgt=false;
+ bool bRotate=false;
+ bool bShear =false;
+
+ bool bSetAttr=false;
+ SfxItemSet aSetAttr(mpModel->GetItemPool());
+
+ // position
+ if (const SfxInt32Item *pPoolItem = rAttr.GetItemIfSet(SID_ATTR_TRANSFORM_POS_X))
+ {
+ nPosDX = pPoolItem->GetValue() - aRect.Left();
+ bChgPos=true;
+ }
+ if (const SfxInt32Item *pPoolItem = rAttr.GetItemIfSet(SID_ATTR_TRANSFORM_POS_Y))
+ {
+ nPosDY = pPoolItem->GetValue() - aRect.Top();
+ bChgPos=true;
+ }
+ // size
+ if (const SfxUInt32Item *pPoolItem = rAttr.GetItemIfSet(SID_ATTR_TRANSFORM_WIDTH))
+ {
+ nSizX = pPoolItem->GetValue();
+ bChgSiz=true;
+ bChgWdh=true;
+ }
+ if (const SfxUInt32Item *pPoolItem = rAttr.GetItemIfSet(SID_ATTR_TRANSFORM_HEIGHT))
+ {
+ nSizY = pPoolItem->GetValue();
+ bChgSiz=true;
+ bChgHgt=true;
+ }
+ if (bChgSiz) {
+ if (bTiledRendering && SfxItemState::SET != rAttr.GetItemState(SID_ATTR_TRANSFORM_SIZE_POINT))
+ eSizePoint = RectPoint::LT;
+ else
+ eSizePoint = static_cast<RectPoint>(rAttr.Get(SID_ATTR_TRANSFORM_SIZE_POINT).GetValue());
+ }
+
+ // rotation
+ if (const SdrAngleItem *pPoolItem = rAttr.GetItemIfSet(SID_ATTR_TRANSFORM_DELTA_ANGLE))
+ {
+ nRotateAngle = pPoolItem->GetValue();
+ bRotate = (nRotateAngle != 0_deg100);
+ }
+
+ // rotation
+ if (const SdrAngleItem *pPoolItem = rAttr.GetItemIfSet(SID_ATTR_TRANSFORM_ANGLE))
+ {
+ nRotateAngle = pPoolItem->GetValue() - nOldRotateAngle;
+ bRotate = (nRotateAngle != 0_deg100);
+ }
+
+ // position rotation point x
+ if(bRotate || rAttr.GetItemIfSet(SID_ATTR_TRANSFORM_ROT_X))
+ nRotateX = rAttr.Get(SID_ATTR_TRANSFORM_ROT_X).GetValue();
+
+ // position rotation point y
+ if(bRotate || rAttr.GetItemIfSet(SID_ATTR_TRANSFORM_ROT_Y))
+ nRotateY = rAttr.Get(SID_ATTR_TRANSFORM_ROT_Y).GetValue();
+
+ // shearing
+ if (const SdrAngleItem *pPoolItem = rAttr.GetItemIfSet(SID_ATTR_TRANSFORM_SHEAR))
+ {
+ Degree100 nNewShearAngle=pPoolItem->GetValue();
+ if (nNewShearAngle>SDRMAXSHEAR) nNewShearAngle=SDRMAXSHEAR;
+ if (nNewShearAngle<-SDRMAXSHEAR) nNewShearAngle=-SDRMAXSHEAR;
+ if (nNewShearAngle!=nOldShearAngle) {
+ bShearVert = rAttr.Get(SID_ATTR_TRANSFORM_SHEAR_VERTICAL).GetValue();
+ if (bShearVert) {
+ nShearAngle=nNewShearAngle;
+ } else {
+ if (nNewShearAngle!=0_deg100 && nOldShearAngle!=0_deg100) {
+ // bug fix
+ double nOld = tan(toRadians(nOldShearAngle));
+ double nNew = tan(toRadians(nNewShearAngle));
+ nNew-=nOld;
+ nNew = basegfx::rad2deg<100>(atan(nNew));
+ nShearAngle=Degree100(FRound(nNew));
+ } else {
+ nShearAngle=nNewShearAngle-nOldShearAngle;
+ }
+ }
+ bShear=nShearAngle!=0_deg100;
+ if (bShear) {
+ nShearX=static_cast<const SfxInt32Item&>(rAttr.Get(SID_ATTR_TRANSFORM_SHEAR_X)).GetValue();
+ nShearY=static_cast<const SfxInt32Item&>(rAttr.Get(SID_ATTR_TRANSFORM_SHEAR_Y)).GetValue();
+ }
+ }
+ }
+
+ // AutoGrow
+ if (const SfxBoolItem *pPoolItem = rAttr.GetItemIfSet(SID_ATTR_TRANSFORM_AUTOWIDTH))
+ {
+ bool bAutoGrow = pPoolItem->GetValue();
+ aSetAttr.Put(makeSdrTextAutoGrowWidthItem(bAutoGrow));
+ bSetAttr=true;
+ }
+
+ if (const SfxBoolItem *pPoolItem = rAttr.GetItemIfSet(SID_ATTR_TRANSFORM_AUTOHEIGHT))
+ {
+ bool bAutoGrow = pPoolItem->GetValue();
+ aSetAttr.Put(makeSdrTextAutoGrowHeightItem(bAutoGrow));
+ bSetAttr=true;
+ }
+
+ // corner radius
+ if (m_bEdgeRadiusAllowed)
+ if (const SdrMetricItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_CORNER_RADIUS))
+ {
+ tools::Long nRadius = pPoolItem->GetValue();
+ aSetAttr.Put(makeSdrEckenradiusItem(nRadius));
+ bSetAttr=true;
+ }
+
+ ForcePossibilities();
+
+ BegUndo(SvxResId(STR_EditTransform),GetDescriptionOfMarkedObjects());
+
+ if (bSetAttr) {
+ SetAttrToMarked(aSetAttr,false);
+ }
+
+ // change size and height
+ if (bChgSiz && (m_bResizeFreeAllowed || m_bResizePropAllowed)) {
+ Fraction aWdt(nSizX,aRect.Right()-aRect.Left());
+ Fraction aHgt(nSizY,aRect.Bottom()-aRect.Top());
+ Point aRef(ImpGetPoint(aRect,eSizePoint));
+
+ if(GetSdrPageView())
+ {
+ GetSdrPageView()->PagePosToLogic(aRef);
+ }
+
+ ResizeMultMarkedObj(aRef, aWdt, aHgt, bChgWdh, bChgHgt);
+ }
+
+ // rotate
+ if (bRotate && (m_bRotateFreeAllowed || m_bRotate90Allowed)) {
+ Point aRef(nRotateX,nRotateY);
+
+ if(GetSdrPageView())
+ {
+ GetSdrPageView()->PagePosToLogic(aRef);
+ }
+
+ RotateMarkedObj(aRef,nRotateAngle);
+ }
+
+ // set rotation point position
+ if(bModeIsRotate && (nRotateX != nOldRotateX || nRotateY != nOldRotateY))
+ {
+ Point aNewRef1(nRotateX, nRotateY);
+
+ if(GetSdrPageView())
+ {
+ GetSdrPageView()->PagePosToLogic(aNewRef1);
+ }
+
+ SetRef1(aNewRef1);
+ }
+
+ // shear
+ if (bShear && m_bShearAllowed) {
+ Point aRef(nShearX,nShearY);
+
+ if(GetSdrPageView())
+ {
+ GetSdrPageView()->PagePosToLogic(aRef);
+ }
+
+ ShearMarkedObj(aRef,nShearAngle,bShearVert);
+
+ // #i74358#
+ // ShearMarkedObj creates a linear combination of the existing transformation and
+ // the new shear to apply. If the object is already transformed (e.g. rotated) the
+ // linear combination will not decompose to the same start values again, but to a
+ // new combination. Thus it makes no sense to check if the wanted shear is reached
+ // or not. Taking out.
+ }
+
+ // change position
+ if (bChgPos && m_bMoveAllowed) {
+ MoveMarkedObj(Size(nPosDX,nPosDY));
+ }
+
+ const size_t nMarkCount=rMarkList.GetMarkCount();
+ // protect position
+ if(const SfxBoolItem *pPoolItem = rAttr.GetItemIfSet(SID_ATTR_TRANSFORM_PROTECT_POS))
+ {
+ const bool bProtPos(pPoolItem->GetValue());
+ bool bChanged(false);
+
+ for(size_t i = 0; i < nMarkCount; ++i)
+ {
+ pObj = rMarkList.GetMark(i)->GetMarkedSdrObj();
+
+ if(pObj->IsMoveProtect() != bProtPos)
+ {
+ bChanged = true;
+ pObj->SetMoveProtect(bProtPos);
+
+ if(bProtPos)
+ {
+ pObj->SetResizeProtect(true);
+ }
+ }
+ }
+
+ if(bChanged)
+ {
+ m_bMoveProtect = bProtPos;
+
+ if(bProtPos)
+ {
+ m_bResizeProtect = true;
+ }
+
+ // #i77187# there is no simple method to get the toolbars updated
+ // in the application. The App is listening to selection change and i
+ // will use it here (even if not true). It's acceptable since changing
+ // this model data is pretty rare and only possible using the F4 dialog
+ MarkListHasChanged();
+ }
+ }
+
+ if(!m_bMoveProtect)
+ {
+ // protect size
+ if(const SfxBoolItem *pPoolItem = rAttr.GetItemIfSet(SID_ATTR_TRANSFORM_PROTECT_SIZE))
+ {
+ const bool bProtSize(pPoolItem->GetValue());
+ bool bChanged(false);
+
+ for(size_t i = 0; i < nMarkCount; ++i)
+ {
+ pObj = rMarkList.GetMark(i)->GetMarkedSdrObj();
+
+ if(pObj->IsResizeProtect() != bProtSize)
+ {
+ bChanged = true;
+ pObj->SetResizeProtect(bProtSize);
+ }
+ }
+
+ if(bChanged)
+ {
+ m_bResizeProtect = bProtSize;
+
+ // #i77187# see above
+ MarkListHasChanged();
+ }
+ }
+ }
+
+ EndUndo();
+}
+
+
+bool SdrEditView::IsAlignPossible() const
+{ // at least two selected objects, at least one of them movable
+ ForcePossibilities();
+ const size_t nCount=GetMarkedObjectCount();
+ if (nCount==0) return false; // nothing selected!
+ if (nCount==1) return m_bMoveAllowed; // align single object to page
+ return m_bOneOrMoreMovable; // otherwise: MarkCount>=2
+}
+
+void SdrEditView::AlignMarkedObjects(SdrHorAlign eHor, SdrVertAlign eVert)
+{
+ if (eHor==SdrHorAlign::NONE && eVert==SdrVertAlign::NONE)
+ return;
+
+ SortMarkedObjects();
+ if (!GetMarkedObjectCount())
+ return;
+
+ const bool bUndo = IsUndoEnabled();
+ if( bUndo )
+ {
+ EndTextEditCurrentView();
+ OUString aStr(GetDescriptionOfMarkedObjects());
+ if (eHor==SdrHorAlign::NONE)
+ {
+ switch (eVert)
+ {
+ case SdrVertAlign::Top:
+ aStr = ImpGetDescriptionString(STR_EditAlignVTop);
+ break;
+ case SdrVertAlign::Bottom:
+ aStr = ImpGetDescriptionString(STR_EditAlignVBottom);
+ break;
+ case SdrVertAlign::Center:
+ aStr = ImpGetDescriptionString(STR_EditAlignVCenter);
+ break;
+ default: break;
+ }
+ }
+ else if (eVert==SdrVertAlign::NONE)
+ {
+ switch (eHor)
+ {
+ case SdrHorAlign::Left:
+ aStr = ImpGetDescriptionString(STR_EditAlignHLeft);
+ break;
+ case SdrHorAlign::Right:
+ aStr = ImpGetDescriptionString(STR_EditAlignHRight);
+ break;
+ case SdrHorAlign::Center:
+ aStr = ImpGetDescriptionString(STR_EditAlignHCenter);
+ break;
+ default: break;
+ }
+ }
+ else if (eHor==SdrHorAlign::Center && eVert==SdrVertAlign::Center)
+ {
+ aStr = ImpGetDescriptionString(STR_EditAlignCenter);
+ }
+ else
+ {
+ aStr = ImpGetDescriptionString(STR_EditAlign);
+ }
+ BegUndo(aStr);
+ }
+
+ tools::Rectangle aBound;
+ const size_t nMarkCount=GetMarkedObjectCount();
+ bool bHasFixed=false;
+ for (size_t nm=0; nm<nMarkCount; ++nm)
+ {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ SdrObjTransformInfoRec aInfo;
+ pObj->TakeObjInfo(aInfo);
+ if (!aInfo.bMoveAllowed || pObj->IsMoveProtect())
+ {
+ tools::Rectangle aObjRect(pObj->GetSnapRect());
+ aBound.Union(aObjRect);
+ bHasFixed=true;
+ }
+ }
+ if (!bHasFixed)
+ {
+ if (nMarkCount==1)
+ { // align single object to page
+ const SdrObject* pObj=GetMarkedObjectByIndex(0);
+ const SdrPage* pPage=pObj->getSdrPageFromSdrObject();
+ const SdrPageGridFrameList* pGFL=pPage->GetGridFrameList(GetSdrPageViewOfMarkedByIndex(0),&(pObj->GetSnapRect()));
+ const SdrPageGridFrame* pFrame=nullptr;
+ if (pGFL!=nullptr && pGFL->GetCount()!=0)
+ { // Writer
+ pFrame=&((*pGFL)[0]);
+ }
+
+ if (pFrame!=nullptr)
+ { // Writer
+ aBound=pFrame->GetUserArea();
+ }
+ else
+ {
+ aBound=tools::Rectangle(pPage->GetLeftBorder(),pPage->GetUpperBorder(),
+ pPage->GetWidth()-pPage->GetRightBorder(),
+ pPage->GetHeight()-pPage->GetLowerBorder());
+ }
+ }
+ else
+ {
+ aBound=GetMarkedObjRect();
+ }
+ }
+ Point aCenter(aBound.Center());
+ for (size_t nm=0; nm<nMarkCount; ++nm)
+ {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ SdrObjTransformInfoRec aInfo;
+ pObj->TakeObjInfo(aInfo);
+ if (aInfo.bMoveAllowed && !pObj->IsMoveProtect())
+ {
+ tools::Long nXMov=0;
+ tools::Long nYMov=0;
+ tools::Rectangle aObjRect(pObj->GetSnapRect());
+ switch (eVert)
+ {
+ case SdrVertAlign::Top : nYMov=aBound.Top() -aObjRect.Top() ; break;
+ case SdrVertAlign::Bottom: nYMov=aBound.Bottom()-aObjRect.Bottom() ; break;
+ case SdrVertAlign::Center: nYMov=aCenter.Y() -aObjRect.Center().Y(); break;
+ default: break;
+ }
+ switch (eHor)
+ {
+ case SdrHorAlign::Left : nXMov=aBound.Left() -aObjRect.Left() ; break;
+ case SdrHorAlign::Right : nXMov=aBound.Right() -aObjRect.Right() ; break;
+ case SdrHorAlign::Center: nXMov=aCenter.X() -aObjRect.Center().X(); break;
+ default: break;
+ }
+ if (nXMov!=0 || nYMov!=0)
+ {
+ // SdrEdgeObj needs an extra SdrUndoGeoObj since the
+ // connections may need to be saved
+ if( bUndo )
+ {
+ if( dynamic_cast<SdrEdgeObj*>(pObj) )
+ {
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pObj));
+ }
+
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoMoveObject(*pObj,Size(nXMov,nYMov)));
+ }
+
+ pObj->Move(Size(nXMov,nYMov));
+ }
+ }
+ }
+
+ if( bUndo )
+ EndUndo();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdedtv2.cxx b/svx/source/svdraw/svdedtv2.cxx
new file mode 100644
index 000000000..50a7940ca
--- /dev/null
+++ b/svx/source/svdraw/svdedtv2.cxx
@@ -0,0 +1,2250 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/svdedtv.hxx>
+#include <svx/svdundo.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svdoutl.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svditer.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/sdooitm.hxx>
+#include <svx/sdshitm.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/xtextit0.hxx>
+#include "svdfmtf.hxx"
+#include <svdpdf.hxx>
+#include <svx/svdetc.hxx>
+#include <editeng/outlobj.hxx>
+#include <editeng/eeitem.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <svx/strings.hrc>
+#include <svx/svdoashp.hxx>
+#include <basegfx/polygon/b2dpolypolygoncutter.hxx>
+#include <i18nutil/unicode.hxx>
+#include <sal/log.hxx>
+#include <tools/debug.hxx>
+#include <memory>
+#include <vector>
+#include <vcl/graph.hxx>
+#include <svx/svxids.hrc>
+#include <dstribut_enum.hxx>
+#include <osl/diagnose.h>
+
+using namespace com::sun::star;
+
+SdrObject* SdrEditView::GetMaxToTopObj(SdrObject* /*pObj*/) const
+{
+ return nullptr;
+}
+
+SdrObject* SdrEditView::GetMaxToBtmObj(SdrObject* /*pObj*/) const
+{
+ return nullptr;
+}
+
+void SdrEditView::ObjOrderChanged(SdrObject* /*pObj*/, size_t /*nOldPos*/, size_t /*nNewPos*/)
+{
+}
+
+void SdrEditView::MovMarkedToTop()
+{
+ const size_t nCount=GetMarkedObjectCount();
+ if (nCount==0)
+ return;
+
+ const bool bUndo = IsUndoEnabled();
+
+ if( bUndo )
+ BegUndo(SvxResId(STR_EditMovToTop),GetDescriptionOfMarkedObjects(),SdrRepeatFunc::MoveToTop);
+
+ SortMarkedObjects();
+ for (size_t nm=0; nm<nCount; ++nm)
+ { // All Ordnums have to be correct!
+ GetMarkedObjectByIndex(nm)->GetOrdNum();
+ }
+ bool bChg=false;
+ SdrObjList* pOL0=nullptr;
+ size_t nNewPos=0;
+ for (size_t nm=nCount; nm>0;)
+ {
+ --nm;
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
+ if (pOL!=pOL0)
+ {
+ nNewPos = pOL->GetObjCount()-1;
+ pOL0=pOL;
+ }
+ const size_t nNowPos = pObj->GetOrdNumDirect();
+ const tools::Rectangle& rBR=pObj->GetCurrentBoundRect();
+ size_t nCmpPos = nNowPos+1;
+ SdrObject* pMaxObj=GetMaxToTopObj(pObj);
+ if (pMaxObj!=nullptr)
+ {
+ size_t nMaxPos=pMaxObj->GetOrdNum();
+ if (nMaxPos!=0)
+ nMaxPos--;
+ if (nNewPos>nMaxPos)
+ nNewPos=nMaxPos; // neither go faster...
+ if (nNewPos<nNowPos)
+ nNewPos=nNowPos; // nor go in the other direction
+ }
+ bool bEnd=false;
+ while (nCmpPos<nNewPos && !bEnd)
+ {
+ SdrObject* pCmpObj=pOL->GetObj(nCmpPos);
+ if (pCmpObj==nullptr)
+ {
+ OSL_FAIL("MovMarkedToTop(): Reference object not found.");
+ bEnd=true;
+ }
+ else if (pCmpObj==pMaxObj)
+ {
+ nNewPos=nCmpPos;
+ nNewPos--;
+ bEnd=true;
+ }
+ else if (rBR.Overlaps(pCmpObj->GetCurrentBoundRect()))
+ {
+ nNewPos=nCmpPos;
+ bEnd=true;
+ }
+ else
+ {
+ nCmpPos++;
+ }
+ }
+ if (nNowPos!=nNewPos)
+ {
+ bChg=true;
+ pOL->SetObjectOrdNum(nNowPos,nNewPos);
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj,nNowPos,nNewPos));
+ ObjOrderChanged(pObj,nNowPos,nNewPos);
+ }
+ nNewPos--;
+ }
+
+ if( bUndo )
+ EndUndo();
+
+ if (bChg)
+ MarkListHasChanged();
+}
+
+void SdrEditView::MovMarkedToBtm()
+{
+ const size_t nCount=GetMarkedObjectCount();
+ if (nCount==0)
+ return;
+
+ const bool bUndo = IsUndoEnabled();
+
+ if( bUndo )
+ BegUndo(SvxResId(STR_EditMovToBtm),GetDescriptionOfMarkedObjects(),SdrRepeatFunc::MoveToBottom);
+
+ SortMarkedObjects();
+ for (size_t nm=0; nm<nCount; ++nm)
+ { // All Ordnums have to be correct!
+ GetMarkedObjectByIndex(nm)->GetOrdNum();
+ }
+
+ bool bChg=false;
+ SdrObjList* pOL0=nullptr;
+ size_t nNewPos=0;
+ for (size_t nm=0; nm<nCount; ++nm)
+ {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
+ if (pOL!=pOL0)
+ {
+ nNewPos=0;
+ pOL0=pOL;
+ }
+ const size_t nNowPos = pObj->GetOrdNumDirect();
+ const tools::Rectangle& rBR=pObj->GetCurrentBoundRect();
+ size_t nCmpPos = nNowPos;
+ if (nCmpPos>0)
+ --nCmpPos;
+ SdrObject* pMaxObj=GetMaxToBtmObj(pObj);
+ if (pMaxObj!=nullptr)
+ {
+ const size_t nMinPos=pMaxObj->GetOrdNum()+1;
+ if (nNewPos<nMinPos)
+ nNewPos=nMinPos; // neither go faster...
+ if (nNewPos>nNowPos)
+ nNewPos=nNowPos; // nor go in the other direction
+ }
+ bool bEnd=false;
+ // nNewPos in this case is the "maximum" position
+ // the object may reach without going faster than the object before
+ // it (multiple selection).
+ while (nCmpPos>nNewPos && !bEnd)
+ {
+ SdrObject* pCmpObj=pOL->GetObj(nCmpPos);
+ if (pCmpObj==nullptr)
+ {
+ OSL_FAIL("MovMarkedToBtm(): Reference object not found.");
+ bEnd=true;
+ }
+ else if (pCmpObj==pMaxObj)
+ {
+ nNewPos=nCmpPos;
+ nNewPos++;
+ bEnd=true;
+ }
+ else if (rBR.Overlaps(pCmpObj->GetCurrentBoundRect()))
+ {
+ nNewPos=nCmpPos;
+ bEnd=true;
+ }
+ else
+ {
+ nCmpPos--;
+ }
+ }
+ if (nNowPos!=nNewPos)
+ {
+ bChg=true;
+ pOL->SetObjectOrdNum(nNowPos,nNewPos);
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj,nNowPos,nNewPos));
+ ObjOrderChanged(pObj,nNowPos,nNewPos);
+ }
+ nNewPos++;
+ }
+
+ if(bUndo)
+ EndUndo();
+
+ if(bChg)
+ MarkListHasChanged();
+}
+
+void SdrEditView::PutMarkedToTop()
+{
+ PutMarkedInFrontOfObj(nullptr);
+}
+
+void SdrEditView::PutMarkedInFrontOfObj(const SdrObject* pRefObj)
+{
+ const size_t nCount=GetMarkedObjectCount();
+ if (nCount==0)
+ return;
+
+ const bool bUndo = IsUndoEnabled();
+ if( bUndo )
+ BegUndo(SvxResId(STR_EditPutToTop),GetDescriptionOfMarkedObjects(),SdrRepeatFunc::PutToTop);
+
+ SortMarkedObjects();
+
+ if (pRefObj!=nullptr)
+ {
+ // Make "in front of the object" work, even if the
+ // selected objects are already in front of the other object
+ const size_t nRefMark=TryToFindMarkedObject(pRefObj);
+ SdrMark aRefMark;
+ if (nRefMark!=SAL_MAX_SIZE)
+ {
+ aRefMark=*GetSdrMarkByIndex(nRefMark);
+ GetMarkedObjectListWriteAccess().DeleteMark(nRefMark);
+ }
+ PutMarkedToBtm();
+ if (nRefMark!=SAL_MAX_SIZE)
+ {
+ GetMarkedObjectListWriteAccess().InsertEntry(aRefMark);
+ SortMarkedObjects();
+ }
+ }
+ for (size_t nm=0; nm<nCount; ++nm)
+ { // All Ordnums have to be correct!
+ GetMarkedObjectByIndex(nm)->GetOrdNum();
+ }
+ bool bChg=false;
+ SdrObjList* pOL0=nullptr;
+ size_t nNewPos=0;
+ for (size_t nm=nCount; nm>0;)
+ {
+ --nm;
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ if (pObj!=pRefObj)
+ {
+ SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
+ if (pOL!=pOL0)
+ {
+ nNewPos=pOL->GetObjCount()-1;
+ pOL0=pOL;
+ }
+ const size_t nNowPos=pObj->GetOrdNumDirect();
+ SdrObject* pMaxObj=GetMaxToTopObj(pObj);
+ if (pMaxObj!=nullptr)
+ {
+ size_t nMaxOrd=pMaxObj->GetOrdNum(); // sadly doesn't work any other way
+ if (nMaxOrd>0)
+ nMaxOrd--;
+ if (nNewPos>nMaxOrd)
+ nNewPos=nMaxOrd; // neither go faster...
+ if (nNewPos<nNowPos)
+ nNewPos=nNowPos; // nor go into the other direction
+ }
+ if (pRefObj!=nullptr)
+ {
+ if (pRefObj->getParentSdrObjListFromSdrObject()==pObj->getParentSdrObjListFromSdrObject())
+ {
+ const size_t nMaxOrd=pRefObj->GetOrdNum(); // sadly doesn't work any other way
+ if (nNewPos>nMaxOrd)
+ nNewPos=nMaxOrd; // neither go faster...
+ if (nNewPos<nNowPos)
+ nNewPos=nNowPos; // nor go into the other direction
+ }
+ else
+ {
+ nNewPos=nNowPos; // different PageView, so don't change
+ }
+ }
+ if (nNowPos!=nNewPos)
+ {
+ bChg=true;
+ pOL->SetObjectOrdNum(nNowPos,nNewPos);
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj,nNowPos,nNewPos));
+ ObjOrderChanged(pObj,nNowPos,nNewPos);
+ }
+ nNewPos--;
+ } // if (pObj!=pRefObj)
+ } // for loop over all selected objects
+
+ if( bUndo )
+ EndUndo();
+
+ if(bChg)
+ MarkListHasChanged();
+}
+
+void SdrEditView::PutMarkedToBtm()
+{
+ PutMarkedBehindObj(nullptr);
+}
+
+void SdrEditView::PutMarkedBehindObj(const SdrObject* pRefObj)
+{
+ const size_t nCount=GetMarkedObjectCount();
+ if (nCount==0)
+ return;
+
+ const bool bUndo = IsUndoEnabled();
+
+ if( bUndo )
+ BegUndo(SvxResId(STR_EditPutToBtm),GetDescriptionOfMarkedObjects(),SdrRepeatFunc::PutToBottom);
+
+ SortMarkedObjects();
+ if (pRefObj!=nullptr)
+ {
+ // Make "behind the object" work, even if the
+ // selected objects are already behind the other object
+ const size_t nRefMark=TryToFindMarkedObject(pRefObj);
+ SdrMark aRefMark;
+ if (nRefMark!=SAL_MAX_SIZE)
+ {
+ aRefMark=*GetSdrMarkByIndex(nRefMark);
+ GetMarkedObjectListWriteAccess().DeleteMark(nRefMark);
+ }
+ PutMarkedToTop();
+ if (nRefMark!=SAL_MAX_SIZE)
+ {
+ GetMarkedObjectListWriteAccess().InsertEntry(aRefMark);
+ SortMarkedObjects();
+ }
+ }
+ for (size_t nm=0; nm<nCount; ++nm) { // All Ordnums have to be correct!
+ GetMarkedObjectByIndex(nm)->GetOrdNum();
+ }
+ bool bChg=false;
+ SdrObjList* pOL0=nullptr;
+ size_t nNewPos=0;
+ for (size_t nm=0; nm<nCount; ++nm) {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ if (pObj!=pRefObj) {
+ SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
+ if (pOL!=pOL0) {
+ nNewPos=0;
+ pOL0=pOL;
+ }
+ const size_t nNowPos=pObj->GetOrdNumDirect();
+ SdrObject* pMinObj=GetMaxToBtmObj(pObj);
+ if (pMinObj!=nullptr) {
+ const size_t nMinOrd=pMinObj->GetOrdNum()+1; // sadly doesn't work any differently
+ if (nNewPos<nMinOrd) nNewPos=nMinOrd; // neither go faster...
+ if (nNewPos>nNowPos) nNewPos=nNowPos; // nor go into the other direction
+ }
+ if (pRefObj!=nullptr) {
+ if (pRefObj->getParentSdrObjListFromSdrObject()==pObj->getParentSdrObjListFromSdrObject()) {
+ const size_t nMinOrd=pRefObj->GetOrdNum(); // sadly doesn't work any differently
+ if (nNewPos<nMinOrd) nNewPos=nMinOrd; // neither go faster...
+ if (nNewPos>nNowPos) nNewPos=nNowPos; // nor go into the other direction
+ } else {
+ nNewPos=nNowPos; // different PageView, so don't change
+ }
+ }
+ if (nNowPos!=nNewPos) {
+ bChg=true;
+ pOL->SetObjectOrdNum(nNowPos,nNewPos);
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj,nNowPos,nNewPos));
+ ObjOrderChanged(pObj,nNowPos,nNewPos);
+ }
+ nNewPos++;
+ } // if (pObj!=pRefObj)
+ } // for loop over all selected objects
+
+ if(bUndo)
+ EndUndo();
+
+ if(bChg)
+ MarkListHasChanged();
+
+}
+
+void SdrEditView::ReverseOrderOfMarked()
+{
+ SortMarkedObjects();
+ const size_t nMarkCount=GetMarkedObjectCount();
+ if (nMarkCount<=0)
+ return;
+
+ bool bChg=false;
+
+ bool bUndo = IsUndoEnabled();
+ if( bUndo )
+ BegUndo(SvxResId(STR_EditRevOrder),GetDescriptionOfMarkedObjects(),SdrRepeatFunc::ReverseOrder);
+
+ size_t a=0;
+ do {
+ // take into account selection across multiple PageViews
+ size_t b=a+1;
+ while (b<nMarkCount && GetSdrPageViewOfMarkedByIndex(b) == GetSdrPageViewOfMarkedByIndex(a)) ++b;
+ --b;
+ SdrObjList* pOL=GetSdrPageViewOfMarkedByIndex(a)->GetObjList();
+ size_t c=b;
+ if (a<c) { // make sure OrdNums aren't dirty
+ GetMarkedObjectByIndex(a)->GetOrdNum();
+ }
+ while (a<c) {
+ SdrObject* pObj1=GetMarkedObjectByIndex(a);
+ SdrObject* pObj2=GetMarkedObjectByIndex(c);
+ const size_t nOrd1=pObj1->GetOrdNumDirect();
+ const size_t nOrd2=pObj2->GetOrdNumDirect();
+ if( bUndo )
+ {
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj1,nOrd1,nOrd2));
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj2,nOrd2-1,nOrd1));
+ }
+ pOL->SetObjectOrdNum(nOrd1,nOrd2);
+ // Obj 2 has moved forward by one position, so now nOrd2-1
+ pOL->SetObjectOrdNum(nOrd2-1,nOrd1);
+ // use Replace instead of SetOrdNum for performance reasons (recalculation of Ordnums)
+ ++a;
+ --c;
+ bChg=true;
+ }
+ a=b+1;
+ } while (a<nMarkCount);
+
+ if(bUndo)
+ EndUndo();
+
+ if(bChg)
+ MarkListHasChanged();
+}
+
+void SdrEditView::ImpCheckToTopBtmPossible()
+{
+ const size_t nCount=GetMarkedObjectCount();
+ if (nCount==0)
+ return;
+ if (nCount==1)
+ { // special-casing for single selection
+ SdrObject* pObj=GetMarkedObjectByIndex(0);
+ SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
+ SAL_WARN_IF(!pOL, "svx", "Object somehow has no ObjList");
+ size_t nMax = pOL ? pOL->GetObjCount() : 0;
+ size_t nMin = 0;
+ const size_t nObjNum=pObj->GetOrdNum();
+ SdrObject* pRestrict=GetMaxToTopObj(pObj);
+ if (pRestrict!=nullptr) {
+ const size_t nRestrict=pRestrict->GetOrdNum();
+ if (nRestrict<nMax) nMax=nRestrict;
+ }
+ pRestrict=GetMaxToBtmObj(pObj);
+ if (pRestrict!=nullptr) {
+ const size_t nRestrict=pRestrict->GetOrdNum();
+ if (nRestrict>nMin) nMin=nRestrict;
+ }
+ m_bToTopPossible=nObjNum<nMax-1;
+ m_bToBtmPossible=nObjNum>nMin;
+ } else { // multiple selection
+ SdrObjList* pOL0=nullptr;
+ size_t nPos0 = 0;
+ for (size_t nm = 0; !m_bToBtmPossible && nm<nCount; ++nm) { // check 'send to background'
+ SdrObject* pObj=GetMarkedObjectByIndex(nm);
+ SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
+ if (pOL!=pOL0) {
+ nPos0 = 0;
+ pOL0=pOL;
+ }
+ const size_t nPos = pObj->GetOrdNum();
+ m_bToBtmPossible = nPos && (nPos-1 > nPos0);
+ nPos0 = nPos;
+ }
+
+ pOL0=nullptr;
+ nPos0 = SAL_MAX_SIZE;
+ for (size_t nm=nCount; !m_bToTopPossible && nm>0; ) { // check 'bring to front'
+ --nm;
+ SdrObject* pObj=GetMarkedObjectByIndex(nm);
+ SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
+ if (pOL!=pOL0) {
+ nPos0=pOL->GetObjCount();
+ pOL0=pOL;
+ }
+ const size_t nPos = pObj->GetOrdNum();
+ m_bToTopPossible = nPos+1 < nPos0;
+ nPos0=nPos;
+ }
+ }
+}
+
+
+// Combine
+
+
+void SdrEditView::ImpCopyAttributes(const SdrObject* pSource, SdrObject* pDest) const
+{
+ if (pSource!=nullptr) {
+ SdrObjList* pOL=pSource->GetSubList();
+ if (pOL!=nullptr && !pSource->Is3DObj()) { // get first non-group object from group
+ SdrObjListIter aIter(pOL,SdrIterMode::DeepNoGroups);
+ pSource=aIter.Next();
+ }
+ }
+
+ if(!(pSource && pDest))
+ return;
+
+ SfxItemSetFixed<SDRATTR_START, SDRATTR_NOTPERSIST_FIRST-1,
+ SDRATTR_NOTPERSIST_LAST+1, SDRATTR_END,
+ EE_ITEMS_START, EE_ITEMS_END> aSet(mpModel->GetItemPool());
+
+ aSet.Put(pSource->GetMergedItemSet());
+
+ pDest->ClearMergedItem();
+ pDest->SetMergedItemSet(aSet);
+
+ pDest->NbcSetLayer(pSource->GetLayer());
+ pDest->NbcSetStyleSheet(pSource->GetStyleSheet(), true);
+}
+
+bool SdrEditView::ImpCanConvertForCombine1(const SdrObject* pObj)
+{
+ // new condition IsLine() to be able to combine simple Lines
+ bool bIsLine(false);
+
+ const SdrPathObj* pPath = dynamic_cast< const SdrPathObj*>( pObj );
+
+ if(pPath)
+ {
+ bIsLine = pPath->IsLine();
+ }
+
+ SdrObjTransformInfoRec aInfo;
+ pObj->TakeObjInfo(aInfo);
+
+ return (aInfo.bCanConvToPath || aInfo.bCanConvToPoly || bIsLine);
+}
+
+bool SdrEditView::ImpCanConvertForCombine(const SdrObject* pObj)
+{
+ SdrObjList* pOL = pObj->GetSubList();
+
+ if(pOL && !pObj->Is3DObj())
+ {
+ SdrObjListIter aIter(pOL, SdrIterMode::DeepNoGroups);
+
+ while(aIter.IsMore())
+ {
+ SdrObject* pObj1 = aIter.Next();
+
+ // all members of a group have to be convertible
+ if(!ImpCanConvertForCombine1(pObj1))
+ {
+ return false;
+ }
+ }
+ }
+ else
+ {
+ if(!ImpCanConvertForCombine1(pObj))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+basegfx::B2DPolyPolygon SdrEditView::ImpGetPolyPolygon1(const SdrObject* pObj)
+{
+ basegfx::B2DPolyPolygon aRetval;
+ const SdrPathObj* pPath = dynamic_cast<const SdrPathObj*>( pObj );
+
+ if(pPath && !pObj->GetOutlinerParaObject())
+ {
+ aRetval = pPath->GetPathPoly();
+ }
+ else
+ {
+ SdrObjectUniquePtr pConvObj = pObj->ConvertToPolyObj(true/*bCombine*/, false);
+
+ if(pConvObj)
+ {
+ SdrObjList* pOL = pConvObj->GetSubList();
+
+ if(pOL)
+ {
+ SdrObjListIter aIter(pOL, SdrIterMode::DeepNoGroups);
+
+ while(aIter.IsMore())
+ {
+ SdrObject* pObj1 = aIter.Next();
+ pPath = dynamic_cast<SdrPathObj*>( pObj1 );
+
+ if(pPath)
+ {
+ aRetval.append(pPath->GetPathPoly());
+ }
+ }
+ }
+ else
+ {
+ pPath = dynamic_cast<SdrPathObj*>( pConvObj.get() );
+
+ if(pPath)
+ {
+ aRetval = pPath->GetPathPoly();
+ }
+ }
+ }
+ }
+
+ return aRetval;
+}
+
+basegfx::B2DPolyPolygon SdrEditView::ImpGetPolyPolygon(const SdrObject* pObj)
+{
+ SdrObjList* pOL = pObj->GetSubList();
+
+ if(pOL && !pObj->Is3DObj())
+ {
+ basegfx::B2DPolyPolygon aRetval;
+ SdrObjListIter aIter(pOL, SdrIterMode::DeepNoGroups);
+
+ while(aIter.IsMore())
+ {
+ SdrObject* pObj1 = aIter.Next();
+ aRetval.append(ImpGetPolyPolygon1(pObj1));
+ }
+
+ return aRetval;
+ }
+ else
+ {
+ return ImpGetPolyPolygon1(pObj);
+ }
+}
+
+basegfx::B2DPolygon SdrEditView::ImpCombineToSinglePolygon(const basegfx::B2DPolyPolygon& rPolyPolygon)
+{
+ const sal_uInt32 nPolyCount(rPolyPolygon.count());
+
+ if(0 == nPolyCount)
+ {
+ return basegfx::B2DPolygon();
+ }
+ else if(1 == nPolyCount)
+ {
+ return rPolyPolygon.getB2DPolygon(0);
+ }
+ else
+ {
+ basegfx::B2DPolygon aRetval(rPolyPolygon.getB2DPolygon(0));
+
+ for(sal_uInt32 a(1); a < nPolyCount; a++)
+ {
+ basegfx::B2DPolygon aCandidate(rPolyPolygon.getB2DPolygon(a));
+
+ if(aRetval.count())
+ {
+ if(aCandidate.count())
+ {
+ const basegfx::B2DPoint aCA(aCandidate.getB2DPoint(0));
+ const basegfx::B2DPoint aCB(aCandidate.getB2DPoint(aCandidate.count() - 1));
+ const basegfx::B2DPoint aRA(aRetval.getB2DPoint(0));
+ const basegfx::B2DPoint aRB(aRetval.getB2DPoint(aRetval.count() - 1));
+
+ const double fRACA(basegfx::B2DVector(aCA - aRA).getLength());
+ const double fRACB(basegfx::B2DVector(aCB - aRA).getLength());
+ const double fRBCA(basegfx::B2DVector(aCA - aRB).getLength());
+ const double fRBCB(basegfx::B2DVector(aCB - aRB).getLength());
+
+ const double fSmallestRA(std::min(fRACA, fRACB));
+ const double fSmallestRB(std::min(fRBCA, fRBCB));
+
+ if(fSmallestRA < fSmallestRB)
+ {
+ // flip result
+ aRetval.flip();
+ }
+
+ const double fSmallestCA(std::min(fRACA, fRBCA));
+ const double fSmallestCB(std::min(fRACB, fRBCB));
+
+ if(fSmallestCB < fSmallestCA)
+ {
+ // flip candidate
+ aCandidate.flip();
+ }
+
+ // append candidate to retval
+ aRetval.append(aCandidate);
+ }
+ }
+ else
+ {
+ aRetval = aCandidate;
+ }
+ }
+
+ return aRetval;
+ }
+}
+
+namespace {
+
+// for distribution dialog function
+struct ImpDistributeEntry
+{
+ SdrObject* mpObj;
+ sal_Int32 mnPos;
+ sal_Int32 mnLength;
+};
+
+}
+
+typedef std::vector<ImpDistributeEntry> ImpDistributeEntryList;
+
+void SdrEditView::DistributeMarkedObjects(sal_uInt16 SlotID)
+{
+ const size_t nMark(GetMarkedObjectCount());
+
+ if(nMark <= 2)
+ return;
+
+ SvxDistributeHorizontal eHor = SvxDistributeHorizontal::NONE;
+ SvxDistributeVertical eVer = SvxDistributeVertical::NONE;
+
+ switch (SlotID)
+ {
+ case SID_DISTRIBUTE_HLEFT: eHor = SvxDistributeHorizontal::Left; break;
+ case SID_DISTRIBUTE_HCENTER: eHor = SvxDistributeHorizontal::Center; break;
+ case SID_DISTRIBUTE_HDISTANCE: eHor = SvxDistributeHorizontal::Distance; break;
+ case SID_DISTRIBUTE_HRIGHT: eHor = SvxDistributeHorizontal::Right; break;
+ case SID_DISTRIBUTE_VTOP: eVer = SvxDistributeVertical::Top; break;
+ case SID_DISTRIBUTE_VCENTER: eVer = SvxDistributeVertical::Center; break;
+ case SID_DISTRIBUTE_VDISTANCE: eVer = SvxDistributeVertical::Distance; break;
+ case SID_DISTRIBUTE_VBOTTOM: eVer = SvxDistributeVertical::Bottom; break;
+ }
+
+ ImpDistributeEntryList aEntryList;
+ ImpDistributeEntryList::iterator itEntryList;
+ sal_uInt32 nFullLength;
+
+ const bool bUndo = IsUndoEnabled();
+ if( bUndo )
+ BegUndo();
+
+ if(eHor != SvxDistributeHorizontal::NONE)
+ {
+ // build sorted entry list
+ nFullLength = 0;
+
+ for( size_t a = 0; a < nMark; ++a )
+ {
+ SdrMark* pMark = GetSdrMarkByIndex(a);
+ ImpDistributeEntry aNew;
+
+ aNew.mpObj = pMark->GetMarkedSdrObj();
+
+ switch(eHor)
+ {
+ case SvxDistributeHorizontal::Left:
+ {
+ aNew.mnPos = aNew.mpObj->GetSnapRect().Left();
+ break;
+ }
+ case SvxDistributeHorizontal::Center:
+ {
+ aNew.mnPos = (aNew.mpObj->GetSnapRect().Right() + aNew.mpObj->GetSnapRect().Left()) / 2;
+ break;
+ }
+ case SvxDistributeHorizontal::Distance:
+ {
+ aNew.mnLength = aNew.mpObj->GetSnapRect().GetWidth() + 1;
+ nFullLength += aNew.mnLength;
+ aNew.mnPos = (aNew.mpObj->GetSnapRect().Right() + aNew.mpObj->GetSnapRect().Left()) / 2;
+ break;
+ }
+ case SvxDistributeHorizontal::Right:
+ {
+ aNew.mnPos = aNew.mpObj->GetSnapRect().Right();
+ break;
+ }
+ default: break;
+ }
+
+ itEntryList = std::find_if(aEntryList.begin(), aEntryList.end(),
+ [&aNew](const ImpDistributeEntry& rEntry) { return rEntry.mnPos >= aNew.mnPos; });
+ if ( itEntryList < aEntryList.end() )
+ aEntryList.insert( itEntryList, aNew );
+ else
+ aEntryList.push_back( aNew );
+ }
+
+ if(eHor == SvxDistributeHorizontal::Distance)
+ {
+ // calculate room in-between
+ sal_Int32 nWidth = GetAllMarkedBoundRect().GetWidth() + 1;
+ double fStepWidth = (static_cast<double>(nWidth) - static_cast<double>(nFullLength)) / static_cast<double>(aEntryList.size() - 1);
+ double fStepStart = static_cast<double>(aEntryList[ 0 ].mnPos);
+ fStepStart += fStepWidth + static_cast<double>((aEntryList[ 0 ].mnLength + aEntryList[ 1 ].mnLength) / 2);
+
+ // move entries 1..n-1
+ for( size_t i = 1, n = aEntryList.size()-1; i < n; ++i )
+ {
+ ImpDistributeEntry& rCurr = aEntryList[ i ];
+ ImpDistributeEntry& rNext = aEntryList[ i + 1];
+ sal_Int32 nDelta = static_cast<sal_Int32>(fStepStart + 0.5) - rCurr.mnPos;
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*rCurr.mpObj));
+ rCurr.mpObj->Move(Size(nDelta, 0));
+ fStepStart += fStepWidth + static_cast<double>((rCurr.mnLength + rNext.mnLength) / 2);
+ }
+ }
+ else
+ {
+ // calculate distances
+ sal_Int32 nWidth = aEntryList[ aEntryList.size() - 1 ].mnPos - aEntryList[ 0 ].mnPos;
+ double fStepWidth = static_cast<double>(nWidth) / static_cast<double>(aEntryList.size() - 1);
+ double fStepStart = static_cast<double>(aEntryList[ 0 ].mnPos);
+ fStepStart += fStepWidth;
+
+ // move entries 1..n-1
+ for( size_t i = 1 ; i < aEntryList.size()-1 ; ++i )
+ {
+ ImpDistributeEntry& rCurr = aEntryList[ i ];
+ sal_Int32 nDelta = static_cast<sal_Int32>(fStepStart + 0.5) - rCurr.mnPos;
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*rCurr.mpObj));
+ rCurr.mpObj->Move(Size(nDelta, 0));
+ fStepStart += fStepWidth;
+ }
+ }
+
+ // clear list
+ aEntryList.clear();
+ }
+
+ if(eVer != SvxDistributeVertical::NONE)
+ {
+ // build sorted entry list
+ nFullLength = 0;
+
+ for( size_t a = 0; a < nMark; ++a )
+ {
+ SdrMark* pMark = GetSdrMarkByIndex(a);
+ ImpDistributeEntry aNew;
+
+ aNew.mpObj = pMark->GetMarkedSdrObj();
+
+ switch(eVer)
+ {
+ case SvxDistributeVertical::Top:
+ {
+ aNew.mnPos = aNew.mpObj->GetSnapRect().Top();
+ break;
+ }
+ case SvxDistributeVertical::Center:
+ {
+ aNew.mnPos = (aNew.mpObj->GetSnapRect().Bottom() + aNew.mpObj->GetSnapRect().Top()) / 2;
+ break;
+ }
+ case SvxDistributeVertical::Distance:
+ {
+ aNew.mnLength = aNew.mpObj->GetSnapRect().GetHeight() + 1;
+ nFullLength += aNew.mnLength;
+ aNew.mnPos = (aNew.mpObj->GetSnapRect().Bottom() + aNew.mpObj->GetSnapRect().Top()) / 2;
+ break;
+ }
+ case SvxDistributeVertical::Bottom:
+ {
+ aNew.mnPos = aNew.mpObj->GetSnapRect().Bottom();
+ break;
+ }
+ default: break;
+ }
+
+ itEntryList = std::find_if(aEntryList.begin(), aEntryList.end(),
+ [&aNew](const ImpDistributeEntry& rEntry) { return rEntry.mnPos >= aNew.mnPos; });
+ if ( itEntryList < aEntryList.end() )
+ aEntryList.insert( itEntryList, aNew );
+ else
+ aEntryList.push_back( aNew );
+ }
+
+ if(eVer == SvxDistributeVertical::Distance)
+ {
+ // calculate room in-between
+ sal_Int32 nHeight = GetAllMarkedBoundRect().GetHeight() + 1;
+ double fStepWidth = (static_cast<double>(nHeight) - static_cast<double>(nFullLength)) / static_cast<double>(aEntryList.size() - 1);
+ double fStepStart = static_cast<double>(aEntryList[ 0 ].mnPos);
+ fStepStart += fStepWidth + static_cast<double>((aEntryList[ 0 ].mnLength + aEntryList[ 1 ].mnLength) / 2);
+
+ // move entries 1..n-1
+ for( size_t i = 1, n = aEntryList.size()-1; i < n; ++i)
+ {
+ ImpDistributeEntry& rCurr = aEntryList[ i ];
+ ImpDistributeEntry& rNext = aEntryList[ i + 1 ];
+ sal_Int32 nDelta = static_cast<sal_Int32>(fStepStart + 0.5) - rCurr.mnPos;
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*rCurr.mpObj));
+ rCurr.mpObj->Move(Size(0, nDelta));
+ fStepStart += fStepWidth + static_cast<double>((rCurr.mnLength + rNext.mnLength) / 2);
+ }
+ }
+ else
+ {
+ // calculate distances
+ sal_Int32 nHeight = aEntryList[ aEntryList.size() - 1 ].mnPos - aEntryList[ 0 ].mnPos;
+ double fStepWidth = static_cast<double>(nHeight) / static_cast<double>(aEntryList.size() - 1);
+ double fStepStart = static_cast<double>(aEntryList[ 0 ].mnPos);
+ fStepStart += fStepWidth;
+
+ // move entries 1..n-1
+ for(size_t i = 1, n = aEntryList.size()-1; i < n; ++i)
+ {
+ ImpDistributeEntry& rCurr = aEntryList[ i ];
+ sal_Int32 nDelta = static_cast<sal_Int32>(fStepStart + 0.5) - rCurr.mnPos;
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*rCurr.mpObj));
+ rCurr.mpObj->Move(Size(0, nDelta));
+ fStepStart += fStepWidth;
+ }
+ }
+
+ // clear list
+ aEntryList.clear();
+ }
+
+ // UNDO-Comment and end of UNDO
+ mpModel->SetUndoComment(SvxResId(STR_DistributeMarkedObjects));
+
+ if( bUndo )
+ EndUndo();
+}
+
+void SdrEditView::MergeMarkedObjects(SdrMergeMode eMode)
+{
+ // #i73441# check content
+ if(!AreObjectsMarked())
+ return;
+
+ SdrMarkList aRemove;
+ SortMarkedObjects();
+
+ const bool bUndo = IsUndoEnabled();
+
+ if( bUndo )
+ BegUndo();
+
+ size_t nInsPos = SAL_MAX_SIZE;
+ const SdrObject* pAttrObj = nullptr;
+ basegfx::B2DPolyPolygon aMergePolyPolygonA;
+ basegfx::B2DPolyPolygon aMergePolyPolygonB;
+
+ SdrObjList* pInsOL = nullptr;
+ SdrPageView* pInsPV = nullptr;
+ bool bFirstObjectComplete(false);
+
+ // make sure selected objects are contour objects
+ // since now basegfx::utils::adaptiveSubdivide() is used, it is no longer
+ // necessary to use ConvertMarkedToPolyObj which will subdivide curves using the old
+ // mechanisms. In a next step the polygon clipper will even be able to clip curves...
+ // ConvertMarkedToPolyObj(true);
+ ConvertMarkedToPathObj(true);
+ OSL_ENSURE(AreObjectsMarked(), "no more objects selected after preparations (!)");
+
+ for(size_t a=0; a<GetMarkedObjectCount(); ++a)
+ {
+ SdrMark* pM = GetSdrMarkByIndex(a);
+ SdrObject* pObj = pM->GetMarkedSdrObj();
+
+ if(ImpCanConvertForCombine(pObj))
+ {
+ if(!pAttrObj)
+ pAttrObj = pObj;
+
+ nInsPos = pObj->GetOrdNum() + 1;
+ pInsPV = pM->GetPageView();
+ pInsOL = pObj->getParentSdrObjListFromSdrObject();
+
+ // #i76891# use single iteration from SJ here which works on SdrObjects and takes
+ // groups into account by itself
+ SdrObjListIter aIter(*pObj, SdrIterMode::DeepWithGroups);
+
+ while(aIter.IsMore())
+ {
+ SdrObject* pCandidate = aIter.Next();
+ SdrPathObj* pPathObj = dynamic_cast<SdrPathObj*>( pCandidate );
+ if(pPathObj)
+ {
+ basegfx::B2DPolyPolygon aTmpPoly(pPathObj->GetPathPoly());
+
+ // #i76891# unfortunately ConvertMarkedToPathObj has converted all
+ // involved polygon data to curve segments, even if not necessary.
+ // It is better to try to reduce to more simple polygons.
+ aTmpPoly = basegfx::utils::simplifyCurveSegments(aTmpPoly);
+
+ // for each part polygon as preparation, remove self-intersections
+ // correct orientations and get rid of possible neutral polygons.
+ aTmpPoly = basegfx::utils::prepareForPolygonOperation(aTmpPoly);
+
+ if(!bFirstObjectComplete)
+ {
+ // #i111987# Also need to collect ORed source shape when more than
+ // a single polygon is involved
+ if(aMergePolyPolygonA.count())
+ {
+ aMergePolyPolygonA = basegfx::utils::solvePolygonOperationOr(aMergePolyPolygonA, aTmpPoly);
+ }
+ else
+ {
+ aMergePolyPolygonA = aTmpPoly;
+ }
+ }
+ else
+ {
+ if(aMergePolyPolygonB.count())
+ {
+ // to topologically correctly collect the 2nd polygon
+ // group it is necessary to OR the parts (each is seen as
+ // XOR-FillRule polygon and they are drawn over each-other)
+ aMergePolyPolygonB = basegfx::utils::solvePolygonOperationOr(aMergePolyPolygonB, aTmpPoly);
+ }
+ else
+ {
+ aMergePolyPolygonB = aTmpPoly;
+ }
+ }
+ }
+ }
+
+ // was there something added to the first polygon?
+ if(!bFirstObjectComplete && aMergePolyPolygonA.count())
+ {
+ bFirstObjectComplete = true;
+ }
+
+ // move object to temporary delete list
+ aRemove.InsertEntry(SdrMark(pObj, pM->GetPageView()));
+ }
+ }
+
+ switch(eMode)
+ {
+ case SdrMergeMode::Merge:
+ {
+ // merge all contained parts (OR)
+ aMergePolyPolygonA = basegfx::utils::solvePolygonOperationOr(aMergePolyPolygonA, aMergePolyPolygonB);
+ break;
+ }
+ case SdrMergeMode::Subtract:
+ {
+ // Subtract B from A
+ aMergePolyPolygonA = basegfx::utils::solvePolygonOperationDiff(aMergePolyPolygonA, aMergePolyPolygonB);
+ break;
+ }
+ case SdrMergeMode::Intersect:
+ {
+ // AND B and A
+ aMergePolyPolygonA = basegfx::utils::solvePolygonOperationAnd(aMergePolyPolygonA, aMergePolyPolygonB);
+ break;
+ }
+ }
+
+ // #i73441# check insert list before taking actions
+ if(pInsOL)
+ {
+ SdrPathObj* pPath = new SdrPathObj(pAttrObj->getSdrModelFromSdrObject(), SdrObjKind::PathFill, aMergePolyPolygonA);
+ ImpCopyAttributes(pAttrObj, pPath);
+ pInsOL->InsertObject(pPath, nInsPos);
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pPath));
+
+ // #i124760# To have a correct selection with only the new object it is necessary to
+ // unmark all objects first. If not doing so, there may remain invalid pointers to objects
+ // TTTT:Not needed for aw080 (!)
+ UnmarkAllObj(pInsPV);
+
+ MarkObj(pPath, pInsPV, false, true);
+ }
+
+ aRemove.ForceSort();
+ switch(eMode)
+ {
+ case SdrMergeMode::Merge:
+ {
+ SetUndoComment(
+ SvxResId(STR_EditMergeMergePoly),
+ aRemove.GetMarkDescription());
+ break;
+ }
+ case SdrMergeMode::Subtract:
+ {
+ SetUndoComment(
+ SvxResId(STR_EditMergeSubstractPoly),
+ aRemove.GetMarkDescription());
+ break;
+ }
+ case SdrMergeMode::Intersect:
+ {
+ SetUndoComment(
+ SvxResId(STR_EditMergeIntersectPoly),
+ aRemove.GetMarkDescription());
+ break;
+ }
+ }
+ DeleteMarkedList(aRemove);
+
+ if( bUndo )
+ EndUndo();
+}
+
+void SdrEditView::EqualizeMarkedObjects(bool bWidth)
+{
+ const SdrMarkList& rMarkList = GetMarkedObjectList();
+ size_t nMarked = rMarkList.GetMarkCount();
+
+ if (nMarked < 2)
+ return;
+
+ size_t nLastSelected = 0;
+ sal_Int64 nLastSelectedTime = rMarkList.GetMark(0)->getTimeStamp();
+ for (size_t a = 1; a < nMarked; ++a)
+ {
+ sal_Int64 nCandidateTime = rMarkList.GetMark(a)->getTimeStamp();
+ if (nCandidateTime > nLastSelectedTime)
+ {
+ nLastSelectedTime = nCandidateTime;
+ nLastSelected = a;
+ }
+ }
+
+ SdrObject* pLastSelectedObj = rMarkList.GetMark(nLastSelected)->GetMarkedSdrObj();
+ Size aLastRectSize(pLastSelectedObj->GetLogicRect().GetSize());
+
+ const bool bUndo = IsUndoEnabled();
+
+ if (bUndo)
+ BegUndo();
+
+ for (size_t a = 0; a < nMarked; ++a)
+ {
+ if (a == nLastSelected)
+ continue;
+ SdrMark* pM = rMarkList.GetMark(a);
+ SdrObject* pObj = pM->GetMarkedSdrObj();
+ tools::Rectangle aLogicRect(pObj->GetLogicRect());
+ Size aLogicRectSize(aLogicRect.GetSize());
+ if (bWidth)
+ aLogicRectSize.setWidth( aLastRectSize.Width() );
+ else
+ aLogicRectSize.setHeight( aLastRectSize.Height() );
+ aLogicRect.SetSize(aLogicRectSize);
+ if (bUndo)
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pObj));
+ pObj->SetLogicRect(aLogicRect);
+ }
+
+ SetUndoComment(
+ SvxResId(bWidth ? STR_EqualizeWidthMarkedObjects : STR_EqualizeHeightMarkedObjects),
+ rMarkList.GetMarkDescription());
+
+ if (bUndo)
+ EndUndo();
+}
+
+void SdrEditView::CombineMarkedTextObjects()
+{
+ SdrPageView* pPageView = GetSdrPageView();
+ if ( !pPageView || pPageView->IsLayerLocked( GetActiveLayer() ) )
+ return;
+
+ bool bUndo = IsUndoEnabled();
+
+ // Undo-String will be set later
+ if ( bUndo )
+ BegUndo();
+
+ SdrOutliner& rDrawOutliner = getSdrModelFromSdrView().GetDrawOutliner();
+
+ SdrObjListIter aIter( GetMarkedObjectList(), SdrIterMode::Flat);
+ while ( aIter.IsMore() )
+ {
+ SdrObject* pObj = aIter.Next();
+ SdrTextObj* pTextObj = dynamic_cast<SdrTextObj*>( pObj );
+ const OutlinerParaObject* pOPO = pTextObj ? pTextObj->GetOutlinerParaObject() : nullptr;
+ if ( pOPO && pTextObj->IsTextFrame()
+ && pTextObj->GetObjIdentifier() == SdrObjKind::Text // not callouts (OBJ_CAPTION)
+ && !pTextObj->IsOutlText() // not impress presentation objects
+ && pTextObj->GetMergedItem(XATTR_FORMTXTSTYLE).GetValue() == XFormTextStyle::NONE // not Fontwork
+ )
+ {
+ // if the last paragraph does not end in paragraph-end punctuation (ignoring whitespace),
+ // assume this text should be added to the end of the last paragraph, instead of starting a new paragraph.
+ const sal_Int32 nPara = rDrawOutliner.GetParagraphCount();
+ const OUString sLastPara = nPara ? rDrawOutliner.GetText( rDrawOutliner.GetParagraph( nPara - 1 ) ) : "";
+ sal_Int32 n = sLastPara.getLength();
+ while ( n && unicode::isWhiteSpace( sLastPara[--n] ) )
+ ;
+ //TODO: find way to use Locale to identify sentence final punctuation. Copied IsSentenceAtEnd() from autofmt.cxx
+ const bool bAppend = !n || ( sLastPara[n] != '.' && sLastPara[n] != '?' && sLastPara[n] != '!' );
+ rDrawOutliner.AddText( *pOPO, bAppend );
+ }
+ else
+ {
+ // Unmark non-textboxes, because all marked objects are deleted at the end. AdjustMarkHdl later.
+ MarkObj(pObj, pPageView, /*bUnmark=*/true, /*bImpNoSetMarkHdl=*/true);
+ }
+ }
+
+ MarkListHasChanged();
+ AdjustMarkHdl();
+
+ if ( GetMarkedObjectCount() > 1 )
+ {
+ SdrRectObj* pReplacement = new SdrRectObj( getSdrModelFromSdrView(), SdrObjKind::Text );
+ pReplacement->SetOutlinerParaObject( rDrawOutliner.CreateParaObject() );
+ pReplacement->SetSnapRect( GetMarkedObjRect() );
+
+ const SdrInsertFlags nFlags = SdrInsertFlags::DONTMARK | SdrInsertFlags::SETDEFLAYER;
+ if ( InsertObjectAtView( pReplacement, *pPageView, nFlags ) )
+ DeleteMarkedObj();
+ }
+
+ if ( bUndo )
+ EndUndo();
+
+ return;
+}
+
+void SdrEditView::CombineMarkedObjects(bool bNoPolyPoly)
+{
+ // #105899# Start of Combine-Undo put to front, else ConvertMarkedToPolyObj would
+ // create a 2nd Undo-action and Undo-Comment.
+
+ bool bUndo = IsUndoEnabled();
+
+ // Undo-String will be set later
+ if( bUndo )
+ BegUndo("", "", bNoPolyPoly ? SdrRepeatFunc::CombineOnePoly : SdrRepeatFunc::CombinePolyPoly);
+
+ // #105899# First, guarantee that all objects are converted to polyobjects,
+ // especially for SdrGrafObj with bitmap filling this is necessary to not
+ // lose the bitmap filling.
+
+ // #i12392#
+ // ConvertMarkedToPolyObj was too strong here, it will lose quality and
+ // information when curve objects are combined. This can be replaced by
+ // using ConvertMarkedToPathObj without changing the previous fix.
+
+ // #i21250#
+ // Instead of simply passing true as LineToArea, use bNoPolyPoly as info
+ // if this command is a 'Combine' or a 'Connect' command. On Connect it's true.
+ // To not concert line segments with a set line width to polygons in that case,
+ // use this info. Do not convert LineToArea on Connect commands.
+ // ConvertMarkedToPathObj(!bNoPolyPoly);
+
+ // This is used for Combine and Connect. In no case it is necessary to force
+ // the content to curve, but it is also not good to force to polygons. Thus,
+ // curve is the less information losing one. Remember: This place is not
+ // used for merge.
+ // LineToArea is never necessary, both commands are able to take over the
+ // set line style and to display it correctly. Thus, i will use a
+ // ConvertMarkedToPathObj with a false in any case. Only drawback is that
+ // simple polygons will be changed to curves, but with no information loss.
+ ConvertMarkedToPathObj(false /* bLineToArea */);
+
+ // continue as before
+ basegfx::B2DPolyPolygon aPolyPolygon;
+ SdrObjList* pCurrentOL = nullptr;
+ SdrMarkList aRemoveBuffer;
+
+ SortMarkedObjects();
+ size_t nInsPos = SAL_MAX_SIZE;
+ SdrObjList* pInsOL = nullptr;
+ SdrPageView* pInsPV = nullptr;
+ const SdrObject* pAttrObj = nullptr;
+
+ for(size_t a = GetMarkedObjectCount(); a; )
+ {
+ --a;
+ SdrMark* pM = GetSdrMarkByIndex(a);
+ SdrObject* pObj = pM->GetMarkedSdrObj();
+ SdrObjList* pThisOL = pObj->getParentSdrObjListFromSdrObject();
+
+ if(pCurrentOL != pThisOL)
+ {
+ pCurrentOL = pThisOL;
+ }
+
+ if(ImpCanConvertForCombine(pObj))
+ {
+ // remember objects to be able to copy attributes
+ pAttrObj = pObj;
+
+ // unfortunately ConvertMarkedToPathObj has converted all
+ // involved polygon data to curve segments, even if not necessary.
+ // It is better to try to reduce to more simple polygons.
+ basegfx::B2DPolyPolygon aTmpPoly(basegfx::utils::simplifyCurveSegments(ImpGetPolyPolygon(pObj)));
+ aPolyPolygon.insert(0, aTmpPoly);
+
+ if(!pInsOL)
+ {
+ nInsPos = pObj->GetOrdNum() + 1;
+ pInsPV = pM->GetPageView();
+ pInsOL = pObj->getParentSdrObjListFromSdrObject();
+ }
+
+ aRemoveBuffer.InsertEntry(SdrMark(pObj, pM->GetPageView()));
+ }
+ }
+
+ if(bNoPolyPoly)
+ {
+ basegfx::B2DPolygon aCombinedPolygon(ImpCombineToSinglePolygon(aPolyPolygon));
+ aPolyPolygon.clear();
+ aPolyPolygon.append(aCombinedPolygon);
+ }
+
+ const sal_uInt32 nPolyCount(aPolyPolygon.count());
+
+ if (nPolyCount && pAttrObj)
+ {
+ SdrObjKind eKind = SdrObjKind::PathFill;
+
+ if(nPolyCount > 1)
+ {
+ aPolyPolygon.setClosed(true);
+ }
+ else
+ {
+ // check for Polyline
+ const basegfx::B2DPolygon aPolygon(aPolyPolygon.getB2DPolygon(0));
+ const sal_uInt32 nPointCount(aPolygon.count());
+
+ if(nPointCount <= 2)
+ {
+ eKind = SdrObjKind::PathLine;
+ }
+ else
+ {
+ if(!aPolygon.isClosed())
+ {
+ const basegfx::B2DPoint aPointA(aPolygon.getB2DPoint(0));
+ const basegfx::B2DPoint aPointB(aPolygon.getB2DPoint(nPointCount - 1));
+ const double fDistance(basegfx::B2DVector(aPointB - aPointA).getLength());
+ const double fJoinTolerance(10.0);
+
+ if(fDistance < fJoinTolerance)
+ {
+ aPolyPolygon.setClosed(true);
+ }
+ else
+ {
+ eKind = SdrObjKind::PathLine;
+ }
+ }
+ }
+ }
+
+ SdrPathObj* pPath = new SdrPathObj(pAttrObj->getSdrModelFromSdrObject(), eKind, aPolyPolygon);
+
+ // attributes of the lowest object
+ ImpCopyAttributes(pAttrObj, pPath);
+
+ // If LineStyle of pAttrObj is drawing::LineStyle_NONE force to drawing::LineStyle_SOLID to make visible.
+ const drawing::LineStyle eLineStyle = pAttrObj->GetMergedItem(XATTR_LINESTYLE).GetValue();
+ const drawing::FillStyle eFillStyle = pAttrObj->GetMergedItem(XATTR_FILLSTYLE).GetValue();
+
+ // Take fill style/closed state of pAttrObj in account when deciding to change the line style
+ bool bIsClosedPathObj = false;
+ if (auto pPathObj = dynamic_cast<const SdrPathObj*>(pAttrObj))
+ if (pPathObj->IsClosed())
+ bIsClosedPathObj = true;
+
+ if(drawing::LineStyle_NONE == eLineStyle && (drawing::FillStyle_NONE == eFillStyle || !bIsClosedPathObj))
+ {
+ pPath->SetMergedItem(XLineStyleItem(drawing::LineStyle_SOLID));
+ }
+
+ pInsOL->InsertObject(pPath,nInsPos);
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pPath));
+
+ // Here was a severe error: Without UnmarkAllObj, the new object was marked
+ // additionally to the two ones which are deleted below. As long as those are
+ // in the UNDO there is no problem, but as soon as they get deleted, the
+ // MarkList will contain deleted objects -> GPF.
+ UnmarkAllObj(pInsPV);
+ MarkObj(pPath, pInsPV, false, true);
+ }
+
+ // build an UndoComment from the objects actually used
+ aRemoveBuffer.ForceSort(); // important for remove (see below)
+ if( bUndo )
+ SetUndoComment(SvxResId(bNoPolyPoly?STR_EditCombine_OnePoly:STR_EditCombine_PolyPoly),aRemoveBuffer.GetMarkDescription());
+
+ // remove objects actually used from the list
+ DeleteMarkedList(aRemoveBuffer);
+ if( bUndo )
+ EndUndo();
+}
+
+
+// Dismantle
+
+
+bool SdrEditView::ImpCanDismantle(const basegfx::B2DPolyPolygon& rPpolyPolygon, bool bMakeLines)
+{
+ bool bCan(false);
+ const sal_uInt32 nPolygonCount(rPpolyPolygon.count());
+
+ if(nPolygonCount >= 2)
+ {
+ // #i69172# dismantle makes sense with 2 or more polygons in a polyPolygon
+ bCan = true;
+ }
+ else if(bMakeLines && 1 == nPolygonCount)
+ {
+ // #i69172# ..or with at least 2 edges (curves or lines)
+ const basegfx::B2DPolygon& aPolygon(rPpolyPolygon.getB2DPolygon(0));
+ const sal_uInt32 nPointCount(aPolygon.count());
+
+ if(nPointCount > 2)
+ {
+ bCan = true;
+ }
+ }
+
+ return bCan;
+}
+
+bool SdrEditView::ImpCanDismantle(const SdrObject* pObj, bool bMakeLines)
+{
+ bool bOtherObjs(false); // true=objects other than PathObj's existent
+ bool bMin1PolyPoly(false); // true=at least 1 tools::PolyPolygon with more than one Polygon existent
+ SdrObjList* pOL = pObj->GetSubList();
+
+ if(pOL)
+ {
+ // group object -- check all members if they're PathObjs
+ SdrObjListIter aIter(pOL, SdrIterMode::DeepNoGroups);
+
+ while(aIter.IsMore() && !bOtherObjs)
+ {
+ const SdrObject* pObj1 = aIter.Next();
+ const SdrPathObj* pPath = dynamic_cast<const SdrPathObj*>( pObj1 );
+
+ if(pPath)
+ {
+ if(ImpCanDismantle(pPath->GetPathPoly(), bMakeLines))
+ {
+ bMin1PolyPoly = true;
+ }
+
+ SdrObjTransformInfoRec aInfo;
+ pObj1->TakeObjInfo(aInfo);
+
+ if(!aInfo.bCanConvToPath)
+ {
+ // happens e. g. in the case of FontWork
+ bOtherObjs = true;
+ }
+ }
+ else
+ {
+ bOtherObjs = true;
+ }
+ }
+ }
+ else
+ {
+ const SdrPathObj* pPath = dynamic_cast<const SdrPathObj*>(pObj);
+ const SdrObjCustomShape* pCustomShape = dynamic_cast<const SdrObjCustomShape*>(pObj);
+
+ // #i37011#
+ if(pPath)
+ {
+ if(ImpCanDismantle(pPath->GetPathPoly(),bMakeLines))
+ {
+ bMin1PolyPoly = true;
+ }
+
+ SdrObjTransformInfoRec aInfo;
+ pObj->TakeObjInfo(aInfo);
+
+ // new condition IsLine() to be able to break simple Lines
+ if(!(aInfo.bCanConvToPath || aInfo.bCanConvToPoly) && !pPath->IsLine())
+ {
+ // happens e. g. in the case of FontWork
+ bOtherObjs = true;
+ }
+ }
+ else if(pCustomShape)
+ {
+ if(bMakeLines)
+ {
+ // allow break command
+ bMin1PolyPoly = true;
+ }
+ }
+ else
+ {
+ bOtherObjs = true;
+ }
+ }
+ return bMin1PolyPoly && !bOtherObjs;
+}
+
+void SdrEditView::ImpDismantleOneObject(const SdrObject* pObj, SdrObjList& rOL, size_t& rPos, SdrPageView* pPV, bool bMakeLines)
+{
+ const SdrPathObj* pSrcPath = dynamic_cast<const SdrPathObj*>( pObj );
+ const SdrObjCustomShape* pCustomShape = dynamic_cast<const SdrObjCustomShape*>( pObj );
+
+ const bool bUndo = IsUndoEnabled();
+
+ if(pSrcPath)
+ {
+ // #i74631# redesigned due to XpolyPolygon removal and explicit constructors
+ SdrObject* pLast = nullptr; // to be able to apply OutlinerParaObject
+ const basegfx::B2DPolyPolygon& rPolyPolygon(pSrcPath->GetPathPoly());
+ const sal_uInt32 nPolyCount(rPolyPolygon.count());
+
+ for(sal_uInt32 a(0); a < nPolyCount; a++)
+ {
+ const basegfx::B2DPolygon& rCandidate(rPolyPolygon.getB2DPolygon(a));
+ const sal_uInt32 nPointCount(rCandidate.count());
+
+ if(!bMakeLines || nPointCount < 2)
+ {
+ SdrPathObj* pPath = new SdrPathObj(
+ pSrcPath->getSdrModelFromSdrObject(),
+ pSrcPath->GetObjIdentifier(),
+ basegfx::B2DPolyPolygon(rCandidate));
+ ImpCopyAttributes(pSrcPath, pPath);
+ pLast = pPath;
+ rOL.InsertObject(pPath, rPos);
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pPath, true));
+ MarkObj(pPath, pPV, false, true);
+ rPos++;
+ }
+ else
+ {
+ const sal_uInt32 nLoopCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
+
+ for(sal_uInt32 b(0); b < nLoopCount; b++)
+ {
+ SdrObjKind eKind(SdrObjKind::PolyLine);
+ basegfx::B2DPolygon aNewPolygon;
+ const sal_uInt32 nNextIndex((b + 1) % nPointCount);
+
+ aNewPolygon.append(rCandidate.getB2DPoint(b));
+
+ if(rCandidate.areControlPointsUsed())
+ {
+ aNewPolygon.appendBezierSegment(
+ rCandidate.getNextControlPoint(b),
+ rCandidate.getPrevControlPoint(nNextIndex),
+ rCandidate.getB2DPoint(nNextIndex));
+ eKind = SdrObjKind::PathLine;
+ }
+ else
+ {
+ aNewPolygon.append(rCandidate.getB2DPoint(nNextIndex));
+ }
+
+ SdrPathObj* pPath = new SdrPathObj(
+ pSrcPath->getSdrModelFromSdrObject(),
+ eKind,
+ basegfx::B2DPolyPolygon(aNewPolygon));
+ ImpCopyAttributes(pSrcPath, pPath);
+ pLast = pPath;
+ rOL.InsertObject(pPath, rPos);
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pPath, true));
+ MarkObj(pPath, pPV, false, true);
+ rPos++;
+ }
+ }
+ }
+
+ if(pLast && pSrcPath->GetOutlinerParaObject())
+ {
+ pLast->SetOutlinerParaObject(*pSrcPath->GetOutlinerParaObject());
+ }
+ }
+ else if(pCustomShape)
+ {
+ if(bMakeLines)
+ {
+ // break up custom shape
+ const SdrObject* pReplacement = pCustomShape->GetSdrObjectFromCustomShape();
+
+ if(pReplacement)
+ {
+ SdrObject* pCandidate(pReplacement->CloneSdrObject(pReplacement->getSdrModelFromSdrObject()));
+ DBG_ASSERT(pCandidate, "SdrEditView::ImpDismantleOneObject: Could not clone SdrObject (!)");
+
+ if(pCustomShape->GetMergedItem(SDRATTR_SHADOW).GetValue())
+ {
+ if(dynamic_cast<const SdrObjGroup*>( pReplacement) != nullptr)
+ {
+ pCandidate->SetMergedItem(makeSdrShadowItem(true));
+ }
+ }
+
+ rOL.InsertObject(pCandidate, rPos);
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pCandidate, true));
+ MarkObj(pCandidate, pPV, false, true);
+
+ if(pCustomShape->HasText() && !pCustomShape->IsTextPath())
+ {
+ // #i37011# also create a text object and add at rPos + 1
+ SdrObject* pTextObj = SdrObjFactory::MakeNewObject(
+ pCustomShape->getSdrModelFromSdrObject(),
+ pCustomShape->GetObjInventor(),
+ SdrObjKind::Text);
+
+ // Copy text content
+ OutlinerParaObject* pParaObj = pCustomShape->GetOutlinerParaObject();
+ if(pParaObj)
+ {
+ pTextObj->NbcSetOutlinerParaObject(*pParaObj);
+ }
+
+ // copy all attributes
+ SfxItemSet aTargetItemSet(pCustomShape->GetMergedItemSet());
+
+ // clear fill and line style
+ aTargetItemSet.Put(XLineStyleItem(drawing::LineStyle_NONE));
+ aTargetItemSet.Put(XFillStyleItem(drawing::FillStyle_NONE));
+
+ // get the text bounds and set at text object
+ tools::Rectangle aTextBounds = pCustomShape->GetSnapRect();
+ if(pCustomShape->GetTextBounds(aTextBounds))
+ {
+ pTextObj->SetSnapRect(aTextBounds);
+ }
+
+ // if rotated, copy GeoStat, too.
+ const GeoStat& rSourceGeo = pCustomShape->GetGeoStat();
+ if(rSourceGeo.nRotationAngle)
+ {
+ pTextObj->NbcRotate(
+ pCustomShape->GetSnapRect().Center(), rSourceGeo.nRotationAngle,
+ rSourceGeo.mfSinRotationAngle, rSourceGeo.mfCosRotationAngle);
+ }
+
+ // set modified ItemSet at text object
+ pTextObj->SetMergedItemSet(aTargetItemSet);
+
+ // insert object
+ rOL.InsertObject(pTextObj, rPos + 1);
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pTextObj, true));
+ MarkObj(pTextObj, pPV, false, true);
+ }
+ }
+ }
+ }
+}
+
+void SdrEditView::DismantleMarkedObjects(bool bMakeLines)
+{
+ // temporary MarkList
+ SdrMarkList aRemoveBuffer;
+
+ SortMarkedObjects();
+
+ const bool bUndo = IsUndoEnabled();
+
+ if( bUndo )
+ {
+ // comment is constructed later
+ BegUndo("", "", bMakeLines ? SdrRepeatFunc::DismantleLines : SdrRepeatFunc::DismantlePolys);
+ }
+
+ SdrObjList* pOL0=nullptr;
+ for (size_t nm=GetMarkedObjectCount(); nm>0;) {
+ --nm;
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ SdrPageView* pPV=pM->GetPageView();
+ SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
+ if (pOL!=pOL0) { pOL0=pOL; pObj->GetOrdNum(); } // make sure OrdNums are correct!
+ if (ImpCanDismantle(pObj,bMakeLines)) {
+ aRemoveBuffer.InsertEntry(SdrMark(pObj,pM->GetPageView()));
+ const size_t nPos0=pObj->GetOrdNumDirect();
+ size_t nPos=nPos0+1;
+ SdrObjList* pSubList=pObj->GetSubList();
+ if (pSubList!=nullptr && !pObj->Is3DObj()) {
+ SdrObjListIter aIter(pSubList,SdrIterMode::DeepNoGroups);
+ while (aIter.IsMore()) {
+ const SdrObject* pObj1=aIter.Next();
+ ImpDismantleOneObject(pObj1,*pOL,nPos,pPV,bMakeLines);
+ }
+ } else {
+ ImpDismantleOneObject(pObj,*pOL,nPos,pPV,bMakeLines);
+ }
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoDeleteObject(*pObj,true));
+ pOL->RemoveObject(nPos0);
+
+ if( !bUndo )
+ SdrObject::Free(pObj);
+ }
+ }
+
+ if( bUndo )
+ {
+ // construct UndoComment from objects actually used
+ SetUndoComment(SvxResId(bMakeLines?STR_EditDismantle_Lines:STR_EditDismantle_Polys),aRemoveBuffer.GetMarkDescription());
+ // remove objects actually used from the list
+ EndUndo();
+ }
+}
+
+
+// Group
+
+
+void SdrEditView::GroupMarked()
+{
+ if (!AreObjectsMarked())
+ return;
+
+ SortMarkedObjects();
+
+ const bool bUndo = IsUndoEnabled();
+ if( bUndo )
+ {
+ BegUndo(SvxResId(STR_EditGroup),GetDescriptionOfMarkedObjects(),SdrRepeatFunc::Group);
+
+ for(size_t nm = GetMarkedObjectCount(); nm>0; )
+ {
+ // add UndoActions for all affected objects
+ --nm;
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pObj = pM->GetMarkedSdrObj();
+ AddUndoActions( CreateConnectorUndo( *pObj ) );
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoRemoveObject( *pObj ));
+ }
+ }
+
+ SdrMarkList aNewMark;
+ SdrPageView* pPV = GetSdrPageView();
+
+ if(pPV)
+ {
+ SdrObjList* pCurrentLst=pPV->GetObjList();
+ SdrObjList* pSrcLst=pCurrentLst;
+ SdrObjList* pSrcLst0=pSrcLst;
+ // make sure OrdNums are correct
+ if (pSrcLst->IsObjOrdNumsDirty())
+ pSrcLst->RecalcObjOrdNums();
+ SdrObject* pGrp=nullptr;
+ SdrObjList* pDstLst=nullptr;
+ // if all selected objects come from foreign object lists.
+ // the group object is the last one in the list.
+ size_t nInsPos=pSrcLst->GetObjCount();
+ bool bNeedInsPos=true;
+ for (size_t nm=GetMarkedObjectCount(); nm>0;)
+ {
+ --nm;
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ if (pM->GetPageView()==pPV)
+ {
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ if (nullptr==pGrp)
+ {
+ pGrp = new SdrObjGroup(pObj->getSdrModelFromSdrObject());
+ pDstLst=pGrp->GetSubList();
+ DBG_ASSERT(pDstLst!=nullptr,"Alleged group object doesn't return object list.");
+ }
+ pSrcLst=pObj->getParentSdrObjListFromSdrObject();
+ if (pSrcLst!=pSrcLst0)
+ {
+ if (pSrcLst->IsObjOrdNumsDirty())
+ pSrcLst->RecalcObjOrdNums();
+ }
+ bool bForeignList=pSrcLst!=pCurrentLst;
+ if (!bForeignList && bNeedInsPos)
+ {
+ nInsPos=pObj->GetOrdNum(); // this way, all ObjOrdNum of the page are set
+ nInsPos++;
+ bNeedInsPos=false;
+ }
+ pSrcLst->RemoveObject(pObj->GetOrdNumDirect());
+ if (!bForeignList)
+ nInsPos--; // correct InsertPos
+ pDstLst->InsertObject(pObj,0);
+ GetMarkedObjectListWriteAccess().DeleteMark(nm);
+ pSrcLst0=pSrcLst;
+ }
+ }
+ if (pGrp!=nullptr)
+ {
+ aNewMark.InsertEntry(SdrMark(pGrp,pPV));
+ const size_t nCount=pDstLst->GetObjCount();
+ pCurrentLst->InsertObject(pGrp,nInsPos);
+ if( bUndo )
+ {
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pGrp,true)); // no recalculation!
+ for (size_t no=0; no<nCount; ++no)
+ {
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoInsertObject(*pDstLst->GetObj(no)));
+ }
+ }
+ }
+ }
+ GetMarkedObjectListWriteAccess().Merge(aNewMark);
+ MarkListHasChanged();
+
+ if( bUndo )
+ EndUndo();
+}
+
+
+// Ungroup
+
+
+void SdrEditView::UnGroupMarked()
+{
+ SdrMarkList aNewMark;
+
+ const bool bUndo = IsUndoEnabled();
+ if( bUndo )
+ BegUndo("", "", SdrRepeatFunc::Ungroup);
+
+ size_t nCount=0;
+ OUString aName1;
+ OUString aName;
+ bool bNameOk=false;
+ for (size_t nm=GetMarkedObjectCount(); nm>0;) {
+ --nm;
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pGrp=pM->GetMarkedSdrObj();
+ SdrObjList* pSrcLst=pGrp->GetSubList();
+ if (pSrcLst!=nullptr) {
+ nCount++;
+ if (nCount==1) {
+ aName = pGrp->TakeObjNameSingul(); // retrieve name of group
+ aName1 = pGrp->TakeObjNamePlural(); // retrieve name of group
+ bNameOk=true;
+ } else {
+ if (nCount==2) aName=aName1; // set plural name
+ if (bNameOk) {
+ OUString aStr(pGrp->TakeObjNamePlural()); // retrieve name of group
+
+ if (aStr != aName)
+ bNameOk = false;
+ }
+ }
+ size_t nDstCnt=pGrp->GetOrdNum();
+ SdrObjList* pDstLst=pM->GetPageView()->GetObjList();
+ size_t nObjCount=pSrcLst->GetObjCount();
+ const bool bIsDiagram(pGrp->isDiagram());
+
+ // If the Group is a Diagram, it has a filler BG object to guarantee
+ // the Diagam's dimensions. Identify that shape
+ if(bIsDiagram && nObjCount)
+ {
+ SdrObject* pObj(pSrcLst->GetObj(0));
+
+ if(nullptr != pObj && !pObj->IsGroupObject() &&
+ !pObj->HasLineStyle() &&
+ pObj->IsMoveProtect() && pObj->IsResizeProtect())
+ {
+ if(pObj->HasFillStyle())
+ {
+ // If it has FillStyle it is a useful object representing that possible
+ // defined fill from oox import. In this case, we should remove the
+ // Move/Resize protection to allow seamless further processing.
+
+ // Undo of these is handled by SdrUndoGeoObj which holds a SdrObjGeoData,
+ // create one
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pObj));
+
+ pObj->SetMoveProtect(false);
+ pObj->SetResizeProtect(false);
+ }
+ else
+ {
+ // If it has no FillStyle it is not useful for any further processing
+ // but only was used as a placeholder, get directly rid of it
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoDeleteObject(*pObj));
+
+ pObj = pSrcLst->RemoveObject(0);
+
+ if( !bUndo )
+ SdrObject::Free(pObj);
+
+ nObjCount = pSrcLst->GetObjCount();
+ }
+ }
+ }
+
+ // FIRST move contained objects to parent of group, so that
+ // the contained objects are NOT migrated to the UNDO-ItemPool
+ // when AddUndo(new SdrUndoDelObj(*pGrp)) is called.
+ if( bUndo )
+ {
+ for (size_t no=nObjCount; no>0;)
+ {
+ no--;
+ SdrObject* pObj=pSrcLst->GetObj(no);
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoRemoveObject(*pObj));
+ }
+ }
+
+ for (size_t no=0; no<nObjCount; ++no)
+ {
+ SdrObject* pObj=pSrcLst->RemoveObject(0);
+ pDstLst->InsertObject(pObj,nDstCnt);
+ if( bUndo )
+ AddUndo( GetModel()->GetSdrUndoFactory().CreateUndoInsertObject(*pObj,true));
+ nDstCnt++;
+ // No SortCheck when inserting into MarkList, because that would
+ // provoke a RecalcOrdNums() each time because of pObj->GetOrdNum():
+ aNewMark.InsertEntry(SdrMark(pObj,pM->GetPageView()),false);
+ }
+
+ if( bUndo )
+ {
+ // Now it is safe to add the delete-UNDO which triggers the
+ // MigrateItemPool now only for itself, not for the sub-objects.
+ // nDstCnt is right, because previous inserts move group
+ // object deeper and increase nDstCnt.
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoDeleteObject(*pGrp));
+ }
+ pDstLst->RemoveObject(nDstCnt);
+
+ if( !bUndo )
+ SdrObject::Free(pGrp);
+
+ GetMarkedObjectListWriteAccess().DeleteMark(nm);
+ }
+ }
+ if (nCount!=0)
+ {
+ if (!bNameOk)
+ aName=SvxResId(STR_ObjNamePluralGRUP); // Use the term "Group Objects," if different objects are grouped.
+ SetUndoComment(SvxResId(STR_EditUngroup),aName);
+ }
+
+ if( bUndo )
+ EndUndo();
+
+ if (nCount!=0)
+ {
+ GetMarkedObjectListWriteAccess().Merge(aNewMark,true); // Because of the sorting above, aNewMark is reversed
+ MarkListHasChanged();
+ }
+}
+
+
+// ConvertToPoly
+
+
+SdrObject* SdrEditView::ImpConvertOneObj(SdrObject* pObj, bool bPath, bool bLineToArea)
+{
+ SdrObjectUniquePtr pNewObj = pObj->ConvertToPolyObj(bPath, bLineToArea);
+ if (pNewObj)
+ {
+ SdrObjList* pOL = pObj->getParentSdrObjListFromSdrObject();
+ const bool bUndo = IsUndoEnabled();
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoReplaceObject(*pObj,*pNewObj));
+
+ // ownership passed into here (despite the UniquePtr indicating that we are returning it)
+ pOL->ReplaceObject(pNewObj.get(), pObj->GetOrdNum());
+
+ if( !bUndo )
+ SdrObject::Free(pObj);
+ }
+ return pNewObj.release();
+}
+
+void SdrEditView::ImpConvertTo(bool bPath, bool bLineToArea)
+{
+ if (!AreObjectsMarked()) return;
+
+ bool bMrkChg = false;
+ const size_t nMarkCount=GetMarkedObjectCount();
+ TranslateId pDscrID;
+ if(bLineToArea)
+ {
+ if(nMarkCount == 1)
+ pDscrID = STR_EditConvToContour;
+ else
+ pDscrID = STR_EditConvToContours;
+
+ BegUndo(SvxResId(pDscrID), GetDescriptionOfMarkedObjects());
+ }
+ else
+ {
+ if (bPath) {
+ if (nMarkCount==1) pDscrID=STR_EditConvToCurve;
+ else pDscrID=STR_EditConvToCurves;
+ BegUndo(SvxResId(pDscrID),GetDescriptionOfMarkedObjects(),SdrRepeatFunc::ConvertToPath);
+ } else {
+ if (nMarkCount==1) pDscrID=STR_EditConvToPoly;
+ else pDscrID=STR_EditConvToPolys;
+ BegUndo(SvxResId(pDscrID),GetDescriptionOfMarkedObjects(),SdrRepeatFunc::ConvertToPoly);
+ }
+ }
+ for (size_t nm=nMarkCount; nm>0;) {
+ --nm;
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ SdrPageView* pPV=pM->GetPageView();
+ if (pObj->IsGroupObject() && !pObj->Is3DObj()) {
+ SdrObject* pGrp=pObj;
+ SdrObjListIter aIter(*pGrp, SdrIterMode::DeepNoGroups);
+ while (aIter.IsMore()) {
+ pObj=aIter.Next();
+ ImpConvertOneObj(pObj,bPath,bLineToArea);
+ }
+ } else {
+ SdrObject* pNewObj=ImpConvertOneObj(pObj,bPath,bLineToArea);
+ if (pNewObj!=nullptr) {
+ bMrkChg=true;
+ GetMarkedObjectListWriteAccess().ReplaceMark(SdrMark(pNewObj,pPV),nm);
+ }
+ }
+ }
+ EndUndo();
+ if (bMrkChg)
+ {
+ AdjustMarkHdl();
+ MarkListHasChanged();
+ }
+}
+
+void SdrEditView::ConvertMarkedToPathObj(bool bLineToArea)
+{
+ ImpConvertTo(true, bLineToArea);
+}
+
+void SdrEditView::ConvertMarkedToPolyObj()
+{
+ ImpConvertTo(false, false/*bLineToArea*/);
+}
+
+namespace
+{
+ GDIMetaFile GetMetaFile(SdrGrafObj const * pGraf)
+ {
+ if (pGraf->HasGDIMetaFile())
+ return pGraf->GetTransformedGraphic(SdrGrafObjTransformsAttrs::MIRROR).GetGDIMetaFile();
+ assert(pGraf->isEmbeddedVectorGraphicData());
+ return pGraf->getMetafileFromEmbeddedVectorGraphicData();
+ }
+}
+
+// Metafile Import
+void SdrEditView::DoImportMarkedMtf(SvdProgressInfo *pProgrInfo)
+{
+ const bool bUndo = IsUndoEnabled();
+
+ if( bUndo )
+ BegUndo("", "", SdrRepeatFunc::ImportMtf);
+
+ SortMarkedObjects();
+ SdrMarkList aForTheDescription;
+ SdrMarkList aNewMarked;
+ for (size_t nm =GetMarkedObjectCount(); nm > 0; )
+ {
+ // create Undo objects for all new objects
+ // check for cancellation between the metafiles
+ if (pProgrInfo != nullptr)
+ {
+ pProgrInfo->SetNextObject();
+ if (!pProgrInfo->ReportActions(0))
+ break;
+ }
+
+ --nm;
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ SdrPageView* pPV=pM->GetPageView();
+ SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
+ const size_t nInsPos=pObj->GetOrdNum()+1;
+ size_t nInsCnt=0;
+ tools::Rectangle aLogicRect;
+
+ SdrGrafObj* pGraf = dynamic_cast<SdrGrafObj*>( pObj );
+ if (pGraf != nullptr)
+ {
+ Graphic aGraphic = pGraf->GetGraphic();
+ auto const & pVectorGraphicData = aGraphic.getVectorGraphicData();
+
+ if (pVectorGraphicData && pVectorGraphicData->getType() == VectorGraphicDataType::Pdf)
+ {
+ auto pPdfium = vcl::pdf::PDFiumLibrary::get();
+ if (pPdfium)
+ {
+ aLogicRect = pGraf->GetLogicRect();
+ ImpSdrPdfImport aFilter(*mpModel, pObj->GetLayer(), aLogicRect, aGraphic);
+ if (aGraphic.getPageNumber() < aFilter.GetPageCount())
+ {
+ nInsCnt = aFilter.DoImport(*pOL, nInsPos, aGraphic.getPageNumber(), pProgrInfo);
+ }
+ }
+ }
+ else if (pGraf->HasGDIMetaFile() || pGraf->isEmbeddedVectorGraphicData() )
+ {
+ GDIMetaFile aMetaFile(GetMetaFile(pGraf));
+ if (aMetaFile.GetActionSize())
+ {
+ aLogicRect = pGraf->GetLogicRect();
+ ImpSdrGDIMetaFileImport aFilter(*mpModel, pObj->GetLayer(), aLogicRect);
+ nInsCnt = aFilter.DoImport(aMetaFile, *pOL, nInsPos, pProgrInfo);
+ }
+ }
+ }
+
+ SdrOle2Obj* pOle2 = dynamic_cast<SdrOle2Obj*>(pObj);
+ if (pOle2 != nullptr && pOle2->GetGraphic())
+ {
+ aLogicRect = pOle2->GetLogicRect();
+ ImpSdrGDIMetaFileImport aFilter(*mpModel, pObj->GetLayer(), aLogicRect);
+ nInsCnt = aFilter.DoImport(pOle2->GetGraphic()->GetGDIMetaFile(), *pOL, nInsPos, pProgrInfo);
+ }
+
+ if (nInsCnt != 0)
+ {
+ // transformation
+ GeoStat aGeoStat(pGraf ? pGraf->GetGeoStat() : pOle2->GetGeoStat());
+ size_t nObj = nInsPos;
+
+ if (aGeoStat.nShearAngle)
+ aGeoStat.RecalcTan();
+
+ if (aGeoStat.nRotationAngle)
+ aGeoStat.RecalcSinCos();
+
+ for (size_t i = 0; i < nInsCnt; i++)
+ {
+ if (bUndo)
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pOL->GetObj(nObj)));
+
+ // update new MarkList
+ SdrObject* pCandidate = pOL->GetObj(nObj);
+
+ // apply original transformation
+ if (aGeoStat.nShearAngle)
+ pCandidate->NbcShear(aLogicRect.TopLeft(), aGeoStat.nShearAngle, aGeoStat.mfTanShearAngle, false);
+
+ if (aGeoStat.nRotationAngle)
+ pCandidate->NbcRotate(aLogicRect.TopLeft(), aGeoStat.nRotationAngle, aGeoStat.mfSinRotationAngle, aGeoStat.mfCosRotationAngle);
+
+ SdrMark aNewMark(pCandidate, pPV);
+ aNewMarked.InsertEntry(aNewMark);
+
+ nObj++;
+ }
+
+ aForTheDescription.InsertEntry(*pM);
+
+ if (bUndo)
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoDeleteObject(*pObj));
+
+ // remove object from selection and delete
+ GetMarkedObjectListWriteAccess().DeleteMark(TryToFindMarkedObject(pObj));
+ pOL->RemoveObject(nInsPos-1);
+
+ if (!bUndo)
+ SdrObject::Free(pObj);
+ }
+ }
+
+ if (aNewMarked.GetMarkCount())
+ {
+ // create new selection
+ for (size_t a = 0; a < aNewMarked.GetMarkCount(); ++a)
+ {
+ GetMarkedObjectListWriteAccess().InsertEntry(*aNewMarked.GetMark(a));
+ }
+
+ SortMarkedObjects();
+ }
+
+ if (bUndo)
+ {
+ SetUndoComment(SvxResId(STR_EditImportMtf),aForTheDescription.GetMarkDescription());
+ EndUndo();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdedxv.cxx b/svx/source/svdraw/svdedxv.cxx
new file mode 100644
index 000000000..c1c248b21
--- /dev/null
+++ b/svx/source/svdraw/svdedxv.cxx
@@ -0,0 +1,2871 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/i18n/WordType.hpp>
+#include <editeng/editdata.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/editstat.hxx>
+#include <editeng/outlobj.hxx>
+#include <editeng/unotext.hxx>
+#include <o3tl/deleter.hxx>
+#include <svl/itemiter.hxx>
+#include <svl/style.hxx>
+#include <svl/whiter.hxx>
+#include <svtools/accessibilityoptions.hxx>
+#include <svx/sdtfchim.hxx>
+#include <svx/selectioncontroller.hxx>
+#include <svx/svdedxv.hxx>
+#include <svx/svdetc.hxx>
+#include <svx/svdotable.hxx>
+#include <svx/svdotext.hxx>
+#include <svx/svdoutl.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdundo.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/cursor.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/window.hxx>
+#include <comphelper/lok.hxx>
+#include <drawinglayer/processor2d/baseprocessor2d.hxx>
+#include <drawinglayer/processor2d/processor2dtools.hxx>
+#include <editeng/outliner.hxx>
+#include <sal/log.hxx>
+#include <sdr/overlay/overlaytools.hxx>
+#include <sfx2/viewsh.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <svx/sdr/overlay/overlayselection.hxx>
+#include <svx/sdr/table/tablecontroller.hxx>
+#include <svx/sdrpagewindow.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <svx/sdrundomanager.hxx>
+#include <svx/strings.hrc>
+#include <svx/svdviter.hxx>
+#include <svtools/optionsdrawinglayer.hxx>
+#include <textchain.hxx>
+#include <textchaincursor.hxx>
+#include <tools/debug.hxx>
+#include <vcl/svapp.hxx>
+
+#include <memory>
+
+SdrObjEditView::SdrObjEditView(SdrModel& rSdrModel, OutputDevice* pOut)
+ : SdrGlueEditView(rSdrModel, pOut)
+ , mpTextEditPV(nullptr)
+ , mpTextEditOutlinerView(nullptr)
+ , mpTextEditWin(nullptr)
+ , pTextEditCursorBuffer(nullptr)
+ , pMacroObj(nullptr)
+ , pMacroPV(nullptr)
+ , pMacroWin(nullptr)
+ , nMacroTol(0)
+ , mbTextEditDontDelete(false)
+ , mbTextEditOnlyOneView(false)
+ , mbTextEditNewObj(false)
+ , mbQuickTextEditMode(true)
+ , mbMacroDown(false)
+ , mpOldTextEditUndoManager(nullptr)
+{
+}
+
+SdrObjEditView::~SdrObjEditView()
+{
+ mpTextEditWin = nullptr; // so there's no ShowCursor in SdrEndTextEdit
+ assert(!IsTextEdit());
+ if (IsTextEdit())
+ suppress_fun_call_w_exception(SdrEndTextEdit());
+ mpTextEditOutliner.reset();
+ assert(nullptr == mpOldTextEditUndoManager); // should have been reset
+}
+
+bool SdrObjEditView::IsAction() const { return IsMacroObj() || SdrGlueEditView::IsAction(); }
+
+void SdrObjEditView::MovAction(const Point& rPnt)
+{
+ if (IsMacroObj())
+ MovMacroObj(rPnt);
+ SdrGlueEditView::MovAction(rPnt);
+}
+
+void SdrObjEditView::EndAction()
+{
+ if (IsMacroObj())
+ EndMacroObj();
+ SdrGlueEditView::EndAction();
+}
+
+void SdrObjEditView::BckAction()
+{
+ BrkMacroObj();
+ SdrGlueEditView::BckAction();
+}
+
+void SdrObjEditView::BrkAction()
+{
+ BrkMacroObj();
+ SdrGlueEditView::BrkAction();
+}
+
+SdrPageView* SdrObjEditView::ShowSdrPage(SdrPage* pPage)
+{
+ SdrPageView* pPageView = SdrGlueEditView::ShowSdrPage(pPage);
+
+ if (comphelper::LibreOfficeKit::isActive() && pPageView)
+ {
+ // Check if other views have an active text edit on the same page as
+ // this one.
+ SdrViewIter aIter(pPageView->GetPage());
+ for (SdrView* pView = aIter.FirstView(); pView; pView = aIter.NextView())
+ {
+ if (pView == this || !pView->IsTextEdit())
+ continue;
+
+ OutputDevice* pOutDev = GetFirstOutputDevice();
+ if (!pOutDev || pOutDev->GetOutDevType() != OUTDEV_WINDOW)
+ continue;
+
+ // Found one, so create an outliner view, to get invalidations when
+ // the text edit changes.
+ // Call GetSfxViewShell() to make sure ImpMakeOutlinerView()
+ // registers the view shell of this draw view, and not the view
+ // shell of pView.
+ OutlinerView* pOutlinerView
+ = pView->ImpMakeOutlinerView(pOutDev->GetOwnerWindow(), nullptr, GetSfxViewShell());
+ pOutlinerView->HideCursor();
+ pView->GetTextEditOutliner()->InsertView(pOutlinerView);
+ }
+ }
+
+ return pPageView;
+}
+
+namespace
+{
+/// Removes outliner views registered in other draw views that use pOutputDevice.
+void lcl_RemoveTextEditOutlinerViews(SdrObjEditView const* pThis, SdrPageView const* pPageView,
+ OutputDevice const* pOutputDevice)
+{
+ if (!comphelper::LibreOfficeKit::isActive())
+ return;
+
+ if (!pPageView)
+ return;
+
+ if (!pOutputDevice || pOutputDevice->GetOutDevType() != OUTDEV_WINDOW)
+ return;
+
+ SdrViewIter aIter(pPageView->GetPage());
+ for (SdrView* pView = aIter.FirstView(); pView; pView = aIter.NextView())
+ {
+ if (pView == pThis || !pView->IsTextEdit())
+ continue;
+
+ SdrOutliner* pOutliner = pView->GetTextEditOutliner();
+ for (size_t nView = 0; nView < pOutliner->GetViewCount(); ++nView)
+ {
+ OutlinerView* pOutlinerView = pOutliner->GetView(nView);
+ if (pOutlinerView->GetWindow()->GetOutDev() != pOutputDevice)
+ continue;
+
+ pOutliner->RemoveView(pOutlinerView);
+ delete pOutlinerView;
+ }
+ }
+}
+}
+
+void SdrObjEditView::HideSdrPage()
+{
+ lcl_RemoveTextEditOutlinerViews(this, GetSdrPageView(), GetFirstOutputDevice());
+
+ if (mpTextEditPV == GetSdrPageView())
+ {
+ // HideSdrPage() will clear mpPageView, avoid a dangling pointer.
+ mpTextEditPV = nullptr;
+ }
+
+ SdrGlueEditView::HideSdrPage();
+}
+
+void SdrObjEditView::TakeActionRect(tools::Rectangle& rRect) const
+{
+ if (IsMacroObj())
+ {
+ rRect = pMacroObj->GetCurrentBoundRect();
+ }
+ else
+ {
+ SdrGlueEditView::TakeActionRect(rRect);
+ }
+}
+
+void SdrObjEditView::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
+{
+ SdrGlueEditView::Notify(rBC, rHint);
+ if (mpTextEditOutliner == nullptr)
+ return;
+
+ // change of printer while editing
+ if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint)
+ return;
+
+ const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint);
+ SdrHintKind eKind = pSdrHint->GetKind();
+ if (eKind == SdrHintKind::RefDeviceChange)
+ {
+ mpTextEditOutliner->SetRefDevice(mpModel->GetRefDevice());
+ }
+ if (eKind == SdrHintKind::DefaultTabChange)
+ {
+ mpTextEditOutliner->SetDefTab(mpModel->GetDefaultTabulator());
+ }
+}
+
+void SdrObjEditView::ModelHasChanged()
+{
+ SdrGlueEditView::ModelHasChanged();
+ if (mxWeakTextEditObj.is() && !mxWeakTextEditObj->IsInserted())
+ SdrEndTextEdit(); // object deleted
+ // TextEditObj changed?
+ if (!IsTextEdit())
+ return;
+
+ SdrTextObj* pTextObj = mxWeakTextEditObj.get();
+ if (pTextObj != nullptr)
+ {
+ size_t nOutlViewCnt = mpTextEditOutliner->GetViewCount();
+ bool bAreaChg = false;
+ bool bAnchorChg = false;
+ bool bColorChg = false;
+ bool bContourFrame = pTextObj->IsContourTextFrame();
+ EEAnchorMode eNewAnchor(EEAnchorMode::VCenterHCenter);
+ tools::Rectangle aOldArea(aMinTextEditArea);
+ aOldArea.Union(aTextEditArea);
+ Color aNewColor;
+ { // check area
+ Size aPaperMin1;
+ Size aPaperMax1;
+ tools::Rectangle aEditArea1;
+ tools::Rectangle aMinArea1;
+ pTextObj->TakeTextEditArea(&aPaperMin1, &aPaperMax1, &aEditArea1, &aMinArea1);
+ Point aPvOfs(pTextObj->GetTextEditOffset());
+
+ // add possible GridOffset to up-to-now view-independent EditAreas
+ basegfx::B2DVector aGridOffset(0.0, 0.0);
+ if (getPossibleGridOffsetForSdrObject(aGridOffset, pTextObj, GetSdrPageView()))
+ {
+ const Point aOffset(basegfx::fround(aGridOffset.getX()),
+ basegfx::fround(aGridOffset.getY()));
+
+ aEditArea1 += aOffset;
+ aMinArea1 += aOffset;
+ }
+
+ aEditArea1.Move(aPvOfs.X(), aPvOfs.Y());
+ aMinArea1.Move(aPvOfs.X(), aPvOfs.Y());
+ tools::Rectangle aNewArea(aMinArea1);
+ aNewArea.Union(aEditArea1);
+
+ if (aNewArea != aOldArea || aEditArea1 != aTextEditArea || aMinArea1 != aMinTextEditArea
+ || mpTextEditOutliner->GetMinAutoPaperSize() != aPaperMin1
+ || mpTextEditOutliner->GetMaxAutoPaperSize() != aPaperMax1)
+ {
+ aTextEditArea = aEditArea1;
+ aMinTextEditArea = aMinArea1;
+
+ const bool bPrevUpdateLayout = mpTextEditOutliner->SetUpdateLayout(false);
+ mpTextEditOutliner->SetMinAutoPaperSize(aPaperMin1);
+ mpTextEditOutliner->SetMaxAutoPaperSize(aPaperMax1);
+ mpTextEditOutliner->SetPaperSize(Size(0, 0)); // re-format Outliner
+
+ if (!bContourFrame)
+ {
+ mpTextEditOutliner->ClearPolygon();
+ EEControlBits nStat = mpTextEditOutliner->GetControlWord();
+ nStat |= EEControlBits::AUTOPAGESIZE;
+ mpTextEditOutliner->SetControlWord(nStat);
+ }
+ else
+ {
+ EEControlBits nStat = mpTextEditOutliner->GetControlWord();
+ nStat &= ~EEControlBits::AUTOPAGESIZE;
+ mpTextEditOutliner->SetControlWord(nStat);
+ tools::Rectangle aAnchorRect;
+ pTextObj->TakeTextAnchorRect(aAnchorRect);
+ pTextObj->ImpSetContourPolygon(*mpTextEditOutliner, aAnchorRect, true);
+ }
+ for (size_t nOV = 0; nOV < nOutlViewCnt; nOV++)
+ {
+ OutlinerView* pOLV = mpTextEditOutliner->GetView(nOV);
+ EVControlBits nStat0 = pOLV->GetControlWord();
+ EVControlBits nStat = nStat0;
+ // AutoViewSize only if not ContourFrame.
+ if (!bContourFrame)
+ nStat |= EVControlBits::AUTOSIZE;
+ else
+ nStat &= ~EVControlBits::AUTOSIZE;
+ if (nStat != nStat0)
+ pOLV->SetControlWord(nStat);
+ }
+
+ mpTextEditOutliner->SetUpdateLayout(bPrevUpdateLayout);
+ bAreaChg = true;
+ }
+ }
+ if (mpTextEditOutlinerView != nullptr)
+ { // check fill and anchor
+ EEAnchorMode eOldAnchor = mpTextEditOutlinerView->GetAnchorMode();
+ eNewAnchor = pTextObj->GetOutlinerViewAnchorMode();
+ bAnchorChg = eOldAnchor != eNewAnchor;
+ Color aOldColor(mpTextEditOutlinerView->GetBackgroundColor());
+ aNewColor = GetTextEditBackgroundColor(*this);
+ bColorChg = aOldColor != aNewColor;
+ }
+ // refresh always when it's a contour frame. That
+ // refresh is necessary since it triggers the repaint
+ // which makes the Handles visible. Changes at TakeTextRect()
+ // seem to have resulted in a case where no refresh is executed.
+ // Before that, a refresh must have been always executed
+ // (else this error would have happened earlier), thus I
+ // even think here a refresh should be done always.
+ // Since follow-up problems cannot even be guessed I only
+ // add this one more case to the if below.
+ // BTW: It's VERY bad style that here, inside ModelHasChanged()
+ // the outliner is again massively changed for the text object
+ // in text edit mode. Normally, all necessary data should be
+ // set at SdrBeginTextEdit(). Some changes and value assigns in
+ // SdrBeginTextEdit() are completely useless since they are set here
+ // again on ModelHasChanged().
+ if (bContourFrame || bAreaChg || bAnchorChg || bColorChg)
+ {
+ for (size_t nOV = 0; nOV < nOutlViewCnt; nOV++)
+ {
+ OutlinerView* pOLV = mpTextEditOutliner->GetView(nOV);
+ { // invalidate old OutlinerView area
+ vcl::Window* pWin = pOLV->GetWindow();
+ tools::Rectangle aTmpRect(aOldArea);
+ sal_uInt16 nPixSiz = pOLV->GetInvalidateMore() + 1;
+ Size aMore(pWin->PixelToLogic(Size(nPixSiz, nPixSiz)));
+ aTmpRect.AdjustLeft(-(aMore.Width()));
+ aTmpRect.AdjustRight(aMore.Width());
+ aTmpRect.AdjustTop(-(aMore.Height()));
+ aTmpRect.AdjustBottom(aMore.Height());
+ InvalidateOneWin(*pWin->GetOutDev(), aTmpRect);
+ }
+ if (bAnchorChg)
+ pOLV->SetAnchorMode(eNewAnchor);
+ if (bColorChg)
+ pOLV->SetBackgroundColor(aNewColor);
+
+ pOLV->SetOutputArea(
+ aTextEditArea); // because otherwise, we're not re-anchoring correctly
+ ImpInvalidateOutlinerView(*pOLV);
+ }
+ mpTextEditOutlinerView->ShowCursor();
+ }
+ }
+ ImpMakeTextCursorAreaVisible();
+}
+
+namespace
+{
+/**
+ Helper class to visualize the content of an active EditView as an
+ OverlayObject. These objects work with Primitives and are handled
+ from the OverlayManager(s) in place as needed.
+
+ It allows complete visualization of the content of the active
+ EditView without the need of Invalidates triggered by the EditView
+ and thus avoiding potentially expensive repaints by using the
+ automatically buffered Overlay mechanism.
+
+ It buffers as much as possible locally and *only* triggers a real
+ change (see call to objectChange()) when really needed.
+ */
+class TextEditOverlayObject : public sdr::overlay::OverlayObject
+{
+protected:
+ /// local access to associated sdr::overlay::OverlaySelection
+ std::unique_ptr<sdr::overlay::OverlaySelection> mxOverlaySelection;
+
+ /// local definition depends on active OutlinerView
+ OutlinerView& mrOutlinerView;
+
+ /// geometry definitions with buffering
+ basegfx::B2DRange maLastRange;
+ basegfx::B2DRange maRange;
+
+ /// text content definitions with buffering
+ drawinglayer::primitive2d::Primitive2DContainer maTextPrimitives;
+ drawinglayer::primitive2d::Primitive2DContainer maLastTextPrimitives;
+
+ /// bitfield
+ bool mbVisualizeSurroundingFrame : 1;
+
+ // geometry creation for OverlayObject, can use local *Last* values
+ virtual drawinglayer::primitive2d::Primitive2DContainer
+ createOverlayObjectPrimitive2DSequence() override;
+
+public:
+ TextEditOverlayObject(const Color& rColor, OutlinerView& rOutlinerView,
+ bool bVisualizeSurroundingFrame);
+ virtual ~TextEditOverlayObject() override;
+
+ // data read access
+ const sdr::overlay::OverlaySelection* getOverlaySelection() const
+ {
+ return mxOverlaySelection.get();
+ }
+ const OutlinerView& getOutlinerView() const { return mrOutlinerView; }
+
+ /// override to check conditions for last createOverlayObjectPrimitive2DSequence
+ virtual drawinglayer::primitive2d::Primitive2DContainer
+ getOverlayObjectPrimitive2DSequence() const override;
+
+ // data write access. In this OverlayObject we only have the
+ // callback that triggers detecting if something *has* changed
+ void checkDataChange(const basegfx::B2DRange& rMinTextEditArea);
+ void checkSelectionChange();
+};
+
+drawinglayer::primitive2d::Primitive2DContainer
+TextEditOverlayObject::createOverlayObjectPrimitive2DSequence()
+{
+ drawinglayer::primitive2d::Primitive2DContainer aRetval;
+
+ /// outer frame visualization
+ if (mbVisualizeSurroundingFrame)
+ {
+ const double fTransparence(SvtOptionsDrawinglayer::GetTransparentSelectionPercent() * 0.01);
+ const sal_uInt16 nPixSiz(getOutlinerView().GetInvalidateMore() - 1);
+
+ aRetval.push_back(new drawinglayer::primitive2d::OverlayRectanglePrimitive(
+ maRange, getBaseColor().getBColor(), fTransparence, std::max(6, nPixSiz - 2), // grow
+ 0.0, // shrink
+ 0.0));
+ }
+
+ // add buffered TextPrimitives
+ aRetval.append(maTextPrimitives);
+
+ return aRetval;
+}
+
+TextEditOverlayObject::TextEditOverlayObject(const Color& rColor, OutlinerView& rOutlinerView,
+ bool bVisualizeSurroundingFrame)
+ : OverlayObject(rColor)
+ , mrOutlinerView(rOutlinerView)
+ , mbVisualizeSurroundingFrame(bVisualizeSurroundingFrame)
+{
+ // no AA for TextEdit overlay
+ allowAntiAliase(false);
+
+ // create local OverlaySelection - this is an integral part of EditText
+ // visualization
+ std::vector<basegfx::B2DRange> aEmptySelection{};
+ mxOverlaySelection.reset(new sdr::overlay::OverlaySelection(
+ sdr::overlay::OverlayType::Transparent, rColor, std::move(aEmptySelection), true));
+}
+
+TextEditOverlayObject::~TextEditOverlayObject()
+{
+ mxOverlaySelection.reset();
+
+ if (getOverlayManager())
+ {
+ getOverlayManager()->remove(*this);
+ }
+}
+
+drawinglayer::primitive2d::Primitive2DContainer
+TextEditOverlayObject::getOverlayObjectPrimitive2DSequence() const
+{
+ if (!getPrimitive2DSequence().empty())
+ {
+ if (!maRange.equal(maLastRange) || maLastTextPrimitives != maTextPrimitives)
+ {
+ // conditions of last local decomposition have changed, delete to force new evaluation
+ const_cast<TextEditOverlayObject*>(this)->resetPrimitive2DSequence();
+ }
+ }
+
+ if (getPrimitive2DSequence().empty())
+ {
+ // remember new buffered values
+ const_cast<TextEditOverlayObject*>(this)->maLastRange = maRange;
+ const_cast<TextEditOverlayObject*>(this)->maLastTextPrimitives = maTextPrimitives;
+ }
+
+ // call base implementation
+ return OverlayObject::getOverlayObjectPrimitive2DSequence();
+}
+
+void TextEditOverlayObject::checkDataChange(const basegfx::B2DRange& rMinTextEditArea)
+{
+ bool bObjectChange(false);
+
+ // check current range
+ const tools::Rectangle aOutArea(mrOutlinerView.GetOutputArea());
+ basegfx::B2DRange aNewRange = vcl::unotools::b2DRectangleFromRectangle(aOutArea);
+ aNewRange.expand(rMinTextEditArea);
+
+ if (aNewRange != maRange)
+ {
+ maRange = aNewRange;
+ bObjectChange = true;
+ }
+
+ // check if text primitives did change
+ SdrOutliner* pSdrOutliner = dynamic_cast<SdrOutliner*>(getOutlinerView().GetOutliner());
+
+ if (pSdrOutliner)
+ {
+ // get TextPrimitives directly from active Outliner
+ basegfx::B2DHomMatrix aNewTransformA;
+ basegfx::B2DHomMatrix aNewTransformB;
+ basegfx::B2DRange aClipRange;
+ drawinglayer::primitive2d::Primitive2DContainer aNewTextPrimitives;
+
+ // active Outliner is always in unified oriented coordinate system (currently)
+ // so just translate to TopLeft of visible Range. Keep in mind that top-left
+ // depends on vertical text and top-to-bottom text attributes
+ const tools::Rectangle aVisArea(mrOutlinerView.GetVisArea());
+ const bool bVerticalWriting(pSdrOutliner->IsVertical());
+ const bool bTopToBottom(pSdrOutliner->IsTopToBottom());
+ const double fStartInX(bVerticalWriting && bTopToBottom
+ ? aOutArea.Right() - aVisArea.Left()
+ : aOutArea.Left() - aVisArea.Left());
+ const double fStartInY(bVerticalWriting && !bTopToBottom
+ ? aOutArea.Bottom() - aVisArea.Top()
+ : aOutArea.Top() - aVisArea.Top());
+
+ aNewTransformB.translate(fStartInX, fStartInY);
+
+ // get the current TextPrimitives. This is the most expensive part
+ // of this mechanism, it *may* be possible to buffer layouted
+ // primitives per ParaPortion with/in/dependent on the EditEngine
+ // content if needed. For now, get and compare
+ SdrTextObj::impDecomposeBlockTextPrimitiveDirect(
+ aNewTextPrimitives, *pSdrOutliner, aNewTransformA, aNewTransformB, aClipRange);
+
+ if (aNewTextPrimitives != maTextPrimitives)
+ {
+ maTextPrimitives = std::move(aNewTextPrimitives);
+ bObjectChange = true;
+ }
+ }
+
+ if (bObjectChange)
+ {
+ // if there really *was* a change signal the OverlayManager to
+ // refresh this object's visualization
+ objectChange();
+
+ // on data change, always do a SelectionChange, too
+ // since the selection is an integral part of text visualization
+ checkSelectionChange();
+ }
+}
+
+void TextEditOverlayObject::checkSelectionChange()
+{
+ if (!(getOverlaySelection() && getOverlayManager()))
+ return;
+
+ std::vector<tools::Rectangle> aLogicRects;
+ std::vector<basegfx::B2DRange> aLogicRanges;
+ const Size aLogicPixel(getOverlayManager()->getOutputDevice().PixelToLogic(Size(1, 1)));
+
+ // get logic selection
+ getOutlinerView().GetSelectionRectangles(aLogicRects);
+
+ aLogicRanges.reserve(aLogicRects.size());
+ for (const auto& aRect : aLogicRects)
+ {
+ // convert from logic Rectangles to logic Ranges, do not forget to add
+ // one Unit (in this case logical units for one pixel, pre-calculated)
+ aLogicRanges.emplace_back(
+ aRect.Left() - aLogicPixel.Width(), aRect.Top() - aLogicPixel.Height(),
+ aRect.Right() + aLogicPixel.Width(), aRect.Bottom() + aLogicPixel.Height());
+ }
+
+ mxOverlaySelection->setRanges(std::move(aLogicRanges));
+}
+} // end of anonymous namespace
+
+// TextEdit
+
+// callback from the active EditView, forward to evtl. existing instances of the
+// TextEditOverlayObject(s). This will additionally update the selection which
+// is an integral part of the text visualization
+void SdrObjEditView::EditViewInvalidate(const tools::Rectangle&)
+{
+ if (!IsTextEdit())
+ return;
+
+ // MinTextRange may have changed. Forward it, too
+ const basegfx::B2DRange aMinTextRange
+ = vcl::unotools::b2DRectangleFromRectangle(aMinTextEditArea);
+
+ for (sal_uInt32 a(0); a < maTEOverlayGroup.count(); a++)
+ {
+ TextEditOverlayObject* pCandidate
+ = dynamic_cast<TextEditOverlayObject*>(&maTEOverlayGroup.getOverlayObject(a));
+
+ if (pCandidate)
+ {
+ pCandidate->checkDataChange(aMinTextRange);
+ }
+ }
+}
+
+// callback from the active EditView, forward to evtl. existing instances of the
+// TextEditOverlayObject(s). This cvall *only* updates the selection visualization
+// which is e.g. used when only the selection is changed, but not the text
+void SdrObjEditView::EditViewSelectionChange()
+{
+ if (!IsTextEdit())
+ return;
+
+ for (sal_uInt32 a(0); a < maTEOverlayGroup.count(); a++)
+ {
+ TextEditOverlayObject* pCandidate
+ = dynamic_cast<TextEditOverlayObject*>(&maTEOverlayGroup.getOverlayObject(a));
+
+ if (pCandidate)
+ {
+ pCandidate->checkSelectionChange();
+ }
+ }
+}
+
+OutputDevice& SdrObjEditView::EditViewOutputDevice() const { return *mpTextEditWin->GetOutDev(); }
+
+Point SdrObjEditView::EditViewPointerPosPixel() const
+{
+ return mpTextEditWin->GetPointerPosPixel();
+}
+
+css::uno::Reference<css::datatransfer::clipboard::XClipboard> SdrObjEditView::GetClipboard() const
+{
+ if (!mpTextEditWin)
+ return nullptr;
+ return mpTextEditWin->GetClipboard();
+}
+
+css::uno::Reference<css::datatransfer::dnd::XDropTarget> SdrObjEditView::GetDropTarget()
+{
+ if (!mpTextEditWin)
+ return nullptr;
+ return mpTextEditWin->GetDropTarget();
+}
+
+void SdrObjEditView::EditViewInputContext(const InputContext& rInputContext)
+{
+ if (!mpTextEditWin)
+ return;
+ mpTextEditWin->SetInputContext(rInputContext);
+}
+
+void SdrObjEditView::EditViewCursorRect(const tools::Rectangle& rRect, int nExtTextInputWidth)
+{
+ if (!mpTextEditWin)
+ return;
+ mpTextEditWin->SetCursorRect(&rRect, nExtTextInputWidth);
+}
+
+void SdrObjEditView::TextEditDrawing(SdrPaintWindow& rPaintWindow)
+{
+ if (!comphelper::LibreOfficeKit::isActive())
+ {
+ // adapt all TextEditOverlayObject(s), so call EditViewInvalidate()
+ // to update accordingly (will update selection, too). Suppress new
+ // stuff when LibreOfficeKit is active
+ EditViewInvalidate(tools::Rectangle());
+ }
+ else
+ {
+ // draw old text edit stuff
+ if (IsTextEdit())
+ {
+ const SdrOutliner* pActiveOutliner = GetTextEditOutliner();
+
+ if (pActiveOutliner)
+ {
+ const sal_uInt32 nViewCount(pActiveOutliner->GetViewCount());
+
+ if (nViewCount)
+ {
+ const vcl::Region& rRedrawRegion = rPaintWindow.GetRedrawRegion();
+ const tools::Rectangle aCheckRect(rRedrawRegion.GetBoundRect());
+
+ for (sal_uInt32 i(0); i < nViewCount; i++)
+ {
+ OutlinerView* pOLV = pActiveOutliner->GetView(i);
+
+ // If rPaintWindow knows that the output device is a render
+ // context and is aware of the underlying vcl::Window,
+ // compare against that; that's how double-buffering can
+ // still find the matching OutlinerView.
+ OutputDevice* pOutputDevice = rPaintWindow.GetWindow()
+ ? rPaintWindow.GetWindow()->GetOutDev()
+ : &rPaintWindow.GetOutputDevice();
+ if (pOLV->GetWindow()->GetOutDev() == pOutputDevice
+ || comphelper::LibreOfficeKit::isActive())
+ {
+ ImpPaintOutlinerView(*pOLV, aCheckRect,
+ rPaintWindow.GetTargetOutputDevice());
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void SdrObjEditView::ImpPaintOutlinerView(OutlinerView& rOutlView, const tools::Rectangle& rRect,
+ OutputDevice& rTargetDevice) const
+{
+ const SdrTextObj* pText = GetTextEditObject();
+ bool bTextFrame(pText && pText->IsTextFrame());
+ bool bFitToSize(mpTextEditOutliner->GetControlWord() & EEControlBits::STRETCHING);
+ bool bModified(mpTextEditOutliner->IsModified());
+ tools::Rectangle aBlankRect(rOutlView.GetOutputArea());
+ aBlankRect.Union(aMinTextEditArea);
+ tools::Rectangle aPixRect(rTargetDevice.LogicToPixel(aBlankRect));
+
+ // in the tiled rendering case, the setup is incomplete, and we very
+ // easily get an empty rRect on input - that will cause that everything is
+ // clipped; happens in case of editing text inside a shape in Calc.
+ // FIXME would be better to complete the setup so that we don't get an
+ // empty rRect here
+ if (!comphelper::LibreOfficeKit::isActive() || !rRect.IsEmpty())
+ aBlankRect.Intersection(rRect);
+
+ rOutlView.GetOutliner()->SetUpdateLayout(true); // Bugfix #22596#
+ rOutlView.Paint(aBlankRect, &rTargetDevice);
+
+ if (!bModified)
+ {
+ mpTextEditOutliner->ClearModifyFlag();
+ }
+
+ if (bTextFrame && !bFitToSize)
+ {
+ // completely reworked to use primitives; this ensures same look and functionality
+ const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
+ std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> xProcessor(
+ drawinglayer::processor2d::createProcessor2DFromOutputDevice(rTargetDevice,
+ aViewInformation2D));
+
+ const bool bMapModeEnabled(rTargetDevice.IsMapModeEnabled());
+ const basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(aPixRect);
+ const Color aHilightColor(SvtOptionsDrawinglayer::getHilightColor());
+ const double fTransparence(SvtOptionsDrawinglayer::GetTransparentSelectionPercent() * 0.01);
+ const sal_uInt16 nPixSiz(rOutlView.GetInvalidateMore() - 1);
+ const drawinglayer::primitive2d::Primitive2DReference xReference(
+ new drawinglayer::primitive2d::OverlayRectanglePrimitive(
+ aRange, aHilightColor.getBColor(), fTransparence, std::max(6, nPixSiz - 2), // grow
+ 0.0, // shrink
+ 0.0));
+ const drawinglayer::primitive2d::Primitive2DContainer aSequence{ xReference };
+
+ rTargetDevice.EnableMapMode(false);
+ xProcessor->process(aSequence);
+ rTargetDevice.EnableMapMode(bMapModeEnabled);
+ }
+
+ rOutlView.ShowCursor(/*bGotoCursor=*/true, /*bActivate=*/true);
+}
+
+void SdrObjEditView::ImpInvalidateOutlinerView(OutlinerView const& rOutlView) const
+{
+ vcl::Window* pWin = rOutlView.GetWindow();
+
+ if (!pWin)
+ return;
+
+ const SdrTextObj* pText = GetTextEditObject();
+ bool bTextFrame(pText && pText->IsTextFrame());
+ bool bFitToSize(pText && pText->IsFitToSize());
+
+ if (!bTextFrame || bFitToSize)
+ return;
+
+ tools::Rectangle aBlankRect(rOutlView.GetOutputArea());
+ aBlankRect.Union(aMinTextEditArea);
+ tools::Rectangle aPixRect(pWin->LogicToPixel(aBlankRect));
+ sal_uInt16 nPixSiz(rOutlView.GetInvalidateMore() - 1);
+
+ aPixRect.AdjustLeft(-1);
+ aPixRect.AdjustTop(-1);
+ aPixRect.AdjustRight(1);
+ aPixRect.AdjustBottom(1);
+
+ {
+ // limit xPixRect because of driver problems when pixel coordinates are too far out
+ Size aMaxXY(pWin->GetOutputSizePixel());
+ tools::Long a(2 * nPixSiz);
+ tools::Long nMaxX(aMaxXY.Width() + a);
+ tools::Long nMaxY(aMaxXY.Height() + a);
+
+ if (aPixRect.Left() < -a)
+ aPixRect.SetLeft(-a);
+ if (aPixRect.Top() < -a)
+ aPixRect.SetTop(-a);
+ if (aPixRect.Right() > nMaxX)
+ aPixRect.SetRight(nMaxX);
+ if (aPixRect.Bottom() > nMaxY)
+ aPixRect.SetBottom(nMaxY);
+ }
+
+ tools::Rectangle aOuterPix(aPixRect);
+ aOuterPix.AdjustLeft(-nPixSiz);
+ aOuterPix.AdjustTop(-nPixSiz);
+ aOuterPix.AdjustRight(nPixSiz);
+ aOuterPix.AdjustBottom(nPixSiz);
+
+ bool bMapModeEnabled(pWin->IsMapModeEnabled());
+ pWin->EnableMapMode(false);
+ pWin->Invalidate(aOuterPix);
+ pWin->EnableMapMode(bMapModeEnabled);
+}
+
+OutlinerView* SdrObjEditView::ImpMakeOutlinerView(vcl::Window* pWin, OutlinerView* pGivenView,
+ SfxViewShell* pViewShell) const
+{
+ // background
+ Color aBackground(GetTextEditBackgroundColor(*this));
+ SdrTextObj* pText = mxWeakTextEditObj.get();
+ bool bTextFrame = pText != nullptr && pText->IsTextFrame();
+ bool bContourFrame = pText != nullptr && pText->IsContourTextFrame();
+ // create OutlinerView
+ OutlinerView* pOutlView = pGivenView;
+ mpTextEditOutliner->SetUpdateLayout(false);
+
+ if (pOutlView == nullptr)
+ {
+ pOutlView = new OutlinerView(mpTextEditOutliner.get(), pWin);
+ }
+ else
+ {
+ pOutlView->SetWindow(pWin);
+ }
+
+ if (mbNegativeX)
+ pOutlView->GetEditView().SetNegativeX(mbNegativeX);
+
+ // disallow scrolling
+ EVControlBits nStat = pOutlView->GetControlWord();
+ nStat &= ~EVControlBits::AUTOSCROLL;
+ // AutoViewSize only if not ContourFrame.
+ if (!bContourFrame)
+ nStat |= EVControlBits::AUTOSIZE;
+ if (bTextFrame)
+ {
+ sal_uInt16 nPixSiz = maHdlList.GetHdlSize() * 2 + 1;
+ nStat |= EVControlBits::INVONEMORE;
+ pOutlView->SetInvalidateMore(nPixSiz);
+ }
+ pOutlView->SetControlWord(nStat);
+ pOutlView->SetBackgroundColor(aBackground);
+
+ // In case we're in the process of constructing a new view shell,
+ // SfxViewShell::Current() may still point to the old one. So if possible,
+ // depend on the application owning this draw view to provide the view
+ // shell.
+ SfxViewShell* pSfxViewShell = pViewShell ? pViewShell : GetSfxViewShell();
+ pOutlView->RegisterViewShell(pSfxViewShell ? pSfxViewShell : SfxViewShell::Current());
+
+ if (pText != nullptr)
+ {
+ pOutlView->SetAnchorMode(pText->GetOutlinerViewAnchorMode());
+ mpTextEditOutliner->SetFixedCellHeight(
+ pText->GetMergedItem(SDRATTR_TEXT_USEFIXEDCELLHEIGHT).GetValue());
+ }
+ // do update before setting output area so that aTextEditArea can be recalculated
+ mpTextEditOutliner->SetUpdateLayout(true);
+ pOutlView->SetOutputArea(aTextEditArea);
+ ImpInvalidateOutlinerView(*pOutlView);
+ return pOutlView;
+}
+
+IMPL_LINK(SdrObjEditView, ImpOutlinerStatusEventHdl, EditStatus&, rEditStat, void)
+{
+ if (mpTextEditOutliner)
+ {
+ SdrTextObj* pTextObj = mxWeakTextEditObj.get();
+ if (pTextObj)
+ {
+ pTextObj->onEditOutlinerStatusEvent(&rEditStat);
+ }
+ }
+}
+
+void SdrObjEditView::ImpChainingEventHdl()
+{
+ if (!mpTextEditOutliner)
+ return;
+
+ SdrTextObj* pTextObj = mxWeakTextEditObj.get();
+ OutlinerView* pOLV = GetTextEditOutlinerView();
+ if (pTextObj && pOLV)
+ {
+ TextChain* pTextChain = pTextObj->GetTextChain();
+
+ // XXX: IsChainable and GetNilChainingEvent are a bit mixed up atm
+ if (!pTextObj->IsChainable())
+ {
+ return;
+ }
+ // This is true during an underflow-caused overflow (with pEdtOutl->SetText())
+ if (pTextChain->GetNilChainingEvent(pTextObj))
+ {
+ return;
+ }
+
+ // We prevent to trigger further handling of overflow/underflow for pTextObj
+ pTextChain->SetNilChainingEvent(pTextObj, true); // XXX
+
+ // Save previous selection pos // NOTE: It must be done to have the right CursorEvent in KeyInput
+ pTextChain->SetPreChainingSel(pTextObj, pOLV->GetSelection());
+ //maPreChainingSel = new ESelection(pOLV->GetSelection());
+
+ // Handling Undo
+ const int nText = 0; // XXX: hardcoded index (SdrTextObj::getText handles only 0)
+
+ const bool bUndoEnabled = GetModel() && IsUndoEnabled();
+ std::unique_ptr<SdrUndoObjSetText> pTxtUndo;
+ if (bUndoEnabled)
+ pTxtUndo.reset(
+ dynamic_cast<SdrUndoObjSetText*>(GetModel()
+ ->GetSdrUndoFactory()
+ .CreateUndoObjectSetText(*pTextObj, nText)
+ .release()));
+
+ // trigger actual chaining
+ pTextObj->onChainingEvent();
+
+ if (pTxtUndo)
+ {
+ pTxtUndo->AfterSetText();
+ if (!pTxtUndo->IsDifferent())
+ {
+ pTxtUndo.reset();
+ }
+ }
+
+ if (pTxtUndo)
+ AddUndo(std::move(pTxtUndo));
+
+ //maCursorEvent = new CursorChainingEvent(pTextChain->GetCursorEvent(pTextObj));
+ //SdrTextObj *pNextLink = pTextObj->GetNextLinkInChain();
+
+ // NOTE: Must be called. Don't let the function return if you set it to true and not reset it
+ pTextChain->SetNilChainingEvent(pTextObj, false);
+ }
+ else
+ {
+ // XXX
+ SAL_INFO("svx.chaining", "[OnChaining] No Edit Outliner View");
+ }
+}
+
+IMPL_LINK_NOARG(SdrObjEditView, ImpAfterCutOrPasteChainingEventHdl, LinkParamNone*, void)
+{
+ SdrTextObj* pTextObj = GetTextEditObject();
+ if (!pTextObj)
+ return;
+ ImpChainingEventHdl();
+ TextChainCursorManager aCursorManager(this, pTextObj);
+ ImpMoveCursorAfterChainingEvent(&aCursorManager);
+}
+
+void SdrObjEditView::ImpMoveCursorAfterChainingEvent(TextChainCursorManager* pCursorManager)
+{
+ if (!mxWeakTextEditObj.is() || !pCursorManager)
+ return;
+
+ SdrTextObj* pTextObj = mxWeakTextEditObj.get();
+
+ // Check if it has links to move it to
+ if (!pTextObj || !pTextObj->IsChainable())
+ return;
+
+ TextChain* pTextChain = pTextObj->GetTextChain();
+ ESelection aNewSel = pTextChain->GetPostChainingSel(pTextObj);
+
+ pCursorManager->HandleCursorEventAfterChaining(pTextChain->GetCursorEvent(pTextObj), aNewSel);
+
+ // Reset event
+ pTextChain->SetCursorEvent(pTextObj, CursorChainingEvent::NULL_EVENT);
+}
+
+IMPL_LINK(SdrObjEditView, ImpOutlinerCalcFieldValueHdl, EditFieldInfo*, pFI, void)
+{
+ bool bOk = false;
+ OUString& rStr = pFI->GetRepresentation();
+ rStr.clear();
+ SdrTextObj* pTextObj = mxWeakTextEditObj.get();
+ if (pTextObj != nullptr)
+ {
+ std::optional<Color> pTxtCol;
+ std::optional<Color> pFldCol;
+ bOk = pTextObj->CalcFieldValue(pFI->GetField(), pFI->GetPara(), pFI->GetPos(), true,
+ pTxtCol, pFldCol, rStr);
+ if (bOk)
+ {
+ if (pTxtCol)
+ {
+ pFI->SetTextColor(*pTxtCol);
+ }
+ if (pFldCol)
+ {
+ pFI->SetFieldColor(*pFldCol);
+ }
+ else
+ {
+ pFI->SetFieldColor(COL_LIGHTGRAY); // TODO: remove this later on (357)
+ }
+ }
+ }
+ Outliner& rDrawOutl = mpModel->GetDrawOutliner(pTextObj);
+ Link<EditFieldInfo*, void> aDrawOutlLink = rDrawOutl.GetCalcFieldValueHdl();
+ if (!bOk && aDrawOutlLink.IsSet())
+ {
+ aDrawOutlLink.Call(pFI);
+ bOk = !rStr.isEmpty();
+ }
+ if (!bOk)
+ {
+ aOldCalcFieldValueLink.Call(pFI);
+ }
+}
+
+IMPL_LINK_NOARG(SdrObjEditView, EndTextEditHdl, SdrUndoManager*, void) { SdrEndTextEdit(); }
+
+// Default implementation - null UndoManager
+std::unique_ptr<SdrUndoManager> SdrObjEditView::createLocalTextUndoManager()
+{
+ SAL_WARN("svx", "SdrObjEditView::createLocalTextUndoManager needs to be overridden");
+ return std::unique_ptr<SdrUndoManager>();
+}
+
+bool SdrObjEditView::SdrBeginTextEdit(SdrObject* pObj_, SdrPageView* pPV, vcl::Window* pWin,
+ bool bIsNewObj, SdrOutliner* pGivenOutliner,
+ OutlinerView* pGivenOutlinerView, bool bDontDeleteOutliner,
+ bool bOnlyOneView, bool bGrabFocus)
+{
+ // FIXME cannot be an assert() yet, the code is not ready for that;
+ // eg. press F7 in Impress when you are inside a text object with spelling
+ // mistakes => boom; and it is unclear how to avoid that
+ SAL_WARN_IF(IsTextEdit(), "svx", "SdrBeginTextEdit called when IsTextEdit() is already true.");
+ // FIXME this encourages all sorts of bad habits and should be removed
+ SdrEndTextEdit();
+
+ SdrTextObj* pObj = dynamic_cast<SdrTextObj*>(pObj_);
+ if (!pObj)
+ return false; // currently only possible with text objects
+
+ if (bGrabFocus && pWin)
+ {
+ // attention, this call may cause an EndTextEdit() call to this view
+ pWin->GrabFocus(); // to force the cursor into the edit view
+ }
+
+ mbTextEditDontDelete = bDontDeleteOutliner && pGivenOutliner != nullptr;
+ mbTextEditOnlyOneView = bOnlyOneView;
+ mbTextEditNewObj = bIsNewObj;
+ const sal_uInt32 nWinCount(PaintWindowCount());
+
+ bool bBrk(false);
+
+ if (!pWin)
+ {
+ for (sal_uInt32 i = 0; i < nWinCount && !pWin; i++)
+ {
+ SdrPaintWindow* pPaintWindow = GetPaintWindow(i);
+
+ if (OUTDEV_WINDOW == pPaintWindow->GetOutputDevice().GetOutDevType())
+ {
+ pWin = pPaintWindow->GetOutputDevice().GetOwnerWindow();
+ }
+ }
+
+ // break, when no window exists
+ if (!pWin)
+ {
+ bBrk = true;
+ }
+ }
+
+ if (!bBrk && !pPV)
+ {
+ pPV = GetSdrPageView();
+
+ // break, when no PageView for the object exists
+ if (!pPV)
+ {
+ bBrk = true;
+ }
+ }
+
+ // no TextEdit on objects in locked Layer
+ if (pPV && pPV->GetLockedLayers().IsSet(pObj->GetLayer()))
+ {
+ bBrk = true;
+ }
+
+ if (mpTextEditOutliner)
+ {
+ OSL_FAIL("SdrObjEditView::SdrBeginTextEdit(): Old Outliner still exists.");
+ mpTextEditOutliner.reset();
+ }
+
+ if (!bBrk)
+ {
+ mpTextEditWin = pWin;
+ mpTextEditPV = pPV;
+ mxWeakTextEditObj.reset(pObj);
+ if (pGivenOutliner)
+ {
+ mpTextEditOutliner.reset(pGivenOutliner);
+ pGivenOutliner = nullptr; // so we don't delete it on the error path
+ }
+ else
+ mpTextEditOutliner = SdrMakeOutliner(OutlinerMode::TextObject,
+ mxWeakTextEditObj->getSdrModelFromSdrObject());
+
+ {
+ SvtAccessibilityOptions aOptions;
+ mpTextEditOutliner->ForceAutoColor(aOptions.GetIsAutomaticFontColor());
+ }
+
+ aOldCalcFieldValueLink = mpTextEditOutliner->GetCalcFieldValueHdl();
+ // FieldHdl has to be set by SdrBeginTextEdit, because this call an UpdateFields
+ mpTextEditOutliner->SetCalcFieldValueHdl(
+ LINK(this, SdrObjEditView, ImpOutlinerCalcFieldValueHdl));
+ mpTextEditOutliner->SetBeginPasteOrDropHdl(LINK(this, SdrObjEditView, BeginPasteOrDropHdl));
+ mpTextEditOutliner->SetEndPasteOrDropHdl(LINK(this, SdrObjEditView, EndPasteOrDropHdl));
+
+ // It is just necessary to make the visualized page known. Set it.
+ mpTextEditOutliner->setVisualizedPage(pPV->GetPage());
+
+ mpTextEditOutliner->SetTextObjNoInit(mxWeakTextEditObj.get());
+
+ if (mxWeakTextEditObj->BegTextEdit(*mpTextEditOutliner))
+ {
+ SdrTextObj* pTextObj = mxWeakTextEditObj.get();
+ DBG_ASSERT(pTextObj, "svx::SdrObjEditView::BegTextEdit(), no text object?");
+ if (!pTextObj)
+ return false;
+
+ // switch off any running TextAnimations
+ pTextObj->SetTextAnimationAllowed(false);
+
+ // remember old cursor
+ if (mpTextEditOutliner->GetViewCount() != 0)
+ {
+ mpTextEditOutliner->RemoveView(static_cast<size_t>(0));
+ }
+
+ // Determine EditArea via TakeTextEditArea.
+ // TODO: This could theoretically be left out, because TakeTextRect() calculates the aTextEditArea,
+ // but aMinTextEditArea has to happen, too (therefore leaving this in right now)
+ pTextObj->TakeTextEditArea(nullptr, nullptr, &aTextEditArea, &aMinTextEditArea);
+
+ tools::Rectangle aTextRect;
+ tools::Rectangle aAnchorRect;
+ pTextObj->TakeTextRect(*mpTextEditOutliner, aTextRect, true,
+ &aAnchorRect /* Give true here, not false */);
+
+ if (!pTextObj->IsContourTextFrame())
+ {
+ // FitToSize not together with ContourFrame, for now
+ if (pTextObj->IsFitToSize())
+ aTextRect = aAnchorRect;
+ }
+
+ aTextEditArea = aTextRect;
+
+ // add possible GridOffset to up-to-now view-independent EditAreas
+ basegfx::B2DVector aGridOffset(0.0, 0.0);
+ if (getPossibleGridOffsetForSdrObject(aGridOffset, pTextObj, pPV))
+ {
+ const Point aOffset(basegfx::fround(aGridOffset.getX()),
+ basegfx::fround(aGridOffset.getY()));
+
+ aTextEditArea += aOffset;
+ aMinTextEditArea += aOffset;
+ }
+
+ Point aPvOfs(pTextObj->GetTextEditOffset());
+ aTextEditArea.Move(aPvOfs.X(), aPvOfs.Y());
+ aMinTextEditArea.Move(aPvOfs.X(), aPvOfs.Y());
+ pTextEditCursorBuffer = pWin->GetCursor();
+
+ maHdlList.SetMoveOutside(true);
+
+ // Since IsMarkHdlWhenTextEdit() is ignored, it is necessary
+ // to call AdjustMarkHdl() always.
+ AdjustMarkHdl();
+
+ mpTextEditOutlinerView = ImpMakeOutlinerView(pWin, pGivenOutlinerView);
+
+ if (!comphelper::LibreOfficeKit::isActive() && mpTextEditOutlinerView)
+ {
+ // activate visualization of EditView on Overlay, suppress when
+ // LibreOfficeKit is active
+ mpTextEditOutlinerView->GetEditView().setEditViewCallbacks(this);
+
+ const Color aHilightColor(SvtOptionsDrawinglayer::getHilightColor());
+ const SdrTextObj* pText = GetTextEditObject();
+ const bool bTextFrame(pText && pText->IsTextFrame());
+ const bool bFitToSize(mpTextEditOutliner->GetControlWord()
+ & EEControlBits::STRETCHING);
+ const bool bVisualizeSurroundingFrame(bTextFrame && !bFitToSize);
+ SdrPageView* pPageView = GetSdrPageView();
+
+ if (pPageView)
+ {
+ for (sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
+ {
+ const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);
+
+ if (rPageWindow.GetPaintWindow().OutputToWindow())
+ {
+ const rtl::Reference<sdr::overlay::OverlayManager>& xManager
+ = rPageWindow.GetOverlayManager();
+ if (xManager.is())
+ {
+ std::unique_ptr<TextEditOverlayObject> pNewTextEditOverlayObject(
+ new TextEditOverlayObject(aHilightColor,
+ *mpTextEditOutlinerView,
+ bVisualizeSurroundingFrame));
+
+ xManager->add(*pNewTextEditOverlayObject);
+ xManager->add(const_cast<sdr::overlay::OverlaySelection&>(
+ *pNewTextEditOverlayObject->getOverlaySelection()));
+
+ maTEOverlayGroup.append(std::move(pNewTextEditOverlayObject));
+ }
+ }
+ }
+ }
+ }
+
+ // check if this view is already inserted
+ size_t i2, nCount = mpTextEditOutliner->GetViewCount();
+ for (i2 = 0; i2 < nCount; i2++)
+ {
+ if (mpTextEditOutliner->GetView(i2) == mpTextEditOutlinerView)
+ break;
+ }
+
+ if (i2 == nCount)
+ mpTextEditOutliner->InsertView(mpTextEditOutlinerView, 0);
+
+ maHdlList.SetMoveOutside(false);
+ maHdlList.SetMoveOutside(true);
+
+ // register all windows as OutlinerViews with the Outliner
+ if (!bOnlyOneView)
+ {
+ for (sal_uInt32 i = 0; i < nWinCount; i++)
+ {
+ SdrPaintWindow* pPaintWindow = GetPaintWindow(i);
+ OutputDevice& rOutDev = pPaintWindow->GetOutputDevice();
+
+ if (&rOutDev != pWin->GetOutDev() && OUTDEV_WINDOW == rOutDev.GetOutDevType())
+ {
+ OutlinerView* pOutlView
+ = ImpMakeOutlinerView(rOutDev.GetOwnerWindow(), nullptr);
+ mpTextEditOutliner->InsertView(pOutlView, static_cast<sal_uInt16>(i));
+ }
+ }
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ // Register an outliner view for all other sdr views that
+ // show the same page, so that when the text edit changes,
+ // all interested windows get an invalidation.
+ SdrViewIter aIter(pObj->getSdrPageFromSdrObject());
+ for (SdrView* pView = aIter.FirstView(); pView; pView = aIter.NextView())
+ {
+ if (pView == this)
+ continue;
+
+ for (sal_uInt32 nViewPaintWindow = 0;
+ nViewPaintWindow < pView->PaintWindowCount(); ++nViewPaintWindow)
+ {
+ SdrPaintWindow* pPaintWindow = pView->GetPaintWindow(nViewPaintWindow);
+ OutputDevice& rOutDev = pPaintWindow->GetOutputDevice();
+
+ if (&rOutDev != pWin->GetOutDev()
+ && OUTDEV_WINDOW == rOutDev.GetOutDevType())
+ {
+ OutlinerView* pOutlView
+ = ImpMakeOutlinerView(rOutDev.GetOwnerWindow(), nullptr);
+ pOutlView->HideCursor();
+ rOutDev.GetOwnerWindow()->SetCursor(nullptr);
+ mpTextEditOutliner->InsertView(pOutlView);
+ }
+ }
+ }
+ }
+ }
+
+ mpTextEditOutlinerView->ShowCursor();
+ mpTextEditOutliner->SetStatusEventHdl(
+ LINK(this, SdrObjEditView, ImpOutlinerStatusEventHdl));
+ if (pTextObj->IsChainable())
+ {
+ mpTextEditOutlinerView->SetEndCutPasteLinkHdl(
+ LINK(this, SdrObjEditView, ImpAfterCutOrPasteChainingEventHdl));
+ }
+
+ mpTextEditOutliner->ClearModifyFlag();
+
+ if (pTextObj->IsFitToSize())
+ {
+ pWin->Invalidate(aTextEditArea);
+ }
+
+ if (GetModel())
+ {
+ SdrHint aHint(SdrHintKind::BeginEdit, *pTextObj);
+ GetModel()->Broadcast(aHint);
+ }
+
+ mpTextEditOutliner->setVisualizedPage(nullptr);
+
+ if (mxSelectionController.is())
+ mxSelectionController->onSelectionHasChanged();
+
+ if (GetModel() && IsUndoEnabled()
+ && !GetModel()->GetDisableTextEditUsesCommonUndoManager())
+ {
+ SdrUndoManager* pSdrUndoManager = nullptr;
+ mpLocalTextEditUndoManager = createLocalTextUndoManager();
+
+ if (mpLocalTextEditUndoManager)
+ pSdrUndoManager = mpLocalTextEditUndoManager.get();
+
+ if (pSdrUndoManager)
+ {
+ // we have an outliner, undo manager and it's an EditUndoManager, exchange
+ // the document undo manager and the default one from the outliner and tell
+ // it that text edit starts by setting a callback if it needs to end text edit mode.
+ assert(nullptr == mpOldTextEditUndoManager);
+
+ mpOldTextEditUndoManager = mpTextEditOutliner->SetUndoManager(pSdrUndoManager);
+ pSdrUndoManager->SetEndTextEditHdl(LINK(this, SdrObjEditView, EndTextEditHdl));
+ }
+ else
+ {
+ OSL_ENSURE(false,
+ "The document undo manager is not derived from SdrUndoManager (!)");
+ }
+ }
+
+ return true; // ran fine, let TextEdit run now
+ }
+ else
+ {
+ mpTextEditOutliner->SetCalcFieldValueHdl(aOldCalcFieldValueLink);
+ mpTextEditOutliner->SetBeginPasteOrDropHdl(Link<PasteOrDropInfos*, void>());
+ mpTextEditOutliner->SetEndPasteOrDropHdl(Link<PasteOrDropInfos*, void>());
+ }
+ }
+ if (mpTextEditOutliner != nullptr)
+ {
+ mpTextEditOutliner->setVisualizedPage(nullptr);
+ }
+
+ // something went wrong...
+ if (!bDontDeleteOutliner)
+ {
+ delete pGivenOutliner;
+ if (pGivenOutlinerView != nullptr)
+ {
+ delete pGivenOutlinerView;
+ pGivenOutlinerView = nullptr;
+ }
+ }
+ mpTextEditOutliner.reset();
+
+ mpTextEditOutlinerView = nullptr;
+ mxWeakTextEditObj.reset(nullptr);
+ mpTextEditPV = nullptr;
+ mpTextEditWin = nullptr;
+ maHdlList.SetMoveOutside(false);
+
+ return false;
+}
+
+SdrEndTextEditKind SdrObjEditView::SdrEndTextEdit(bool bDontDeleteReally)
+{
+ SdrEndTextEditKind eRet = SdrEndTextEditKind::Unchanged;
+ SdrTextObj* pTEObj = mxWeakTextEditObj.get();
+ vcl::Window* pTEWin = mpTextEditWin;
+ OutlinerView* pTEOutlinerView = mpTextEditOutlinerView;
+ vcl::Cursor* pTECursorBuffer = pTextEditCursorBuffer;
+ SdrUndoManager* pUndoEditUndoManager = nullptr;
+ bool bNeedToUndoSavedRedoTextEdit(false);
+
+ if (GetModel() && IsUndoEnabled() && pTEObj && mpTextEditOutliner
+ && !GetModel()->GetDisableTextEditUsesCommonUndoManager())
+ {
+ // change back the UndoManager to the remembered original one
+ SfxUndoManager* pOriginal = mpTextEditOutliner->SetUndoManager(mpOldTextEditUndoManager);
+ mpOldTextEditUndoManager = nullptr;
+
+ if (pOriginal)
+ {
+ // check if we got back our document undo manager
+ SdrUndoManager* pSdrUndoManager = mpLocalTextEditUndoManager.get();
+
+ if (pSdrUndoManager && dynamic_cast<SdrUndoManager*>(pOriginal) == pSdrUndoManager)
+ {
+ if (pSdrUndoManager->isEndTextEditTriggeredFromUndo())
+ {
+ // remember the UndoManager where missing Undos have to be triggered after end
+ // text edit. When the undo had triggered the end text edit, the original action
+ // which had to be undone originally is not yet undone.
+ pUndoEditUndoManager = pSdrUndoManager;
+
+ // We are ending text edit; if text edit was triggered from undo, execute all redos
+ // to create a complete text change undo action for the redo buffer. Also mark this
+ // state when at least one redo was executed; the created extra TextChange needs to
+ // be undone in addition to the first real undo outside the text edit changes
+ while (pSdrUndoManager->GetRedoActionCount()
+ > pSdrUndoManager->GetRedoActionCountBeforeTextEdit())
+ {
+ bNeedToUndoSavedRedoTextEdit = true;
+ pSdrUndoManager->Redo();
+ }
+ }
+
+ // reset the callback link and let the undo manager cleanup all text edit
+ // undo actions to get the stack back to the form before the text edit
+ pSdrUndoManager->SetEndTextEditHdl(Link<SdrUndoManager*, void>());
+ }
+ else
+ {
+ OSL_ENSURE(false, "Got UndoManager back in SdrEndTextEdit which is NOT the "
+ "expected document UndoManager (!)");
+ delete pOriginal;
+ }
+
+ // cid#1493241 - Wrapper object use after free
+ if (pUndoEditUndoManager == mpLocalTextEditUndoManager.get())
+ pUndoEditUndoManager = nullptr;
+ mpLocalTextEditUndoManager.reset();
+ }
+ }
+ else
+ {
+ assert(nullptr == mpOldTextEditUndoManager); // cannot be restored!
+ }
+
+ if (GetModel() && mxWeakTextEditObj.is())
+ {
+ SdrHint aHint(SdrHintKind::EndEdit, *mxWeakTextEditObj);
+ GetModel()->Broadcast(aHint);
+ }
+
+ // if new mechanism was used, clean it up. At cleanup no need to check
+ // for LibreOfficeKit
+ if (mpTextEditOutlinerView)
+ {
+ mpTextEditOutlinerView->GetEditView().setEditViewCallbacks(nullptr);
+ maTEOverlayGroup.clear();
+ }
+
+ mxWeakTextEditObj.reset(nullptr);
+ mpTextEditPV = nullptr;
+ mpTextEditWin = nullptr;
+ SdrOutliner* pTEOutliner = mpTextEditOutliner.release();
+ mpTextEditOutlinerView = nullptr;
+ pTextEditCursorBuffer = nullptr;
+ aTextEditArea = tools::Rectangle();
+
+ if (pTEOutliner != nullptr)
+ {
+ bool bModified = pTEOutliner->IsModified();
+ if (pTEOutlinerView != nullptr)
+ {
+ pTEOutlinerView->HideCursor();
+ }
+ if (pTEObj != nullptr)
+ {
+ pTEOutliner->CompleteOnlineSpelling();
+
+ std::unique_ptr<SdrUndoObjSetText> pTxtUndo;
+
+ if (bModified)
+ {
+ sal_Int32 nText;
+ for (nText = 0; nText < pTEObj->getTextCount(); ++nText)
+ if (pTEObj->getText(nText) == pTEObj->getActiveText())
+ break;
+
+ pTxtUndo.reset(
+ dynamic_cast<SdrUndoObjSetText*>(GetModel()
+ ->GetSdrUndoFactory()
+ .CreateUndoObjectSetText(*pTEObj, nText)
+ .release()));
+ }
+ DBG_ASSERT(!bModified || pTxtUndo,
+ "svx::SdrObjEditView::EndTextEdit(), could not create undo action!");
+ // Set old CalcFieldValue-Handler again, this
+ // has to happen before Obj::EndTextEdit(), as this does UpdateFields().
+ pTEOutliner->SetCalcFieldValueHdl(aOldCalcFieldValueLink);
+ pTEOutliner->SetBeginPasteOrDropHdl(Link<PasteOrDropInfos*, void>());
+ pTEOutliner->SetEndPasteOrDropHdl(Link<PasteOrDropInfos*, void>());
+
+ const bool bUndo = IsUndoEnabled();
+ if (bUndo)
+ {
+ OUString aObjName(pTEObj->TakeObjNameSingul());
+ BegUndo(SvxResId(STR_UndoObjSetText), aObjName);
+ }
+
+ pTEObj->EndTextEdit(*pTEOutliner);
+
+ if ((pTEObj->GetRotateAngle() != 0_deg100) || (pTEObj && pTEObj->IsFontwork()))
+ {
+ pTEObj->ActionChanged();
+ }
+
+ if (pTxtUndo != nullptr)
+ {
+ pTxtUndo->AfterSetText();
+ if (!pTxtUndo->IsDifferent())
+ {
+ pTxtUndo.reset();
+ }
+ }
+ // check deletion of entire TextObj
+ std::unique_ptr<SdrUndoAction> pDelUndo;
+ bool bDelObj = false;
+ if (mbTextEditNewObj)
+ {
+ bDelObj = pTEObj->IsTextFrame() && !pTEObj->HasText() && !pTEObj->IsEmptyPresObj()
+ && !pTEObj->HasFill() && !pTEObj->HasLine();
+
+ if (pTEObj->IsInserted() && bDelObj
+ && pTEObj->GetObjInventor() == SdrInventor::Default && !bDontDeleteReally)
+ {
+ SdrObjKind eIdent = pTEObj->GetObjIdentifier();
+ if (eIdent == SdrObjKind::Text)
+ {
+ pDelUndo = GetModel()->GetSdrUndoFactory().CreateUndoDeleteObject(*pTEObj);
+ }
+ }
+ }
+ if (pTxtUndo)
+ {
+ if (bUndo)
+ AddUndo(std::move(pTxtUndo));
+ eRet = SdrEndTextEditKind::Changed;
+ }
+ if (pDelUndo != nullptr)
+ {
+ if (bUndo)
+ {
+ AddUndo(std::move(pDelUndo));
+ }
+ eRet = SdrEndTextEditKind::Deleted;
+ DBG_ASSERT(pTEObj->getParentSdrObjListFromSdrObject() != nullptr,
+ "SdrObjEditView::SdrEndTextEdit(): Fatal: Object edited doesn't have an "
+ "ObjList!");
+ if (pTEObj->getParentSdrObjListFromSdrObject() != nullptr)
+ {
+ pTEObj->getParentSdrObjListFromSdrObject()->RemoveObject(pTEObj->GetOrdNum());
+ CheckMarked(); // remove selection immediately...
+ }
+ }
+ else if (bDelObj)
+ { // for Writer: the app has to do the deletion itself.
+ eRet = SdrEndTextEditKind::ShouldBeDeleted;
+ }
+
+ if (bUndo)
+ EndUndo(); // EndUndo after Remove, in case UndoStack is deleted immediately
+
+ // Switch on any TextAnimation again after TextEdit
+ if (pTEObj)
+ {
+ pTEObj->SetTextAnimationAllowed(true);
+ }
+
+ // Since IsMarkHdlWhenTextEdit() is ignored, it is necessary
+ // to call AdjustMarkHdl() always.
+ AdjustMarkHdl();
+ }
+ // delete all OutlinerViews
+ for (size_t i = pTEOutliner->GetViewCount(); i > 0;)
+ {
+ i--;
+ OutlinerView* pOLV = pTEOutliner->GetView(i);
+ sal_uInt16 nMorePix = pOLV->GetInvalidateMore() + 10;
+ vcl::Window* pWin = pOLV->GetWindow();
+ tools::Rectangle aRect(pOLV->GetOutputArea());
+ pTEOutliner->RemoveView(i);
+ if (!mbTextEditDontDelete || i != 0)
+ {
+ // may not own the zeroth one
+ delete pOLV;
+ }
+ aRect.Union(aTextEditArea);
+ aRect.Union(aMinTextEditArea);
+ aRect = pWin->LogicToPixel(aRect);
+ aRect.AdjustLeft(-nMorePix);
+ aRect.AdjustTop(-nMorePix);
+ aRect.AdjustRight(nMorePix);
+ aRect.AdjustBottom(nMorePix);
+ aRect = pWin->PixelToLogic(aRect);
+ InvalidateOneWin(*pWin->GetOutDev(), aRect);
+ pWin->GetOutDev()->SetFillColor();
+ pWin->GetOutDev()->SetLineColor(COL_BLACK);
+ }
+ // and now the Outliner itself
+ if (!mbTextEditDontDelete)
+ delete pTEOutliner;
+ else
+ pTEOutliner->Clear();
+ if (pTEWin != nullptr)
+ {
+ pTEWin->SetCursor(pTECursorBuffer);
+ }
+ maHdlList.SetMoveOutside(false);
+ if (eRet != SdrEndTextEditKind::Unchanged)
+ {
+ GetMarkedObjectListWriteAccess().SetNameDirty();
+ }
+ }
+
+ if (pTEObj && !pTEObj->getSdrModelFromSdrObject().isLocked() && pTEObj->GetBroadcaster())
+ {
+ SdrHint aHint(SdrHintKind::EndEdit, *pTEObj);
+ const_cast<SfxBroadcaster*>(pTEObj->GetBroadcaster())->Broadcast(aHint);
+ }
+
+ if (pUndoEditUndoManager)
+ {
+ if (bNeedToUndoSavedRedoTextEdit)
+ {
+ // undo the text edit action since it was created as part of an EndTextEdit
+ // callback from undo itself. This needs to be done after the call to
+ // FmFormView::SdrEndTextEdit since it gets created there
+ pUndoEditUndoManager->Undo();
+ }
+
+ // trigger the Undo which was not executed, but lead to this
+ // end text edit
+ pUndoEditUndoManager->Undo();
+ }
+
+ return eRet;
+}
+
+// info about TextEdit. Default is false.
+bool SdrObjEditView::IsTextEdit() const { return mxWeakTextEditObj.is(); }
+
+// info about TextEditPageView. Default is 0L.
+SdrPageView* SdrObjEditView::GetTextEditPageView() const { return mpTextEditPV; }
+
+OutlinerView* SdrObjEditView::ImpFindOutlinerView(vcl::Window const* pWin) const
+{
+ if (pWin == nullptr)
+ return nullptr;
+ if (mpTextEditOutliner == nullptr)
+ return nullptr;
+ OutlinerView* pNewView = nullptr;
+ size_t nWinCount = mpTextEditOutliner->GetViewCount();
+ for (size_t i = 0; i < nWinCount && pNewView == nullptr; i++)
+ {
+ OutlinerView* pView = mpTextEditOutliner->GetView(i);
+ if (pView->GetWindow() == pWin)
+ pNewView = pView;
+ }
+ return pNewView;
+}
+
+void SdrObjEditView::SetTextEditWin(vcl::Window* pWin)
+{
+ if (!(mxWeakTextEditObj.is() && pWin != nullptr && pWin != mpTextEditWin))
+ return;
+
+ OutlinerView* pNewView = ImpFindOutlinerView(pWin);
+ if (pNewView != nullptr && pNewView != mpTextEditOutlinerView)
+ {
+ if (mpTextEditOutlinerView != nullptr)
+ {
+ mpTextEditOutlinerView->HideCursor();
+ }
+ mpTextEditOutlinerView = pNewView;
+ mpTextEditWin = pWin;
+ pWin->GrabFocus(); // Make the cursor blink here as well
+ pNewView->ShowCursor();
+ ImpMakeTextCursorAreaVisible();
+ }
+}
+
+bool SdrObjEditView::IsTextEditHit(const Point& rHit) const
+{
+ bool bOk = false;
+ if (mxWeakTextEditObj.is())
+ {
+ tools::Rectangle aEditArea;
+ if (OutlinerView* pOLV = mpTextEditOutliner->GetView(0))
+ aEditArea.Union(pOLV->GetOutputArea());
+
+ if (aEditArea.Contains(rHit))
+ { // check if any characters were actually hit
+ const Point aPnt(rHit - aEditArea.TopLeft());
+ tools::Long nHitTol = 2000;
+ if (OutputDevice* pRef = mpTextEditOutliner->GetRefDevice())
+ nHitTol = OutputDevice::LogicToLogic(nHitTol, MapUnit::Map100thMM,
+ pRef->GetMapMode().GetMapUnit());
+
+ bOk = mpTextEditOutliner->IsTextPos(aPnt, static_cast<sal_uInt16>(nHitTol));
+ }
+ }
+ return bOk;
+}
+
+bool SdrObjEditView::IsTextEditFrameHit(const Point& rHit) const
+{
+ bool bOk = false;
+ if (mxWeakTextEditObj.is())
+ {
+ SdrTextObj* pText = mxWeakTextEditObj.get();
+ OutlinerView* pOLV = mpTextEditOutliner->GetView(0);
+ if (pOLV)
+ {
+ vcl::Window* pWin = pOLV->GetWindow();
+ if (pText != nullptr && pText->IsTextFrame() && pWin != nullptr)
+ {
+ sal_uInt16 nPixSiz = pOLV->GetInvalidateMore();
+ tools::Rectangle aEditArea(aMinTextEditArea);
+ aEditArea.Union(pOLV->GetOutputArea());
+ if (!aEditArea.Contains(rHit))
+ {
+ Size aSiz(pWin->PixelToLogic(Size(nPixSiz, nPixSiz)));
+ aEditArea.AdjustLeft(-(aSiz.Width()));
+ aEditArea.AdjustTop(-(aSiz.Height()));
+ aEditArea.AdjustRight(aSiz.Width());
+ aEditArea.AdjustBottom(aSiz.Height());
+ bOk = aEditArea.Contains(rHit);
+ }
+ }
+ }
+ }
+ return bOk;
+}
+
+std::unique_ptr<TextChainCursorManager>
+SdrObjEditView::ImpHandleMotionThroughBoxesKeyInput(const KeyEvent& rKEvt, bool* bOutHandled)
+{
+ *bOutHandled = false;
+
+ SdrTextObj* pTextObj = mxWeakTextEditObj.get();
+ if (!pTextObj)
+ return nullptr;
+
+ if (!pTextObj->GetNextLinkInChain() && !pTextObj->GetPrevLinkInChain())
+ return nullptr;
+
+ std::unique_ptr<TextChainCursorManager> pCursorManager(
+ new TextChainCursorManager(this, pTextObj));
+ if (pCursorManager->HandleKeyEvent(rKEvt))
+ {
+ // Possibly do other stuff here if necessary...
+ // XXX: Careful with the checks below (in KeyInput) for pWin and co. You should do them here I guess.
+ *bOutHandled = true;
+ }
+
+ return pCursorManager;
+}
+
+bool SdrObjEditView::KeyInput(const KeyEvent& rKEvt, vcl::Window* pWin)
+{
+ if (mpTextEditOutlinerView)
+ {
+ /* Start special handling of keys within a chain */
+ // We possibly move to another box before any handling
+ bool bHandled = false;
+ std::unique_ptr<TextChainCursorManager> xCursorManager(
+ ImpHandleMotionThroughBoxesKeyInput(rKEvt, &bHandled));
+ if (bHandled)
+ return true;
+ /* End special handling of keys within a chain */
+
+ if (mpTextEditOutlinerView->PostKeyEvent(rKEvt, pWin))
+ {
+ if (mpModel)
+ {
+ if (mpTextEditOutliner && mpTextEditOutliner->IsModified())
+ mpModel->SetChanged();
+ }
+
+ /* Start chaining processing */
+ ImpChainingEventHdl();
+ ImpMoveCursorAfterChainingEvent(xCursorManager.get());
+ /* End chaining processing */
+
+ if (pWin != nullptr && pWin != mpTextEditWin)
+ SetTextEditWin(pWin);
+ ImpMakeTextCursorAreaVisible();
+ return true;
+ }
+ }
+ return SdrGlueEditView::KeyInput(rKEvt, pWin);
+}
+
+bool SdrObjEditView::MouseButtonDown(const MouseEvent& rMEvt, OutputDevice* pWin)
+{
+ if (mpTextEditOutlinerView != nullptr)
+ {
+ bool bPostIt = mpTextEditOutliner->IsInSelectionMode();
+ if (!bPostIt)
+ {
+ Point aPt(rMEvt.GetPosPixel());
+ if (pWin != nullptr)
+ aPt = pWin->PixelToLogic(aPt);
+ else if (mpTextEditWin != nullptr)
+ aPt = mpTextEditWin->PixelToLogic(aPt);
+ bPostIt = IsTextEditHit(aPt);
+ }
+ if (bPostIt)
+ {
+ Point aPixPos(rMEvt.GetPosPixel());
+ if (pWin)
+ {
+ tools::Rectangle aR(pWin->LogicToPixel(mpTextEditOutlinerView->GetOutputArea()));
+ if (aPixPos.X() < aR.Left())
+ aPixPos.setX(aR.Left());
+ if (aPixPos.X() > aR.Right())
+ aPixPos.setX(aR.Right());
+ if (aPixPos.Y() < aR.Top())
+ aPixPos.setY(aR.Top());
+ if (aPixPos.Y() > aR.Bottom())
+ aPixPos.setY(aR.Bottom());
+ }
+ MouseEvent aMEvt(aPixPos, rMEvt.GetClicks(), rMEvt.GetMode(), rMEvt.GetButtons(),
+ rMEvt.GetModifier());
+ if (mpTextEditOutlinerView->MouseButtonDown(aMEvt))
+ {
+ if (pWin != nullptr && pWin != mpTextEditWin->GetOutDev()
+ && pWin->GetOutDevType() == OUTDEV_WINDOW)
+ SetTextEditWin(pWin->GetOwnerWindow());
+ ImpMakeTextCursorAreaVisible();
+ return true;
+ }
+ }
+ }
+ return SdrGlueEditView::MouseButtonDown(rMEvt, pWin);
+}
+
+bool SdrObjEditView::MouseButtonUp(const MouseEvent& rMEvt, OutputDevice* pWin)
+{
+ if (mpTextEditOutlinerView != nullptr)
+ {
+ bool bPostIt = mpTextEditOutliner->IsInSelectionMode();
+ if (!bPostIt)
+ {
+ Point aPt(rMEvt.GetPosPixel());
+ if (pWin != nullptr)
+ aPt = pWin->PixelToLogic(aPt);
+ else if (mpTextEditWin != nullptr)
+ aPt = mpTextEditWin->PixelToLogic(aPt);
+ bPostIt = IsTextEditHit(aPt);
+ }
+ if (bPostIt && pWin)
+ {
+ Point aPixPos(rMEvt.GetPosPixel());
+ tools::Rectangle aR(pWin->LogicToPixel(mpTextEditOutlinerView->GetOutputArea()));
+ if (aPixPos.X() < aR.Left())
+ aPixPos.setX(aR.Left());
+ if (aPixPos.X() > aR.Right())
+ aPixPos.setX(aR.Right());
+ if (aPixPos.Y() < aR.Top())
+ aPixPos.setY(aR.Top());
+ if (aPixPos.Y() > aR.Bottom())
+ aPixPos.setY(aR.Bottom());
+ MouseEvent aMEvt(aPixPos, rMEvt.GetClicks(), rMEvt.GetMode(), rMEvt.GetButtons(),
+ rMEvt.GetModifier());
+ if (mpTextEditOutlinerView->MouseButtonUp(aMEvt))
+ {
+ ImpMakeTextCursorAreaVisible();
+ return true;
+ }
+ }
+ }
+ return SdrGlueEditView::MouseButtonUp(rMEvt, pWin);
+}
+
+bool SdrObjEditView::MouseMove(const MouseEvent& rMEvt, OutputDevice* pWin)
+{
+ if (mpTextEditOutlinerView != nullptr)
+ {
+ bool bSelMode = mpTextEditOutliner->IsInSelectionMode();
+ bool bPostIt = bSelMode;
+ if (!bPostIt)
+ {
+ Point aPt(rMEvt.GetPosPixel());
+ if (pWin)
+ aPt = pWin->PixelToLogic(aPt);
+ else if (mpTextEditWin)
+ aPt = mpTextEditWin->PixelToLogic(aPt);
+ bPostIt = IsTextEditHit(aPt);
+ }
+ if (bPostIt)
+ {
+ Point aPixPos(rMEvt.GetPosPixel());
+ tools::Rectangle aR(mpTextEditOutlinerView->GetOutputArea());
+ if (pWin)
+ aR = pWin->LogicToPixel(aR);
+ else if (mpTextEditWin)
+ aR = mpTextEditWin->LogicToPixel(aR);
+ if (aPixPos.X() < aR.Left())
+ aPixPos.setX(aR.Left());
+ if (aPixPos.X() > aR.Right())
+ aPixPos.setX(aR.Right());
+ if (aPixPos.Y() < aR.Top())
+ aPixPos.setY(aR.Top());
+ if (aPixPos.Y() > aR.Bottom())
+ aPixPos.setY(aR.Bottom());
+ MouseEvent aMEvt(aPixPos, rMEvt.GetClicks(), rMEvt.GetMode(), rMEvt.GetButtons(),
+ rMEvt.GetModifier());
+ if (mpTextEditOutlinerView->MouseMove(aMEvt) && bSelMode)
+ {
+ ImpMakeTextCursorAreaVisible();
+ return true;
+ }
+ }
+ }
+ return SdrGlueEditView::MouseMove(rMEvt, pWin);
+}
+
+bool SdrObjEditView::Command(const CommandEvent& rCEvt, vcl::Window* pWin)
+{
+ // as long as OutlinerView returns a sal_Bool, it only gets CommandEventId::StartDrag
+ if (mpTextEditOutlinerView != nullptr)
+ {
+ if (rCEvt.GetCommand() == CommandEventId::StartDrag)
+ {
+ bool bPostIt = mpTextEditOutliner->IsInSelectionMode() || !rCEvt.IsMouseEvent();
+ if (!bPostIt && rCEvt.IsMouseEvent())
+ {
+ Point aPt(rCEvt.GetMousePosPixel());
+ if (pWin != nullptr)
+ aPt = pWin->PixelToLogic(aPt);
+ else if (mpTextEditWin != nullptr)
+ aPt = mpTextEditWin->PixelToLogic(aPt);
+ bPostIt = IsTextEditHit(aPt);
+ }
+ if (bPostIt)
+ {
+ Point aPixPos(rCEvt.GetMousePosPixel());
+ if (rCEvt.IsMouseEvent() && pWin)
+ {
+ tools::Rectangle aR(
+ pWin->LogicToPixel(mpTextEditOutlinerView->GetOutputArea()));
+ if (aPixPos.X() < aR.Left())
+ aPixPos.setX(aR.Left());
+ if (aPixPos.X() > aR.Right())
+ aPixPos.setX(aR.Right());
+ if (aPixPos.Y() < aR.Top())
+ aPixPos.setY(aR.Top());
+ if (aPixPos.Y() > aR.Bottom())
+ aPixPos.setY(aR.Bottom());
+ }
+ CommandEvent aCEvt(aPixPos, rCEvt.GetCommand(), rCEvt.IsMouseEvent());
+ // Command is void at the OutlinerView, sadly
+ mpTextEditOutlinerView->Command(aCEvt);
+ if (pWin != nullptr && pWin != mpTextEditWin)
+ SetTextEditWin(pWin);
+ ImpMakeTextCursorAreaVisible();
+ return true;
+ }
+ }
+ else
+ {
+ mpTextEditOutlinerView->Command(rCEvt);
+ if (mpModel && comphelper::LibreOfficeKit::isActive())
+ {
+ // It could execute CommandEventId::ExtTextInput, while SdrObjEditView::KeyInput
+ // isn't called
+ if (mpTextEditOutliner && mpTextEditOutliner->IsModified())
+ mpModel->SetChanged();
+ }
+ return true;
+ }
+ }
+ return SdrGlueEditView::Command(rCEvt, pWin);
+}
+
+bool SdrObjEditView::ImpIsTextEditAllSelected() const
+{
+ bool bRet = false;
+ if (mpTextEditOutliner != nullptr && mpTextEditOutlinerView != nullptr)
+ {
+ if (SdrTextObj::HasTextImpl(mpTextEditOutliner.get()))
+ {
+ const sal_Int32 nParaCnt = mpTextEditOutliner->GetParagraphCount();
+ Paragraph* pLastPara
+ = mpTextEditOutliner->GetParagraph(nParaCnt > 1 ? nParaCnt - 1 : 0);
+
+ ESelection aESel(mpTextEditOutlinerView->GetSelection());
+ if (aESel.nStartPara == 0 && aESel.nStartPos == 0 && aESel.nEndPara == (nParaCnt - 1))
+ {
+ if (mpTextEditOutliner->GetText(pLastPara).getLength() == aESel.nEndPos)
+ bRet = true;
+ }
+ // in case the selection was done backwards
+ if (!bRet && aESel.nEndPara == 0 && aESel.nEndPos == 0
+ && aESel.nStartPara == (nParaCnt - 1))
+ {
+ if (mpTextEditOutliner->GetText(pLastPara).getLength() == aESel.nStartPos)
+ bRet = true;
+ }
+ }
+ else
+ {
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+void SdrObjEditView::ImpMakeTextCursorAreaVisible()
+{
+ if (mpTextEditOutlinerView != nullptr && mpTextEditWin != nullptr)
+ {
+ vcl::Cursor* pCsr = mpTextEditWin->GetCursor();
+ if (pCsr != nullptr)
+ {
+ Size aSiz(pCsr->GetSize());
+ if (!aSiz.IsEmpty())
+ {
+ MakeVisible(tools::Rectangle(pCsr->GetPos(), aSiz), *mpTextEditWin);
+ }
+ }
+ }
+}
+
+SvtScriptType SdrObjEditView::GetScriptType() const
+{
+ SvtScriptType nScriptType = SvtScriptType::NONE;
+
+ if (IsTextEdit())
+ {
+ if (mxWeakTextEditObj->GetOutlinerParaObject())
+ nScriptType
+ = mxWeakTextEditObj->GetOutlinerParaObject()->GetTextObject().GetScriptType();
+
+ if (mpTextEditOutlinerView)
+ nScriptType = mpTextEditOutlinerView->GetSelectedScriptType();
+ }
+ else
+ {
+ const size_t nMarkCount(GetMarkedObjectCount());
+
+ for (size_t i = 0; i < nMarkCount; ++i)
+ {
+ OutlinerParaObject* pParaObj = GetMarkedObjectByIndex(i)->GetOutlinerParaObject();
+
+ if (pParaObj)
+ {
+ nScriptType |= pParaObj->GetTextObject().GetScriptType();
+ }
+ }
+ }
+
+ if (nScriptType == SvtScriptType::NONE)
+ nScriptType = SvtScriptType::LATIN;
+
+ return nScriptType;
+}
+
+void SdrObjEditView::GetAttributes(SfxItemSet& rTargetSet, bool bOnlyHardAttr) const
+{
+ if (mxSelectionController.is())
+ if (mxSelectionController->GetAttributes(rTargetSet, bOnlyHardAttr))
+ return;
+
+ if (IsTextEdit())
+ {
+ DBG_ASSERT(mpTextEditOutlinerView != nullptr,
+ "SdrObjEditView::GetAttributes(): mpTextEditOutlinerView=NULL");
+ DBG_ASSERT(mpTextEditOutliner != nullptr,
+ "SdrObjEditView::GetAttributes(): mpTextEditOutliner=NULL");
+
+ // take care of bOnlyHardAttr(!)
+ if (!bOnlyHardAttr && mxWeakTextEditObj->GetStyleSheet())
+ rTargetSet.Put(mxWeakTextEditObj->GetStyleSheet()->GetItemSet());
+
+ // add object attributes
+ rTargetSet.Put(mxWeakTextEditObj->GetMergedItemSet());
+
+ if (mpTextEditOutlinerView)
+ {
+ // FALSE= regard InvalidItems as "holes," not as Default
+ rTargetSet.Put(mpTextEditOutlinerView->GetAttribs(), false);
+ }
+
+ if (GetMarkedObjectCount() == 1 && GetMarkedObjectByIndex(0) == mxWeakTextEditObj.get())
+ {
+ MergeNotPersistAttrFromMarked(rTargetSet);
+ }
+ }
+ else
+ {
+ SdrGlueEditView::GetAttributes(rTargetSet, bOnlyHardAttr);
+ }
+}
+
+bool SdrObjEditView::SetAttributes(const SfxItemSet& rSet, bool bReplaceAll)
+{
+ bool bRet = false;
+ bool bTextEdit = mpTextEditOutlinerView != nullptr && mxWeakTextEditObj.is();
+ bool bAllTextSelected = ImpIsTextEditAllSelected();
+ const SfxItemSet* pSet = &rSet;
+
+ if (!bTextEdit)
+ {
+ // no TextEdit active -> all Items to drawing object
+ if (mxSelectionController.is())
+ bRet = mxSelectionController->SetAttributes(*pSet, bReplaceAll);
+
+ if (!bRet)
+ {
+ SdrGlueEditView::SetAttributes(*pSet, bReplaceAll);
+ bRet = true;
+ }
+ }
+ else
+ {
+#ifdef DBG_UTIL
+ {
+ bool bHasEEFeatureItems = false;
+ SfxItemIter aIter(rSet);
+ for (const SfxPoolItem* pItem = aIter.GetCurItem(); !bHasEEFeatureItems && pItem;
+ pItem = aIter.NextItem())
+ {
+ if (!IsInvalidItem(pItem))
+ {
+ sal_uInt16 nW = pItem->Which();
+ if (nW >= EE_FEATURE_START && nW <= EE_FEATURE_END)
+ bHasEEFeatureItems = true;
+ }
+ }
+
+ if (bHasEEFeatureItems)
+ {
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(
+ nullptr, VclMessageType::Info, VclButtonsType::Ok,
+ "SdrObjEditView::SetAttributes(): Setting EE_FEATURE items "
+ "at the SdrView does not make sense! It only leads to "
+ "overhead and unreadable documents."));
+ xInfoBox->run();
+ }
+ }
+#endif
+
+ bool bOnlyEEItems;
+ bool bNoEEItems = !SearchOutlinerItems(*pSet, bReplaceAll, &bOnlyEEItems);
+ // everything selected? -> attributes to the border, too
+ // if no EEItems, attributes to the border only
+ if (bAllTextSelected || bNoEEItems)
+ {
+ if (mxSelectionController.is())
+ bRet = mxSelectionController->SetAttributes(*pSet, bReplaceAll);
+
+ if (!bRet)
+ {
+ const bool bUndo = IsUndoEnabled();
+
+ if (bUndo)
+ {
+ BegUndo(ImpGetDescriptionString(STR_EditSetAttributes));
+ AddUndo(
+ GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*mxWeakTextEditObj));
+
+ // If this is a text object also rescue the OutlinerParaObject since
+ // applying attributes to the object may change text layout when
+ // multiple portions exist with multiple formats. If an OutlinerParaObject
+ // really exists and needs to be rescued is evaluated in the undo
+ // implementation itself.
+ bool bRescueText = mxWeakTextEditObj;
+
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoAttrObject(
+ *mxWeakTextEditObj, false, !bNoEEItems || bRescueText));
+ EndUndo();
+ }
+
+ mxWeakTextEditObj->SetMergedItemSetAndBroadcast(*pSet, bReplaceAll);
+
+ FlushComeBackTimer(); // to set ModeHasChanged immediately
+ }
+ }
+ else if (!bOnlyEEItems)
+ {
+ // Otherwise split Set, if necessary.
+ // Now we build an ItemSet aSet that doesn't contain EE_Items from
+ // *pSet (otherwise it would be a copy).
+ WhichRangesContainer pNewWhichTable
+ = RemoveWhichRange(pSet->GetRanges(), EE_ITEMS_START, EE_ITEMS_END);
+ SfxItemSet aSet(mpModel->GetItemPool(), std::move(pNewWhichTable));
+ SfxWhichIter aIter(aSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while (nWhich != 0)
+ {
+ const SfxPoolItem* pItem;
+ SfxItemState eState = pSet->GetItemState(nWhich, false, &pItem);
+ if (eState == SfxItemState::SET)
+ aSet.Put(*pItem);
+ nWhich = aIter.NextWhich();
+ }
+
+ if (mxSelectionController.is())
+ bRet = mxSelectionController->SetAttributes(aSet, bReplaceAll);
+
+ if (!bRet)
+ {
+ if (IsUndoEnabled())
+ {
+ BegUndo(ImpGetDescriptionString(STR_EditSetAttributes));
+ AddUndo(
+ GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*mxWeakTextEditObj));
+ AddUndo(
+ GetModel()->GetSdrUndoFactory().CreateUndoAttrObject(*mxWeakTextEditObj));
+ EndUndo();
+ }
+
+ mxWeakTextEditObj->SetMergedItemSetAndBroadcast(aSet, bReplaceAll);
+
+ if (GetMarkedObjectCount() == 1
+ && GetMarkedObjectByIndex(0) == mxWeakTextEditObj.get())
+ {
+ SetNotPersistAttrToMarked(aSet);
+ }
+ }
+ FlushComeBackTimer();
+ }
+ if (!bNoEEItems)
+ {
+ // and now the attributes to the EditEngine
+ if (bReplaceAll)
+ {
+ mpTextEditOutlinerView->RemoveAttribs(true);
+ }
+ mpTextEditOutlinerView->SetAttribs(rSet);
+
+ Outliner* pTEOutliner = mpTextEditOutlinerView->GetOutliner();
+ if (mpModel && pTEOutliner && pTEOutliner->IsModified())
+ mpModel->SetChanged();
+
+ ImpMakeTextCursorAreaVisible();
+ }
+ bRet = true;
+ }
+ return bRet;
+}
+
+SfxStyleSheet* SdrObjEditView::GetStyleSheet() const
+{
+ SfxStyleSheet* pSheet = nullptr;
+
+ if (mxSelectionController.is())
+ {
+ if (mxSelectionController->GetStyleSheet(pSheet))
+ return pSheet;
+ }
+
+ if (mpTextEditOutlinerView)
+ {
+ pSheet = mpTextEditOutlinerView->GetStyleSheet();
+ }
+ else
+ {
+ pSheet = SdrGlueEditView::GetStyleSheet();
+ }
+ return pSheet;
+}
+
+void SdrObjEditView::SetStyleSheet(SfxStyleSheet* pStyleSheet, bool bDontRemoveHardAttr)
+{
+ if (mxSelectionController.is())
+ {
+ if (mxSelectionController->SetStyleSheet(pStyleSheet, bDontRemoveHardAttr))
+ return;
+ }
+
+ // if we are currently in edit mode we must also set the stylesheet
+ // on all paragraphs in the Outliner for the edit view
+ if (nullptr != mpTextEditOutlinerView)
+ {
+ Outliner* pOutliner = mpTextEditOutlinerView->GetOutliner();
+
+ const sal_Int32 nParaCount = pOutliner->GetParagraphCount();
+ for (sal_Int32 nPara = 0; nPara < nParaCount; nPara++)
+ {
+ pOutliner->SetStyleSheet(nPara, pStyleSheet);
+ }
+ }
+
+ SdrGlueEditView::SetStyleSheet(pStyleSheet, bDontRemoveHardAttr);
+}
+
+void SdrObjEditView::AddWindowToPaintView(OutputDevice* pNewWin, vcl::Window* pWindow)
+{
+ SdrGlueEditView::AddWindowToPaintView(pNewWin, pWindow);
+
+ if (mxWeakTextEditObj.is() && !mbTextEditOnlyOneView
+ && pNewWin->GetOutDevType() == OUTDEV_WINDOW)
+ {
+ OutlinerView* pOutlView = ImpMakeOutlinerView(pNewWin->GetOwnerWindow(), nullptr);
+ mpTextEditOutliner->InsertView(pOutlView);
+ }
+}
+
+void SdrObjEditView::DeleteWindowFromPaintView(OutputDevice* pOldWin)
+{
+ SdrGlueEditView::DeleteWindowFromPaintView(pOldWin);
+
+ if (mxWeakTextEditObj.is() && !mbTextEditOnlyOneView
+ && pOldWin->GetOutDevType() == OUTDEV_WINDOW)
+ {
+ for (size_t i = mpTextEditOutliner->GetViewCount(); i > 0;)
+ {
+ i--;
+ OutlinerView* pOLV = mpTextEditOutliner->GetView(i);
+ if (pOLV && pOLV->GetWindow() == pOldWin->GetOwnerWindow())
+ {
+ mpTextEditOutliner->RemoveView(i);
+ }
+ }
+ }
+
+ lcl_RemoveTextEditOutlinerViews(this, GetSdrPageView(), pOldWin);
+}
+
+bool SdrObjEditView::IsTextEditInSelectionMode() const
+{
+ return mpTextEditOutliner != nullptr && mpTextEditOutliner->IsInSelectionMode();
+}
+
+// MacroMode
+
+void SdrObjEditView::BegMacroObj(const Point& rPnt, short nTol, SdrObject* pObj, SdrPageView* pPV,
+ vcl::Window* pWin)
+{
+ BrkMacroObj();
+ if (pObj != nullptr && pPV != nullptr && pWin != nullptr && pObj->HasMacro())
+ {
+ nTol = ImpGetHitTolLogic(nTol, nullptr);
+ pMacroObj = pObj;
+ pMacroPV = pPV;
+ pMacroWin = pWin;
+ mbMacroDown = false;
+ nMacroTol = sal_uInt16(nTol);
+ aMacroDownPos = rPnt;
+ MovMacroObj(rPnt);
+ }
+}
+
+void SdrObjEditView::ImpMacroUp(const Point& rUpPos)
+{
+ if (pMacroObj != nullptr && mbMacroDown)
+ {
+ SdrObjMacroHitRec aHitRec;
+ aHitRec.aPos = rUpPos;
+ aHitRec.nTol = nMacroTol;
+ aHitRec.pVisiLayer = &pMacroPV->GetVisibleLayers();
+ aHitRec.pPageView = pMacroPV;
+ pMacroObj->PaintMacro(*pMacroWin->GetOutDev(), tools::Rectangle(), aHitRec);
+ mbMacroDown = false;
+ }
+}
+
+void SdrObjEditView::ImpMacroDown(const Point& rDownPos)
+{
+ if (pMacroObj != nullptr && !mbMacroDown)
+ {
+ SdrObjMacroHitRec aHitRec;
+ aHitRec.aPos = rDownPos;
+ aHitRec.nTol = nMacroTol;
+ aHitRec.pVisiLayer = &pMacroPV->GetVisibleLayers();
+ aHitRec.pPageView = pMacroPV;
+ pMacroObj->PaintMacro(*pMacroWin->GetOutDev(), tools::Rectangle(), aHitRec);
+ mbMacroDown = true;
+ }
+}
+
+void SdrObjEditView::MovMacroObj(const Point& rPnt)
+{
+ if (pMacroObj == nullptr)
+ return;
+
+ SdrObjMacroHitRec aHitRec;
+ aHitRec.aPos = rPnt;
+ aHitRec.nTol = nMacroTol;
+ aHitRec.pVisiLayer = &pMacroPV->GetVisibleLayers();
+ aHitRec.pPageView = pMacroPV;
+ bool bDown = pMacroObj->IsMacroHit(aHitRec);
+ if (bDown)
+ ImpMacroDown(rPnt);
+ else
+ ImpMacroUp(rPnt);
+}
+
+void SdrObjEditView::BrkMacroObj()
+{
+ if (pMacroObj != nullptr)
+ {
+ ImpMacroUp(aMacroDownPos);
+ pMacroObj = nullptr;
+ pMacroPV = nullptr;
+ pMacroWin = nullptr;
+ }
+}
+
+bool SdrObjEditView::EndMacroObj()
+{
+ if (pMacroObj != nullptr && mbMacroDown)
+ {
+ ImpMacroUp(aMacroDownPos);
+ SdrObjMacroHitRec aHitRec;
+ aHitRec.aPos = aMacroDownPos;
+ aHitRec.nTol = nMacroTol;
+ aHitRec.pVisiLayer = &pMacroPV->GetVisibleLayers();
+ aHitRec.pPageView = pMacroPV;
+ bool bRet = pMacroObj->DoMacro(aHitRec);
+ pMacroObj = nullptr;
+ pMacroPV = nullptr;
+ pMacroWin = nullptr;
+ return bRet;
+ }
+ else
+ {
+ BrkMacroObj();
+ return false;
+ }
+}
+
+/** fills the given any with a XTextCursor for the current text selection.
+ Leaves the any untouched if there currently is no text selected */
+void SdrObjEditView::getTextSelection(css::uno::Any& rSelection)
+{
+ if (!IsTextEdit())
+ return;
+
+ OutlinerView* pOutlinerView = GetTextEditOutlinerView();
+ if (!(pOutlinerView && pOutlinerView->HasSelection()))
+ return;
+
+ SdrObject* pObj = GetTextEditObject();
+
+ if (!pObj)
+ return;
+
+ css::uno::Reference<css::text::XText> xText(pObj->getUnoShape(), css::uno::UNO_QUERY);
+ if (xText.is())
+ {
+ SvxUnoTextBase* pRange = comphelper::getFromUnoTunnel<SvxUnoTextBase>(xText);
+ if (pRange)
+ {
+ rSelection <<= pRange->createTextCursorBySelection(pOutlinerView->GetSelection());
+ }
+ }
+}
+
+/* check if we have a single selection and that single object likes
+ to handle the mouse and keyboard events itself
+
+ TODO: the selection controller should be queried from the
+ object specific view contact. Currently this method only
+ works for tables.
+*/
+void SdrObjEditView::MarkListHasChanged()
+{
+ SdrGlueEditView::MarkListHasChanged();
+
+ if (mxSelectionController.is())
+ {
+ mxLastSelectionController = mxSelectionController;
+ mxSelectionController->onSelectionHasChanged();
+ }
+
+ mxSelectionController.clear();
+
+ const SdrMarkList& rMarkList = GetMarkedObjectList();
+ if (rMarkList.GetMarkCount() != 1)
+ return;
+
+ const SdrObject* pObj(rMarkList.GetMark(0)->GetMarkedSdrObj());
+ SdrView* pView(dynamic_cast<SdrView*>(this));
+
+ // check for table
+ if (pObj && pView && (pObj->GetObjInventor() == SdrInventor::Default)
+ && (pObj->GetObjIdentifier() == SdrObjKind::Table))
+ {
+ mxSelectionController = sdr::table::CreateTableController(
+ *pView, static_cast<const sdr::table::SdrTableObj&>(*pObj), mxLastSelectionController);
+
+ if (mxSelectionController.is())
+ {
+ mxLastSelectionController.clear();
+ mxSelectionController->onSelectionHasChanged();
+ }
+ }
+}
+
+IMPL_LINK(SdrObjEditView, EndPasteOrDropHdl, PasteOrDropInfos*, pInfo, void)
+{
+ OnEndPasteOrDrop(pInfo);
+}
+
+IMPL_LINK(SdrObjEditView, BeginPasteOrDropHdl, PasteOrDropInfos*, pInfo, void)
+{
+ OnBeginPasteOrDrop(pInfo);
+}
+
+void SdrObjEditView::OnBeginPasteOrDrop(PasteOrDropInfos*)
+{
+ // applications can derive from these virtual methods to do something before a drop or paste operation
+}
+
+void SdrObjEditView::OnEndPasteOrDrop(PasteOrDropInfos*)
+{
+ // applications can derive from these virtual methods to do something before a drop or paste operation
+}
+
+sal_uInt16 SdrObjEditView::GetSelectionLevel() const
+{
+ sal_uInt16 nLevel = 0xFFFF;
+ if (IsTextEdit())
+ {
+ DBG_ASSERT(mpTextEditOutlinerView != nullptr,
+ "SdrObjEditView::GetAttributes(): mpTextEditOutlinerView=NULL");
+ DBG_ASSERT(mpTextEditOutliner != nullptr,
+ "SdrObjEditView::GetAttributes(): mpTextEditOutliner=NULL");
+ if (mpTextEditOutlinerView)
+ {
+ //start and end position
+ ESelection aSelect = mpTextEditOutlinerView->GetSelection();
+ sal_uInt16 nStartPara = ::std::min(aSelect.nStartPara, aSelect.nEndPara);
+ sal_uInt16 nEndPara = ::std::max(aSelect.nStartPara, aSelect.nEndPara);
+ //get level from each paragraph
+ nLevel = 0;
+ for (sal_uInt16 nPara = nStartPara; nPara <= nEndPara; nPara++)
+ {
+ sal_uInt16 nParaDepth
+ = 1 << static_cast<sal_uInt16>(mpTextEditOutliner->GetDepth(nPara));
+ if (!(nLevel & nParaDepth))
+ nLevel += nParaDepth;
+ }
+ //reduce one level for Outliner Object
+ //if( nLevel > 0 && GetTextEditObject()->GetObjIdentifier() == OBJ_OUTLINETEXT )
+ // nLevel = nLevel >> 1;
+ //no bullet paragraph selected
+ if (nLevel == 0)
+ nLevel = 0xFFFF;
+ }
+ }
+ return nLevel;
+}
+
+bool SdrObjEditView::SupportsFormatPaintbrush(SdrInventor nObjectInventor,
+ SdrObjKind nObjectIdentifier)
+{
+ if (nObjectInventor != SdrInventor::Default && nObjectInventor != SdrInventor::E3d)
+ return false;
+ switch (nObjectIdentifier)
+ {
+ case SdrObjKind::NONE:
+ case SdrObjKind::Group:
+ return false;
+ case SdrObjKind::Line:
+ case SdrObjKind::Rectangle:
+ case SdrObjKind::CircleOrEllipse:
+ case SdrObjKind::CircleSection:
+ case SdrObjKind::CircleArc:
+ case SdrObjKind::CircleCut:
+ case SdrObjKind::Polygon:
+ case SdrObjKind::PolyLine:
+ case SdrObjKind::PathLine:
+ case SdrObjKind::PathFill:
+ case SdrObjKind::FreehandLine:
+ case SdrObjKind::FreehandFill:
+ case SdrObjKind::SplineLine:
+ case SdrObjKind::SplineFill:
+ case SdrObjKind::Text:
+ case SdrObjKind::TitleText:
+ case SdrObjKind::OutlineText:
+ case SdrObjKind::Graphic:
+ case SdrObjKind::OLE2:
+ case SdrObjKind::Table:
+ return true;
+ case SdrObjKind::Caption:
+ return false;
+ case SdrObjKind::Edge:
+ case SdrObjKind::PathPoly:
+ case SdrObjKind::PathPolyLine:
+ return true;
+ case SdrObjKind::Page:
+ case SdrObjKind::Measure:
+ case SdrObjKind::OLEPluginFrame:
+ case SdrObjKind::UNO:
+ return false;
+ case SdrObjKind::CustomShape:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const WhichRangesContainer& GetFormatRangeImpl(bool bTextOnly)
+{
+ static const WhichRangesContainer gFull(
+ svl::Items<XATTR_LINE_FIRST, XATTR_LINE_LAST, XATTR_FILL_FIRST, XATTRSET_FILL,
+ SDRATTR_SHADOW_FIRST, SDRATTR_SHADOW_LAST, SDRATTR_MISC_FIRST,
+ SDRATTR_MISC_LAST, // table cell formats
+ SDRATTR_GRAF_FIRST, SDRATTR_GRAF_LAST, SDRATTR_TABLE_FIRST, SDRATTR_TABLE_LAST,
+ SDRATTR_GLOW_FIRST, SDRATTR_GLOW_LAST, SDRATTR_SOFTEDGE_FIRST,
+ SDRATTR_SOFTEDGE_LAST, EE_PARA_START, EE_PARA_END, EE_CHAR_START, EE_CHAR_END>);
+
+ static const WhichRangesContainer gTextOnly(
+ svl::Items<SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST, EE_PARA_START, EE_PARA_END, EE_CHAR_START,
+ EE_CHAR_END>);
+
+ return bTextOnly ? gTextOnly : gFull;
+}
+
+void SdrObjEditView::TakeFormatPaintBrush(std::shared_ptr<SfxItemSet>& rFormatSet)
+{
+ const SdrMarkList& rMarkList = GetMarkedObjectList();
+ if (rMarkList.GetMarkCount() <= 0)
+ return;
+
+ OutlinerView* pOLV = GetTextEditOutlinerView();
+
+ rFormatSet = std::make_shared<SfxItemSet>(GetModel()->GetItemPool(),
+ GetFormatRangeImpl(pOLV != nullptr));
+ if (pOLV)
+ {
+ rFormatSet->Put(pOLV->GetAttribs());
+ }
+ else
+ {
+ const bool bOnlyHardAttr = false;
+ rFormatSet->Put(GetAttrFromMarked(bOnlyHardAttr));
+ }
+
+ // check for cloning from table cell, in which case we need to copy cell-specific formatting attributes
+ const SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
+ if (pObj && (pObj->GetObjInventor() == SdrInventor::Default)
+ && (pObj->GetObjIdentifier() == SdrObjKind::Table))
+ {
+ auto pTable = static_cast<const sdr::table::SdrTableObj*>(pObj);
+ if (mxSelectionController.is() && pTable->getActiveCell().is())
+ {
+ mxSelectionController->GetAttributes(*rFormatSet, false);
+ }
+ }
+}
+
+static SfxItemSet CreatePaintSet(const WhichRangesContainer& pRanges, SfxItemPool& rPool,
+ const SfxItemSet& rSourceSet, const SfxItemSet& rTargetSet,
+ bool bNoCharacterFormats, bool bNoParagraphFormats)
+{
+ SfxItemSet aPaintSet(rPool, pRanges);
+
+ for (const auto& pRange : pRanges)
+ {
+ sal_uInt16 nWhich = pRange.first;
+ const sal_uInt16 nLastWhich = pRange.second;
+
+ if (bNoCharacterFormats && (nWhich == EE_CHAR_START))
+ continue;
+
+ if (bNoParagraphFormats && (nWhich == EE_PARA_START))
+ continue;
+
+ for (; nWhich <= nLastWhich; nWhich++)
+ {
+ const SfxPoolItem* pSourceItem = rSourceSet.GetItem(nWhich);
+ const SfxPoolItem* pTargetItem = rTargetSet.GetItem(nWhich);
+
+ if ((pSourceItem && !pTargetItem)
+ || (pSourceItem && pTargetItem && *pSourceItem != *pTargetItem))
+ {
+ aPaintSet.Put(*pSourceItem);
+ }
+ }
+ }
+ return aPaintSet;
+}
+
+void SdrObjEditView::ApplyFormatPaintBrushToText(SfxItemSet const& rFormatSet, SdrTextObj& rTextObj,
+ SdrText* pText, bool bNoCharacterFormats,
+ bool bNoParagraphFormats)
+{
+ OutlinerParaObject* pParaObj = pText ? pText->GetOutlinerParaObject() : nullptr;
+ if (!pParaObj)
+ return;
+
+ SdrOutliner& rOutliner = rTextObj.ImpGetDrawOutliner();
+ rOutliner.SetText(*pParaObj);
+
+ sal_Int32 nParaCount(rOutliner.GetParagraphCount());
+
+ if (!nParaCount)
+ return;
+
+ for (sal_Int32 nPara = 0; nPara < nParaCount; nPara++)
+ {
+ if (!bNoCharacterFormats)
+ rOutliner.RemoveCharAttribs(nPara);
+
+ SfxItemSet aSet(rOutliner.GetParaAttribs(nPara));
+ aSet.Put(CreatePaintSet(GetFormatRangeImpl(true), *aSet.GetPool(), rFormatSet, aSet,
+ bNoCharacterFormats, bNoParagraphFormats));
+ rOutliner.SetParaAttribs(nPara, aSet);
+ }
+
+ std::optional<OutlinerParaObject> pTemp = rOutliner.CreateParaObject(0, nParaCount);
+ rOutliner.Clear();
+
+ rTextObj.NbcSetOutlinerParaObjectForText(std::move(pTemp), pText);
+}
+
+void SdrObjEditView::DisposeUndoManager()
+{
+ if (mpTextEditOutliner)
+ {
+ if (typeid(mpTextEditOutliner->GetUndoManager()) != typeid(EditUndoManager))
+ {
+ // Non-owning pointer, clear it.
+ mpTextEditOutliner->SetUndoManager(nullptr);
+ }
+ }
+
+ mpOldTextEditUndoManager = nullptr;
+}
+
+void SdrObjEditView::ApplyFormatPaintBrush(SfxItemSet& rFormatSet, bool bNoCharacterFormats,
+ bool bNoParagraphFormats)
+{
+ if (mxSelectionController.is()
+ && mxSelectionController->ApplyFormatPaintBrush(rFormatSet, bNoCharacterFormats,
+ bNoParagraphFormats))
+ {
+ return;
+ }
+
+ OutlinerView* pOLV = GetTextEditOutlinerView();
+ const SdrMarkList& rMarkList = GetMarkedObjectList();
+ if (!pOLV)
+ {
+ SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
+ const SfxItemSet& rShapeSet = pObj->GetMergedItemSet();
+
+ // if not in text edit mode (aka the user selected text or clicked on a word)
+ // apply formatting attributes to selected shape
+ // All formatting items (see ranges above) that are unequal in selected shape and
+ // the format paintbrush are hard set on the selected shape.
+
+ const WhichRangesContainer& pRanges = rFormatSet.GetRanges();
+ bool bTextOnly = true;
+
+ for (const auto& pRange : pRanges)
+ {
+ if ((pRange.first != EE_PARA_START) && (pRange.first != EE_CHAR_START))
+ {
+ bTextOnly = false;
+ break;
+ }
+ }
+
+ if (!bTextOnly)
+ {
+ SfxItemSet aPaintSet(CreatePaintSet(GetFormatRangeImpl(false), *rShapeSet.GetPool(),
+ rFormatSet, rShapeSet, bNoCharacterFormats,
+ bNoParagraphFormats));
+ SetAttrToMarked(aPaintSet, false /*bReplaceAll*/);
+ }
+
+ // now apply character and paragraph formatting to text, if the shape has any
+ SdrTextObj* pTextObj = dynamic_cast<SdrTextObj*>(pObj);
+ if (pTextObj)
+ {
+ sal_Int32 nText = pTextObj->getTextCount();
+
+ while (--nText >= 0)
+ {
+ SdrText* pText = pTextObj->getText(nText);
+ ApplyFormatPaintBrushToText(rFormatSet, *pTextObj, pText, bNoCharacterFormats,
+ bNoParagraphFormats);
+ }
+ }
+ }
+ else
+ {
+ ::Outliner* pOutliner = pOLV->GetOutliner();
+ if (pOutliner)
+ {
+ const EditEngine& rEditEngine = pOutliner->GetEditEngine();
+
+ ESelection aSel(pOLV->GetSelection());
+ if (!aSel.HasRange())
+ pOLV->SetSelection(rEditEngine.GetWord(aSel, css::i18n::WordType::DICTIONARY_WORD));
+
+ const bool bRemoveParaAttribs = !bNoParagraphFormats;
+ pOLV->RemoveAttribsKeepLanguages(bRemoveParaAttribs);
+ SfxItemSet aSet(pOLV->GetAttribs());
+ SfxItemSet aPaintSet(CreatePaintSet(GetFormatRangeImpl(true), *aSet.GetPool(),
+ rFormatSet, aSet, bNoCharacterFormats,
+ bNoParagraphFormats));
+ pOLV->SetAttribs(aPaintSet);
+ }
+ }
+
+ // check for cloning to table cell, in which case we need to copy cell-specific formatting attributes
+ SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
+ if (pObj && (pObj->GetObjInventor() == SdrInventor::Default)
+ && (pObj->GetObjIdentifier() == SdrObjKind::Table))
+ {
+ auto pTable = static_cast<sdr::table::SdrTableObj*>(pObj);
+ if (pTable->getActiveCell().is() && mxSelectionController.is())
+ {
+ mxSelectionController->SetAttributes(rFormatSet, false);
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdetc.cxx b/svx/source/svdraw/svdetc.cxx
new file mode 100644
index 000000000..154bd3c7b
--- /dev/null
+++ b/svx/source/svdraw/svdetc.cxx
@@ -0,0 +1,726 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <algorithm>
+
+#include <officecfg/Office/Common.hxx>
+#include <svtools/colorcfg.hxx>
+#include <svx/svdetc.hxx>
+#include <svx/svdedxv.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdoutl.hxx>
+#include <vcl/BitmapReadAccess.hxx>
+#include <editeng/eeitem.hxx>
+#include <svl/itemset.hxx>
+#include <svl/whiter.hxx>
+#include <svx/xgrad.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflclit.hxx>
+#include <svx/xflhtit.hxx>
+#include <svx/xbtmpit.hxx>
+#include <svx/xflgrit.hxx>
+#include <svx/svdoole2.hxx>
+#include <svl/itempool.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <unotools/syslocale.hxx>
+#include <svx/xflbckit.hxx>
+#include <svx/extrusionbar.hxx>
+#include <svx/fontworkbar.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <svx/sdr/contact/viewcontact.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdotable.hxx>
+#include <svx/sdrhittesthelper.hxx>
+
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+
+using namespace ::com::sun::star;
+
+// Global data of the DrawingEngine
+SdrGlobalData::SdrGlobalData()
+ : pSysLocale(nullptr)
+{
+ if (!utl::ConfigManager::IsFuzzing())
+ {
+ svx::ExtrusionBar::RegisterInterface();
+ svx::FontworkBar::RegisterInterface();
+ }
+}
+
+const SvtSysLocale* SdrGlobalData::GetSysLocale()
+{
+ if ( !pSysLocale )
+ pSysLocale = new SvtSysLocale;
+ return pSysLocale;
+}
+const LocaleDataWrapper* SdrGlobalData::GetLocaleData()
+{
+ return &GetSysLocale()->GetLocaleData();
+}
+
+namespace {
+
+struct TheSdrGlobalData: public rtl::Static<SdrGlobalData, TheSdrGlobalData> {};
+
+}
+
+SdrGlobalData & GetSdrGlobalData() {
+ return TheSdrGlobalData::get();
+}
+
+OLEObjCache::OLEObjCache()
+{
+ if (!utl::ConfigManager::IsFuzzing())
+ {
+// This limit is only useful on 32-bit windows, where we can run out of virtual memory (see tdf#95579)
+// For everything else, we are better off keeping it in main memory rather than using our hacky page-out thing
+#if defined _WIN32 && !defined _WIN64
+ nSize = officecfg::Office::Common::Cache::DrawingEngine::OLE_Objects::get();
+#else
+ nSize = SAL_MAX_INT32; // effectively disable the page-out mechanism
+#endif
+ }
+ else
+ nSize = 100;
+ pTimer.reset( new AutoTimer( "svx OLEObjCache pTimer UnloadCheck" ) );
+ pTimer->SetInvokeHandler( LINK(this, OLEObjCache, UnloadCheckHdl) );
+ pTimer->SetTimeout(20000);
+ pTimer->SetStatic();
+}
+
+OLEObjCache::~OLEObjCache()
+{
+ pTimer->Stop();
+}
+
+IMPL_LINK_NOARG(OLEObjCache, UnloadCheckHdl, Timer*, void)
+{
+ if (nSize >= maObjs.size())
+ return;
+
+ // more objects than configured cache size try to remove objects
+ // of course not the freshly inserted one at nIndex=0
+ size_t nCount2 = maObjs.size();
+ size_t nIndex = nCount2-1;
+ while( nIndex && nCount2 > nSize )
+ {
+ SdrOle2Obj* pUnloadObj = maObjs[nIndex--];
+ if (!pUnloadObj)
+ continue;
+
+ try
+ {
+ // it is important to get object without reinitialization to avoid reentrance
+ const uno::Reference< embed::XEmbeddedObject > & xUnloadObj = pUnloadObj->GetObjRef_NoInit();
+
+ bool bUnload = !xUnloadObj || SdrOle2Obj::CanUnloadRunningObj( xUnloadObj, pUnloadObj->GetAspect() );
+
+ // check whether the object can be unloaded before looking for the parent objects
+ if ( xUnloadObj.is() && bUnload )
+ {
+ uno::Reference< frame::XModel > xUnloadModel( xUnloadObj->getComponent(), uno::UNO_QUERY );
+ if ( xUnloadModel.is() )
+ {
+ for (SdrOle2Obj* pCacheObj : maObjs)
+ {
+ if ( pCacheObj && pCacheObj != pUnloadObj )
+ {
+ uno::Reference< frame::XModel > xParentModel = pCacheObj->GetParentXModel();
+ if ( xUnloadModel == xParentModel )
+ {
+ bUnload = false; // the object has running embedded objects
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (bUnload && UnloadObj(*pUnloadObj))
+ {
+ // object was successfully unloaded
+ RemoveObj(pUnloadObj);
+ nCount2 = std::min(nCount2 - 1, maObjs.size());
+ if (nIndex >= nCount2)
+ nIndex = nCount2 - 1;
+ }
+ }
+ catch( uno::Exception& )
+ {}
+ }
+}
+
+void OLEObjCache::InsertObj(SdrOle2Obj* pObj)
+{
+ if (!maObjs.empty())
+ {
+ SdrOle2Obj* pExistingObj = maObjs.front();
+ if ( pObj == pExistingObj )
+ // the object is already on the top, nothing has to be changed
+ return;
+ }
+
+ // get the old position of the object to know whether it is already in container
+ std::vector<SdrOle2Obj*>::iterator it = std::find(maObjs.begin(), maObjs.end(), pObj);
+ bool bFound = it != maObjs.end();
+
+ if (bFound)
+ maObjs.erase(it);
+ // insert object into first position
+ maObjs.insert(maObjs.begin(), pObj);
+
+ // if a new object was inserted, recalculate the cache
+ if (!bFound)
+ pTimer->Invoke();
+
+ if (!bFound || !pTimer->IsActive())
+ pTimer->Start();
+}
+
+void OLEObjCache::RemoveObj(SdrOle2Obj* pObj)
+{
+ std::vector<SdrOle2Obj*>::iterator it = std::find(maObjs.begin(), maObjs.end(), pObj);
+ if (it != maObjs.end())
+ maObjs.erase(it);
+ if (maObjs.empty())
+ pTimer->Stop();
+}
+
+size_t OLEObjCache::size() const
+{
+ return maObjs.size();
+}
+
+SdrOle2Obj* OLEObjCache::operator[](size_t nPos)
+{
+ return maObjs[nPos];
+}
+
+const SdrOle2Obj* OLEObjCache::operator[](size_t nPos) const
+{
+ return maObjs[nPos];
+}
+
+bool OLEObjCache::UnloadObj(SdrOle2Obj& rObj)
+{
+ bool bUnloaded = false;
+
+ //#i80528# The old mechanism is completely useless, only taking into account if
+ // in all views the GrafDraft feature is used. This will nearly never have been the
+ // case since no one ever used this option.
+
+ // A much better (and working) criteria would be the VOC contact count.
+ // The question is what will happen when i make it work now suddenly? I
+ // will try it for 2.4.
+ const sdr::contact::ViewContact& rViewContact = rObj.GetViewContact();
+ const bool bVisible(rViewContact.HasViewObjectContacts());
+
+ if(!bVisible)
+ {
+ bUnloaded = rObj.Unload();
+ }
+
+ return bUnloaded;
+}
+
+bool GetDraftFillColor(const SfxItemSet& rSet, Color& rCol)
+{
+ drawing::FillStyle eFill=rSet.Get(XATTR_FILLSTYLE).GetValue();
+ bool bRetval = false;
+
+ switch(eFill)
+ {
+ case drawing::FillStyle_SOLID:
+ {
+ rCol = rSet.Get(XATTR_FILLCOLOR).GetColorValue();
+ bRetval = true;
+
+ break;
+ }
+ case drawing::FillStyle_HATCH:
+ {
+ Color aCol1(rSet.Get(XATTR_FILLHATCH).GetHatchValue().GetColor());
+ Color aCol2(COL_WHITE);
+
+ // when hatched background is activated, use object fill color as hatch color
+ bool bFillHatchBackground = rSet.Get(XATTR_FILLBACKGROUND).GetValue();
+ if(bFillHatchBackground)
+ {
+ aCol2 = rSet.Get(XATTR_FILLCOLOR).GetColorValue();
+ }
+
+ const basegfx::BColor aAverageColor(basegfx::average(aCol1.getBColor(), aCol2.getBColor()));
+ rCol = Color(aAverageColor);
+ bRetval = true;
+
+ break;
+ }
+ case drawing::FillStyle_GRADIENT: {
+ const XGradient& rGrad=rSet.Get(XATTR_FILLGRADIENT).GetGradientValue();
+ Color aCol1(rGrad.GetStartColor());
+ Color aCol2(rGrad.GetEndColor());
+ const basegfx::BColor aAverageColor(basegfx::average(aCol1.getBColor(), aCol2.getBColor()));
+ rCol = Color(aAverageColor);
+ bRetval = true;
+
+ break;
+ }
+ case drawing::FillStyle_BITMAP:
+ {
+ Bitmap aBitmap(rSet.Get(XATTR_FILLBITMAP).GetGraphicObject().GetGraphic().GetBitmapEx().GetBitmap());
+ const Size aSize(aBitmap.GetSizePixel());
+ const sal_uInt32 nWidth = aSize.Width();
+ const sal_uInt32 nHeight = aSize.Height();
+ if (nWidth <= 0 || nHeight <= 0)
+ return bRetval;
+
+ Bitmap::ScopedReadAccess pAccess(aBitmap);
+
+ if (pAccess)
+ {
+ sal_uInt32 nRt(0);
+ sal_uInt32 nGn(0);
+ sal_uInt32 nBl(0);
+ const sal_uInt32 nMaxSteps(8);
+ const sal_uInt32 nXStep((nWidth > nMaxSteps) ? nWidth / nMaxSteps : 1);
+ const sal_uInt32 nYStep((nHeight > nMaxSteps) ? nHeight / nMaxSteps : 1);
+ sal_uInt32 nCount(0);
+
+ for(sal_uInt32 nY(0); nY < nHeight; nY += nYStep)
+ {
+ for(sal_uInt32 nX(0); nX < nWidth; nX += nXStep)
+ {
+ const BitmapColor& rCol2 = pAccess->GetColor(nY, nX);
+
+ nRt += rCol2.GetRed();
+ nGn += rCol2.GetGreen();
+ nBl += rCol2.GetBlue();
+ nCount++;
+ }
+ }
+
+ nRt /= nCount;
+ nGn /= nCount;
+ nBl /= nCount;
+
+ rCol = Color(sal_uInt8(nRt), sal_uInt8(nGn), sal_uInt8(nBl));
+
+ bRetval = true;
+ }
+ break;
+ }
+ default: break;
+ }
+
+ return bRetval;
+}
+
+std::unique_ptr<SdrOutliner> SdrMakeOutliner(OutlinerMode nOutlinerMode, SdrModel& rModel)
+{
+ SfxItemPool* pPool = &rModel.GetItemPool();
+ std::unique_ptr<SdrOutliner> pOutl(new SdrOutliner( pPool, nOutlinerMode ));
+ pOutl->SetEditTextObjectPool( pPool );
+ pOutl->SetStyleSheetPool( static_cast<SfxStyleSheetPool*>(rModel.GetStyleSheetPool()));
+ pOutl->SetDefTab(rModel.GetDefaultTabulator());
+ Outliner::SetForbiddenCharsTable(rModel.GetForbiddenCharsTable());
+ pOutl->SetAsianCompressionMode(rModel.GetCharCompressType());
+ pOutl->SetKernAsianPunctuation(rModel.IsKernAsianPunctuation());
+ pOutl->SetAddExtLeading(rModel.IsAddExtLeading());
+ return pOutl;
+}
+
+std::vector<Link<SdrObjCreatorParams, SdrObject*>>& ImpGetUserMakeObjHdl()
+{
+ SdrGlobalData& rGlobalData=GetSdrGlobalData();
+ return rGlobalData.aUserMakeObjHdl;
+}
+
+bool SearchOutlinerItems(const SfxItemSet& rSet, bool bInklDefaults, bool* pbOnlyEE)
+{
+ bool bHas=false;
+ bool bOnly=true;
+ bool bLookOnly=pbOnlyEE!=nullptr;
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich=aIter.FirstWhich();
+ while (((bLookOnly && bOnly) || !bHas) && nWhich!=0) {
+ // For bInklDefaults, the entire Which range is decisive,
+ // in other cases only the set items are.
+ // Disabled and DontCare are regarded as holes in the Which range.
+ SfxItemState eState=aIter.GetItemState();
+ if ((eState==SfxItemState::DEFAULT && bInklDefaults) || eState==SfxItemState::SET) {
+ if (nWhich<EE_ITEMS_START || nWhich>EE_ITEMS_END) bOnly=false;
+ else bHas=true;
+ }
+ nWhich=aIter.NextWhich();
+ }
+ if (!bHas) bOnly=false;
+ if (pbOnlyEE!=nullptr) *pbOnlyEE=bOnly;
+ return bHas;
+}
+
+WhichRangesContainer RemoveWhichRange(const WhichRangesContainer& pOldWhichTable, sal_uInt16 nRangeBeg, sal_uInt16 nRangeEnd)
+{
+ // Six possible cases (per range):
+ // [Beg..End] [nRangeBeg, nRangeEnd], to delete
+ // [b..e] [b..e] [b..e] Cases 1,3,2: doesn't matter, delete, doesn't matter + Ranges
+ // [b........e] [b........e] Cases 4,5 : shrink range | in
+ // [b......................e] Case 6 : splitting + pOldWhichTable
+ std::vector<WhichPair> buf;
+ for (const auto & rPair : pOldWhichTable) {
+ auto const begin = rPair.first;
+ auto const end = rPair.second;
+ if (end < nRangeBeg || begin > nRangeEnd) { // cases 1, 2
+ buf.push_back({begin, end});
+ } else if (begin >= nRangeBeg && end <= nRangeEnd) { // case 3
+ // drop
+ } else if (end <= nRangeEnd) { // case 4
+ buf.push_back({begin, nRangeBeg - 1});
+ } else if (begin >= nRangeBeg) { // case 5
+ buf.push_back({nRangeEnd + 1, end});
+ } else { // case 6
+ buf.push_back({begin, nRangeBeg - 1});
+ buf.push_back({nRangeEnd + 1, end});
+ }
+ }
+ std::unique_ptr<WhichPair[]> pNewWhichTable(new WhichPair[buf.size()]);
+ std::copy(buf.begin(), buf.end(), pNewWhichTable.get());
+ return WhichRangesContainer(std::move(pNewWhichTable), buf.size());
+}
+
+
+SvdProgressInfo::SvdProgressInfo( const Link<void*,bool>&_rLink )
+{
+ maLink = _rLink;
+ m_nSumCurAction = 0;
+
+ m_nObjCount = 0;
+ m_nCurObj = 0;
+
+ m_nActionCount = 0;
+ m_nCurAction = 0;
+
+ m_nInsertCount = 0;
+ m_nCurInsert = 0;
+}
+
+void SvdProgressInfo::Init( size_t nObjCount )
+{
+ m_nObjCount = nObjCount;
+}
+
+bool SvdProgressInfo::ReportActions( size_t nActionCount )
+{
+ m_nSumCurAction += nActionCount;
+ m_nCurAction += nActionCount;
+ if(m_nCurAction > m_nActionCount)
+ m_nCurAction = m_nActionCount;
+
+ return maLink.Call(nullptr);
+}
+
+void SvdProgressInfo::ReportInserts( size_t nInsertCount )
+{
+ m_nSumCurAction += nInsertCount;
+ m_nCurInsert += nInsertCount;
+
+ maLink.Call(nullptr);
+}
+
+void SvdProgressInfo::ReportRescales( size_t nRescaleCount )
+{
+ m_nSumCurAction += nRescaleCount;
+ maLink.Call(nullptr);
+}
+
+void SvdProgressInfo::SetActionCount( size_t nActionCount )
+{
+ m_nActionCount = nActionCount;
+}
+
+void SvdProgressInfo::SetInsertCount( size_t nInsertCount )
+{
+ m_nInsertCount = nInsertCount;
+}
+
+void SvdProgressInfo::SetNextObject()
+{
+ m_nActionCount = 0;
+ m_nCurAction = 0;
+
+ m_nInsertCount = 0;
+ m_nCurInsert = 0;
+
+ m_nCurObj++;
+ ReportActions(0);
+}
+
+// #i101872# isolate GetTextEditBackgroundColor to tooling; it will anyways only be used as long
+// as text edit is not running on overlay
+
+namespace
+{
+ bool impGetSdrObjListFillColor(
+ const SdrObjList& rList,
+ const Point& rPnt,
+ const SdrPageView& rTextEditPV,
+ const SdrLayerIDSet& rVisLayers,
+ Color& rCol)
+ {
+ bool bRet(false);
+ bool bMaster(rList.getSdrPageFromSdrObjList() && rList.getSdrPageFromSdrObjList()->IsMasterPage());
+
+ for(size_t no(rList.GetObjCount()); !bRet && no > 0; )
+ {
+ no--;
+ SdrObject* pObj = rList.GetObj(no);
+ SdrObjList* pOL = pObj->GetSubList();
+
+ if(pOL)
+ {
+ // group object
+ bRet = impGetSdrObjListFillColor(*pOL, rPnt, rTextEditPV, rVisLayers, rCol);
+ }
+ else
+ {
+ SdrTextObj* pText = dynamic_cast< SdrTextObj * >(pObj);
+
+ // Exclude zero master page object (i.e. background shape) from color query
+ if(pText
+ && pObj->IsClosedObj()
+ && (!bMaster || (!pObj->IsNotVisibleAsMaster() && 0 != no))
+ && pObj->GetCurrentBoundRect().Contains(rPnt)
+ && !pText->IsHideContour()
+ && SdrObjectPrimitiveHit(*pObj, rPnt, 0, rTextEditPV, &rVisLayers, false))
+ {
+ bRet = GetDraftFillColor(pObj->GetMergedItemSet(), rCol);
+ }
+ }
+ }
+
+ return bRet;
+ }
+
+ bool impGetSdrPageFillColor(
+ const SdrPage& rPage,
+ const Point& rPnt,
+ const SdrPageView& rTextEditPV,
+ const SdrLayerIDSet& rVisLayers,
+ Color& rCol,
+ bool bSkipBackgroundShape)
+ {
+ bool bRet(impGetSdrObjListFillColor(rPage, rPnt, rTextEditPV, rVisLayers, rCol));
+
+ if(!bRet && !rPage.IsMasterPage())
+ {
+ if(rPage.TRG_HasMasterPage())
+ {
+ SdrLayerIDSet aSet(rVisLayers);
+ aSet &= rPage.TRG_GetMasterPageVisibleLayers();
+ SdrPage& rMasterPage = rPage.TRG_GetMasterPage();
+
+ // Don't fall back to background shape on
+ // master pages. This is later handled by
+ // GetBackgroundColor, and is necessary to cater for
+ // the silly ordering: 1. shapes, 2. master page
+ // shapes, 3. page background, 4. master page
+ // background.
+ bRet = impGetSdrPageFillColor(rMasterPage, rPnt, rTextEditPV, aSet, rCol, true);
+ }
+ }
+
+ // Only now determine background color from background shapes
+ if(!bRet && !bSkipBackgroundShape)
+ {
+ rCol = rPage.GetPageBackgroundColor();
+ return true;
+ }
+
+ return bRet;
+ }
+
+ Color impCalcBackgroundColor(
+ const tools::Rectangle& rArea,
+ const SdrPageView& rTextEditPV,
+ const SdrPage& rPage)
+ {
+ svtools::ColorConfig aColorConfig;
+ Color aBackground(aColorConfig.GetColorValue(svtools::DOCCOLOR).nColor);
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+
+ if(!rStyleSettings.GetHighContrastMode())
+ {
+ // search in page
+ const sal_uInt16 SPOTCOUNT(5);
+ Point aSpotPos[SPOTCOUNT];
+ Color aSpotColor[SPOTCOUNT];
+ sal_uInt32 nHeight( rArea.GetSize().Height() );
+ sal_uInt32 nWidth( rArea.GetSize().Width() );
+ sal_uInt32 nWidth14 = nWidth / 4;
+ sal_uInt32 nHeight14 = nHeight / 4;
+ sal_uInt32 nWidth34 = ( 3 * nWidth ) / 4;
+ sal_uInt32 nHeight34 = ( 3 * nHeight ) / 4;
+
+ sal_uInt16 i;
+ for ( i = 0; i < SPOTCOUNT; i++ )
+ {
+ // five spots are used
+ switch ( i )
+ {
+ case 0 :
+ {
+ // Center-Spot
+ aSpotPos[i] = rArea.Center();
+ }
+ break;
+
+ case 1 :
+ {
+ // TopLeft-Spot
+ aSpotPos[i] = rArea.TopLeft();
+ aSpotPos[i].AdjustX(nWidth14 );
+ aSpotPos[i].AdjustY(nHeight14 );
+ }
+ break;
+
+ case 2 :
+ {
+ // TopRight-Spot
+ aSpotPos[i] = rArea.TopLeft();
+ aSpotPos[i].AdjustX(nWidth34 );
+ aSpotPos[i].AdjustY(nHeight14 );
+ }
+ break;
+
+ case 3 :
+ {
+ // BottomLeft-Spot
+ aSpotPos[i] = rArea.TopLeft();
+ aSpotPos[i].AdjustX(nWidth14 );
+ aSpotPos[i].AdjustY(nHeight34 );
+ }
+ break;
+
+ case 4 :
+ {
+ // BottomRight-Spot
+ aSpotPos[i] = rArea.TopLeft();
+ aSpotPos[i].AdjustX(nWidth34 );
+ aSpotPos[i].AdjustY(nHeight34 );
+ }
+ break;
+
+ }
+
+ aSpotColor[i] = COL_WHITE;
+ impGetSdrPageFillColor(rPage, aSpotPos[i], rTextEditPV, rTextEditPV.GetVisibleLayers(), aSpotColor[i], false);
+ }
+
+ sal_uInt16 aMatch[SPOTCOUNT];
+
+ for ( i = 0; i < SPOTCOUNT; i++ )
+ {
+ // were same spot colors found?
+ aMatch[i] = 0;
+
+ for ( sal_uInt16 j = 0; j < SPOTCOUNT; j++ )
+ {
+ if( j != i )
+ {
+ if( aSpotColor[i] == aSpotColor[j] )
+ {
+ aMatch[i]++;
+ }
+ }
+ }
+ }
+
+ // highest weight to center spot
+ aBackground = aSpotColor[0];
+
+ for ( sal_uInt16 nMatchCount = SPOTCOUNT - 1; nMatchCount > 1; nMatchCount-- )
+ {
+ // which spot color was found most?
+ for ( i = 0; i < SPOTCOUNT; i++ )
+ {
+ if( aMatch[i] == nMatchCount )
+ {
+ aBackground = aSpotColor[i];
+ nMatchCount = 1; // break outer for-loop
+ break;
+ }
+ }
+ }
+ }
+
+ return aBackground;
+ }
+} // end of anonymous namespace
+
+Color GetTextEditBackgroundColor(const SdrObjEditView& rView)
+{
+ svtools::ColorConfig aColorConfig;
+ Color aBackground(aColorConfig.GetColorValue(svtools::DOCCOLOR).nColor);
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+
+ if(!rStyleSettings.GetHighContrastMode())
+ {
+ bool bFound(false);
+ SdrTextObj* pText = rView.GetTextEditObject();
+
+ if(pText && pText->IsClosedObj())
+ {
+ sdr::table::SdrTableObj* pTable = dynamic_cast< sdr::table::SdrTableObj * >( pText );
+
+ if( pTable )
+ bFound = GetDraftFillColor(pTable->GetActiveCellItemSet(), aBackground );
+
+ if( !bFound )
+ bFound=GetDraftFillColor(pText->GetMergedItemSet(), aBackground);
+ }
+
+ if(!bFound && pText)
+ {
+ SdrPageView* pTextEditPV = rView.GetTextEditPageView();
+
+ if(pTextEditPV)
+ {
+ Point aPvOfs(pText->GetTextEditOffset());
+ const SdrPage* pPg = pTextEditPV->GetPage();
+
+ if(pPg)
+ {
+ tools::Rectangle aSnapRect( pText->GetSnapRect() );
+ aSnapRect.Move(aPvOfs.X(), aPvOfs.Y());
+
+ return impCalcBackgroundColor(aSnapRect, *pTextEditPV, *pPg);
+ }
+ }
+ }
+ }
+
+ return aBackground;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdfmtf.cxx b/svx/source/svdraw/svdfmtf.cxx
new file mode 100644
index 000000000..659fae090
--- /dev/null
+++ b/svx/source/svdraw/svdfmtf.cxx
@@ -0,0 +1,1641 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "svdfmtf.hxx"
+#include <math.h>
+#include <editeng/eeitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/shdditem.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/xlnclit.hxx>
+#include <svx/xlncapit.hxx>
+#include <svx/xlnwtit.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflclit.hxx>
+#include <svx/xgrad.hxx>
+#include <svx/xflgrit.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/wrlmitem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/colritem.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/metric.hxx>
+#include <editeng/charscaleitem.hxx>
+#include <svx/xflhtit.hxx>
+#include <svx/sdmetitm.hxx>
+#include <svx/sdtagitm.hxx>
+#include <svx/sdtaitm.hxx>
+#include <svx/sdtditm.hxx>
+#include <svx/sdtfsitm.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdotext.hxx>
+#include <svx/svdorect.hxx>
+#include <svx/svdocirc.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/svdetc.hxx>
+#include <svl/itemset.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <tools/helpers.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <svx/xlinjoit.hxx>
+#include <svx/xlndsit.hxx>
+#include <basegfx/polygon/b2dpolygonclipper.hxx>
+#include <svx/xbtmpit.hxx>
+#include <svx/xfltrit.hxx>
+#include <svx/xflbmtit.hxx>
+#include <svx/xflbstit.hxx>
+#include <svx/svdpntv.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <svx/svditer.hxx>
+#include <svx/svdogrp.hxx>
+#include <vcl/BitmapTools.hxx>
+#include <osl/diagnose.h>
+
+using namespace com::sun::star;
+
+ImpSdrGDIMetaFileImport::ImpSdrGDIMetaFileImport(
+ SdrModel& rModel,
+ SdrLayerID nLay,
+ const tools::Rectangle& rRect)
+: mpVD(VclPtr<VirtualDevice>::Create()),
+ maScaleRect(rRect),
+ mnMapScalingOfs(0),
+ mpModel(&rModel),
+ mnLayer(nLay),
+ mnLineWidth(0),
+ maLineJoin(basegfx::B2DLineJoin::NONE),
+ maLineCap(css::drawing::LineCap_BUTT),
+ maDash(css::drawing::DashStyle_RECT, 0, 0, 0, 0, 0),
+ mbMov(false),
+ mbSize(false),
+ maOfs(0, 0),
+ mfScaleX(1.0),
+ mfScaleY(1.0),
+ maScaleX(1.0),
+ maScaleY(1.0),
+ mbFntDirty(true),
+ mbLastObjWasPolyWithoutLine(false),
+ mbNoLine(false),
+ mbNoFill(false),
+ mbLastObjWasLine(false)
+{
+ mpVD->EnableOutput(false);
+ mpVD->SetLineColor();
+ mpVD->SetFillColor();
+ maOldLineColor.SetRed( mpVD->GetLineColor().GetRed() + 1 );
+ mpLineAttr = std::make_unique<SfxItemSetFixed<XATTR_LINE_FIRST, XATTR_LINE_LAST>>(rModel.GetItemPool());
+ mpFillAttr = std::make_unique<SfxItemSetFixed<XATTR_FILL_FIRST, XATTR_FILL_LAST>>(rModel.GetItemPool());
+ mpTextAttr = std::make_unique<SfxItemSetFixed<EE_ITEMS_START, EE_ITEMS_END>>(rModel.GetItemPool());
+ checkClip();
+}
+
+void ImpSdrGDIMetaFileImport::DoLoopActions(GDIMetaFile const & rMtf, SvdProgressInfo* pProgrInfo, sal_uInt32* pActionsToReport)
+{
+ const sal_uLong nCount(rMtf.GetActionSize());
+
+ for(sal_uLong a(0); a < nCount; a++)
+ {
+ MetaAction* pAct = rMtf.GetAction(a);
+
+ if(!pAct)
+ {
+ OSL_ENSURE(false, "OOps, no action at valid position (!)");
+ pAct = rMtf.GetAction(0);
+ }
+
+ switch (pAct->GetType())
+ {
+ case MetaActionType::PIXEL : break;
+ case MetaActionType::POINT : break;
+ case MetaActionType::LINE : DoAction(static_cast<MetaLineAction &>(*pAct)); break;
+ case MetaActionType::RECT : DoAction(static_cast<MetaRectAction &>(*pAct)); break;
+ case MetaActionType::ROUNDRECT : DoAction(static_cast<MetaRoundRectAction &>(*pAct)); break;
+ case MetaActionType::ELLIPSE : DoAction(static_cast<MetaEllipseAction &>(*pAct)); break;
+ case MetaActionType::ARC : DoAction(static_cast<MetaArcAction &>(*pAct)); break;
+ case MetaActionType::PIE : DoAction(static_cast<MetaPieAction &>(*pAct)); break;
+ case MetaActionType::CHORD : DoAction(static_cast<MetaChordAction &>(*pAct)); break;
+ case MetaActionType::POLYLINE : DoAction(static_cast<MetaPolyLineAction &>(*pAct)); break;
+ case MetaActionType::POLYGON : DoAction(static_cast<MetaPolygonAction &>(*pAct)); break;
+ case MetaActionType::POLYPOLYGON : DoAction(static_cast<MetaPolyPolygonAction &>(*pAct)); break;
+ case MetaActionType::TEXT : DoAction(static_cast<MetaTextAction &>(*pAct)); break;
+ case MetaActionType::TEXTARRAY : DoAction(static_cast<MetaTextArrayAction &>(*pAct)); break;
+ case MetaActionType::STRETCHTEXT : DoAction(static_cast<MetaStretchTextAction &>(*pAct)); break;
+ case MetaActionType::BMP : DoAction(static_cast<MetaBmpAction &>(*pAct)); break;
+ case MetaActionType::BMPSCALE : DoAction(static_cast<MetaBmpScaleAction &>(*pAct)); break;
+ case MetaActionType::BMPEX : DoAction(static_cast<MetaBmpExAction &>(*pAct)); break;
+ case MetaActionType::BMPEXSCALE : DoAction(static_cast<MetaBmpExScaleAction &>(*pAct)); break;
+ case MetaActionType::LINECOLOR : DoAction(static_cast<MetaLineColorAction &>(*pAct)); break;
+ case MetaActionType::FILLCOLOR : DoAction(static_cast<MetaFillColorAction &>(*pAct)); break;
+ case MetaActionType::TEXTCOLOR : DoAction(static_cast<MetaTextColorAction &>(*pAct)); break;
+ case MetaActionType::TEXTFILLCOLOR : DoAction(static_cast<MetaTextFillColorAction &>(*pAct)); break;
+ case MetaActionType::FONT : DoAction(static_cast<MetaFontAction &>(*pAct)); break;
+ case MetaActionType::TEXTALIGN : DoAction(static_cast<MetaTextAlignAction &>(*pAct)); break;
+ case MetaActionType::MAPMODE : DoAction(static_cast<MetaMapModeAction &>(*pAct)); break;
+ case MetaActionType::CLIPREGION : DoAction(static_cast<MetaClipRegionAction &>(*pAct)); break;
+ case MetaActionType::MOVECLIPREGION : DoAction(static_cast<MetaMoveClipRegionAction &>(*pAct)); break;
+ case MetaActionType::ISECTRECTCLIPREGION: DoAction(static_cast<MetaISectRectClipRegionAction&>(*pAct)); break;
+ case MetaActionType::ISECTREGIONCLIPREGION: DoAction(static_cast<MetaISectRegionClipRegionAction&>(*pAct)); break;
+ case MetaActionType::RASTEROP : DoAction(static_cast<MetaRasterOpAction &>(*pAct)); break;
+ case MetaActionType::PUSH : DoAction(static_cast<MetaPushAction &>(*pAct)); break;
+ case MetaActionType::POP : DoAction(static_cast<MetaPopAction &>(*pAct)); break;
+ case MetaActionType::HATCH : DoAction(static_cast<MetaHatchAction &>(*pAct)); break;
+
+ // #i125211# MetaCommentAction may change index, thus hand it over
+ case MetaActionType::COMMENT : DoAction(static_cast<MetaCommentAction&>(*pAct), rMtf, a);
+ break;
+
+ // missing actions added
+ case MetaActionType::TEXTRECT : DoAction(static_cast<MetaTextRectAction&>(*pAct)); break;
+ case MetaActionType::BMPSCALEPART : DoAction(static_cast<MetaBmpScalePartAction&>(*pAct)); break;
+ case MetaActionType::BMPEXSCALEPART : DoAction(static_cast<MetaBmpExScalePartAction&>(*pAct)); break;
+ case MetaActionType::MASK : DoAction(static_cast<MetaMaskAction&>(*pAct)); break;
+ case MetaActionType::MASKSCALE : DoAction(static_cast<MetaMaskScaleAction&>(*pAct)); break;
+ case MetaActionType::MASKSCALEPART : DoAction(static_cast<MetaMaskScalePartAction&>(*pAct)); break;
+ case MetaActionType::GRADIENT : DoAction(static_cast<MetaGradientAction&>(*pAct)); break;
+ case MetaActionType::WALLPAPER : OSL_ENSURE(false, "Tried to construct SdrObject from MetaWallpaperAction: not supported (!)"); break;
+ case MetaActionType::Transparent : DoAction(static_cast<MetaTransparentAction&>(*pAct)); break;
+ case MetaActionType::EPS : OSL_ENSURE(false, "Tried to construct SdrObject from MetaEPSAction: not supported (!)"); break;
+ case MetaActionType::REFPOINT : DoAction(static_cast<MetaRefPointAction&>(*pAct)); break;
+ case MetaActionType::TEXTLINECOLOR : DoAction(static_cast<MetaTextLineColorAction&>(*pAct)); break;
+ case MetaActionType::TEXTLINE : OSL_ENSURE(false, "Tried to construct SdrObject from MetaTextLineAction: not supported (!)"); break;
+ case MetaActionType::FLOATTRANSPARENT : DoAction(static_cast<MetaFloatTransparentAction&>(*pAct)); break;
+ case MetaActionType::GRADIENTEX : DoAction(static_cast<MetaGradientExAction&>(*pAct)); break;
+ case MetaActionType::LAYOUTMODE : DoAction(static_cast<MetaLayoutModeAction&>(*pAct)); break;
+ case MetaActionType::TEXTLANGUAGE : DoAction(static_cast<MetaTextLanguageAction&>(*pAct)); break;
+ case MetaActionType::OVERLINECOLOR : DoAction(static_cast<MetaOverlineColorAction&>(*pAct)); break;
+ default: break;
+ }
+
+ if(pProgrInfo && pActionsToReport)
+ {
+ (*pActionsToReport)++;
+
+ if(*pActionsToReport >= 16) // update all 16 actions
+ {
+ if(!pProgrInfo->ReportActions(*pActionsToReport))
+ break;
+
+ *pActionsToReport = 0;
+ }
+ }
+ }
+}
+
+size_t ImpSdrGDIMetaFileImport::DoImport(
+ const GDIMetaFile& rMtf,
+ SdrObjList& rOL,
+ size_t nInsPos,
+ SvdProgressInfo* pProgrInfo)
+{
+ // setup some global scale parameter
+ // mfScaleX, mfScaleY, maScaleX, maScaleY, mbMov, mbSize
+ mfScaleX = mfScaleY = 1.0;
+ const Size aMtfSize(rMtf.GetPrefSize());
+
+ if(aMtfSize.Width() & aMtfSize.Height() && (!maScaleRect.IsEmpty()))
+ {
+ maOfs = maScaleRect.TopLeft();
+
+ if(aMtfSize.Width() != (maScaleRect.GetWidth() - 1))
+ {
+ mfScaleX = static_cast<double>( maScaleRect.GetWidth() - 1 ) / static_cast<double>(aMtfSize.Width());
+ }
+
+ if(aMtfSize.Height() != (maScaleRect.GetHeight() - 1))
+ {
+ mfScaleY = static_cast<double>( maScaleRect.GetHeight() - 1 ) / static_cast<double>(aMtfSize.Height());
+ }
+ }
+
+ mbMov = maOfs.X()!=0 || maOfs.Y()!=0;
+ mbSize = false;
+ maScaleX = Fraction( 1, 1 );
+ maScaleY = Fraction( 1, 1 );
+
+ if(aMtfSize.Width() != (maScaleRect.GetWidth() - 1))
+ {
+ maScaleX = Fraction(maScaleRect.GetWidth() - 1, aMtfSize.Width());
+ mbSize = true;
+ }
+
+ if(aMtfSize.Height() != (maScaleRect.GetHeight() - 1))
+ {
+ maScaleY = Fraction(maScaleRect.GetHeight() - 1, aMtfSize.Height());
+ mbSize = true;
+ }
+
+ if(pProgrInfo)
+ {
+ pProgrInfo->SetActionCount(rMtf.GetActionSize());
+ }
+
+ sal_uInt32 nActionsToReport(0);
+
+ // execute
+ DoLoopActions(rMtf, pProgrInfo, &nActionsToReport);
+
+ if(pProgrInfo)
+ {
+ pProgrInfo->ReportActions(nActionsToReport);
+ nActionsToReport = 0;
+ }
+
+ // MapMode scaling
+ MapScaling();
+
+ // To calculate the progress meter, we use GetActionSize()*3.
+ // However, maTmpList has a lower entry count limit than GetActionSize(),
+ // so the actions that were assumed were too much have to be re-added.
+ nActionsToReport = (rMtf.GetActionSize() - maTmpList.size()) * 2;
+
+ // announce all currently unannounced rescales
+ if(pProgrInfo)
+ {
+ pProgrInfo->ReportRescales(nActionsToReport);
+ pProgrInfo->SetInsertCount(maTmpList.size());
+ }
+
+ nActionsToReport = 0;
+
+ // insert all objects cached in aTmpList now into rOL from nInsPos
+ nInsPos = std::min(nInsPos, rOL.GetObjCount());
+
+ for(SdrObject* pObj : maTmpList)
+ {
+ rOL.NbcInsertObject(pObj, nInsPos);
+ nInsPos++;
+
+ if(pProgrInfo)
+ {
+ nActionsToReport++;
+
+ if(nActionsToReport >= 32) // update all 32 actions
+ {
+ pProgrInfo->ReportInserts(nActionsToReport);
+ nActionsToReport = 0;
+ }
+ }
+ }
+
+ // report all remaining inserts for the last time
+ if(pProgrInfo)
+ {
+ pProgrInfo->ReportInserts(nActionsToReport);
+ }
+
+ return maTmpList.size();
+}
+
+void ImpSdrGDIMetaFileImport::SetAttributes(SdrObject* pObj, bool bForceTextAttr)
+{
+ mbNoLine = false;
+ mbNoFill = false;
+ bool bLine(!bForceTextAttr);
+ bool bFill(!pObj || (pObj->IsClosedObj() && !bForceTextAttr));
+ bool bText(bForceTextAttr || (pObj && pObj->GetOutlinerParaObject()));
+
+ if(bLine)
+ {
+ if(mnLineWidth)
+ {
+ mpLineAttr->Put(XLineWidthItem(mnLineWidth));
+ }
+ else
+ {
+ mpLineAttr->Put(XLineWidthItem(0));
+ }
+
+ maOldLineColor = mpVD->GetLineColor();
+
+ if(mpVD->IsLineColor())
+ {
+ mpLineAttr->Put(XLineStyleItem(drawing::LineStyle_SOLID));
+ mpLineAttr->Put(XLineColorItem(OUString(), mpVD->GetLineColor()));
+ }
+ else
+ {
+ mpLineAttr->Put(XLineStyleItem(drawing::LineStyle_NONE));
+ }
+
+ switch(maLineJoin)
+ {
+ case basegfx::B2DLineJoin::NONE:
+ mpLineAttr->Put(XLineJointItem(css::drawing::LineJoint_NONE));
+ break;
+ case basegfx::B2DLineJoin::Bevel:
+ mpLineAttr->Put(XLineJointItem(css::drawing::LineJoint_BEVEL));
+ break;
+ case basegfx::B2DLineJoin::Miter:
+ mpLineAttr->Put(XLineJointItem(css::drawing::LineJoint_MITER));
+ break;
+ case basegfx::B2DLineJoin::Round:
+ mpLineAttr->Put(XLineJointItem(css::drawing::LineJoint_ROUND));
+ break;
+ }
+
+ // Add LineCap support
+ mpLineAttr->Put(XLineCapItem(maLineCap));
+
+ if(((maDash.GetDots() && maDash.GetDotLen()) || (maDash.GetDashes() && maDash.GetDashLen())) && maDash.GetDistance())
+ {
+ mpLineAttr->Put(XLineDashItem(OUString(), maDash));
+ }
+ else
+ {
+ mpLineAttr->Put(XLineDashItem(OUString(), XDash(css::drawing::DashStyle_RECT)));
+ }
+ }
+ else
+ {
+ mbNoLine = true;
+ }
+
+ if(bFill)
+ {
+ if(mpVD->IsFillColor())
+ {
+ mpFillAttr->Put(XFillStyleItem(drawing::FillStyle_SOLID));
+ mpFillAttr->Put(XFillColorItem(OUString(), mpVD->GetFillColor()));
+ }
+ else
+ {
+ mpFillAttr->Put(XFillStyleItem(drawing::FillStyle_NONE));
+ }
+ }
+ else
+ {
+ mbNoFill = true;
+ }
+
+ if(bText && mbFntDirty)
+ {
+ vcl::Font aFnt(mpVD->GetFont());
+ const sal_uInt32 nHeight(FRound(aFnt.GetFontSize().Height() * mfScaleY));
+
+ mpTextAttr->Put( SvxFontItem( aFnt.GetFamilyType(), aFnt.GetFamilyName(), aFnt.GetStyleName(), aFnt.GetPitch(), aFnt.GetCharSet(), EE_CHAR_FONTINFO ) );
+ mpTextAttr->Put( SvxFontItem( aFnt.GetFamilyType(), aFnt.GetFamilyName(), aFnt.GetStyleName(), aFnt.GetPitch(), aFnt.GetCharSet(), EE_CHAR_FONTINFO_CJK ) );
+ mpTextAttr->Put( SvxFontItem( aFnt.GetFamilyType(), aFnt.GetFamilyName(), aFnt.GetStyleName(), aFnt.GetPitch(), aFnt.GetCharSet(), EE_CHAR_FONTINFO_CTL ) );
+ mpTextAttr->Put(SvxPostureItem(aFnt.GetItalic(), EE_CHAR_ITALIC));
+ mpTextAttr->Put(SvxWeightItem(aFnt.GetWeight(), EE_CHAR_WEIGHT));
+ mpTextAttr->Put( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT ) );
+ mpTextAttr->Put( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT_CJK ) );
+ mpTextAttr->Put( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT_CTL ) );
+ mpTextAttr->Put(SvxCharScaleWidthItem(100, EE_CHAR_FONTWIDTH));
+ mpTextAttr->Put(SvxUnderlineItem(aFnt.GetUnderline(), EE_CHAR_UNDERLINE));
+ mpTextAttr->Put(SvxOverlineItem(aFnt.GetOverline(), EE_CHAR_OVERLINE));
+ mpTextAttr->Put(SvxCrossedOutItem(aFnt.GetStrikeout(), EE_CHAR_STRIKEOUT));
+ mpTextAttr->Put(SvxShadowedItem(aFnt.IsShadow(), EE_CHAR_SHADOW));
+
+ // #i118485# Setting this item leads to problems (written #i118498# for this)
+ // mpTextAttr->Put(SvxAutoKernItem(aFnt.IsKerning(), EE_CHAR_KERNING));
+
+ mpTextAttr->Put(SvxWordLineModeItem(aFnt.IsWordLineMode(), EE_CHAR_WLM));
+ mpTextAttr->Put(SvxContourItem(aFnt.IsOutline(), EE_CHAR_OUTLINE));
+ mpTextAttr->Put(SvxColorItem(mpVD->GetTextColor(), EE_CHAR_COLOR));
+ //... svxfont textitem svditext
+ mbFntDirty = false;
+ }
+
+ if(!pObj)
+ return;
+
+ pObj->SetLayer(mnLayer);
+
+ if(bLine)
+ {
+ pObj->SetMergedItemSet(*mpLineAttr);
+ }
+
+ if(bFill)
+ {
+ pObj->SetMergedItemSet(*mpFillAttr);
+ }
+
+ if(bText)
+ {
+ pObj->SetMergedItemSet(*mpTextAttr);
+ pObj->SetMergedItem(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_LEFT));
+ }
+}
+
+void ImpSdrGDIMetaFileImport::InsertObj(SdrObject* pObj, bool bScale)
+{
+ if(bScale && !maScaleRect.IsEmpty())
+ {
+ if(mbSize)
+ {
+ pObj->NbcResize(Point(), maScaleX, maScaleY);
+ }
+
+ if(mbMov)
+ {
+ pObj->NbcMove(Size(maOfs.X(), maOfs.Y()));
+ }
+ }
+
+ if(isClip())
+ {
+ const basegfx::B2DPolyPolygon aPoly(pObj->TakeXorPoly());
+ const basegfx::B2DRange aOldRange(aPoly.getB2DRange());
+ const SdrLayerID aOldLayer(pObj->GetLayer());
+ const SfxItemSet aOldItemSet(pObj->GetMergedItemSet());
+ const SdrGrafObj* pSdrGrafObj = dynamic_cast< SdrGrafObj* >(pObj);
+ const SdrTextObj* pSdrTextObj = dynamic_cast< SdrTextObj* >(pObj);
+
+ if(pSdrTextObj && pSdrTextObj->HasText())
+ {
+ // all text objects are created from ImportText and have no line or fill attributes, so
+ // it is okay to concentrate on the text itself
+ while(true)
+ {
+ const basegfx::B2DPolyPolygon aTextContour(pSdrTextObj->TakeContour());
+ const basegfx::B2DRange aTextRange(aTextContour.getB2DRange());
+ const basegfx::B2DRange aClipRange(maClip.getB2DRange());
+
+ // no overlap -> completely outside
+ if(!aClipRange.overlaps(aTextRange))
+ {
+ SdrObject::Free(pObj);
+ break;
+ }
+
+ // when the clip is a rectangle fast check for inside is possible
+ if(basegfx::utils::isRectangle(maClip) && aClipRange.isInside(aTextRange))
+ {
+ // completely inside ClipRect
+ break;
+ }
+
+ // here text needs to be clipped; to do so, convert to SdrObjects with polygons
+ // and add these recursively. Delete original object, do not add in this run
+ SdrObject* pConverted = pSdrTextObj->ConvertToPolyObj(true, true).release();
+ SdrObject::Free(pObj);
+
+ if(pConverted)
+ {
+ // recursively add created conversion; per definition this shall not
+ // contain further SdrTextObjs. Visit only non-group objects
+ SdrObjListIter aIter(*pConverted, SdrIterMode::DeepNoGroups);
+
+ // work with clones; the created conversion may contain group objects
+ // and when working with the original objects the loop itself could
+ // break and the cleanup later would be pretty complicated (only delete group
+ // objects, are these empty, ...?)
+ while(aIter.IsMore())
+ {
+ SdrObject* pCandidate = aIter.Next();
+ OSL_ENSURE(pCandidate && dynamic_cast< SdrObjGroup* >(pCandidate) == nullptr, "SdrObjListIter with SdrIterMode::DeepNoGroups error (!)");
+ SdrObject* pNewClone(pCandidate->CloneSdrObject(pCandidate->getSdrModelFromSdrObject()));
+
+ if(pNewClone)
+ {
+ InsertObj(pNewClone, false);
+ }
+ else
+ {
+ OSL_ENSURE(false, "SdrObject::Clone() failed (!)");
+ }
+ }
+
+ // cleanup temporary conversion objects
+ SdrObject::Free(pConverted);
+ }
+
+ break;
+ }
+ }
+ else
+ {
+ BitmapEx aBitmapEx;
+
+ if(pSdrGrafObj)
+ {
+ aBitmapEx = pSdrGrafObj->GetGraphic().GetBitmapEx();
+ }
+
+ SdrObject::Free(pObj);
+
+ if(!aOldRange.isEmpty())
+ {
+ // clip against ClipRegion
+ const basegfx::B2DPolyPolygon aNewPoly(
+ basegfx::utils::clipPolyPolygonOnPolyPolygon(
+ aPoly,
+ maClip,
+ true,
+ !aPoly.isClosed()));
+ const basegfx::B2DRange aNewRange(aNewPoly.getB2DRange());
+
+ if(!aNewRange.isEmpty())
+ {
+ pObj = new SdrPathObj(
+ *mpModel,
+ aNewPoly.isClosed() ? SdrObjKind::Polygon : SdrObjKind::PolyLine,
+ aNewPoly);
+
+ pObj->SetLayer(aOldLayer);
+ pObj->SetMergedItemSet(aOldItemSet);
+
+ if(!aBitmapEx.IsEmpty())
+ {
+ // aNewRange is inside of aOldRange and defines which part of aBitmapEx is used
+ const double fScaleX(aBitmapEx.GetSizePixel().Width() / (aOldRange.getWidth() ? aOldRange.getWidth() : 1.0));
+ const double fScaleY(aBitmapEx.GetSizePixel().Height() / (aOldRange.getHeight() ? aOldRange.getHeight() : 1.0));
+ basegfx::B2DRange aPixel(aNewRange);
+ basegfx::B2DHomMatrix aTrans;
+
+ aTrans.translate(-aOldRange.getMinX(), -aOldRange.getMinY());
+ aTrans.scale(fScaleX, fScaleY);
+ aPixel.transform(aTrans);
+
+ const Size aOrigSizePixel(aBitmapEx.GetSizePixel());
+ const Point aClipTopLeft(
+ basegfx::fround(floor(std::max(0.0, aPixel.getMinX()))),
+ basegfx::fround(floor(std::max(0.0, aPixel.getMinY()))));
+ const Size aClipSize(
+ basegfx::fround(ceil(std::min(static_cast<double>(aOrigSizePixel.Width()), aPixel.getWidth()))),
+ basegfx::fround(ceil(std::min(static_cast<double>(aOrigSizePixel.Height()), aPixel.getHeight()))));
+ const BitmapEx aClippedBitmap(
+ aBitmapEx,
+ aClipTopLeft,
+ aClipSize);
+
+ pObj->SetMergedItem(XFillStyleItem(drawing::FillStyle_BITMAP));
+ pObj->SetMergedItem(XFillBitmapItem(OUString(), Graphic(aClippedBitmap)));
+ pObj->SetMergedItem(XFillBmpTileItem(false));
+ pObj->SetMergedItem(XFillBmpStretchItem(true));
+ }
+ }
+ }
+ }
+ }
+
+ if(!pObj)
+ return;
+
+ // #i111954# check object for visibility
+ // used are SdrPathObj, SdrRectObj, SdrCircObj, SdrGrafObj
+ bool bVisible(false);
+
+ if(pObj->HasLineStyle())
+ {
+ bVisible = true;
+ }
+
+ if(!bVisible && pObj->HasFillStyle())
+ {
+ bVisible = true;
+ }
+
+ if(!bVisible)
+ {
+ SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >(pObj);
+
+ if(pTextObj && pTextObj->HasText())
+ {
+ bVisible = true;
+ }
+ }
+
+ if(!bVisible)
+ {
+ SdrGrafObj* pGrafObj = dynamic_cast< SdrGrafObj* >(pObj);
+
+ if(pGrafObj)
+ {
+ // this may be refined to check if the graphic really is visible. It
+ // is here to ensure that graphic objects without fill, line and text
+ // get created
+ bVisible = true;
+ }
+ }
+
+ if(!bVisible)
+ {
+ SdrObject::Free(pObj);
+ }
+ else
+ {
+ maTmpList.push_back(pObj);
+
+ if(dynamic_cast< SdrPathObj* >(pObj))
+ {
+ const bool bClosed(pObj->IsClosedObj());
+
+ mbLastObjWasPolyWithoutLine = mbNoLine && bClosed;
+ mbLastObjWasLine = !bClosed;
+ }
+ else
+ {
+ mbLastObjWasPolyWithoutLine = false;
+ mbLastObjWasLine = false;
+ }
+ }
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaLineAction const & rAct)
+{
+ // #i73407# reformulation to use new B2DPolygon classes
+ const basegfx::B2DPoint aStart(rAct.GetStartPoint().X(), rAct.GetStartPoint().Y());
+ const basegfx::B2DPoint aEnd(rAct.GetEndPoint().X(), rAct.GetEndPoint().Y());
+
+ if(aStart.equal(aEnd))
+ return;
+
+ basegfx::B2DPolygon aLine;
+ const basegfx::B2DHomMatrix aTransform(basegfx::utils::createScaleTranslateB2DHomMatrix(mfScaleX, mfScaleY, maOfs.X(), maOfs.Y()));
+
+ aLine.append(aStart);
+ aLine.append(aEnd);
+ aLine.transform(aTransform);
+
+ const LineInfo& rLineInfo = rAct.GetLineInfo();
+ const sal_Int32 nNewLineWidth(rLineInfo.GetWidth());
+ bool bCreateLineObject(true);
+
+ if(mbLastObjWasLine && (nNewLineWidth == mnLineWidth) && CheckLastLineMerge(aLine))
+ {
+ bCreateLineObject = false;
+ }
+
+ if(!bCreateLineObject)
+ return;
+
+ SdrPathObj* pPath = new SdrPathObj(
+ *mpModel,
+ SdrObjKind::Line,
+ basegfx::B2DPolyPolygon(aLine));
+ mnLineWidth = nNewLineWidth;
+ maLineJoin = rLineInfo.GetLineJoin();
+ maLineCap = rLineInfo.GetLineCap();
+ maDash = XDash(css::drawing::DashStyle_RECT,
+ rLineInfo.GetDotCount(), rLineInfo.GetDotLen(),
+ rLineInfo.GetDashCount(), rLineInfo.GetDashLen(),
+ rLineInfo.GetDistance());
+ SetAttributes(pPath);
+ mnLineWidth = 0;
+ maLineJoin = basegfx::B2DLineJoin::NONE;
+ maDash = XDash();
+ InsertObj(pPath, false);
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaRectAction const & rAct)
+{
+ SdrRectObj* pRect = new SdrRectObj(
+ *mpModel,
+ rAct.GetRect());
+ SetAttributes(pRect);
+ InsertObj(pRect);
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaRoundRectAction const & rAct)
+{
+ SdrRectObj* pRect = new SdrRectObj(
+ *mpModel,
+ rAct.GetRect());
+ SetAttributes(pRect);
+ tools::Long nRad=(rAct.GetHorzRound()+rAct.GetVertRound())/2;
+ if (nRad!=0) {
+ SfxItemSetFixed<SDRATTR_CORNER_RADIUS, SDRATTR_CORNER_RADIUS> aSet(*mpLineAttr->GetPool());
+ aSet.Put(SdrMetricItem(SDRATTR_CORNER_RADIUS, nRad));
+ pRect->SetMergedItemSet(aSet);
+ }
+ InsertObj(pRect);
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaEllipseAction const & rAct)
+{
+ SdrCircObj* pCirc=new SdrCircObj(
+ *mpModel,
+ SdrCircKind::Full,
+ rAct.GetRect());
+ SetAttributes(pCirc);
+ InsertObj(pCirc);
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaArcAction const & rAct)
+{
+ Point aCenter(rAct.GetRect().Center());
+ Degree100 nStart=GetAngle(rAct.GetStartPoint()-aCenter);
+ Degree100 nEnd=GetAngle(rAct.GetEndPoint()-aCenter);
+ SdrCircObj* pCirc = new SdrCircObj(
+ *mpModel,
+ SdrCircKind::Arc,
+ rAct.GetRect(),nStart,nEnd);
+ SetAttributes(pCirc);
+ InsertObj(pCirc);
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaPieAction const & rAct)
+{
+ Point aCenter(rAct.GetRect().Center());
+ Degree100 nStart=GetAngle(rAct.GetStartPoint()-aCenter);
+ Degree100 nEnd=GetAngle(rAct.GetEndPoint()-aCenter);
+ SdrCircObj* pCirc = new SdrCircObj(
+ *mpModel,
+ SdrCircKind::Section,
+ rAct.GetRect(),
+ nStart,
+ nEnd);
+ SetAttributes(pCirc);
+ InsertObj(pCirc);
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaChordAction const & rAct)
+{
+ Point aCenter(rAct.GetRect().Center());
+ Degree100 nStart=GetAngle(rAct.GetStartPoint()-aCenter);
+ Degree100 nEnd=GetAngle(rAct.GetEndPoint()-aCenter);
+ SdrCircObj* pCirc = new SdrCircObj(
+ *mpModel,
+ SdrCircKind::Cut,
+ rAct.GetRect(),
+ nStart,
+ nEnd);
+ SetAttributes(pCirc);
+ InsertObj(pCirc);
+}
+
+bool ImpSdrGDIMetaFileImport::CheckLastLineMerge(const basegfx::B2DPolygon& rSrcPoly)
+{
+ // #i102706# Do not merge closed polygons
+ if(rSrcPoly.isClosed())
+ {
+ return false;
+ }
+
+ // #i73407# reformulation to use new B2DPolygon classes
+ if(mbLastObjWasLine && (maOldLineColor == mpVD->GetLineColor()) && rSrcPoly.count())
+ {
+ SdrObject* pTmpObj = !maTmpList.empty() ? maTmpList[maTmpList.size() - 1] : nullptr;
+ SdrPathObj* pLastPoly = dynamic_cast< SdrPathObj* >(pTmpObj);
+
+ if(pLastPoly)
+ {
+ if(1 == pLastPoly->GetPathPoly().count())
+ {
+ bool bOk(false);
+ basegfx::B2DPolygon aDstPoly(pLastPoly->GetPathPoly().getB2DPolygon(0));
+
+ // #i102706# Do not merge closed polygons
+ if(aDstPoly.isClosed())
+ {
+ return false;
+ }
+
+ if(aDstPoly.count())
+ {
+ const sal_uInt32 nMaxDstPnt(aDstPoly.count() - 1);
+ const sal_uInt32 nMaxSrcPnt(rSrcPoly.count() - 1);
+
+ if(aDstPoly.getB2DPoint(nMaxDstPnt) == rSrcPoly.getB2DPoint(0))
+ {
+ aDstPoly.append(rSrcPoly, 1, rSrcPoly.count() - 1);
+ bOk = true;
+ }
+ else if(aDstPoly.getB2DPoint(0) == rSrcPoly.getB2DPoint(nMaxSrcPnt))
+ {
+ basegfx::B2DPolygon aNew(rSrcPoly);
+ aNew.append(aDstPoly, 1, aDstPoly.count() - 1);
+ aDstPoly = aNew;
+ bOk = true;
+ }
+ else if(aDstPoly.getB2DPoint(0) == rSrcPoly.getB2DPoint(0))
+ {
+ aDstPoly.flip();
+ aDstPoly.append(rSrcPoly, 1, rSrcPoly.count() - 1);
+ bOk = true;
+ }
+ else if(aDstPoly.getB2DPoint(nMaxDstPnt) == rSrcPoly.getB2DPoint(nMaxSrcPnt))
+ {
+ basegfx::B2DPolygon aNew(rSrcPoly);
+ aNew.flip();
+ aDstPoly.append(aNew, 1, aNew.count() - 1);
+ bOk = true;
+ }
+ }
+
+ if(bOk)
+ {
+ pLastPoly->NbcSetPathPoly(basegfx::B2DPolyPolygon(aDstPoly));
+ }
+
+ return bOk;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool ImpSdrGDIMetaFileImport::CheckLastPolyLineAndFillMerge(const basegfx::B2DPolyPolygon & rPolyPolygon)
+{
+ // #i73407# reformulation to use new B2DPolygon classes
+ if(mbLastObjWasPolyWithoutLine)
+ {
+ SdrObject* pTmpObj = !maTmpList.empty() ? maTmpList[maTmpList.size() - 1] : nullptr;
+ SdrPathObj* pLastPoly = dynamic_cast< SdrPathObj* >(pTmpObj);
+
+ if(pLastPoly)
+ {
+ if(pLastPoly->GetPathPoly() == rPolyPolygon)
+ {
+ SetAttributes(nullptr);
+
+ if(!mbNoLine && mbNoFill)
+ {
+ pLastPoly->SetMergedItemSet(*mpLineAttr);
+
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+void ImpSdrGDIMetaFileImport::checkClip()
+{
+ if(!mpVD->IsClipRegion())
+ return;
+
+ maClip = mpVD->GetClipRegion().GetAsB2DPolyPolygon();
+
+ if(isClip())
+ {
+ const basegfx::B2DHomMatrix aTransform(
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ mfScaleX,
+ mfScaleY,
+ maOfs.X(),
+ maOfs.Y()));
+
+ maClip.transform(aTransform);
+ }
+}
+
+bool ImpSdrGDIMetaFileImport::isClip() const
+{
+ return !maClip.getB2DRange().isEmpty();
+}
+
+void ImpSdrGDIMetaFileImport::DoAction( MetaPolyLineAction const & rAct )
+{
+ // #i73407# reformulation to use new B2DPolygon classes
+ basegfx::B2DPolygon aSource(rAct.GetPolygon().getB2DPolygon());
+
+ if(aSource.count())
+ {
+ const basegfx::B2DHomMatrix aTransform(basegfx::utils::createScaleTranslateB2DHomMatrix(mfScaleX, mfScaleY, maOfs.X(), maOfs.Y()));
+ aSource.transform(aTransform);
+ }
+
+ const LineInfo& rLineInfo = rAct.GetLineInfo();
+ const sal_Int32 nNewLineWidth(rLineInfo.GetWidth());
+ bool bCreateLineObject(true);
+
+ if(mbLastObjWasLine && (nNewLineWidth == mnLineWidth) && CheckLastLineMerge(aSource))
+ {
+ bCreateLineObject = false;
+ }
+ else if(mbLastObjWasPolyWithoutLine && CheckLastPolyLineAndFillMerge(basegfx::B2DPolyPolygon(aSource)))
+ {
+ bCreateLineObject = false;
+ }
+
+ if(!bCreateLineObject)
+ return;
+
+ SdrPathObj* pPath = new SdrPathObj(
+ *mpModel,
+ aSource.isClosed() ? SdrObjKind::Polygon : SdrObjKind::PolyLine,
+ basegfx::B2DPolyPolygon(aSource));
+ mnLineWidth = nNewLineWidth;
+ maLineJoin = rLineInfo.GetLineJoin();
+ maLineCap = rLineInfo.GetLineCap();
+ maDash = XDash(css::drawing::DashStyle_RECT,
+ rLineInfo.GetDotCount(), rLineInfo.GetDotLen(),
+ rLineInfo.GetDashCount(), rLineInfo.GetDashLen(),
+ rLineInfo.GetDistance());
+ SetAttributes(pPath);
+ mnLineWidth = 0;
+ maLineJoin = basegfx::B2DLineJoin::NONE;
+ maDash = XDash();
+ InsertObj(pPath, false);
+}
+
+void ImpSdrGDIMetaFileImport::DoAction( MetaPolygonAction const & rAct )
+{
+ // #i73407# reformulation to use new B2DPolygon classes
+ basegfx::B2DPolygon aSource(rAct.GetPolygon().getB2DPolygon());
+
+ if(!aSource.count())
+ return;
+
+ const basegfx::B2DHomMatrix aTransform(basegfx::utils::createScaleTranslateB2DHomMatrix(mfScaleX, mfScaleY, maOfs.X(), maOfs.Y()));
+ aSource.transform(aTransform);
+
+ if(!mbLastObjWasPolyWithoutLine || !CheckLastPolyLineAndFillMerge(basegfx::B2DPolyPolygon(aSource)))
+ {
+ // #i73407# make sure polygon is closed, it's a filled primitive
+ aSource.setClosed(true);
+ SdrPathObj* pPath = new SdrPathObj(
+ *mpModel,
+ SdrObjKind::Polygon,
+ basegfx::B2DPolyPolygon(aSource));
+ SetAttributes(pPath);
+ InsertObj(pPath, false);
+ }
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaPolyPolygonAction const & rAct)
+{
+ // #i73407# reformulation to use new B2DPolygon classes
+ basegfx::B2DPolyPolygon aSource(rAct.GetPolyPolygon().getB2DPolyPolygon());
+
+ if(!aSource.count())
+ return;
+
+ const basegfx::B2DHomMatrix aTransform(basegfx::utils::createScaleTranslateB2DHomMatrix(mfScaleX, mfScaleY, maOfs.X(), maOfs.Y()));
+ aSource.transform(aTransform);
+
+ if(!mbLastObjWasPolyWithoutLine || !CheckLastPolyLineAndFillMerge(aSource))
+ {
+ // #i73407# make sure polygon is closed, it's a filled primitive
+ aSource.setClosed(true);
+ SdrPathObj* pPath = new SdrPathObj(
+ *mpModel,
+ SdrObjKind::Polygon,
+ aSource);
+ SetAttributes(pPath);
+ InsertObj(pPath, false);
+ }
+}
+
+void ImpSdrGDIMetaFileImport::ImportText( const Point& rPos, const OUString& rStr, const MetaAction& rAct )
+{
+ // calc text box size, add 5% to make it fit safely
+
+ FontMetric aFontMetric( mpVD->GetFontMetric() );
+ vcl::Font aFnt( mpVD->GetFont() );
+ TextAlign eAlg( aFnt.GetAlignment() );
+
+ sal_Int32 nTextWidth = static_cast<sal_Int32>( mpVD->GetTextWidth( rStr ) * mfScaleX );
+ sal_Int32 nTextHeight = static_cast<sal_Int32>( mpVD->GetTextHeight() * mfScaleY );
+
+ Point aPos( FRound(rPos.X() * mfScaleX + maOfs.X()), FRound(rPos.Y() * mfScaleY + maOfs.Y()) );
+ Size aSize( nTextWidth, nTextHeight );
+
+ if ( eAlg == ALIGN_BASELINE )
+ aPos.AdjustY( -(FRound(aFontMetric.GetAscent() * mfScaleY)) );
+ else if ( eAlg == ALIGN_BOTTOM )
+ aPos.AdjustY( -nTextHeight );
+
+ tools::Rectangle aTextRect( aPos, aSize );
+ SdrRectObj* pText = new SdrRectObj(
+ *mpModel,
+ SdrObjKind::Text,
+ aTextRect);
+
+ pText->SetMergedItem ( makeSdrTextUpperDistItem (0));
+ pText->SetMergedItem ( makeSdrTextLowerDistItem (0));
+ pText->SetMergedItem ( makeSdrTextRightDistItem (0));
+ pText->SetMergedItem ( makeSdrTextLeftDistItem (0));
+
+ if ( aFnt.GetAverageFontWidth() || ( rAct.GetType() == MetaActionType::STRETCHTEXT ) )
+ {
+ pText->ClearMergedItem( SDRATTR_TEXT_AUTOGROWWIDTH );
+ pText->SetMergedItem( makeSdrTextAutoGrowHeightItem( false ) );
+ // don't let the margins eat the space needed for the text
+ pText->SetMergedItem( SdrTextFitToSizeTypeItem(drawing::TextFitToSizeType_ALLLINES) );
+ }
+ else
+ {
+ pText->SetMergedItem( makeSdrTextAutoGrowWidthItem( true ) );
+ }
+
+ pText->SetLayer(mnLayer);
+ pText->NbcSetText( rStr );
+ SetAttributes( pText, true );
+ pText->SetSnapRect( aTextRect );
+
+ if (!aFnt.IsTransparent())
+ {
+ SfxItemSetFixed<XATTR_FILL_FIRST, XATTR_FILL_LAST> aAttr(*mpFillAttr->GetPool());
+ aAttr.Put(XFillStyleItem(drawing::FillStyle_SOLID));
+ aAttr.Put(XFillColorItem(OUString(), aFnt.GetFillColor()));
+ pText->SetMergedItemSet(aAttr);
+ }
+ Degree100 nAngle = to<Degree100>(aFnt.GetOrientation());
+ if ( nAngle )
+ pText->SdrAttrObj::NbcRotate(aPos,nAngle);
+ InsertObj( pText, false );
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaTextAction const & rAct)
+{
+ OUString aStr(rAct.GetText());
+ aStr = aStr.copy(rAct.GetIndex(), rAct.GetLen());
+ ImportText( rAct.GetPoint(), aStr, rAct );
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaTextArrayAction const & rAct)
+{
+ OUString aStr(rAct.GetText());
+ aStr = aStr.copy(rAct.GetIndex(), rAct.GetLen());
+ ImportText( rAct.GetPoint(), aStr, rAct );
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaStretchTextAction const & rAct)
+{
+ OUString aStr(rAct.GetText());
+ aStr = aStr.copy(rAct.GetIndex(), rAct.GetLen());
+ ImportText( rAct.GetPoint(), aStr, rAct );
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaBmpAction const & rAct)
+{
+ tools::Rectangle aRect(rAct.GetPoint(),rAct.GetBitmap().GetSizePixel());
+ aRect.AdjustRight( 1 ); aRect.AdjustBottom( 1 );
+ SdrGrafObj* pGraf = new SdrGrafObj(
+ *mpModel,
+ Graphic(BitmapEx(rAct.GetBitmap())),
+ aRect);
+
+ // This action is not creating line and fill, set directly, do not use SetAttributes(..)
+ pGraf->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE));
+ pGraf->SetMergedItem(XFillStyleItem(drawing::FillStyle_NONE));
+ InsertObj(pGraf);
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaBmpScaleAction const & rAct)
+{
+ tools::Rectangle aRect(rAct.GetPoint(),rAct.GetSize());
+ aRect.AdjustRight( 1 ); aRect.AdjustBottom( 1 );
+ SdrGrafObj* pGraf = new SdrGrafObj(
+ *mpModel,
+ Graphic(BitmapEx(rAct.GetBitmap())),
+ aRect);
+
+ // This action is not creating line and fill, set directly, do not use SetAttributes(..)
+ pGraf->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE));
+ pGraf->SetMergedItem(XFillStyleItem(drawing::FillStyle_NONE));
+ InsertObj(pGraf);
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaBmpExAction const & rAct)
+{
+ tools::Rectangle aRect(rAct.GetPoint(),rAct.GetBitmapEx().GetSizePixel());
+ aRect.AdjustRight( 1 ); aRect.AdjustBottom( 1 );
+ SdrGrafObj* pGraf = new SdrGrafObj(
+ *mpModel,
+ rAct.GetBitmapEx(),
+ aRect);
+
+ // This action is not creating line and fill, set directly, do not use SetAttributes(..)
+ pGraf->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE));
+ pGraf->SetMergedItem(XFillStyleItem(drawing::FillStyle_NONE));
+ InsertObj(pGraf);
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaBmpExScaleAction const & rAct)
+{
+ tools::Rectangle aRect(rAct.GetPoint(),rAct.GetSize());
+ aRect.AdjustRight( 1 ); aRect.AdjustBottom( 1 );
+ SdrGrafObj* pGraf = new SdrGrafObj(
+ *mpModel,
+ rAct.GetBitmapEx(),
+ aRect);
+
+ // This action is not creating line and fill, set directly, do not use SetAttributes(..)
+ pGraf->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE));
+ pGraf->SetMergedItem(XFillStyleItem(drawing::FillStyle_NONE));
+ InsertObj(pGraf);
+}
+
+
+void ImpSdrGDIMetaFileImport::DoAction( MetaHatchAction const & rAct )
+{
+ // #i73407# reformulation to use new B2DPolygon classes
+ basegfx::B2DPolyPolygon aSource(rAct.GetPolyPolygon().getB2DPolyPolygon());
+
+ if(!aSource.count())
+ return;
+
+ const basegfx::B2DHomMatrix aTransform(basegfx::utils::createScaleTranslateB2DHomMatrix(mfScaleX, mfScaleY, maOfs.X(), maOfs.Y()));
+ aSource.transform(aTransform);
+
+ if(mbLastObjWasPolyWithoutLine && CheckLastPolyLineAndFillMerge(aSource))
+ return;
+
+ const Hatch& rHatch = rAct.GetHatch();
+ SdrPathObj* pPath = new SdrPathObj(
+ *mpModel,
+ SdrObjKind::Polygon,
+ aSource);
+ // #i125211# Use the ranges from the SdrObject to create a new empty SfxItemSet
+ SfxItemSet aHatchAttr(mpModel->GetItemPool(), pPath->GetMergedItemSet().GetRanges());
+ css::drawing::HatchStyle eStyle;
+
+ switch(rHatch.GetStyle())
+ {
+ case HatchStyle::Triple :
+ {
+ eStyle = css::drawing::HatchStyle_TRIPLE;
+ break;
+ }
+
+ case HatchStyle::Double :
+ {
+ eStyle = css::drawing::HatchStyle_DOUBLE;
+ break;
+ }
+
+ default:
+ {
+ eStyle = css::drawing::HatchStyle_SINGLE;
+ break;
+ }
+ }
+
+ SetAttributes(pPath);
+ aHatchAttr.Put(XFillStyleItem(drawing::FillStyle_HATCH));
+ aHatchAttr.Put(XFillHatchItem(XHatch(rHatch.GetColor(), eStyle, rHatch.GetDistance(), rHatch.GetAngle())));
+ pPath->SetMergedItemSet(aHatchAttr);
+
+ InsertObj(pPath, false);
+}
+
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaLineColorAction& rAct)
+{
+ rAct.Execute(mpVD);
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaMapModeAction& rAct)
+{
+ MapScaling();
+ rAct.Execute(mpVD);
+ mbLastObjWasPolyWithoutLine = false;
+ mbLastObjWasLine = false;
+}
+
+void ImpSdrGDIMetaFileImport::MapScaling()
+{
+ const size_t nCount(maTmpList.size());
+ const MapMode& rMap = mpVD->GetMapMode();
+ Point aMapOrg( rMap.GetOrigin() );
+ bool bMov2(aMapOrg.X() != 0 || aMapOrg.Y() != 0);
+
+ if(bMov2)
+ {
+ for(size_t i = mnMapScalingOfs; i < nCount; i++)
+ {
+ SdrObject* pObj = maTmpList[i];
+
+ pObj->NbcMove(Size(aMapOrg.X(), aMapOrg.Y()));
+ }
+ }
+
+ mnMapScalingOfs = nCount;
+}
+
+
+void ImpSdrGDIMetaFileImport::DoAction( MetaCommentAction const & rAct, GDIMetaFile const & rMtf, sal_uLong& a) // GDIMetaFile* pMtf )
+{
+ bool aSkipComment = false;
+
+ if (a < rMtf.GetActionSize() && rAct.GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_BEGIN"))
+ {
+ // #i125211# Check if next action is a MetaGradientExAction
+ MetaGradientExAction* pAct = dynamic_cast< MetaGradientExAction* >(rMtf.GetAction(a + 1));
+
+ if( pAct && pAct->GetType() == MetaActionType::GRADIENTEX )
+ {
+ // #i73407# reformulation to use new B2DPolygon classes
+ basegfx::B2DPolyPolygon aSource(pAct->GetPolyPolygon().getB2DPolyPolygon());
+
+ if(aSource.count())
+ {
+ if(!mbLastObjWasPolyWithoutLine || !CheckLastPolyLineAndFillMerge(aSource))
+ {
+ const Gradient& rGrad = pAct->GetGradient();
+ SdrPathObj* pPath = new SdrPathObj(
+ *mpModel,
+ SdrObjKind::Polygon,
+ aSource);
+ // #i125211# Use the ranges from the SdrObject to create a new empty SfxItemSet
+ SfxItemSet aGradAttr(mpModel->GetItemPool(), pPath->GetMergedItemSet().GetRanges());
+ XGradient aXGradient;
+
+ aXGradient.SetGradientStyle(static_cast<css::awt::GradientStyle>(rGrad.GetStyle()));
+ aXGradient.SetStartColor(rGrad.GetStartColor());
+ aXGradient.SetEndColor(rGrad.GetEndColor());
+ aXGradient.SetAngle(rGrad.GetAngle());
+ aXGradient.SetBorder(rGrad.GetBorder());
+ aXGradient.SetXOffset(rGrad.GetOfsX());
+ aXGradient.SetYOffset(rGrad.GetOfsY());
+ aXGradient.SetStartIntens(rGrad.GetStartIntensity());
+ aXGradient.SetEndIntens(rGrad.GetEndIntensity());
+ aXGradient.SetSteps(rGrad.GetSteps());
+
+ // no need to use SetAttributes(..) here since line and fill style
+ // need to be set individually
+ // SetAttributes(pPath);
+
+ // switch line off; if there was one there will be a
+ // MetaActionType::POLYLINE following creating another object
+ aGradAttr.Put(XLineStyleItem(drawing::LineStyle_NONE));
+
+ // add detected gradient fillstyle
+ aGradAttr.Put(XFillStyleItem(drawing::FillStyle_GRADIENT));
+ aGradAttr.Put(XFillGradientItem(aXGradient));
+
+ pPath->SetMergedItemSet(aGradAttr);
+
+ InsertObj(pPath);
+ }
+ }
+
+ aSkipComment = true;
+ }
+ }
+
+ if(aSkipComment)
+ {
+ // #i125211# forward until closing MetaCommentAction
+ MetaAction* pSkipAct = rMtf.GetAction(++a);
+
+ while( pSkipAct
+ && ((pSkipAct->GetType() != MetaActionType::COMMENT )
+ || !(static_cast<MetaCommentAction*>(pSkipAct)->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_END"))))
+ {
+ pSkipAct = rMtf.GetAction(++a);
+ }
+ }
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaTextRectAction const & rAct)
+{
+ GDIMetaFile aTemp;
+
+ mpVD->AddTextRectActions(rAct.GetRect(), rAct.GetText(), rAct.GetStyle(), aTemp);
+ DoLoopActions(aTemp, nullptr, nullptr);
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaBmpScalePartAction const & rAct)
+{
+ tools::Rectangle aRect(rAct.GetDestPoint(), rAct.GetDestSize());
+ BitmapEx aBitmapEx(rAct.GetBitmap());
+
+ aRect.AdjustRight( 1 );
+ aRect.AdjustBottom( 1 );
+ aBitmapEx.Crop(tools::Rectangle(rAct.GetSrcPoint(), rAct.GetSrcSize()));
+ SdrGrafObj* pGraf = new SdrGrafObj(
+ *mpModel,
+ aBitmapEx,
+ aRect);
+
+ // This action is not creating line and fill, set directly, do not use SetAttributes(..)
+ pGraf->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE));
+ pGraf->SetMergedItem(XFillStyleItem(drawing::FillStyle_NONE));
+ InsertObj(pGraf);
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaBmpExScalePartAction const & rAct)
+{
+ tools::Rectangle aRect(rAct.GetDestPoint(),rAct.GetDestSize());
+ BitmapEx aBitmapEx(rAct.GetBitmapEx());
+
+ aRect.AdjustRight( 1 );
+ aRect.AdjustBottom( 1 );
+ aBitmapEx.Crop(tools::Rectangle(rAct.GetSrcPoint(), rAct.GetSrcSize()));
+ SdrGrafObj* pGraf = new SdrGrafObj(
+ *mpModel,
+ aBitmapEx,
+ aRect);
+
+ // This action is not creating line and fill, set directly, do not use SetAttributes(..)
+ pGraf->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE));
+ pGraf->SetMergedItem(XFillStyleItem(drawing::FillStyle_NONE));
+ InsertObj(pGraf);
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaMaskAction const & rAct)
+{
+ tools::Rectangle aRect(rAct.GetPoint(), rAct.GetBitmap().GetSizePixel());
+ BitmapEx aBitmapEx(rAct.GetBitmap(), rAct.GetColor());
+
+ aRect.AdjustRight( 1 ); aRect.AdjustBottom( 1 );
+ SdrGrafObj* pGraf = new SdrGrafObj(
+ *mpModel,
+ aBitmapEx,
+ aRect);
+
+ // This action is not creating line and fill, set directly, do not use SetAttributes(..)
+ pGraf->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE));
+ pGraf->SetMergedItem(XFillStyleItem(drawing::FillStyle_NONE));
+ InsertObj(pGraf);
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaMaskScaleAction const & rAct)
+{
+ tools::Rectangle aRect(rAct.GetPoint(), rAct.GetSize());
+ BitmapEx aBitmapEx(rAct.GetBitmap(), rAct.GetColor());
+
+ aRect.AdjustRight( 1 ); aRect.AdjustBottom( 1 );
+ SdrGrafObj* pGraf = new SdrGrafObj(
+ *mpModel,
+ aBitmapEx,
+ aRect);
+
+ // This action is not creating line and fill, set directly, do not use SetAttributes(..)
+ pGraf->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE));
+ pGraf->SetMergedItem(XFillStyleItem(drawing::FillStyle_NONE));
+ InsertObj(pGraf);
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaMaskScalePartAction const & rAct)
+{
+ tools::Rectangle aRect(rAct.GetDestPoint(), rAct.GetDestSize());
+ BitmapEx aBitmapEx(rAct.GetBitmap(), rAct.GetColor());
+
+ aRect.AdjustRight( 1 ); aRect.AdjustBottom( 1 );
+ aBitmapEx.Crop(tools::Rectangle(rAct.GetSrcPoint(), rAct.GetSrcSize()));
+ SdrGrafObj* pGraf = new SdrGrafObj(
+ *mpModel,
+ aBitmapEx,
+ aRect);
+
+ // This action is not creating line and fill, set directly, do not use SetAttributes(..)
+ pGraf->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE));
+ pGraf->SetMergedItem(XFillStyleItem(drawing::FillStyle_NONE));
+ InsertObj(pGraf);
+}
+
+namespace
+{
+ css::awt::GradientStyle getXGradientStyleFromGradientStyle(const GradientStyle& rGradientStyle)
+ {
+ css::awt::GradientStyle aXGradientStyle(css::awt::GradientStyle_LINEAR);
+
+ switch(rGradientStyle)
+ {
+ case GradientStyle::Linear: aXGradientStyle = css::awt::GradientStyle_LINEAR; break;
+ case GradientStyle::Axial: aXGradientStyle = css::awt::GradientStyle_AXIAL; break;
+ case GradientStyle::Radial: aXGradientStyle = css::awt::GradientStyle_RADIAL; break;
+ case GradientStyle::Elliptical: aXGradientStyle = css::awt::GradientStyle_ELLIPTICAL; break;
+ case GradientStyle::Square: aXGradientStyle = css::awt::GradientStyle_SQUARE; break;
+ case GradientStyle::Rect: aXGradientStyle = css::awt::GradientStyle_RECT; break;
+
+ // Needed due to GradientStyle::FORCE_EQUAL_SIZE; this again is needed
+ // to force the enum defines in VCL to a defined size for the compilers,
+ // so despite it is never used it cannot be removed (would break the
+ // API implementation probably).
+ case GradientStyle::FORCE_EQUAL_SIZE: break;
+ default:
+ break;
+ }
+
+ return aXGradientStyle;
+ }
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaGradientAction const & rAct)
+{
+ basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(rAct.GetRect());
+
+ if(aRange.isEmpty())
+ return;
+
+ const basegfx::B2DHomMatrix aTransform(basegfx::utils::createScaleTranslateB2DHomMatrix(mfScaleX, mfScaleY, maOfs.X(), maOfs.Y()));
+ aRange.transform(aTransform);
+ const Gradient& rGradient = rAct.GetGradient();
+ SdrRectObj* pRect = new SdrRectObj(
+ *mpModel,
+ tools::Rectangle(
+ floor(aRange.getMinX()),
+ floor(aRange.getMinY()),
+ ceil(aRange.getMaxX()),
+ ceil(aRange.getMaxY())));
+ // #i125211# Use the ranges from the SdrObject to create a new empty SfxItemSet
+ SfxItemSet aGradientAttr(mpModel->GetItemPool(), pRect->GetMergedItemSet().GetRanges());
+ const css::awt::GradientStyle aXGradientStyle(getXGradientStyleFromGradientStyle(rGradient.GetStyle()));
+ const XFillGradientItem aXFillGradientItem(
+ XGradient(
+ rGradient.GetStartColor(),
+ rGradient.GetEndColor(),
+ aXGradientStyle,
+ rGradient.GetAngle(),
+ rGradient.GetOfsX(),
+ rGradient.GetOfsY(),
+ rGradient.GetBorder(),
+ rGradient.GetStartIntensity(),
+ rGradient.GetEndIntensity(),
+ rGradient.GetSteps()));
+
+ SetAttributes(pRect);
+ aGradientAttr.Put(XFillStyleItem(drawing::FillStyle_GRADIENT)); // #i125211#
+ aGradientAttr.Put(aXFillGradientItem);
+ pRect->SetMergedItemSet(aGradientAttr);
+
+ InsertObj(pRect, false);
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaTransparentAction const & rAct)
+{
+ basegfx::B2DPolyPolygon aSource(rAct.GetPolyPolygon().getB2DPolyPolygon());
+
+ if(!aSource.count())
+ return;
+
+ const basegfx::B2DHomMatrix aTransform(basegfx::utils::createScaleTranslateB2DHomMatrix(mfScaleX, mfScaleY, maOfs.X(), maOfs.Y()));
+ aSource.transform(aTransform);
+ aSource.setClosed(true);
+
+ SdrPathObj* pPath = new SdrPathObj(
+ *mpModel,
+ SdrObjKind::Polygon,
+ aSource);
+ SetAttributes(pPath);
+ pPath->SetMergedItem(XFillTransparenceItem(rAct.GetTransparence()));
+ InsertObj(pPath, false);
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaGradientExAction const & rAct)
+{
+ basegfx::B2DPolyPolygon aSource(rAct.GetPolyPolygon().getB2DPolyPolygon());
+
+ if(!aSource.count())
+ return;
+
+ const basegfx::B2DHomMatrix aTransform(basegfx::utils::createScaleTranslateB2DHomMatrix(mfScaleX, mfScaleY, maOfs.X(), maOfs.Y()));
+ aSource.transform(aTransform);
+
+ if(mbLastObjWasPolyWithoutLine && CheckLastPolyLineAndFillMerge(aSource))
+ return;
+
+ const Gradient& rGradient = rAct.GetGradient();
+ SdrPathObj* pPath = new SdrPathObj(
+ *mpModel,
+ SdrObjKind::Polygon,
+ aSource);
+ // #i125211# Use the ranges from the SdrObject to create a new empty SfxItemSet
+ SfxItemSet aGradientAttr(mpModel->GetItemPool(), pPath->GetMergedItemSet().GetRanges());
+ const css::awt::GradientStyle aXGradientStyle(getXGradientStyleFromGradientStyle(rGradient.GetStyle()));
+ const XFillGradientItem aXFillGradientItem(
+ XGradient(
+ rGradient.GetStartColor(),
+ rGradient.GetEndColor(),
+ aXGradientStyle,
+ rGradient.GetAngle(),
+ rGradient.GetOfsX(),
+ rGradient.GetOfsY(),
+ rGradient.GetBorder(),
+ rGradient.GetStartIntensity(),
+ rGradient.GetEndIntensity(),
+ rGradient.GetSteps()));
+
+ SetAttributes(pPath);
+ aGradientAttr.Put(XFillStyleItem(drawing::FillStyle_GRADIENT)); // #i125211#
+ aGradientAttr.Put(aXFillGradientItem);
+ pPath->SetMergedItemSet(aGradientAttr);
+
+ InsertObj(pPath, false);
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaFloatTransparentAction const & rAct)
+{
+ const GDIMetaFile& rMtf = rAct.GetGDIMetaFile();
+
+ if(!rMtf.GetActionSize())
+ return;
+
+ const tools::Rectangle aRect(rAct.GetPoint(),rAct.GetSize());
+
+ // convert metafile sub-content to BitmapEx
+ BitmapEx aBitmapEx(
+ convertMetafileToBitmapEx(
+ rMtf,
+ vcl::unotools::b2DRectangleFromRectangle(aRect),
+ 125000));
+
+ // handle colors
+ const Gradient& rGradient = rAct.GetGradient();
+ basegfx::BColor aStart(rGradient.GetStartColor().getBColor());
+ basegfx::BColor aEnd(rGradient.GetEndColor().getBColor());
+
+ if(100 != rGradient.GetStartIntensity())
+ {
+ aStart *= static_cast<double>(rGradient.GetStartIntensity()) / 100.0;
+ }
+
+ if(100 != rGradient.GetEndIntensity())
+ {
+ aEnd *= static_cast<double>(rGradient.GetEndIntensity()) / 100.0;
+ }
+
+ const bool bEqualColors(aStart == aEnd);
+ const bool bNoSteps(1 == rGradient.GetSteps());
+ bool bCreateObject(true);
+ bool bHasNewMask(false);
+ AlphaMask aNewMask;
+ double fTransparence(0.0);
+ bool bFixedTransparence(false);
+
+ if(bEqualColors || bNoSteps)
+ {
+ // single transparence
+ const basegfx::BColor aMedium(basegfx::average(aStart, aEnd));
+ fTransparence = aMedium.luminance();
+
+ if(basegfx::fTools::lessOrEqual(fTransparence, 0.0))
+ {
+ // no transparence needed, all done
+ }
+ else if(basegfx::fTools::moreOrEqual(fTransparence, 1.0))
+ {
+ // all transparent, no object
+ bCreateObject = false;
+ }
+ else
+ {
+ // 0.0 < transparence < 1.0, apply fixed transparence
+ bFixedTransparence = true;
+ }
+ }
+ else
+ {
+ // gradient transparence
+ ScopedVclPtrInstance< VirtualDevice > pVDev;
+
+ pVDev->SetOutputSizePixel(aBitmapEx.GetBitmap().GetSizePixel());
+ pVDev->DrawGradient(tools::Rectangle(Point(0, 0), pVDev->GetOutputSizePixel()), rGradient);
+
+ aNewMask = AlphaMask(pVDev->GetBitmap(Point(0, 0), pVDev->GetOutputSizePixel()));
+ bHasNewMask = true;
+ }
+
+ if(!bCreateObject)
+ return;
+
+ if(bHasNewMask || bFixedTransparence)
+ {
+ if(!aBitmapEx.IsAlpha())
+ {
+ // no transparence yet, apply new one
+ if(bFixedTransparence)
+ {
+ sal_uInt8 aAlpha(basegfx::fround(fTransparence * 255.0));
+
+ aNewMask = AlphaMask(aBitmapEx.GetBitmap().GetSizePixel(), &aAlpha);
+ }
+
+ aBitmapEx = BitmapEx(aBitmapEx.GetBitmap(), aNewMask);
+ }
+ else
+ {
+ vcl::bitmap::DrawAlphaBitmapAndAlphaGradient(aBitmapEx, bFixedTransparence, fTransparence, aNewMask);
+ }
+ }
+
+ // create and add object
+ SdrGrafObj* pGraf = new SdrGrafObj(
+ *mpModel,
+ aBitmapEx,
+ aRect);
+
+ // for MetaFloatTransparentAction, do not use SetAttributes(...)
+ // since these metafile content is not used to draw line/fill
+ // dependent of these setting at the device content
+ pGraf->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE));
+ pGraf->SetMergedItem(XFillStyleItem(drawing::FillStyle_NONE));
+ InsertObj(pGraf);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdfmtf.hxx b/svx/source/svdraw/svdfmtf.hxx
new file mode 100644
index 000000000..0e788bcd5
--- /dev/null
+++ b/svx/source/svdraw/svdfmtf.hxx
@@ -0,0 +1,173 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SVX_SOURCE_SVDRAW_SVDFMTF_HXX
+#define INCLUDED_SVX_SOURCE_SVDRAW_SVDFMTF_HXX
+
+#include <sal/config.h>
+
+#include <memory>
+
+#include <svl/itemset.hxx>
+#include <tools/fract.hxx>
+#include <vcl/metaact.hxx>
+#include <vcl/virdev.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/xdash.hxx>
+
+// Forward Declarations
+
+
+class SfxItemSet;
+class SdrObjList;
+class SdrModel;
+class SdrPage;
+class SdrObject;
+class SvdProgressInfo;
+
+
+// Helper Class ImpSdrGDIMetaFileImport
+class ImpSdrGDIMetaFileImport final
+{
+ ::std::vector< SdrObject* > maTmpList;
+ ScopedVclPtr<VirtualDevice> mpVD;
+ tools::Rectangle maScaleRect;
+ size_t mnMapScalingOfs; // from here on, not edited with MapScaling
+ std::unique_ptr<SfxItemSet> mpLineAttr;
+ std::unique_ptr<SfxItemSet> mpFillAttr;
+ std::unique_ptr<SfxItemSet> mpTextAttr;
+ SdrModel* mpModel;
+ SdrLayerID mnLayer;
+ Color maOldLineColor;
+ sal_Int32 mnLineWidth;
+ basegfx::B2DLineJoin maLineJoin;
+ css::drawing::LineCap maLineCap;
+ XDash maDash;
+
+ bool mbMov;
+ bool mbSize;
+ Point maOfs;
+ double mfScaleX;
+ double mfScaleY;
+ Fraction maScaleX;
+ Fraction maScaleY;
+
+ bool mbFntDirty;
+
+ // to optimize (PenNULL,Brush,DrawPoly),(Pen,BrushNULL,DrawPoly) -> two-in-one
+ bool mbLastObjWasPolyWithoutLine;
+ bool mbNoLine;
+ bool mbNoFill;
+
+ // to optimize multiple lines into a Polyline
+ bool mbLastObjWasLine;
+
+ // clipregion
+ basegfx::B2DPolyPolygon maClip;
+
+ // check for clip and evtl. fill maClip
+ void checkClip();
+ bool isClip() const;
+
+ // actions
+ void DoAction(MetaLineAction const & rAct);
+ void DoAction(MetaRectAction const & rAct);
+ void DoAction(MetaRoundRectAction const & rAct);
+ void DoAction(MetaEllipseAction const & rAct);
+ void DoAction(MetaArcAction const & rAct);
+ void DoAction(MetaPieAction const & rAct);
+ void DoAction(MetaChordAction const & rAct);
+ void DoAction(MetaPolyLineAction const & rAct);
+ void DoAction(MetaPolygonAction const & rAct);
+ void DoAction(MetaPolyPolygonAction const & rAct);
+ void DoAction(MetaTextAction const & rAct);
+ void DoAction(MetaTextArrayAction const & rAct);
+ void DoAction(MetaStretchTextAction const & rAct);
+ void DoAction(MetaBmpAction const & rAct);
+ void DoAction(MetaBmpScaleAction const & rAct);
+ void DoAction(MetaBmpExAction const & rAct);
+ void DoAction(MetaBmpExScaleAction const & rAct);
+ void DoAction(MetaHatchAction const & rAct);
+ void DoAction(MetaLineColorAction & rAct);
+ void DoAction(MetaMapModeAction & rAct);
+ void DoAction(MetaFillColorAction & rAct) { rAct.Execute(mpVD); }
+ void DoAction(MetaTextColorAction & rAct) { rAct.Execute(mpVD); }
+ void DoAction(MetaTextFillColorAction & rAct) { rAct.Execute(mpVD); }
+ void DoAction(MetaFontAction & rAct) { rAct.Execute(mpVD); mbFntDirty = true; }
+ void DoAction(MetaTextAlignAction & rAct) { rAct.Execute(mpVD); mbFntDirty = true; }
+ void DoAction(MetaClipRegionAction & rAct) { rAct.Execute(mpVD); checkClip(); }
+ void DoAction(MetaRasterOpAction & rAct) { rAct.Execute(mpVD); }
+ void DoAction(MetaPushAction & rAct) { rAct.Execute(mpVD); checkClip(); }
+ void DoAction(MetaPopAction & rAct) { rAct.Execute(mpVD); mbFntDirty = true; checkClip(); }
+ void DoAction(MetaMoveClipRegionAction & rAct) { rAct.Execute(mpVD); checkClip(); }
+ void DoAction(MetaISectRectClipRegionAction& rAct) { rAct.Execute(mpVD); checkClip(); }
+ void DoAction(MetaISectRegionClipRegionAction& rAct) { rAct.Execute(mpVD); checkClip(); }
+
+ // #i125211# The MetaCommentAction needs to advance (if used), thus
+ // give current metafile and index which may be changed
+ void DoAction(MetaCommentAction const & rAct, GDIMetaFile const & rMtf, sal_uLong& a);
+
+ // missing actions added
+ void DoAction(MetaTextRectAction const & rAct);
+ void DoAction(MetaBmpScalePartAction const & rAct);
+ void DoAction(MetaBmpExScalePartAction const & rAct);
+ void DoAction(MetaMaskAction const & rAct);
+ void DoAction(MetaMaskScaleAction const & rAct);
+ void DoAction(MetaMaskScalePartAction const & rAct);
+ void DoAction(MetaGradientAction const & rAct);
+ void DoAction(MetaTransparentAction const & rAct);
+ void DoAction(MetaRefPointAction& rAct) { rAct.Execute(mpVD); }
+ void DoAction(MetaTextLineColorAction& rAct) { rAct.Execute(mpVD); mbFntDirty = true; }
+ void DoAction(MetaFloatTransparentAction const & rAct);
+ void DoAction(MetaGradientExAction const & rAct);
+ void DoAction(MetaLayoutModeAction& rAct) { rAct.Execute(mpVD); mbFntDirty = true; }
+ void DoAction(MetaTextLanguageAction& rAct) { rAct.Execute(mpVD); mbFntDirty = true; }
+ void DoAction(MetaOverlineColorAction& rAct) { rAct.Execute(mpVD); mbFntDirty = true; }
+
+ void ImportText(const Point& rPos, const OUString& rStr, const MetaAction& rAct);
+ void SetAttributes(SdrObject* pObj, bool bForceTextAttr = false);
+ void InsertObj(SdrObject* pObj, bool bScale = true);
+ void MapScaling();
+
+ // #i73407# reformulation to use new B2DPolygon classes
+ bool CheckLastLineMerge(const basegfx::B2DPolygon& rSrcPoly);
+ bool CheckLastPolyLineAndFillMerge(const basegfx::B2DPolyPolygon& rPolyPolygon);
+
+ void DoLoopActions(GDIMetaFile const & rMtf, SvdProgressInfo* pProgrInfo, sal_uInt32* pActionsToReport);
+
+ // Copy assignment is forbidden and not implemented.
+ ImpSdrGDIMetaFileImport (const ImpSdrGDIMetaFileImport &) = delete;
+ ImpSdrGDIMetaFileImport & operator= (const ImpSdrGDIMetaFileImport &) = delete;
+
+public:
+ ImpSdrGDIMetaFileImport(
+ SdrModel& rModel,
+ SdrLayerID nLay,
+ const tools::Rectangle& rRect);
+
+ size_t DoImport(
+ const GDIMetaFile& rMtf,
+ SdrObjList& rDestList,
+ size_t nInsPos,
+ SvdProgressInfo* pProgrInfo = nullptr);
+};
+
+#endif // INCLUDED_SVX_SOURCE_SVDRAW_SVDFMTF_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdglev.cxx b/svx/source/svdraw/svdglev.cxx
new file mode 100644
index 000000000..67c64eb1a
--- /dev/null
+++ b/svx/source/svdraw/svdglev.cxx
@@ -0,0 +1,400 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <svx/svdglev.hxx>
+#include <math.h>
+
+#include <svx/svdundo.hxx>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include <svx/svdglue.hxx>
+#include <svx/svdtrans.hxx>
+#include <svx/svdobj.hxx>
+
+SdrGlueEditView::SdrGlueEditView(
+ SdrModel& rSdrModel,
+ OutputDevice* pOut)
+: SdrPolyEditView(rSdrModel, pOut)
+{
+}
+
+SdrGlueEditView::~SdrGlueEditView()
+{
+}
+
+void SdrGlueEditView::ImpDoMarkedGluePoints(PGlueDoFunc pDoFunc, bool bConst, const void* p1, const void* p2, const void* p3, const void* p4)
+{
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nm=0; nm<nMarkCount; ++nm) {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ const SdrUShortCont& rPts = pM->GetMarkedGluePoints();
+ if (!rPts.empty())
+ {
+ SdrGluePointList* pGPL=nullptr;
+ if (bConst) {
+ const SdrGluePointList* pConstGPL=pObj->GetGluePointList();
+ pGPL=const_cast<SdrGluePointList*>(pConstGPL);
+ } else {
+ pGPL=pObj->ForceGluePointList();
+ }
+ if (pGPL!=nullptr)
+ {
+ if(!bConst && IsUndoEnabled() )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pObj));
+
+ for(sal_uInt16 nPtId : rPts)
+ {
+ sal_uInt16 nGlueIdx=pGPL->FindGluePoint(nPtId);
+ if (nGlueIdx!=SDRGLUEPOINT_NOTFOUND)
+ {
+ SdrGluePoint& rGP=(*pGPL)[nGlueIdx];
+ (*pDoFunc)(rGP,pObj,p1,p2,p3,p4);
+ }
+ }
+ if (!bConst)
+ {
+ pObj->SetChanged();
+ pObj->BroadcastObjectChange();
+ }
+ }
+ }
+ }
+ if (!bConst && nMarkCount!=0) mpModel->SetChanged();
+}
+
+
+static void ImpGetEscDir(SdrGluePoint & rGP, const SdrObject* /*pObj*/, const void* pbFirst, const void* pnThisEsc, const void* pnRet, const void*)
+{
+ TriState& nRet=*const_cast<TriState *>(static_cast<TriState const *>(pnRet));
+ if (nRet!=TRISTATE_INDET) {
+ SdrEscapeDirection nEsc = rGP.GetEscDir();
+ bool bOn = bool(nEsc & *static_cast<SdrEscapeDirection const *>(pnThisEsc));
+ bool& bFirst=*const_cast<bool *>(static_cast<bool const *>(pbFirst));
+ if (bFirst) {
+ nRet = bOn ? TRISTATE_TRUE : TRISTATE_FALSE;
+ bFirst = false;
+ }
+ else if (nRet != (bOn ? TRISTATE_TRUE : TRISTATE_FALSE)) nRet=TRISTATE_INDET;
+ }
+}
+
+TriState SdrGlueEditView::IsMarkedGluePointsEscDir(SdrEscapeDirection nThisEsc) const
+{
+ ForceUndirtyMrkPnt();
+ bool bFirst=true;
+ TriState nRet=TRISTATE_FALSE;
+ const_cast<SdrGlueEditView*>(this)->ImpDoMarkedGluePoints(ImpGetEscDir,true,&bFirst,&nThisEsc,&nRet);
+ return nRet;
+}
+
+static void ImpSetEscDir(SdrGluePoint& rGP, const SdrObject* /*pObj*/, const void* pnThisEsc, const void* pbOn, const void*, const void*)
+{
+ SdrEscapeDirection nEsc=rGP.GetEscDir();
+ if (*static_cast<bool const *>(pbOn))
+ nEsc |= *static_cast<SdrEscapeDirection const *>(pnThisEsc);
+ else
+ nEsc &= ~*static_cast<SdrEscapeDirection const *>(pnThisEsc);
+ rGP.SetEscDir(nEsc);
+}
+
+void SdrGlueEditView::SetMarkedGluePointsEscDir(SdrEscapeDirection nThisEsc, bool bOn)
+{
+ ForceUndirtyMrkPnt();
+ BegUndo(SvxResId(STR_EditSetGlueEscDir),GetDescriptionOfMarkedGluePoints());
+ ImpDoMarkedGluePoints(ImpSetEscDir,false,&nThisEsc,&bOn);
+ EndUndo();
+}
+
+
+static void ImpGetPercent(SdrGluePoint & rGP, const SdrObject* /*pObj*/, const void* pbFirst, const void* pnRet, const void*, const void*)
+{
+ TriState& nRet=*const_cast<TriState *>(static_cast<TriState const *>(pnRet));
+ if (nRet!=TRISTATE_INDET) {
+ bool bOn=rGP.IsPercent();
+ bool& bFirst=*const_cast<bool *>(static_cast<bool const *>(pbFirst));
+ if (bFirst) {
+ nRet = bOn ? TRISTATE_TRUE : TRISTATE_FALSE;
+ bFirst = false;
+ }
+ else if ((nRet!=TRISTATE_FALSE)!=bOn)
+ nRet=TRISTATE_INDET;
+ }
+}
+
+TriState SdrGlueEditView::IsMarkedGluePointsPercent() const
+{
+ ForceUndirtyMrkPnt();
+ bool bFirst=true;
+ TriState nRet = TRISTATE_TRUE;
+ const_cast<SdrGlueEditView*>(this)->ImpDoMarkedGluePoints(ImpGetPercent,true,&bFirst,&nRet);
+ return nRet;
+}
+
+static void ImpSetPercent(SdrGluePoint& rGP, const SdrObject* pObj, const void* pbOn, const void*, const void*, const void*)
+{
+ Point aPos(rGP.GetAbsolutePos(*pObj));
+ rGP.SetPercent(*static_cast<bool const *>(pbOn));
+ rGP.SetAbsolutePos(aPos,*pObj);
+}
+
+void SdrGlueEditView::SetMarkedGluePointsPercent(bool bOn)
+{
+ ForceUndirtyMrkPnt();
+ BegUndo(SvxResId(STR_EditSetGluePercent),GetDescriptionOfMarkedGluePoints());
+ ImpDoMarkedGluePoints(ImpSetPercent,false,&bOn);
+ EndUndo();
+}
+
+
+static void ImpGetAlign(SdrGluePoint & rGP, const SdrObject* /*pObj*/, const void* pbFirst, const void* pbDontCare, const void* pbVert, const void* pnRet)
+{
+ SdrAlign& nRet=*const_cast<SdrAlign *>(static_cast<SdrAlign const *>(pnRet));
+ bool& bDontCare=*const_cast<bool *>(static_cast<bool const *>(pbDontCare));
+ bool bVert=*static_cast<bool const *>(pbVert);
+ if (bDontCare)
+ return;
+
+ SdrAlign nAlg=SdrAlign::NONE;
+ if (bVert) {
+ nAlg=rGP.GetVertAlign();
+ } else {
+ nAlg=rGP.GetHorzAlign();
+ }
+ bool& bFirst=*const_cast<bool *>(static_cast<bool const *>(pbFirst));
+ if (bFirst) { nRet=nAlg; bFirst=false; }
+ else if (nRet!=nAlg) {
+ if (bVert) {
+ nRet=SdrAlign::VERT_DONTCARE;
+ } else {
+ nRet=SdrAlign::HORZ_DONTCARE;
+ }
+ bDontCare=true;
+ }
+}
+
+SdrAlign SdrGlueEditView::GetMarkedGluePointsAlign(bool bVert) const
+{
+ ForceUndirtyMrkPnt();
+ bool bFirst=true;
+ bool bDontCare=false;
+ SdrAlign nRet=SdrAlign::NONE;
+ const_cast<SdrGlueEditView*>(this)->ImpDoMarkedGluePoints(ImpGetAlign,true,&bFirst,&bDontCare,&bVert,&nRet);
+ return nRet;
+}
+
+static void ImpSetAlign(SdrGluePoint& rGP, const SdrObject* pObj, const void* pbVert, const void* pnAlign, const void*, const void*)
+{
+ Point aPos(rGP.GetAbsolutePos(*pObj));
+ if (*static_cast<bool const *>(pbVert)) { // bVert?
+ rGP.SetVertAlign(*static_cast<SdrAlign const *>(pnAlign));
+ } else {
+ rGP.SetHorzAlign(*static_cast<SdrAlign const *>(pnAlign));
+ }
+ rGP.SetAbsolutePos(aPos,*pObj);
+}
+
+void SdrGlueEditView::SetMarkedGluePointsAlign(bool bVert, SdrAlign nAlign)
+{
+ ForceUndirtyMrkPnt();
+ BegUndo(SvxResId(STR_EditSetGlueAlign),GetDescriptionOfMarkedGluePoints());
+ ImpDoMarkedGluePoints(ImpSetAlign,false,&bVert,&nAlign);
+ EndUndo();
+}
+
+void SdrGlueEditView::DeleteMarkedGluePoints()
+{
+ BrkAction();
+ ForceUndirtyMrkPnt();
+ const bool bUndo = IsUndoEnabled();
+ if( bUndo )
+ BegUndo(SvxResId(STR_EditDelete),GetDescriptionOfMarkedGluePoints(),SdrRepeatFunc::Delete);
+
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nm=0; nm<nMarkCount; ++nm)
+ {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ const SdrUShortCont& rPts = pM->GetMarkedGluePoints();
+ if (!rPts.empty())
+ {
+ SdrGluePointList* pGPL=pObj->ForceGluePointList();
+ if (pGPL!=nullptr)
+ {
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pObj));
+
+ for(sal_uInt16 nPtId : rPts)
+ {
+ sal_uInt16 nGlueIdx=pGPL->FindGluePoint(nPtId);
+ if (nGlueIdx!=SDRGLUEPOINT_NOTFOUND)
+ {
+ pGPL->Delete(nGlueIdx);
+ }
+ }
+ pObj->SetChanged();
+ pObj->BroadcastObjectChange();
+ }
+ }
+ }
+ if( bUndo )
+ EndUndo();
+ UnmarkAllGluePoints();
+ if (nMarkCount!=0)
+ mpModel->SetChanged();
+}
+
+
+void SdrGlueEditView::ImpCopyMarkedGluePoints()
+{
+ const bool bUndo = IsUndoEnabled();
+
+ if( bUndo )
+ BegUndo();
+
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nm=0; nm<nMarkCount; ++nm)
+ {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ SdrUShortCont& rPts = pM->GetMarkedGluePoints();
+ SdrGluePointList* pGPL=pObj->ForceGluePointList();
+ if (!rPts.empty() && pGPL!=nullptr)
+ {
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pObj));
+
+ SdrUShortCont aIdsToErase;
+ SdrUShortCont aIdsToInsert;
+ for(sal_uInt16 nPtId : rPts)
+ {
+ sal_uInt16 nGlueIdx=pGPL->FindGluePoint(nPtId);
+ if (nGlueIdx!=SDRGLUEPOINT_NOTFOUND)
+ {
+ SdrGluePoint aNewGP((*pGPL)[nGlueIdx]); // clone GluePoint
+ sal_uInt16 nNewIdx=pGPL->Insert(aNewGP); // and insert it
+ sal_uInt16 nNewId=(*pGPL)[nNewIdx].GetId(); // retrieve ID of new GluePoints
+ aIdsToErase.insert(nPtId); // select it (instead of the old one)
+ aIdsToInsert.insert(nNewId);
+ }
+ }
+ for(const auto& rId : aIdsToErase)
+ rPts.erase(rId);
+ rPts.insert(aIdsToInsert);
+ }
+ }
+ if( bUndo )
+ EndUndo();
+
+ if (nMarkCount!=0)
+ mpModel->SetChanged();
+}
+
+
+void SdrGlueEditView::ImpTransformMarkedGluePoints(PGlueTrFunc pTrFunc, const void* p1, const void* p2, const void* p3, const void* p4)
+{
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nm=0; nm<nMarkCount; ++nm) {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ const SdrUShortCont& rPts = pM->GetMarkedGluePoints();
+ if (!rPts.empty()) {
+ SdrGluePointList* pGPL=pObj->ForceGluePointList();
+ if (pGPL!=nullptr)
+ {
+ if( IsUndoEnabled() )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pObj));
+
+ for(sal_uInt16 nPtId : rPts)
+ {
+ sal_uInt16 nGlueIdx=pGPL->FindGluePoint(nPtId);
+ if (nGlueIdx!=SDRGLUEPOINT_NOTFOUND) {
+ SdrGluePoint& rGP=(*pGPL)[nGlueIdx];
+ Point aPos(rGP.GetAbsolutePos(*pObj));
+ (*pTrFunc)(aPos,p1,p2,p3,p4);
+ rGP.SetAbsolutePos(aPos,*pObj);
+ }
+ }
+ pObj->SetChanged();
+ pObj->BroadcastObjectChange();
+ }
+ }
+ }
+ if (nMarkCount!=0) mpModel->SetChanged();
+}
+
+
+static void ImpMove(Point& rPt, const void* p1, const void* /*p2*/, const void* /*p3*/, const void* /*p4*/)
+{
+ rPt.AdjustX(static_cast<const Size*>(p1)->Width() );
+ rPt.AdjustY(static_cast<const Size*>(p1)->Height() );
+}
+
+void SdrGlueEditView::MoveMarkedGluePoints(const Size& rSiz, bool bCopy)
+{
+ ForceUndirtyMrkPnt();
+ OUString aStr(SvxResId(STR_EditMove));
+ if (bCopy) aStr += SvxResId(STR_EditWithCopy);
+ BegUndo(aStr,GetDescriptionOfMarkedGluePoints(),SdrRepeatFunc::Move);
+ if (bCopy) ImpCopyMarkedGluePoints();
+ ImpTransformMarkedGluePoints(ImpMove,&rSiz);
+ EndUndo();
+ AdjustMarkHdl();
+}
+
+
+static void ImpResize(Point& rPt, const void* p1, const void* p2, const void* p3, const void* /*p4*/)
+{
+ ResizePoint(rPt,*static_cast<const Point*>(p1),*static_cast<const Fraction*>(p2),*static_cast<const Fraction*>(p3));
+}
+
+void SdrGlueEditView::ResizeMarkedGluePoints(const Point& rRef, const Fraction& xFact, const Fraction& yFact, bool bCopy)
+{
+ ForceUndirtyMrkPnt();
+ OUString aStr(SvxResId(STR_EditResize));
+ if (bCopy) aStr+=SvxResId(STR_EditWithCopy);
+ BegUndo(aStr,GetDescriptionOfMarkedGluePoints(),SdrRepeatFunc::Resize);
+ if (bCopy) ImpCopyMarkedGluePoints();
+ ImpTransformMarkedGluePoints(ImpResize,&rRef,&xFact,&yFact);
+ EndUndo();
+ AdjustMarkHdl();
+}
+
+
+static void ImpRotate(Point& rPt, const void* p1, const void* /*p2*/, const void* p3, const void* p4)
+{
+ RotatePoint(rPt,*static_cast<const Point*>(p1),*static_cast<const double*>(p3),*static_cast<const double*>(p4));
+}
+
+void SdrGlueEditView::RotateMarkedGluePoints(const Point& rRef, Degree100 nAngle, bool bCopy)
+{
+ ForceUndirtyMrkPnt();
+ OUString aStr(SvxResId(STR_EditRotate));
+ if (bCopy) aStr+=SvxResId(STR_EditWithCopy);
+ BegUndo(aStr,GetDescriptionOfMarkedGluePoints(),SdrRepeatFunc::Rotate);
+ if (bCopy) ImpCopyMarkedGluePoints();
+ double nSin = sin(toRadians(nAngle));
+ double nCos = cos(toRadians(nAngle));
+ ImpTransformMarkedGluePoints(ImpRotate,&rRef,&nAngle,&nSin,&nCos);
+ EndUndo();
+ AdjustMarkHdl();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdglue.cxx b/svx/source/svdraw/svdglue.cxx
new file mode 100644
index 000000000..9808372f8
--- /dev/null
+++ b/svx/source/svdraw/svdglue.cxx
@@ -0,0 +1,394 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <tools/debug.hxx>
+#include <vcl/window.hxx>
+
+#include <svx/svdglue.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdtrans.hxx>
+#include <comphelper/lok.hxx>
+
+const Size aGlueHalfSize(4,4);
+
+void SdrGluePoint::SetReallyAbsolute(bool bOn, const SdrObject& rObj)
+{
+ if ( bReallyAbsolute == bOn )
+ return;
+
+ if ( bOn )
+ {
+ aPos=GetAbsolutePos(rObj);
+ bReallyAbsolute=bOn;
+ }
+ else
+ {
+ bReallyAbsolute=bOn;
+ Point aPt(aPos);
+ SetAbsolutePos(aPt,rObj);
+ }
+}
+
+Point SdrGluePoint::GetAbsolutePos(const SdrObject& rObj) const
+{
+ if (bReallyAbsolute) return aPos;
+ tools::Rectangle aSnap(rObj.GetSnapRect());
+ tools::Rectangle aBound(rObj.GetSnapRect());
+ Point aPt(aPos);
+
+ Point aOfs(aSnap.Center());
+ switch (GetHorzAlign()) {
+ case SdrAlign::HORZ_LEFT : aOfs.setX(aSnap.Left() ); break;
+ case SdrAlign::HORZ_RIGHT : aOfs.setX(aSnap.Right() ); break;
+ default: break;
+ }
+ switch (GetVertAlign()) {
+ case SdrAlign::VERT_TOP : aOfs.setY(aSnap.Top() ); break;
+ case SdrAlign::VERT_BOTTOM: aOfs.setY(aSnap.Bottom() ); break;
+ default: break;
+ }
+ if (!bNoPercent) {
+ tools::Long nXMul=aSnap.Right()-aSnap.Left();
+ tools::Long nYMul=aSnap.Bottom()-aSnap.Top();
+ tools::Long nXDiv=10000;
+ tools::Long nYDiv=10000;
+ if (nXMul!=nXDiv) {
+ aPt.setX( aPt.X() * nXMul );
+ aPt.setX( aPt.X() / nXDiv );
+ }
+ if (nYMul!=nYDiv) {
+ aPt.setY( aPt.Y() * nYMul );
+ aPt.setY( aPt.Y() / nYDiv );
+ }
+ }
+ aPt+=aOfs;
+ // Now limit to the BoundRect of the object
+ if (aPt.X()<aBound.Left ()) aPt.setX(aBound.Left () );
+ if (aPt.X()>aBound.Right ()) aPt.setX(aBound.Right () );
+ if (aPt.Y()<aBound.Top ()) aPt.setY(aBound.Top () );
+ if (aPt.Y()>aBound.Bottom()) aPt.setY(aBound.Bottom() );
+ return aPt;
+}
+
+void SdrGluePoint::SetAbsolutePos(const Point& rNewPos, const SdrObject& rObj)
+{
+ if (bReallyAbsolute) {
+ aPos=rNewPos;
+ return;
+ }
+ tools::Rectangle aSnap(rObj.GetSnapRect());
+ Point aPt(rNewPos);
+
+ Point aOfs(aSnap.Center());
+ switch (GetHorzAlign()) {
+ case SdrAlign::HORZ_LEFT : aOfs.setX(aSnap.Left() ); break;
+ case SdrAlign::HORZ_RIGHT : aOfs.setX(aSnap.Right() ); break;
+ default: break;
+ }
+ switch (GetVertAlign()) {
+ case SdrAlign::VERT_TOP : aOfs.setY(aSnap.Top() ); break;
+ case SdrAlign::VERT_BOTTOM: aOfs.setY(aSnap.Bottom() ); break;
+ default: break;
+ }
+ aPt-=aOfs;
+ if (!bNoPercent) {
+ tools::Long nXMul=aSnap.Right()-aSnap.Left();
+ tools::Long nYMul=aSnap.Bottom()-aSnap.Top();
+ if (nXMul==0) nXMul=1;
+ if (nYMul==0) nYMul=1;
+ tools::Long nXDiv=10000;
+ tools::Long nYDiv=10000;
+ if (nXMul!=nXDiv) {
+ aPt.setX( aPt.X() * nXDiv );
+ aPt.setX( aPt.X() / nXMul );
+ }
+ if (nYMul!=nYDiv) {
+ aPt.setY( aPt.Y() * nYDiv );
+ aPt.setY( aPt.Y() / nYMul );
+ }
+ }
+ aPos=aPt;
+}
+
+Degree100 SdrGluePoint::GetAlignAngle() const
+{
+ if (nAlign == (SdrAlign::HORZ_CENTER|SdrAlign::VERT_CENTER))
+ return 0_deg100; // Invalid!
+ else if (nAlign == (SdrAlign::HORZ_RIGHT |SdrAlign::VERT_CENTER))
+ return 0_deg100;
+ else if (nAlign == (SdrAlign::HORZ_RIGHT |SdrAlign::VERT_TOP))
+ return 4500_deg100;
+ else if (nAlign == (SdrAlign::HORZ_CENTER|SdrAlign::VERT_TOP))
+ return 9000_deg100;
+ else if (nAlign == (SdrAlign::HORZ_LEFT |SdrAlign::VERT_TOP))
+ return 13500_deg100;
+ else if (nAlign == (SdrAlign::HORZ_LEFT |SdrAlign::VERT_CENTER))
+ return 18000_deg100;
+ else if (nAlign == (SdrAlign::HORZ_LEFT |SdrAlign::VERT_BOTTOM))
+ return 22500_deg100;
+ else if (nAlign == (SdrAlign::HORZ_CENTER|SdrAlign::VERT_BOTTOM))
+ return 27000_deg100;
+ else if (nAlign == (SdrAlign::HORZ_RIGHT |SdrAlign::VERT_BOTTOM))
+ return 31500_deg100;
+ return 0_deg100;
+}
+
+void SdrGluePoint::SetAlignAngle(Degree100 nAngle)
+{
+ nAngle=NormAngle36000(nAngle);
+ if (nAngle>=33750_deg100 || nAngle<2250_deg100) nAlign=SdrAlign::HORZ_RIGHT |SdrAlign::VERT_CENTER;
+ else if (nAngle< 6750_deg100) nAlign=SdrAlign::HORZ_RIGHT |SdrAlign::VERT_TOP ;
+ else if (nAngle<11250_deg100) nAlign=SdrAlign::HORZ_CENTER|SdrAlign::VERT_TOP ;
+ else if (nAngle<15750_deg100) nAlign=SdrAlign::HORZ_LEFT |SdrAlign::VERT_TOP ;
+ else if (nAngle<20250_deg100) nAlign=SdrAlign::HORZ_LEFT |SdrAlign::VERT_CENTER;
+ else if (nAngle<24750_deg100) nAlign=SdrAlign::HORZ_LEFT |SdrAlign::VERT_BOTTOM;
+ else if (nAngle<29250_deg100) nAlign=SdrAlign::HORZ_CENTER|SdrAlign::VERT_BOTTOM;
+ else if (nAngle<33750_deg100) nAlign=SdrAlign::HORZ_RIGHT |SdrAlign::VERT_BOTTOM;
+}
+
+Degree100 SdrGluePoint::EscDirToAngle(SdrEscapeDirection nEsc)
+{
+ switch (nEsc) {
+ case SdrEscapeDirection::RIGHT : return 0_deg100;
+ case SdrEscapeDirection::TOP : return 9000_deg100;
+ case SdrEscapeDirection::LEFT : return 18000_deg100;
+ case SdrEscapeDirection::BOTTOM: return 27000_deg100;
+ default: break;
+ } // switch
+ return 0_deg100;
+}
+
+SdrEscapeDirection SdrGluePoint::EscAngleToDir(Degree100 nAngle)
+{
+ nAngle=NormAngle36000(nAngle);
+ if (nAngle>=31500_deg100 || nAngle<4500_deg100)
+ return SdrEscapeDirection::RIGHT;
+ if (nAngle<13500_deg100)
+ return SdrEscapeDirection::TOP;
+ if (nAngle<22500_deg100)
+ return SdrEscapeDirection::LEFT;
+ /* (nAngle<31500)*/
+ return SdrEscapeDirection::BOTTOM;
+}
+
+void SdrGluePoint::Rotate(const Point& rRef, Degree100 nAngle, double sn, double cs, const SdrObject* pObj)
+{
+ Point aPt(pObj!=nullptr ? GetAbsolutePos(*pObj) : GetPos());
+ RotatePoint(aPt,rRef,sn,cs);
+ // rotate reference edge
+ if(nAlign != (SdrAlign::HORZ_CENTER|SdrAlign::VERT_CENTER))
+ {
+ SetAlignAngle(GetAlignAngle()+nAngle);
+ }
+ // rotate exit directions
+ SdrEscapeDirection nEscDir0=nEscDir;
+ SdrEscapeDirection nEscDir1=SdrEscapeDirection::SMART;
+ if (nEscDir0&SdrEscapeDirection::LEFT ) nEscDir1 |= EscAngleToDir(EscDirToAngle(SdrEscapeDirection::LEFT )+nAngle);
+ if (nEscDir0&SdrEscapeDirection::TOP ) nEscDir1 |= EscAngleToDir(EscDirToAngle(SdrEscapeDirection::TOP )+nAngle);
+ if (nEscDir0&SdrEscapeDirection::RIGHT ) nEscDir1 |= EscAngleToDir(EscDirToAngle(SdrEscapeDirection::RIGHT )+nAngle);
+ if (nEscDir0&SdrEscapeDirection::BOTTOM) nEscDir1 |= EscAngleToDir(EscDirToAngle(SdrEscapeDirection::BOTTOM)+nAngle);
+ nEscDir=nEscDir1;
+ if (pObj!=nullptr) SetAbsolutePos(aPt,*pObj); else SetPos(aPt);
+}
+
+void SdrGluePoint::Mirror(const Point& rRef1, const Point& rRef2, Degree100 nAngle, const SdrObject* pObj)
+{
+ Point aPt(pObj!=nullptr ? GetAbsolutePos(*pObj) : GetPos());
+ MirrorPoint(aPt,rRef1,rRef2);
+ // mirror reference edge
+ if(nAlign != (SdrAlign::HORZ_CENTER|SdrAlign::VERT_CENTER))
+ {
+ Degree100 nAW=GetAlignAngle();
+ nAW+=2_deg100*(nAngle-nAW);
+ SetAlignAngle(nAW);
+ }
+ // mirror exit directions
+ SdrEscapeDirection nEscDir0=nEscDir;
+ SdrEscapeDirection nEscDir1=SdrEscapeDirection::SMART;
+ if (nEscDir0&SdrEscapeDirection::LEFT) {
+ Degree100 nEW=EscDirToAngle(SdrEscapeDirection::LEFT);
+ nEW+=2_deg100*(nAngle-nEW);
+ nEscDir1|=EscAngleToDir(nEW);
+ }
+ if (nEscDir0&SdrEscapeDirection::TOP) {
+ Degree100 nEW=EscDirToAngle(SdrEscapeDirection::TOP);
+ nEW+=2_deg100*(nAngle-nEW);
+ nEscDir1|=EscAngleToDir(nEW);
+ }
+ if (nEscDir0&SdrEscapeDirection::RIGHT) {
+ Degree100 nEW=EscDirToAngle(SdrEscapeDirection::RIGHT);
+ nEW+=2_deg100*(nAngle-nEW);
+ nEscDir1|=EscAngleToDir(nEW);
+ }
+ if (nEscDir0&SdrEscapeDirection::BOTTOM) {
+ Degree100 nEW=EscDirToAngle(SdrEscapeDirection::BOTTOM);
+ nEW+=2_deg100*(nAngle-nEW);
+ nEscDir1|=EscAngleToDir(nEW);
+ }
+ nEscDir=nEscDir1;
+ if (pObj!=nullptr) SetAbsolutePos(aPt,*pObj); else SetPos(aPt);
+}
+
+void SdrGluePoint::Shear(const Point& rRef, double tn, bool bVShear, const SdrObject* pObj)
+{
+ Point aPt(pObj!=nullptr ? GetAbsolutePos(*pObj) : GetPos());
+ ShearPoint(aPt,rRef,tn,bVShear);
+ if (pObj!=nullptr) SetAbsolutePos(aPt,*pObj); else SetPos(aPt);
+}
+
+void SdrGluePoint::Invalidate(vcl::Window& rWin, const SdrObject* pObj) const
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ return;
+ bool bMapMode=rWin.IsMapModeEnabled();
+ Point aPt(pObj!=nullptr ? GetAbsolutePos(*pObj) : GetPos());
+ aPt=rWin.LogicToPixel(aPt);
+ rWin.EnableMapMode(false);
+
+ Size aSiz( aGlueHalfSize );
+ tools::Rectangle aRect(aPt.X()-aSiz.Width(),aPt.Y()-aSiz.Height(),
+ aPt.X()+aSiz.Width(),aPt.Y()+aSiz.Height());
+
+ // do not erase background, that causes flicker (!)
+ rWin.Invalidate(aRect, InvalidateFlags::NoErase);
+
+ rWin.EnableMapMode(bMapMode);
+}
+
+bool SdrGluePoint::IsHit(const Point& rPnt, const OutputDevice& rOut, const SdrObject* pObj) const
+{
+ Point aPt(pObj!=nullptr ? GetAbsolutePos(*pObj) : GetPos());
+ Size aSiz=rOut.PixelToLogic(aGlueHalfSize);
+ tools::Rectangle aRect(aPt.X()-aSiz.Width(),aPt.Y()-aSiz.Height(),aPt.X()+aSiz.Width(),aPt.Y()+aSiz.Height());
+ return aRect.Contains(rPnt);
+}
+
+
+SdrGluePointList& SdrGluePointList::operator=(const SdrGluePointList& rSrcList)
+{
+ if (GetCount()!=0) aList.clear();
+ sal_uInt16 nCount=rSrcList.GetCount();
+ for (sal_uInt16 i=0; i<nCount; i++) {
+ Insert(rSrcList[i]);
+ }
+ return *this;
+}
+
+// The ID's of the gluepoints always increase monotonously!
+// If an ID is taken already, the new gluepoint gets a new ID. ID 0 is reserved.
+sal_uInt16 SdrGluePointList::Insert(const SdrGluePoint& rGP)
+{
+ SdrGluePoint aGP(rGP);
+ sal_uInt16 nId=aGP.GetId();
+ sal_uInt16 nCount=GetCount();
+ sal_uInt16 nInsPos=nCount;
+ sal_uInt16 nLastId=nCount!=0 ? aList[nCount-1].GetId() : 0;
+ DBG_ASSERT(nLastId>=nCount,"SdrGluePointList::Insert(): nLastId<nCount");
+ bool bHole = nLastId>nCount;
+ if (nId<=nLastId) {
+ if (!bHole || nId==0) {
+ nId=nLastId+1;
+ } else {
+ bool bBrk = false;
+ for (sal_uInt16 nNum=0; nNum<nCount && !bBrk; nNum++) {
+ const auto& pGP2=aList[nNum];
+ sal_uInt16 nTmpId=pGP2.GetId();
+ if (nTmpId==nId) {
+ nId=nLastId+1; // already in use
+ bBrk = true;
+ }
+ if (nTmpId>nId) {
+ nInsPos=nNum; // insert here (sort)
+ bBrk = true;
+ }
+ }
+ }
+ aGP.SetId(nId);
+ }
+ aList.emplace(aList.begin()+nInsPos, aGP);
+ return nInsPos;
+}
+
+void SdrGluePointList::Invalidate(vcl::Window& rWin, const SdrObject* pObj) const
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ return;
+ for (auto& xGP : aList)
+ xGP.Invalidate(rWin,pObj);
+}
+
+sal_uInt16 SdrGluePointList::FindGluePoint(sal_uInt16 nId) const
+{
+ // TODO: Implement a better search algorithm
+ // List should be sorted at all times!
+ sal_uInt16 nCount=GetCount();
+ sal_uInt16 nRet=SDRGLUEPOINT_NOTFOUND;
+ for (sal_uInt16 nNum=0; nNum<nCount && nRet==SDRGLUEPOINT_NOTFOUND; nNum++) {
+ const auto& pGP=aList[nNum];
+ if (pGP.GetId()==nId) nRet=nNum;
+ }
+ return nRet;
+}
+
+sal_uInt16 SdrGluePointList::HitTest(const Point& rPnt, const OutputDevice& rOut, const SdrObject* pObj) const
+{
+ sal_uInt16 nCount = GetCount();
+ sal_uInt16 nRet = SDRGLUEPOINT_NOTFOUND;
+ sal_uInt16 nNum = nCount;
+ while ((nNum>0) && nRet==SDRGLUEPOINT_NOTFOUND) {
+ nNum--;
+ const auto& pGP = aList[nNum];
+ if (pGP.IsHit(rPnt,rOut,pObj))
+ nRet = nNum;
+ }
+ return nRet;
+}
+
+void SdrGluePointList::SetReallyAbsolute(bool bOn, const SdrObject& rObj)
+{
+ for (auto& xGP : aList)
+ xGP.SetReallyAbsolute(bOn,rObj);
+}
+
+void SdrGluePointList::Rotate(const Point& rRef, Degree100 nAngle, double sn, double cs, const SdrObject* pObj)
+{
+ for (auto& xGP : aList)
+ xGP.Rotate(rRef,nAngle,sn,cs,pObj);
+}
+
+void SdrGluePointList::Mirror(const Point& rRef1, const Point& rRef2, const SdrObject* pObj)
+{
+ Point aPt(rRef2); aPt-=rRef1;
+ Degree100 nAngle=GetAngle(aPt);
+ Mirror(rRef1,rRef2,nAngle,pObj);
+}
+
+void SdrGluePointList::Mirror(const Point& rRef1, const Point& rRef2, Degree100 nAngle, const SdrObject* pObj)
+{
+ for (auto& xGP : aList)
+ xGP.Mirror(rRef1,rRef2,nAngle,pObj);
+}
+
+void SdrGluePointList::Shear(const Point& rRef, double tn, bool bVShear, const SdrObject* pObj)
+{
+ for (auto& xGP : aList)
+ xGP.Shear(rRef,tn,bVShear,pObj);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdhdl.cxx b/svx/source/svdraw/svdhdl.cxx
new file mode 100644
index 000000000..31aa07578
--- /dev/null
+++ b/svx/source/svdraw/svdhdl.cxx
@@ -0,0 +1,2675 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <algorithm>
+#include <cassert>
+
+#include <svx/svdhdl.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdmrkv.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/ptrstyle.hxx>
+
+#include <svx/sxekitm.hxx>
+#include <svx/strings.hrc>
+#include <svx/svdmodel.hxx>
+#include "gradtrns.hxx"
+#include <svx/xflgrit.hxx>
+#include <svx/svdundo.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/xflftrit.hxx>
+
+#include <svx/svdopath.hxx>
+#include <basegfx/vector/b2dvector.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <svx/sdr/overlay/overlayanimatedbitmapex.hxx>
+#include <svx/sdr/overlay/overlaybitmapex.hxx>
+#include <sdr/overlay/overlayline.hxx>
+#include <sdr/overlay/overlaytriangle.hxx>
+#include <sdr/overlay/overlayhandle.hxx>
+#include <sdr/overlay/overlayrectangle.hxx>
+#include <svx/sdrpagewindow.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <vcl/svapp.hxx>
+#include <svx/sdr/overlay/overlaypolypolygon.hxx>
+#include <vcl/lazydelete.hxx>
+#include <vcl/BitmapTools.hxx>
+#include <svx/sdr/contact/objectcontact.hxx>
+#include <svx/sdr/contact/viewcontact.hxx>
+#include <osl/diagnose.h>
+
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <svx/sdr/overlay/overlayprimitive2dsequenceobject.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/graphicprimitive2d.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
+#include <svtools/optionsdrawinglayer.hxx>
+#include <memory>
+#include <bitmaps.hlst>
+
+namespace {
+
+// #i15222#
+// Due to the resource problems in Win95/98 with bitmap resources I
+// will change this handle bitmap providing class. Old version was splitting
+// and preparing all small handle bitmaps in device bitmap format, now this will
+// be done on the fly. Thus, there is only one big bitmap in memory. With
+// three source bitmaps, this will be 3 system bitmap resources instead of hundreds.
+// The price for that needs to be evaluated. Maybe we will need another change here
+// if this is too expensive.
+class SdrHdlBitmapSet
+{
+ // the bitmap holding all information
+ BitmapEx maMarkersBitmap;
+
+ // the cropped Bitmaps for reusage
+ ::std::vector< BitmapEx > maRealMarkers;
+
+ // helpers
+ BitmapEx& impGetOrCreateTargetBitmap(sal_uInt16 nIndex, const tools::Rectangle& rRectangle);
+
+public:
+ explicit SdrHdlBitmapSet();
+
+ const BitmapEx& GetBitmapEx(BitmapMarkerKind eKindOfMarker, sal_uInt16 nInd);
+};
+
+}
+
+#define KIND_COUNT (14)
+#define INDEX_COUNT (6)
+#define INDIVIDUAL_COUNT (5)
+
+SdrHdlBitmapSet::SdrHdlBitmapSet()
+ : maMarkersBitmap(SIP_SA_MARKERS),
+ // 15 kinds (BitmapMarkerKind) use index [0..5] + 5 extra
+ maRealMarkers((KIND_COUNT * INDEX_COUNT) + INDIVIDUAL_COUNT)
+{
+}
+
+BitmapEx& SdrHdlBitmapSet::impGetOrCreateTargetBitmap(sal_uInt16 nIndex, const tools::Rectangle& rRectangle)
+{
+ BitmapEx& rTargetBitmap = maRealMarkers[nIndex];
+
+ if(rTargetBitmap.IsEmpty())
+ {
+ rTargetBitmap = maMarkersBitmap;
+ rTargetBitmap.Crop(rRectangle);
+ }
+
+ return rTargetBitmap;
+}
+
+// change getting of bitmap to use the big resource bitmap
+const BitmapEx& SdrHdlBitmapSet::GetBitmapEx(BitmapMarkerKind eKindOfMarker, sal_uInt16 nInd)
+{
+ // fill in size and source position in maMarkersBitmap
+ const sal_uInt16 nYPos(nInd * 11);
+
+ switch(eKindOfMarker)
+ {
+ default:
+ {
+ OSL_FAIL( "Unknown kind of marker." );
+ [[fallthrough]]; // return Rect_9x9 as default
+ }
+ case BitmapMarkerKind::Rect_9x9:
+ {
+ return impGetOrCreateTargetBitmap((1 * INDEX_COUNT) + nInd, tools::Rectangle(Point(7, nYPos), Size(9, 9)));
+ }
+
+ case BitmapMarkerKind::Rect_7x7:
+ {
+ return impGetOrCreateTargetBitmap((0 * INDEX_COUNT) + nInd, tools::Rectangle(Point(0, nYPos), Size(7, 7)));
+ }
+
+ case BitmapMarkerKind::Rect_11x11:
+ {
+ return impGetOrCreateTargetBitmap((2 * INDEX_COUNT) + nInd, tools::Rectangle(Point(16, nYPos), Size(11, 11)));
+ }
+
+ case BitmapMarkerKind::Rect_13x13:
+ {
+ const sal_uInt16 nIndex((3 * INDEX_COUNT) + nInd);
+
+ switch(nInd)
+ {
+ case 0:
+ {
+ return impGetOrCreateTargetBitmap(nIndex, tools::Rectangle(Point(72, 66), Size(13, 13)));
+ }
+ case 1:
+ {
+ return impGetOrCreateTargetBitmap(nIndex, tools::Rectangle(Point(85, 66), Size(13, 13)));
+ }
+ case 2:
+ {
+ return impGetOrCreateTargetBitmap(nIndex, tools::Rectangle(Point(72, 79), Size(13, 13)));
+ }
+ case 3:
+ {
+ return impGetOrCreateTargetBitmap(nIndex, tools::Rectangle(Point(85, 79), Size(13, 13)));
+ }
+ case 4:
+ {
+ return impGetOrCreateTargetBitmap(nIndex, tools::Rectangle(Point(98, 79), Size(13, 13)));
+ }
+ default: // case 5:
+ {
+ return impGetOrCreateTargetBitmap(nIndex, tools::Rectangle(Point(98, 66), Size(13, 13)));
+ }
+ }
+ }
+
+ case BitmapMarkerKind::Circ_7x7:
+ case BitmapMarkerKind::Customshape_7x7:
+ {
+ return impGetOrCreateTargetBitmap((4 * INDEX_COUNT) + nInd, tools::Rectangle(Point(27, nYPos), Size(7, 7)));
+ }
+
+ case BitmapMarkerKind::Circ_9x9:
+ case BitmapMarkerKind::Customshape_9x9:
+ {
+ return impGetOrCreateTargetBitmap((5 * INDEX_COUNT) + nInd, tools::Rectangle(Point(34, nYPos), Size(9, 9)));
+ }
+
+ case BitmapMarkerKind::Circ_11x11:
+ case BitmapMarkerKind::Customshape_11x11:
+ {
+ return impGetOrCreateTargetBitmap((6 * INDEX_COUNT) + nInd, tools::Rectangle(Point(43, nYPos), Size(11, 11)));
+ }
+
+ case BitmapMarkerKind::Elli_7x9:
+ {
+ return impGetOrCreateTargetBitmap((7 * INDEX_COUNT) + nInd, tools::Rectangle(Point(54, nYPos), Size(7, 9)));
+ }
+
+ case BitmapMarkerKind::Elli_9x11:
+ {
+ return impGetOrCreateTargetBitmap((8 * INDEX_COUNT) + nInd, tools::Rectangle(Point(61, nYPos), Size(9, 11)));
+ }
+
+ case BitmapMarkerKind::Elli_9x7:
+ {
+ return impGetOrCreateTargetBitmap((9 * INDEX_COUNT) + nInd, tools::Rectangle(Point(70, nYPos), Size(9, 7)));
+ }
+
+ case BitmapMarkerKind::Elli_11x9:
+ {
+ return impGetOrCreateTargetBitmap((10 * INDEX_COUNT) + nInd, tools::Rectangle(Point(79, nYPos), Size(11, 9)));
+ }
+
+ case BitmapMarkerKind::RectPlus_7x7:
+ {
+ return impGetOrCreateTargetBitmap((11 * INDEX_COUNT) + nInd, tools::Rectangle(Point(90, nYPos), Size(7, 7)));
+ }
+
+ case BitmapMarkerKind::RectPlus_9x9:
+ {
+ return impGetOrCreateTargetBitmap((12 * INDEX_COUNT) + nInd, tools::Rectangle(Point(97, nYPos), Size(9, 9)));
+ }
+
+ case BitmapMarkerKind::RectPlus_11x11:
+ {
+ return impGetOrCreateTargetBitmap((13 * INDEX_COUNT) + nInd, tools::Rectangle(Point(106, nYPos), Size(11, 11)));
+ }
+
+ case BitmapMarkerKind::Crosshair:
+ {
+ return impGetOrCreateTargetBitmap((KIND_COUNT * INDEX_COUNT) + 0, tools::Rectangle(Point(0, 68), Size(15, 15)));
+ }
+
+ case BitmapMarkerKind::Glue:
+ {
+ return impGetOrCreateTargetBitmap((KIND_COUNT * INDEX_COUNT) + 1, tools::Rectangle(Point(15, 76), Size(9, 9)));
+ }
+
+ case BitmapMarkerKind::Glue_Deselected:
+ {
+ return impGetOrCreateTargetBitmap((KIND_COUNT * INDEX_COUNT) + 2, tools::Rectangle(Point(15, 67), Size(9, 9)));
+ }
+
+ case BitmapMarkerKind::Anchor: // AnchorTR for SW
+ case BitmapMarkerKind::AnchorTR:
+ {
+ return impGetOrCreateTargetBitmap((KIND_COUNT * INDEX_COUNT) + 3, tools::Rectangle(Point(24, 67), Size(24, 24)));
+ }
+
+ // add AnchorPressed to be able to animate anchor control
+ case BitmapMarkerKind::AnchorPressed:
+ case BitmapMarkerKind::AnchorPressedTR:
+ {
+ return impGetOrCreateTargetBitmap((KIND_COUNT * INDEX_COUNT) + 4, tools::Rectangle(Point(48, 67), Size(24, 24)));
+ }
+ }
+}
+
+
+SdrHdl::SdrHdl():
+ pObj(nullptr),
+ pPV(nullptr),
+ pHdlList(nullptr),
+ eKind(SdrHdlKind::Move),
+ nRotationAngle(0),
+ nObjHdlNum(0),
+ nPolyNum(0),
+ nPPntNum(0),
+ nSourceHdlNum(0),
+ bSelect(false),
+ b1PixMore(false),
+ bPlusHdl(false),
+ mbMoveOutside(false),
+ mbMouseOver(false)
+{
+}
+
+SdrHdl::SdrHdl(const Point& rPnt, SdrHdlKind eNewKind):
+ pObj(nullptr),
+ pPV(nullptr),
+ pHdlList(nullptr),
+ aPos(rPnt),
+ eKind(eNewKind),
+ nRotationAngle(0),
+ nObjHdlNum(0),
+ nPolyNum(0),
+ nPPntNum(0),
+ nSourceHdlNum(0),
+ bSelect(false),
+ b1PixMore(false),
+ bPlusHdl(false),
+ mbMoveOutside(false),
+ mbMouseOver(false)
+{
+}
+
+SdrHdl::~SdrHdl()
+{
+ GetRidOfIAObject();
+}
+
+void SdrHdl::Set1PixMore(bool bJa)
+{
+ if(b1PixMore != bJa)
+ {
+ b1PixMore = bJa;
+
+ // create new display
+ Touch();
+ }
+}
+
+void SdrHdl::SetMoveOutside( bool bMoveOutside )
+{
+ if(mbMoveOutside != bMoveOutside)
+ {
+ mbMoveOutside = bMoveOutside;
+
+ // create new display
+ Touch();
+ }
+}
+
+void SdrHdl::SetRotationAngle(Degree100 n)
+{
+ if(nRotationAngle != n)
+ {
+ nRotationAngle = n;
+
+ // create new display
+ Touch();
+ }
+}
+
+void SdrHdl::SetPos(const Point& rPnt)
+{
+ if(aPos != rPnt)
+ {
+ // remember new position
+ aPos = rPnt;
+
+ // create new display
+ Touch();
+ }
+}
+
+void SdrHdl::SetSelected(bool bJa)
+{
+ if(bSelect != bJa)
+ {
+ // remember new value
+ bSelect = bJa;
+
+ // create new display
+ Touch();
+ }
+}
+
+void SdrHdl::SetHdlList(SdrHdlList* pList)
+{
+ if(pHdlList != pList)
+ {
+ // remember list
+ pHdlList = pList;
+
+ // now it's possible to create graphic representation
+ Touch();
+ }
+}
+
+void SdrHdl::SetObj(SdrObject* pNewObj)
+{
+ if(pObj != pNewObj)
+ {
+ // remember new object
+ pObj = pNewObj;
+
+ // graphic representation may have changed
+ Touch();
+ }
+}
+
+void SdrHdl::Touch()
+{
+ // force update of graphic representation
+ CreateB2dIAObject();
+}
+
+void SdrHdl::GetRidOfIAObject()
+{
+
+ // OVERLAYMANAGER
+ maOverlayGroup.clear();
+}
+
+void SdrHdl::CreateB2dIAObject()
+{
+ // first throw away old one
+ GetRidOfIAObject();
+
+ if(!pHdlList || !pHdlList->GetView() || pHdlList->GetView()->areMarkHandlesHidden())
+ return;
+
+ BitmapColorIndex eColIndex = BitmapColorIndex::LightGreen;
+ BitmapMarkerKind eKindOfMarker = BitmapMarkerKind::Rect_7x7;
+
+ bool bRot = pHdlList->IsRotateShear();
+ if(pObj)
+ eColIndex = bSelect ? BitmapColorIndex::Cyan : BitmapColorIndex::LightCyan;
+ if(bRot)
+ {
+ // red rotation handles
+ if(pObj && bSelect)
+ eColIndex = BitmapColorIndex::Red;
+ else
+ eColIndex = BitmapColorIndex::LightRed;
+ }
+
+ switch(eKind)
+ {
+ case SdrHdlKind::Move:
+ {
+ eKindOfMarker = b1PixMore ? BitmapMarkerKind::Rect_9x9 : BitmapMarkerKind::Rect_7x7;
+ break;
+ }
+ case SdrHdlKind::UpperLeft:
+ case SdrHdlKind::UpperRight:
+ case SdrHdlKind::LowerLeft:
+ case SdrHdlKind::LowerRight:
+ {
+ // corner handles
+ if(bRot)
+ {
+ eKindOfMarker = BitmapMarkerKind::Circ_7x7;
+ }
+ else
+ {
+ eKindOfMarker = BitmapMarkerKind::Rect_7x7;
+ }
+ break;
+ }
+ case SdrHdlKind::Upper:
+ case SdrHdlKind::Lower:
+ {
+ // Upper/Lower handles
+ if(bRot)
+ {
+ eKindOfMarker = BitmapMarkerKind::Elli_9x7;
+ }
+ else
+ {
+ eKindOfMarker = BitmapMarkerKind::Rect_7x7;
+ }
+ break;
+ }
+ case SdrHdlKind::Left:
+ case SdrHdlKind::Right:
+ {
+ // Left/Right handles
+ if(bRot)
+ {
+ eKindOfMarker = BitmapMarkerKind::Elli_7x9;
+ }
+ else
+ {
+ eKindOfMarker = BitmapMarkerKind::Rect_7x7;
+ }
+ break;
+ }
+ case SdrHdlKind::Poly:
+ {
+ if(bRot)
+ {
+ eKindOfMarker = b1PixMore ? BitmapMarkerKind::Circ_9x9 : BitmapMarkerKind::Circ_7x7;
+ }
+ else
+ {
+ eKindOfMarker = b1PixMore ? BitmapMarkerKind::Rect_9x9 : BitmapMarkerKind::Rect_7x7;
+ }
+ break;
+ }
+ case SdrHdlKind::BezierWeight: // weight at poly
+ {
+ eKindOfMarker = BitmapMarkerKind::Circ_7x7;
+ break;
+ }
+ case SdrHdlKind::Circle:
+ {
+ eKindOfMarker = BitmapMarkerKind::Rect_11x11;
+ break;
+ }
+ case SdrHdlKind::Ref1:
+ case SdrHdlKind::Ref2:
+ {
+ eKindOfMarker = BitmapMarkerKind::Crosshair;
+ break;
+ }
+ case SdrHdlKind::Glue:
+ {
+ eKindOfMarker = BitmapMarkerKind::Glue;
+ break;
+ }
+ case SdrHdlKind::Anchor:
+ {
+ eKindOfMarker = BitmapMarkerKind::Anchor;
+ break;
+ }
+ case SdrHdlKind::User:
+ {
+ break;
+ }
+ // top right anchor for SW
+ case SdrHdlKind::Anchor_TR:
+ {
+ eKindOfMarker = BitmapMarkerKind::AnchorTR;
+ break;
+ }
+
+ // for SJ and the CustomShapeHandles:
+ case SdrHdlKind::CustomShape1:
+ {
+ eKindOfMarker = b1PixMore ? BitmapMarkerKind::Customshape_9x9 : BitmapMarkerKind::Customshape_7x7;
+ eColIndex = BitmapColorIndex::Yellow;
+ break;
+ }
+ default:
+ break;
+ }
+
+ SdrMarkView* pView = pHdlList->GetView();
+ SdrPageView* pPageView = pView->GetSdrPageView();
+
+ if(!pPageView)
+ return;
+
+ for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
+ {
+ // const SdrPageViewWinRec& rPageViewWinRec = rPageViewWinList[b];
+ const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);
+
+ if(rPageWindow.GetPaintWindow().OutputToWindow())
+ {
+ Point aMoveOutsideOffset(0, 0);
+ OutputDevice& rOutDev = rPageWindow.GetPaintWindow().GetOutputDevice();
+
+ // add offset if necessary
+ if(pHdlList->IsMoveOutside() || mbMoveOutside)
+ {
+ Size aOffset = rOutDev.PixelToLogic(Size(4, 4));
+
+ if(eKind == SdrHdlKind::UpperLeft || eKind == SdrHdlKind::Upper || eKind == SdrHdlKind::UpperRight)
+ aMoveOutsideOffset.AdjustY( -(aOffset.Width()) );
+ if(eKind == SdrHdlKind::LowerLeft || eKind == SdrHdlKind::Lower || eKind == SdrHdlKind::LowerRight)
+ aMoveOutsideOffset.AdjustY(aOffset.Height() );
+ if(eKind == SdrHdlKind::UpperLeft || eKind == SdrHdlKind::Left || eKind == SdrHdlKind::LowerLeft)
+ aMoveOutsideOffset.AdjustX( -(aOffset.Width()) );
+ if(eKind == SdrHdlKind::UpperRight || eKind == SdrHdlKind::Right || eKind == SdrHdlKind::LowerRight)
+ aMoveOutsideOffset.AdjustX(aOffset.Height() );
+ }
+
+ const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager();
+ if (xManager.is())
+ {
+ basegfx::B2DPoint aPosition(aPos.X(), aPos.Y());
+ std::unique_ptr<sdr::overlay::OverlayObject> pNewOverlayObject;
+ if (getenv ("SVX_DRAW_HANDLES") && (eKindOfMarker == BitmapMarkerKind::Rect_7x7 || eKindOfMarker == BitmapMarkerKind::Rect_9x9 || eKindOfMarker == BitmapMarkerKind::Rect_11x11))
+ {
+ double fSize = 7.0;
+ switch (eKindOfMarker)
+ {
+ case BitmapMarkerKind::Rect_9x9:
+ fSize = 9.0;
+ break;
+ case BitmapMarkerKind::Rect_11x11:
+ fSize = 11.0;
+ break;
+ default:
+ break;
+ }
+ float fScalingFactor = rOutDev.GetDPIScaleFactor();
+ basegfx::B2DSize aB2DSize(fSize * fScalingFactor, fSize * fScalingFactor);
+
+ Color aHandleFillColor(COL_LIGHTGREEN);
+ switch (eColIndex)
+ {
+ case BitmapColorIndex::Cyan:
+ aHandleFillColor = COL_CYAN;
+ break;
+ case BitmapColorIndex::LightCyan:
+ aHandleFillColor = COL_LIGHTCYAN;
+ break;
+ case BitmapColorIndex::Red:
+ aHandleFillColor = COL_RED;
+ break;
+ case BitmapColorIndex::LightRed:
+ aHandleFillColor = COL_LIGHTRED;
+ break;
+ case BitmapColorIndex::Yellow:
+ aHandleFillColor = COL_YELLOW;
+ break;
+ default:
+ break;
+ }
+ pNewOverlayObject.reset(new sdr::overlay::OverlayHandle(aPosition, aB2DSize, /*HandleStrokeColor*/COL_BLACK, aHandleFillColor));
+ }
+ else
+ {
+ pNewOverlayObject = CreateOverlayObject(
+ aPosition, eColIndex, eKindOfMarker,
+ aMoveOutsideOffset);
+ }
+
+ // OVERLAYMANAGER
+ insertNewlyCreatedOverlayObjectForSdrHdl(
+ std::move(pNewOverlayObject),
+ rPageWindow.GetObjectContact(),
+ *xManager);
+ }
+ }
+ }
+}
+
+BitmapMarkerKind SdrHdl::GetNextBigger(BitmapMarkerKind eKnd)
+{
+ BitmapMarkerKind eRetval(eKnd);
+
+ switch(eKnd)
+ {
+ case BitmapMarkerKind::Rect_7x7: eRetval = BitmapMarkerKind::Rect_9x9; break;
+ case BitmapMarkerKind::Rect_9x9: eRetval = BitmapMarkerKind::Rect_11x11; break;
+ case BitmapMarkerKind::Rect_11x11: eRetval = BitmapMarkerKind::Rect_13x13; break;
+
+ case BitmapMarkerKind::Circ_7x7: eRetval = BitmapMarkerKind::Circ_9x9; break;
+ case BitmapMarkerKind::Circ_9x9: eRetval = BitmapMarkerKind::Circ_11x11; break;
+
+ case BitmapMarkerKind::Customshape_7x7: eRetval = BitmapMarkerKind::Customshape_9x9; break;
+ case BitmapMarkerKind::Customshape_9x9: eRetval = BitmapMarkerKind::Customshape_11x11; break;
+ //case BitmapMarkerKind::Customshape_11x11: eRetval = ; break;
+
+ case BitmapMarkerKind::Elli_7x9: eRetval = BitmapMarkerKind::Elli_9x11; break;
+
+ case BitmapMarkerKind::Elli_9x7: eRetval = BitmapMarkerKind::Elli_11x9; break;
+
+ case BitmapMarkerKind::RectPlus_7x7: eRetval = BitmapMarkerKind::RectPlus_9x9; break;
+ case BitmapMarkerKind::RectPlus_9x9: eRetval = BitmapMarkerKind::RectPlus_11x11; break;
+
+ // let anchor blink with its pressed state
+ case BitmapMarkerKind::Anchor: eRetval = BitmapMarkerKind::AnchorPressed; break;
+
+ // same for AnchorTR
+ case BitmapMarkerKind::AnchorTR: eRetval = BitmapMarkerKind::AnchorPressedTR; break;
+ default:
+ break;
+ }
+
+ return eRetval;
+}
+
+namespace
+{
+
+OUString appendMarkerName(BitmapMarkerKind eKindOfMarker)
+{
+ switch(eKindOfMarker)
+ {
+ case BitmapMarkerKind::Rect_7x7:
+ return "rect7";
+ case BitmapMarkerKind::Rect_9x9:
+ return "rect9";
+ case BitmapMarkerKind::Rect_11x11:
+ return "rect11";
+ case BitmapMarkerKind::Rect_13x13:
+ return "rect13";
+ case BitmapMarkerKind::Circ_7x7:
+ case BitmapMarkerKind::Customshape_7x7:
+ return "circ7";
+ case BitmapMarkerKind::Circ_9x9:
+ case BitmapMarkerKind::Customshape_9x9:
+ return "circ9";
+ case BitmapMarkerKind::Circ_11x11:
+ case BitmapMarkerKind::Customshape_11x11:
+ return "circ11";
+ case BitmapMarkerKind::Elli_7x9:
+ return "elli7x9";
+ case BitmapMarkerKind::Elli_9x11:
+ return "elli9x11";
+ case BitmapMarkerKind::Elli_9x7:
+ return "elli9x7";
+ case BitmapMarkerKind::Elli_11x9:
+ return "elli11x9";
+ case BitmapMarkerKind::RectPlus_7x7:
+ return "rectplus7";
+ case BitmapMarkerKind::RectPlus_9x9:
+ return "rectplus9";
+ case BitmapMarkerKind::RectPlus_11x11:
+ return "rectplus11";
+ case BitmapMarkerKind::Crosshair:
+ return "cross";
+ case BitmapMarkerKind::Anchor:
+ case BitmapMarkerKind::AnchorTR:
+ return "anchor";
+ case BitmapMarkerKind::AnchorPressed:
+ case BitmapMarkerKind::AnchorPressedTR:
+ return "anchor-pressed";
+ case BitmapMarkerKind::Glue:
+ return "glue-selected";
+ case BitmapMarkerKind::Glue_Deselected:
+ return "glue-unselected";
+ default:
+ break;
+ }
+ return OUString();
+}
+
+OUString appendMarkerColor(BitmapColorIndex eIndex)
+{
+ switch(eIndex)
+ {
+ case BitmapColorIndex::LightGreen:
+ return "1";
+ case BitmapColorIndex::Cyan:
+ return "2";
+ case BitmapColorIndex::LightCyan:
+ return "3";
+ case BitmapColorIndex::Red:
+ return "4";
+ case BitmapColorIndex::LightRed:
+ return "5";
+ case BitmapColorIndex::Yellow:
+ return "6";
+ default:
+ break;
+ }
+ return OUString();
+}
+
+BitmapEx ImpGetBitmapEx(BitmapMarkerKind eKindOfMarker, BitmapColorIndex eIndex)
+{
+ // use this code path only when we use HiDPI (for now)
+ if (Application::GetDefaultDevice()->GetDPIScalePercentage() > 100)
+ {
+ OUString sMarkerName = appendMarkerName(eKindOfMarker);
+ if (!sMarkerName.isEmpty())
+ {
+ OUString sMarkerPrefix("svx/res/marker-");
+ BitmapEx aBitmapEx;
+
+ if (eKindOfMarker == BitmapMarkerKind::Crosshair
+ || eKindOfMarker == BitmapMarkerKind::Anchor
+ || eKindOfMarker == BitmapMarkerKind::AnchorTR
+ || eKindOfMarker == BitmapMarkerKind::AnchorPressed
+ || eKindOfMarker == BitmapMarkerKind::AnchorPressedTR
+ || eKindOfMarker == BitmapMarkerKind::Glue
+ || eKindOfMarker == BitmapMarkerKind::Glue_Deselected)
+ {
+ aBitmapEx = vcl::bitmap::loadFromName(sMarkerPrefix + sMarkerName + ".png");
+ }
+ else
+ {
+ aBitmapEx = vcl::bitmap::loadFromName(sMarkerPrefix + sMarkerName + "-" + appendMarkerColor(eIndex) + ".png");
+ }
+
+ if (!aBitmapEx.IsEmpty())
+ return aBitmapEx;
+ }
+ }
+
+ // if we can't load the marker...
+
+ static vcl::DeleteOnDeinit< SdrHdlBitmapSet > aModernSet {};
+ return aModernSet.get()->GetBitmapEx(eKindOfMarker, sal_uInt16(eIndex));
+}
+
+} // end anonymous namespace
+
+std::unique_ptr<sdr::overlay::OverlayObject> SdrHdl::CreateOverlayObject(
+ const basegfx::B2DPoint& rPos,
+ BitmapColorIndex eColIndex, BitmapMarkerKind eKindOfMarker, Point aMoveOutsideOffset)
+{
+ std::unique_ptr<sdr::overlay::OverlayObject> pRetval;
+
+ // support bigger sizes
+ bool bForceBiggerSize(false);
+
+ if (pHdlList && pHdlList->GetHdlSize() > 3)
+ {
+ switch(eKindOfMarker)
+ {
+ case BitmapMarkerKind::Anchor:
+ case BitmapMarkerKind::AnchorPressed:
+ case BitmapMarkerKind::AnchorTR:
+ case BitmapMarkerKind::AnchorPressedTR:
+ {
+ // #i121463# For anchor, do not simply make bigger because of HdlSize,
+ // do it dependent of IsSelected() which Writer can set in drag mode
+ if(IsSelected())
+ {
+ bForceBiggerSize = true;
+ }
+ break;
+ }
+ default:
+ {
+ bForceBiggerSize = true;
+ break;
+ }
+ }
+ }
+
+ if(bForceBiggerSize)
+ {
+ eKindOfMarker = GetNextBigger(eKindOfMarker);
+ }
+
+ // This handle has the focus, visualize it
+ if(IsFocusHdl() && pHdlList && pHdlList->GetFocusHdl() == this)
+ {
+ // create animated handle
+ BitmapMarkerKind eNextBigger = GetNextBigger(eKindOfMarker);
+
+ if(eNextBigger == eKindOfMarker)
+ {
+ // this may happen for the not supported getting-bigger types.
+ // Choose an alternative here
+ switch(eKindOfMarker)
+ {
+ case BitmapMarkerKind::Rect_13x13: eNextBigger = BitmapMarkerKind::Rect_11x11; break;
+ case BitmapMarkerKind::Circ_11x11: eNextBigger = BitmapMarkerKind::Elli_11x9; break;
+ case BitmapMarkerKind::Elli_9x11: eNextBigger = BitmapMarkerKind::Elli_11x9; break;
+ case BitmapMarkerKind::Elli_11x9: eNextBigger = BitmapMarkerKind::Elli_9x11; break;
+ case BitmapMarkerKind::RectPlus_11x11: eNextBigger = BitmapMarkerKind::Rect_13x13; break;
+
+ case BitmapMarkerKind::Crosshair:
+ eNextBigger = BitmapMarkerKind::Glue;
+ break;
+
+ case BitmapMarkerKind::Glue:
+ eNextBigger = BitmapMarkerKind::Crosshair;
+ break;
+ case BitmapMarkerKind::Glue_Deselected:
+ eNextBigger = BitmapMarkerKind::Glue;
+ break;
+ default:
+ break;
+ }
+ }
+
+ // create animated handle
+ BitmapEx aBmpEx1 = ImpGetBitmapEx(eKindOfMarker, eColIndex);
+ BitmapEx aBmpEx2 = ImpGetBitmapEx(eNextBigger, eColIndex);
+
+ // #i53216# Use system cursor blink time. Use the unsigned value.
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ const sal_uInt64 nBlinkTime(rStyleSettings.GetCursorBlinkTime());
+
+ if(eKindOfMarker == BitmapMarkerKind::Anchor || eKindOfMarker == BitmapMarkerKind::AnchorPressed)
+ {
+ // when anchor is used take upper left as reference point inside the handle
+ pRetval.reset(new sdr::overlay::OverlayAnimatedBitmapEx(rPos, aBmpEx1, aBmpEx2, nBlinkTime));
+ }
+ else if(eKindOfMarker == BitmapMarkerKind::AnchorTR || eKindOfMarker == BitmapMarkerKind::AnchorPressedTR)
+ {
+ // AnchorTR for SW, take top right as (0,0)
+ pRetval.reset(new sdr::overlay::OverlayAnimatedBitmapEx(rPos, aBmpEx1, aBmpEx2, nBlinkTime,
+ static_cast<sal_uInt16>(aBmpEx1.GetSizePixel().Width() - 1), 0,
+ static_cast<sal_uInt16>(aBmpEx2.GetSizePixel().Width() - 1), 0));
+ }
+ else
+ {
+ // create centered handle as default
+ pRetval.reset(new sdr::overlay::OverlayAnimatedBitmapEx(rPos, aBmpEx1, aBmpEx2, nBlinkTime,
+ static_cast<sal_uInt16>(aBmpEx1.GetSizePixel().Width() - 1) >> 1,
+ static_cast<sal_uInt16>(aBmpEx1.GetSizePixel().Height() - 1) >> 1,
+ static_cast<sal_uInt16>(aBmpEx2.GetSizePixel().Width() - 1) >> 1,
+ static_cast<sal_uInt16>(aBmpEx2.GetSizePixel().Height() - 1) >> 1));
+ }
+ }
+ else
+ {
+ // create normal handle: use ImpGetBitmapEx(...) now
+ BitmapEx aBmpEx = ImpGetBitmapEx(eKindOfMarker, eColIndex);
+
+ // When the image with handles is not found, the bitmap returned is
+ // empty. This is a problem when we use LibreOffice as a library
+ // (through LOKit - for example on Android) even when we don't show
+ // the handles, because the hit test would always return false.
+ //
+ // This HACK replaces the empty bitmap with a black 13x13 bitmap handle
+ // so that the hit test works for this case.
+ if (aBmpEx.IsEmpty())
+ {
+ aBmpEx = BitmapEx(Size(13, 13), vcl::PixelFormat::N24_BPP);
+ aBmpEx.Erase(COL_BLACK);
+ }
+
+ if(eKindOfMarker == BitmapMarkerKind::Anchor || eKindOfMarker == BitmapMarkerKind::AnchorPressed)
+ {
+ // upper left as reference point inside the handle for AnchorPressed, too
+ pRetval.reset(new sdr::overlay::OverlayBitmapEx(rPos, aBmpEx));
+ }
+ else if(eKindOfMarker == BitmapMarkerKind::AnchorTR || eKindOfMarker == BitmapMarkerKind::AnchorPressedTR)
+ {
+ // AnchorTR for SW, take top right as (0,0)
+ pRetval.reset(new sdr::overlay::OverlayBitmapEx(rPos, aBmpEx,
+ static_cast<sal_uInt16>(aBmpEx.GetSizePixel().Width() - 1), 0));
+ }
+ else
+ {
+ sal_uInt16 nCenX(static_cast<sal_uInt16>(aBmpEx.GetSizePixel().Width() - 1) >> 1);
+ sal_uInt16 nCenY(static_cast<sal_uInt16>(aBmpEx.GetSizePixel().Height() - 1) >> 1);
+
+ if(aMoveOutsideOffset.X() > 0)
+ {
+ nCenX = 0;
+ }
+ else if(aMoveOutsideOffset.X() < 0)
+ {
+ nCenX = static_cast<sal_uInt16>(aBmpEx.GetSizePixel().Width() - 1);
+ }
+
+ if(aMoveOutsideOffset.Y() > 0)
+ {
+ nCenY = 0;
+ }
+ else if(aMoveOutsideOffset.Y() < 0)
+ {
+ nCenY = static_cast<sal_uInt16>(aBmpEx.GetSizePixel().Height() - 1);
+ }
+
+ // create centered handle as default
+ pRetval.reset(new sdr::overlay::OverlayBitmapEx(rPos, aBmpEx, nCenX, nCenY));
+ }
+ }
+
+ return pRetval;
+}
+
+bool SdrHdl::IsHdlHit(const Point& rPnt) const
+{
+ // OVERLAYMANAGER
+ basegfx::B2DPoint aPosition(rPnt.X(), rPnt.Y());
+ return maOverlayGroup.isHitLogic(aPosition);
+}
+
+PointerStyle SdrHdl::GetPointer() const
+{
+ PointerStyle ePtr=PointerStyle::Move;
+ const bool bSize=eKind>=SdrHdlKind::UpperLeft && eKind<=SdrHdlKind::LowerRight;
+ const bool bRot=pHdlList!=nullptr && pHdlList->IsRotateShear();
+ const bool bDis=pHdlList!=nullptr && pHdlList->IsDistortShear();
+ if (bSize && pHdlList!=nullptr && (bRot || bDis)) {
+ switch (eKind) {
+ case SdrHdlKind::UpperLeft: case SdrHdlKind::UpperRight:
+ case SdrHdlKind::LowerLeft: case SdrHdlKind::LowerRight: ePtr=bRot ? PointerStyle::Rotate : PointerStyle::RefHand; break;
+ case SdrHdlKind::Left : case SdrHdlKind::Right: ePtr=PointerStyle::VShear; break;
+ case SdrHdlKind::Upper: case SdrHdlKind::Lower: ePtr=PointerStyle::HShear; break;
+ default:
+ break;
+ }
+ } else {
+ // When resizing rotated rectangles, rotate the mouse cursor slightly, too
+ if (bSize && nRotationAngle!=0_deg100) {
+ Degree100 nHdlAngle(0);
+ switch (eKind) {
+ case SdrHdlKind::LowerRight: nHdlAngle=31500_deg100; break;
+ case SdrHdlKind::Lower: nHdlAngle=27000_deg100; break;
+ case SdrHdlKind::LowerLeft: nHdlAngle=22500_deg100; break;
+ case SdrHdlKind::Left : nHdlAngle=18000_deg100; break;
+ case SdrHdlKind::UpperLeft: nHdlAngle=13500_deg100; break;
+ case SdrHdlKind::Upper: nHdlAngle=9000_deg100; break;
+ case SdrHdlKind::UpperRight: nHdlAngle=4500_deg100; break;
+ case SdrHdlKind::Right: nHdlAngle=0_deg100; break;
+ default:
+ break;
+ }
+ // a little bit more (for rounding)
+ nHdlAngle = NormAngle36000(nHdlAngle + nRotationAngle + 2249_deg100);
+ nHdlAngle/=4500_deg100;
+ switch (static_cast<sal_uInt8>(nHdlAngle.get())) {
+ case 0: ePtr=PointerStyle::ESize; break;
+ case 1: ePtr=PointerStyle::NESize; break;
+ case 2: ePtr=PointerStyle::NSize; break;
+ case 3: ePtr=PointerStyle::NWSize; break;
+ case 4: ePtr=PointerStyle::WSize; break;
+ case 5: ePtr=PointerStyle::SWSize; break;
+ case 6: ePtr=PointerStyle::SSize; break;
+ case 7: ePtr=PointerStyle::SESize; break;
+ } // switch
+ } else {
+ switch (eKind) {
+ case SdrHdlKind::UpperLeft: ePtr=PointerStyle::NWSize; break;
+ case SdrHdlKind::Upper: ePtr=PointerStyle::NSize; break;
+ case SdrHdlKind::UpperRight: ePtr=PointerStyle::NESize; break;
+ case SdrHdlKind::Left : ePtr=PointerStyle::WSize; break;
+ case SdrHdlKind::Right: ePtr=PointerStyle::ESize; break;
+ case SdrHdlKind::LowerLeft: ePtr=PointerStyle::SWSize; break;
+ case SdrHdlKind::Lower: ePtr=PointerStyle::SSize; break;
+ case SdrHdlKind::LowerRight: ePtr=PointerStyle::SESize; break;
+ case SdrHdlKind::Poly : ePtr=PointerStyle::MovePoint; break;
+ case SdrHdlKind::Circle : ePtr=PointerStyle::Hand; break;
+ case SdrHdlKind::Ref1 : ePtr=PointerStyle::RefHand; break;
+ case SdrHdlKind::Ref2 : ePtr=PointerStyle::RefHand; break;
+ case SdrHdlKind::BezierWeight : ePtr=PointerStyle::MoveBezierWeight; break;
+ case SdrHdlKind::Glue : ePtr=PointerStyle::MovePoint; break;
+ case SdrHdlKind::CustomShape1 : ePtr=PointerStyle::Hand; break;
+ default:
+ break;
+ }
+ }
+ }
+ return ePtr;
+}
+
+bool SdrHdl::IsFocusHdl() const
+{
+ switch(eKind)
+ {
+ case SdrHdlKind::UpperLeft:
+ case SdrHdlKind::Upper:
+ case SdrHdlKind::UpperRight:
+ case SdrHdlKind::Left:
+ case SdrHdlKind::Right:
+ case SdrHdlKind::LowerLeft:
+ case SdrHdlKind::Lower:
+ case SdrHdlKind::LowerRight:
+ {
+ // if it's an activated TextEdit, it's moved to extended points
+ return !pHdlList || !pHdlList->IsMoveOutside();
+ }
+
+ case SdrHdlKind::Move: // handle to move object
+ case SdrHdlKind::Poly: // selected point of polygon or curve
+ case SdrHdlKind::BezierWeight: // weight at a curve
+ case SdrHdlKind::Circle: // angle of circle segments, corner radius of rectangles
+ case SdrHdlKind::Ref1: // reference point 1, e. g. center of rotation
+ case SdrHdlKind::Ref2: // reference point 2, e. g. endpoint of reflection axis
+ case SdrHdlKind::Glue: // gluepoint
+
+ // for SJ and the CustomShapeHandles:
+ case SdrHdlKind::CustomShape1:
+
+ case SdrHdlKind::User:
+ {
+ return true;
+ }
+
+ default:
+ {
+ return false;
+ }
+ }
+}
+
+void SdrHdl::onMouseEnter(const MouseEvent& /*rMEvt*/)
+{
+}
+
+void SdrHdl::onHelpRequest()
+{
+}
+
+void SdrHdl::onMouseLeave()
+{
+}
+
+BitmapEx SdrHdl::createGluePointBitmap()
+{
+ return ImpGetBitmapEx(BitmapMarkerKind::Glue_Deselected, BitmapColorIndex::LightGreen);
+}
+
+void SdrHdl::insertNewlyCreatedOverlayObjectForSdrHdl(
+ std::unique_ptr<sdr::overlay::OverlayObject> pOverlayObject,
+ const sdr::contact::ObjectContact& rObjectContact,
+ sdr::overlay::OverlayManager& rOverlayManager)
+{
+ // check if we have an OverlayObject
+ if(!pOverlayObject)
+ {
+ return;
+ }
+
+ // Add GridOffset for non-linear ViewToDevice transformation (calc)
+ if(nullptr != GetObj() && rObjectContact.supportsGridOffsets())
+ {
+ basegfx::B2DVector aOffset(0.0, 0.0);
+ const sdr::contact::ViewObjectContact& rVOC(GetObj()->GetViewContact().GetViewObjectContact(
+ const_cast<sdr::contact::ObjectContact&>(rObjectContact)));
+
+ rObjectContact.calculateGridOffsetForViewOjectContact(aOffset, rVOC);
+
+ if(!aOffset.equalZero())
+ {
+ pOverlayObject->setOffset(aOffset);
+ }
+ }
+
+ // add to OverlayManager
+ rOverlayManager.add(*pOverlayObject);
+
+ // add to local OverlayObjectList - ownership change (!)
+ maOverlayGroup.append(std::move(pOverlayObject));
+}
+
+SdrHdlColor::SdrHdlColor(const Point& rRef, Color aCol, const Size& rSize, bool bLum)
+: SdrHdl(rRef, SdrHdlKind::Color),
+ aMarkerSize(rSize),
+ bUseLuminance(bLum)
+{
+ if(IsUseLuminance())
+ aCol = GetLuminance(aCol);
+
+ // remember color
+ aMarkerColor = aCol;
+}
+
+SdrHdlColor::~SdrHdlColor()
+{
+}
+
+void SdrHdlColor::CreateB2dIAObject()
+{
+ // first throw away old one
+ GetRidOfIAObject();
+
+ if(!pHdlList)
+ return;
+
+ SdrMarkView* pView = pHdlList->GetView();
+
+ if(!pView || pView->areMarkHandlesHidden())
+ return;
+
+ SdrPageView* pPageView = pView->GetSdrPageView();
+
+ if(!pPageView)
+ return;
+
+ for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
+ {
+ const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);
+
+ if(rPageWindow.GetPaintWindow().OutputToWindow())
+ {
+ const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager();
+ if (xManager.is())
+ {
+ BitmapEx aBmpCol(CreateColorDropper(aMarkerColor));
+ basegfx::B2DPoint aPosition(aPos.X(), aPos.Y());
+ std::unique_ptr<sdr::overlay::OverlayObject> pNewOverlayObject(new
+ sdr::overlay::OverlayBitmapEx(
+ aPosition,
+ aBmpCol,
+ static_cast<sal_uInt16>(aBmpCol.GetSizePixel().Width() - 1) >> 1,
+ static_cast<sal_uInt16>(aBmpCol.GetSizePixel().Height() - 1) >> 1
+ ));
+
+ // OVERLAYMANAGER
+ insertNewlyCreatedOverlayObjectForSdrHdl(
+ std::move(pNewOverlayObject),
+ rPageWindow.GetObjectContact(),
+ *xManager);
+ }
+ }
+ }
+}
+
+BitmapEx SdrHdlColor::CreateColorDropper(Color aCol)
+{
+ // get the Bitmap
+ VclPtr<VirtualDevice> pWrite(VclPtr<VirtualDevice>::Create());
+ pWrite->SetOutputSizePixel(aMarkerSize);
+ pWrite->SetBackground(aCol);
+ pWrite->Erase();
+
+ // draw outer border
+ sal_Int32 nWidth = aMarkerSize.Width();
+ sal_Int32 nHeight = aMarkerSize.Height();
+
+ pWrite->SetLineColor(COL_LIGHTGRAY);
+ pWrite->DrawLine(Point(0, 0), Point(0, nHeight - 1));
+ pWrite->DrawLine(Point(1, 0), Point(nWidth - 1, 0));
+ pWrite->SetLineColor(COL_GRAY);
+ pWrite->DrawLine(Point(1, nHeight - 1), Point(nWidth - 1, nHeight - 1));
+ pWrite->DrawLine(Point(nWidth - 1, 1), Point(nWidth - 1, nHeight - 2));
+
+ // draw lighter UpperLeft
+ const Color aLightColor(
+ static_cast<sal_uInt8>(::std::min(static_cast<sal_Int16>(static_cast<sal_Int16>(aCol.GetRed()) + sal_Int16(0x0040)), sal_Int16(0x00ff))),
+ static_cast<sal_uInt8>(::std::min(static_cast<sal_Int16>(static_cast<sal_Int16>(aCol.GetGreen()) + sal_Int16(0x0040)), sal_Int16(0x00ff))),
+ static_cast<sal_uInt8>(::std::min(static_cast<sal_Int16>(static_cast<sal_Int16>(aCol.GetBlue()) + sal_Int16(0x0040)), sal_Int16(0x00ff))));
+ pWrite->SetLineColor(aLightColor);
+ pWrite->DrawLine(Point(1, 1), Point(1, nHeight - 2));
+ pWrite->DrawLine(Point(2, 1), Point(nWidth - 2, 1));
+
+ // draw darker LowerRight
+ const Color aDarkColor(
+ static_cast<sal_uInt8>(::std::max(static_cast<sal_Int16>(static_cast<sal_Int16>(aCol.GetRed()) - sal_Int16(0x0040)), sal_Int16(0x0000))),
+ static_cast<sal_uInt8>(::std::max(static_cast<sal_Int16>(static_cast<sal_Int16>(aCol.GetGreen()) - sal_Int16(0x0040)), sal_Int16(0x0000))),
+ static_cast<sal_uInt8>(::std::max(static_cast<sal_Int16>(static_cast<sal_Int16>(aCol.GetBlue()) - sal_Int16(0x0040)), sal_Int16(0x0000))));
+ pWrite->SetLineColor(aDarkColor);
+ pWrite->DrawLine(Point(2, nHeight - 2), Point(nWidth - 2, nHeight - 2));
+ pWrite->DrawLine(Point(nWidth - 2, 2), Point(nWidth - 2, nHeight - 3));
+
+ return pWrite->GetBitmapEx(Point(0,0), aMarkerSize);
+}
+
+Color SdrHdlColor::GetLuminance(const Color& rCol)
+{
+ sal_uInt8 aLum = rCol.GetLuminance();
+ Color aRetval(aLum, aLum, aLum);
+ return aRetval;
+}
+
+void SdrHdlColor::SetColor(Color aNew, bool bCallLink)
+{
+ if(IsUseLuminance())
+ aNew = GetLuminance(aNew);
+
+ if(aMarkerColor != aNew)
+ {
+ // remember new color
+ aMarkerColor = aNew;
+
+ // create new display
+ Touch();
+
+ // tell about change
+ if(bCallLink)
+ aColorChangeHdl.Call(this);
+ }
+}
+
+void SdrHdlColor::SetSize(const Size& rNew)
+{
+ if(rNew != aMarkerSize)
+ {
+ // remember new size
+ aMarkerSize = rNew;
+
+ // create new display
+ Touch();
+ }
+}
+
+SdrHdlGradient::SdrHdlGradient(const Point& rRef1, const Point& rRef2, bool bGrad)
+ : SdrHdl(rRef1, bGrad ? SdrHdlKind::Gradient : SdrHdlKind::Transparence)
+ , pColHdl1(nullptr)
+ , pColHdl2(nullptr)
+ , a2ndPos(rRef2)
+ , bGradient(bGrad)
+ , bMoveSingleHandle(false)
+ , bMoveFirstHandle(false)
+{
+}
+
+SdrHdlGradient::~SdrHdlGradient()
+{
+}
+
+void SdrHdlGradient::Set2ndPos(const Point& rPnt)
+{
+ if(a2ndPos != rPnt)
+ {
+ // remember new position
+ a2ndPos = rPnt;
+
+ // create new display
+ Touch();
+ }
+}
+
+void SdrHdlGradient::CreateB2dIAObject()
+{
+ // first throw away old one
+ GetRidOfIAObject();
+
+ if(!pHdlList)
+ return;
+
+ SdrMarkView* pView = pHdlList->GetView();
+
+ if(!pView || pView->areMarkHandlesHidden())
+ return;
+
+ SdrPageView* pPageView = pView->GetSdrPageView();
+
+ if(!pPageView)
+ return;
+
+ for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
+ {
+ const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);
+
+ if(rPageWindow.GetPaintWindow().OutputToWindow())
+ {
+ const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager();
+ if (xManager.is())
+ {
+ // striped line in between
+ basegfx::B2DVector aVec(a2ndPos.X() - aPos.X(), a2ndPos.Y() - aPos.Y());
+ double fVecLen = aVec.getLength();
+ double fLongPercentArrow = (1.0 - 0.05) * fVecLen;
+ double fHalfArrowWidth = (0.05 * 0.5) * fVecLen;
+ aVec.normalize();
+ basegfx::B2DVector aPerpend(-aVec.getY(), aVec.getX());
+ sal_Int32 nMidX = static_cast<sal_Int32>(aPos.X() + aVec.getX() * fLongPercentArrow);
+ sal_Int32 nMidY = static_cast<sal_Int32>(aPos.Y() + aVec.getY() * fLongPercentArrow);
+ Point aMidPoint(nMidX, nMidY);
+
+ basegfx::B2DPoint aPosition(aPos.X(), aPos.Y());
+ basegfx::B2DPoint aMidPos(aMidPoint.X(), aMidPoint.Y());
+
+ std::unique_ptr<sdr::overlay::OverlayObject> pNewOverlayObject(new
+ sdr::overlay::OverlayLineStriped(
+ aPosition, aMidPos
+ ));
+
+ pNewOverlayObject->setBaseColor(IsGradient() ? COL_BLACK : COL_BLUE);
+
+ // OVERLAYMANAGER
+ insertNewlyCreatedOverlayObjectForSdrHdl(
+ std::move(pNewOverlayObject),
+ rPageWindow.GetObjectContact(),
+ *xManager);
+
+ // arrowhead
+ Point aLeft(aMidPoint.X() + static_cast<sal_Int32>(aPerpend.getX() * fHalfArrowWidth),
+ aMidPoint.Y() + static_cast<sal_Int32>(aPerpend.getY() * fHalfArrowWidth));
+ Point aRight(aMidPoint.X() - static_cast<sal_Int32>(aPerpend.getX() * fHalfArrowWidth),
+ aMidPoint.Y() - static_cast<sal_Int32>(aPerpend.getY() * fHalfArrowWidth));
+
+ basegfx::B2DPoint aPositionLeft(aLeft.X(), aLeft.Y());
+ basegfx::B2DPoint aPositionRight(aRight.X(), aRight.Y());
+ basegfx::B2DPoint aPosition2(a2ndPos.X(), a2ndPos.Y());
+
+ pNewOverlayObject.reset(new
+ sdr::overlay::OverlayTriangle(
+ aPositionLeft,
+ aPosition2,
+ aPositionRight,
+ IsGradient() ? COL_BLACK : COL_BLUE
+ ));
+
+ // OVERLAYMANAGER
+ insertNewlyCreatedOverlayObjectForSdrHdl(
+ std::move(pNewOverlayObject),
+ rPageWindow.GetObjectContact(),
+ *xManager);
+ }
+ }
+ }
+}
+
+IMPL_LINK_NOARG(SdrHdlGradient, ColorChangeHdl, SdrHdlColor*, void)
+{
+ if(GetObj())
+ FromIAOToItem(GetObj(), true, true);
+}
+
+void SdrHdlGradient::FromIAOToItem(SdrObject* _pObj, bool bSetItemOnObject, bool bUndo)
+{
+ // from IAO positions and colors to gradient
+ const SfxItemSet& rSet = _pObj->GetMergedItemSet();
+
+ GradTransGradient aOldGradTransGradient;
+ GradTransGradient aGradTransGradient;
+ GradTransVector aGradTransVector;
+
+ aGradTransVector.maPositionA = basegfx::B2DPoint(GetPos().X(), GetPos().Y());
+ aGradTransVector.maPositionB = basegfx::B2DPoint(Get2ndPos().X(), Get2ndPos().Y());
+ if(pColHdl1)
+ aGradTransVector.aCol1 = pColHdl1->GetColor();
+ if(pColHdl2)
+ aGradTransVector.aCol2 = pColHdl2->GetColor();
+
+ if(IsGradient())
+ aOldGradTransGradient.aGradient = rSet.Get(XATTR_FILLGRADIENT).GetGradientValue();
+ else
+ aOldGradTransGradient.aGradient = rSet.Get(XATTR_FILLFLOATTRANSPARENCE).GetGradientValue();
+
+ // transform vector data to gradient
+ GradTransformer::VecToGrad(aGradTransVector, aGradTransGradient, aOldGradTransGradient, _pObj, bMoveSingleHandle, bMoveFirstHandle);
+
+ if(bSetItemOnObject)
+ {
+ SdrModel& rModel(_pObj->getSdrModelFromSdrObject());
+ SfxItemSet aNewSet(rModel.GetItemPool());
+ const OUString aString;
+
+ if(IsGradient())
+ {
+ XFillGradientItem aNewGradItem(aString, aGradTransGradient.aGradient);
+ aNewSet.Put(aNewGradItem);
+ }
+ else
+ {
+ XFillFloatTransparenceItem aNewTransItem(aString, aGradTransGradient.aGradient);
+ aNewSet.Put(aNewTransItem);
+ }
+
+ if(bUndo && rModel.IsUndoEnabled())
+ {
+ rModel.BegUndo(SvxResId(IsGradient() ? SIP_XA_FILLGRADIENT : SIP_XA_FILLTRANSPARENCE));
+ rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoAttrObject(*_pObj));
+ rModel.EndUndo();
+ }
+
+ pObj->SetMergedItemSetAndBroadcast(aNewSet);
+ }
+
+ // back transformation, set values on pIAOHandle
+ GradTransformer::GradToVec(aGradTransGradient, aGradTransVector, _pObj);
+
+ SetPos(Point(FRound(aGradTransVector.maPositionA.getX()), FRound(aGradTransVector.maPositionA.getY())));
+ Set2ndPos(Point(FRound(aGradTransVector.maPositionB.getX()), FRound(aGradTransVector.maPositionB.getY())));
+ if(pColHdl1)
+ {
+ pColHdl1->SetPos(Point(FRound(aGradTransVector.maPositionA.getX()), FRound(aGradTransVector.maPositionA.getY())));
+ pColHdl1->SetColor(aGradTransVector.aCol1);
+ }
+ if(pColHdl2)
+ {
+ pColHdl2->SetPos(Point(FRound(aGradTransVector.maPositionB.getX()), FRound(aGradTransVector.maPositionB.getY())));
+ pColHdl2->SetColor(aGradTransVector.aCol2);
+ }
+}
+
+
+SdrHdlLine::~SdrHdlLine() {}
+
+void SdrHdlLine::CreateB2dIAObject()
+{
+ // first throw away old one
+ GetRidOfIAObject();
+
+ if(!pHdlList)
+ return;
+
+ SdrMarkView* pView = pHdlList->GetView();
+
+ if(!(pView && !pView->areMarkHandlesHidden() && pHdl1 && pHdl2))
+ return;
+
+ SdrPageView* pPageView = pView->GetSdrPageView();
+
+ if(!pPageView)
+ return;
+
+ for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
+ {
+ const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);
+
+ if(rPageWindow.GetPaintWindow().OutputToWindow())
+ {
+ const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager();
+ if (xManager.is())
+ {
+ basegfx::B2DPoint aPosition1(pHdl1->GetPos().X(), pHdl1->GetPos().Y());
+ basegfx::B2DPoint aPosition2(pHdl2->GetPos().X(), pHdl2->GetPos().Y());
+
+ std::unique_ptr<sdr::overlay::OverlayObject> pNewOverlayObject(new
+ sdr::overlay::OverlayLineStriped(
+ aPosition1,
+ aPosition2
+ ));
+
+ // color(?)
+ pNewOverlayObject->setBaseColor(COL_LIGHTRED);
+
+ // OVERLAYMANAGER
+ insertNewlyCreatedOverlayObjectForSdrHdl(
+ std::move(pNewOverlayObject),
+ rPageWindow.GetObjectContact(),
+ *xManager);
+ }
+ }
+ }
+}
+
+PointerStyle SdrHdlLine::GetPointer() const
+{
+ return PointerStyle::RefHand;
+}
+
+
+SdrHdlBezWgt::~SdrHdlBezWgt() {}
+
+void SdrHdlBezWgt::CreateB2dIAObject()
+{
+ // call parent
+ SdrHdl::CreateB2dIAObject();
+
+ // create lines
+ if(!pHdlList)
+ return;
+
+ SdrMarkView* pView = pHdlList->GetView();
+
+ if(!pView || pView->areMarkHandlesHidden())
+ return;
+
+ SdrPageView* pPageView = pView->GetSdrPageView();
+
+ if(!pPageView)
+ return;
+
+ for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
+ {
+ const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);
+
+ if(rPageWindow.GetPaintWindow().OutputToWindow())
+ {
+ const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager();
+ if (xManager.is())
+ {
+ basegfx::B2DPoint aPosition1(pHdl1->GetPos().X(), pHdl1->GetPos().Y());
+ basegfx::B2DPoint aPosition2(aPos.X(), aPos.Y());
+
+ if(!aPosition1.equal(aPosition2))
+ {
+ std::unique_ptr<sdr::overlay::OverlayObject> pNewOverlayObject(new
+ sdr::overlay::OverlayLineStriped(
+ aPosition1,
+ aPosition2
+ ));
+
+ // line part is not hittable
+ pNewOverlayObject->setHittable(false);
+
+ // color(?)
+ pNewOverlayObject->setBaseColor(COL_LIGHTBLUE);
+
+ // OVERLAYMANAGER
+ insertNewlyCreatedOverlayObjectForSdrHdl(
+ std::move(pNewOverlayObject),
+ rPageWindow.GetObjectContact(),
+ *xManager);
+ }
+ }
+ }
+ }
+}
+
+
+E3dVolumeMarker::E3dVolumeMarker(const basegfx::B2DPolyPolygon& rWireframePoly)
+{
+ aWireframePoly = rWireframePoly;
+}
+
+void E3dVolumeMarker::CreateB2dIAObject()
+{
+ // create lines
+ if(!pHdlList)
+ return;
+
+ SdrMarkView* pView = pHdlList->GetView();
+
+ if(!pView || pView->areMarkHandlesHidden())
+ return;
+
+ SdrPageView* pPageView = pView->GetSdrPageView();
+
+ if(!pPageView)
+ return;
+
+ for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
+ {
+ const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);
+
+ if(rPageWindow.GetPaintWindow().OutputToWindow())
+ {
+ const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager();
+ if (xManager.is() && aWireframePoly.count())
+ {
+ std::unique_ptr<sdr::overlay::OverlayObject> pNewOverlayObject(new
+ sdr::overlay::OverlayPolyPolygonStripedAndFilled(
+ aWireframePoly));
+
+ pNewOverlayObject->setBaseColor(COL_BLACK);
+
+ // OVERLAYMANAGER
+ insertNewlyCreatedOverlayObjectForSdrHdl(
+ std::move(pNewOverlayObject),
+ rPageWindow.GetObjectContact(),
+ *xManager);
+ }
+ }
+ }
+}
+
+
+ImpEdgeHdl::~ImpEdgeHdl()
+{
+}
+
+void ImpEdgeHdl::CreateB2dIAObject()
+{
+ if(nObjHdlNum <= 1 && pObj)
+ {
+ // first throw away old one
+ GetRidOfIAObject();
+
+ BitmapColorIndex eColIndex = BitmapColorIndex::LightCyan;
+ BitmapMarkerKind eKindOfMarker = BitmapMarkerKind::Rect_7x7;
+
+ if(pHdlList)
+ {
+ SdrMarkView* pView = pHdlList->GetView();
+
+ if(pView && !pView->areMarkHandlesHidden())
+ {
+ const SdrEdgeObj* pEdge = static_cast<SdrEdgeObj*>(pObj);
+
+ if(pEdge->GetConnectedNode(nObjHdlNum == 0) != nullptr)
+ eColIndex = BitmapColorIndex::LightRed;
+
+ if(nPPntNum < 2)
+ {
+ // Handle with plus sign inside
+ eKindOfMarker = BitmapMarkerKind::Circ_7x7;
+ }
+
+ SdrPageView* pPageView = pView->GetSdrPageView();
+
+ if(pPageView)
+ {
+ for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
+ {
+ const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);
+
+ if(rPageWindow.GetPaintWindow().OutputToWindow())
+ {
+ const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager();
+ if (xManager.is())
+ {
+ basegfx::B2DPoint aPosition(aPos.X(), aPos.Y());
+ std::unique_ptr<sdr::overlay::OverlayObject> pNewOverlayObject(CreateOverlayObject(
+ aPosition,
+ eColIndex,
+ eKindOfMarker));
+
+ // OVERLAYMANAGER
+ insertNewlyCreatedOverlayObjectForSdrHdl(
+ std::move(pNewOverlayObject),
+ rPageWindow.GetObjectContact(),
+ *xManager);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ // call parent
+ SdrHdl::CreateB2dIAObject();
+ }
+}
+
+void ImpEdgeHdl::SetLineCode(SdrEdgeLineCode eCode)
+{
+ if(eLineCode != eCode)
+ {
+ // remember new value
+ eLineCode = eCode;
+
+ // create new display
+ Touch();
+ }
+}
+
+PointerStyle ImpEdgeHdl::GetPointer() const
+{
+ SdrEdgeObj* pEdge=dynamic_cast<SdrEdgeObj*>( pObj );
+ if (pEdge==nullptr)
+ return SdrHdl::GetPointer();
+ if (nObjHdlNum<=1)
+ return PointerStyle::MovePoint;
+ if (IsHorzDrag())
+ return PointerStyle::ESize;
+ else
+ return PointerStyle::SSize;
+}
+
+bool ImpEdgeHdl::IsHorzDrag() const
+{
+ SdrEdgeObj* pEdge=dynamic_cast<SdrEdgeObj*>( pObj );
+ if (pEdge==nullptr)
+ return false;
+ if (nObjHdlNum<=1)
+ return false;
+
+ SdrEdgeKind eEdgeKind = pEdge->GetObjectItem(SDRATTR_EDGEKIND).GetValue();
+
+ const SdrEdgeInfoRec& rInfo=pEdge->aEdgeInfo;
+ if (eEdgeKind==SdrEdgeKind::OrthoLines || eEdgeKind==SdrEdgeKind::Bezier)
+ {
+ return !rInfo.ImpIsHorzLine(eLineCode,*pEdge->pEdgeTrack);
+ }
+ else if (eEdgeKind==SdrEdgeKind::ThreeLines)
+ {
+ tools::Long nAngle=nObjHdlNum==2 ? rInfo.nAngle1 : rInfo.nAngle2;
+ return nAngle==0 || nAngle==18000;
+ }
+ return false;
+}
+
+
+ImpMeasureHdl::~ImpMeasureHdl()
+{
+}
+
+void ImpMeasureHdl::CreateB2dIAObject()
+{
+ // first throw away old one
+ GetRidOfIAObject();
+
+ if(!pHdlList)
+ return;
+
+ SdrMarkView* pView = pHdlList->GetView();
+
+ if(!pView || pView->areMarkHandlesHidden())
+ return;
+
+ BitmapColorIndex eColIndex = BitmapColorIndex::LightCyan;
+ BitmapMarkerKind eKindOfMarker = BitmapMarkerKind::Rect_9x9;
+
+ if(nObjHdlNum > 1)
+ {
+ eKindOfMarker = BitmapMarkerKind::Rect_7x7;
+ }
+
+ if(bSelect)
+ {
+ eColIndex = BitmapColorIndex::Cyan;
+ }
+
+ SdrPageView* pPageView = pView->GetSdrPageView();
+
+ if(!pPageView)
+ return;
+
+ for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
+ {
+ const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);
+
+ if(rPageWindow.GetPaintWindow().OutputToWindow())
+ {
+ const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager();
+ if (xManager.is())
+ {
+ basegfx::B2DPoint aPosition(aPos.X(), aPos.Y());
+ std::unique_ptr<sdr::overlay::OverlayObject> pNewOverlayObject(CreateOverlayObject(
+ aPosition,
+ eColIndex,
+ eKindOfMarker));
+
+ // OVERLAYMANAGER
+ insertNewlyCreatedOverlayObjectForSdrHdl(
+ std::move(pNewOverlayObject),
+ rPageWindow.GetObjectContact(),
+ *xManager);
+ }
+ }
+ }
+}
+
+PointerStyle ImpMeasureHdl::GetPointer() const
+{
+ switch (nObjHdlNum)
+ {
+ case 0: case 1: return PointerStyle::Hand;
+ case 2: case 3: return PointerStyle::MovePoint;
+ case 4: case 5: return SdrHdl::GetPointer(); // will then be rotated appropriately
+ } // switch
+ return PointerStyle::NotAllowed;
+}
+
+
+ImpTextframeHdl::ImpTextframeHdl(const tools::Rectangle& rRect) :
+ SdrHdl(rRect.TopLeft(),SdrHdlKind::Move),
+ maRect(rRect)
+{
+}
+
+void ImpTextframeHdl::CreateB2dIAObject()
+{
+ // first throw away old one
+ GetRidOfIAObject();
+
+ if(!pHdlList)
+ return;
+
+ SdrMarkView* pView = pHdlList->GetView();
+
+ if(!pView || pView->areMarkHandlesHidden())
+ return;
+
+ SdrPageView* pPageView = pView->GetSdrPageView();
+
+ if(!pPageView)
+ return;
+
+ for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
+ {
+ const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);
+
+ if(rPageWindow.GetPaintWindow().OutputToWindow())
+ {
+ const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager();
+ if (xManager.is())
+ {
+ const basegfx::B2DPoint aTopLeft(maRect.Left(), maRect.Top());
+ const basegfx::B2DPoint aBottomRight(maRect.Right(), maRect.Bottom());
+ const Color aHilightColor(SvtOptionsDrawinglayer::getHilightColor());
+ const double fTransparence(SvtOptionsDrawinglayer::GetTransparentSelectionPercent() * 0.01);
+
+ std::unique_ptr<sdr::overlay::OverlayRectangle> pNewOverlayObject(new sdr::overlay::OverlayRectangle(
+ aTopLeft,
+ aBottomRight,
+ aHilightColor,
+ fTransparence,
+ 3.0,
+ 3.0,
+ -toRadians(nRotationAngle),
+ true)); // allow animation; the Handle is not shown at text edit time
+
+ pNewOverlayObject->setHittable(false);
+
+ // OVERLAYMANAGER
+ insertNewlyCreatedOverlayObjectForSdrHdl(
+ std::move(pNewOverlayObject),
+ rPageWindow.GetObjectContact(),
+ *xManager);
+ }
+ }
+ }
+}
+
+
+static bool ImpSdrHdlListSorter(std::unique_ptr<SdrHdl> const& lhs, std::unique_ptr<SdrHdl> const& rhs)
+{
+ SdrHdlKind eKind1=lhs->GetKind();
+ SdrHdlKind eKind2=rhs->GetKind();
+ // Level 1: first normal handles, then Glue, then User, then Plus handles, then reference point handles
+ unsigned n1=1;
+ unsigned n2=1;
+ if (eKind1!=eKind2)
+ {
+ if (eKind1==SdrHdlKind::Ref1 || eKind1==SdrHdlKind::Ref2 || eKind1==SdrHdlKind::MirrorAxis) n1=5;
+ else if (eKind1==SdrHdlKind::Glue) n1=2;
+ else if (eKind1==SdrHdlKind::User) n1=3;
+ else if (eKind1==SdrHdlKind::SmartTag) n1=0;
+ if (eKind2==SdrHdlKind::Ref1 || eKind2==SdrHdlKind::Ref2 || eKind2==SdrHdlKind::MirrorAxis) n2=5;
+ else if (eKind2==SdrHdlKind::Glue) n2=2;
+ else if (eKind2==SdrHdlKind::User) n2=3;
+ else if (eKind2==SdrHdlKind::SmartTag) n2=0;
+ }
+ if (lhs->IsPlusHdl()) n1=4;
+ if (rhs->IsPlusHdl()) n2=4;
+ if (n1==n2)
+ {
+ // Level 2: PageView (Pointer)
+ SdrPageView* pPV1=lhs->GetPageView();
+ SdrPageView* pPV2=rhs->GetPageView();
+ if (pPV1==pPV2)
+ {
+ // Level 3: Position (x+y)
+ SdrObject* pObj1=lhs->GetObj();
+ SdrObject* pObj2=rhs->GetObj();
+ if (pObj1==pObj2)
+ {
+ sal_uInt32 nNum1=lhs->GetObjHdlNum();
+ sal_uInt32 nNum2=rhs->GetObjHdlNum();
+ if (nNum1==nNum2)
+ {
+ if (eKind1==eKind2)
+ return lhs<rhs; // Hack, to always get to the same sorting
+ return static_cast<sal_uInt16>(eKind1)<static_cast<sal_uInt16>(eKind2);
+ }
+ else
+ return nNum1<nNum2;
+ }
+ else
+ {
+ return pObj1<pObj2;
+ }
+ }
+ else
+ {
+ return pPV1<pPV2;
+ }
+ }
+ else
+ {
+ return n1<n2;
+ }
+}
+
+namespace {
+
+// Helper struct for re-sorting handles
+struct ImplHdlAndIndex
+{
+ SdrHdl* mpHdl;
+ sal_uInt32 mnIndex;
+};
+
+}
+
+extern "C" {
+
+// Helper method for sorting handles taking care of OrdNums, keeping order in
+// single objects and re-sorting polygon handles intuitively
+static int ImplSortHdlFunc( const void* pVoid1, const void* pVoid2 )
+{
+ const ImplHdlAndIndex* p1 = static_cast<ImplHdlAndIndex const *>(pVoid1);
+ const ImplHdlAndIndex* p2 = static_cast<ImplHdlAndIndex const *>(pVoid2);
+
+ if(p1->mpHdl->GetObj() == p2->mpHdl->GetObj())
+ {
+ if(p1->mpHdl->GetObj() && dynamic_cast<const SdrPathObj*>(p1->mpHdl->GetObj()) != nullptr)
+ {
+ // same object and a path object
+ if((p1->mpHdl->GetKind() == SdrHdlKind::Poly || p1->mpHdl->GetKind() == SdrHdlKind::BezierWeight)
+ && (p2->mpHdl->GetKind() == SdrHdlKind::Poly || p2->mpHdl->GetKind() == SdrHdlKind::BezierWeight))
+ {
+ // both handles are point or control handles
+ if(p1->mpHdl->GetPolyNum() == p2->mpHdl->GetPolyNum())
+ {
+ if(p1->mpHdl->GetPointNum() < p2->mpHdl->GetPointNum())
+ {
+ return -1;
+ }
+ else
+ {
+ return 1;
+ }
+ }
+ else if(p1->mpHdl->GetPolyNum() < p2->mpHdl->GetPolyNum())
+ {
+ return -1;
+ }
+ else
+ {
+ return 1;
+ }
+ }
+ }
+ }
+ else
+ {
+ if(!p1->mpHdl->GetObj())
+ {
+ return -1;
+ }
+ else if(!p2->mpHdl->GetObj())
+ {
+ return 1;
+ }
+ else
+ {
+ // different objects, use OrdNum for sort
+ const sal_uInt32 nOrdNum1 = p1->mpHdl->GetObj()->GetOrdNum();
+ const sal_uInt32 nOrdNum2 = p2->mpHdl->GetObj()->GetOrdNum();
+
+ if(nOrdNum1 < nOrdNum2)
+ {
+ return -1;
+ }
+ else
+ {
+ return 1;
+ }
+ }
+ }
+
+ // fallback to indices
+ if(p1->mnIndex < p2->mnIndex)
+ {
+ return -1;
+ }
+ else
+ {
+ return 1;
+ }
+}
+
+}
+
+void SdrHdlList::TravelFocusHdl(bool bForward)
+{
+ // security correction
+ if (mnFocusIndex >= GetHdlCount())
+ mnFocusIndex = SAL_MAX_SIZE;
+
+ if(maList.empty())
+ return;
+
+ // take care of old handle
+ const size_t nOldHdlNum(mnFocusIndex);
+ SdrHdl* pOld = nullptr;
+ if (nOldHdlNum < GetHdlCount())
+ pOld = GetHdl(nOldHdlNum);
+
+ if(pOld)
+ {
+ // switch off old handle
+ mnFocusIndex = SAL_MAX_SIZE;
+ pOld->Touch();
+ }
+
+ // allocate pointer array for sorted handle list
+ std::unique_ptr<ImplHdlAndIndex[]> pHdlAndIndex(new ImplHdlAndIndex[maList.size()]);
+
+ // build sorted handle list
+ for( size_t a = 0; a < maList.size(); ++a)
+ {
+ pHdlAndIndex[a].mpHdl = maList[a].get();
+ pHdlAndIndex[a].mnIndex = a;
+ }
+
+ qsort(pHdlAndIndex.get(), maList.size(), sizeof(ImplHdlAndIndex), ImplSortHdlFunc);
+
+ // look for old num in sorted array
+ size_t nOldHdl(nOldHdlNum);
+
+ if(nOldHdlNum != SAL_MAX_SIZE)
+ {
+ for(size_t a = 0; a < maList.size(); ++a)
+ {
+ if(pHdlAndIndex[a].mpHdl == pOld)
+ {
+ nOldHdl = a;
+ break;
+ }
+ }
+ }
+
+ // build new HdlNum
+ size_t nNewHdl(nOldHdl);
+
+ // do the focus travel
+ if(bForward)
+ {
+ if(nOldHdl != SAL_MAX_SIZE)
+ {
+ if(nOldHdl == maList.size() - 1)
+ {
+ // end forward run
+ nNewHdl = SAL_MAX_SIZE;
+ }
+ else
+ {
+ // simply the next handle
+ nNewHdl++;
+ }
+ }
+ else
+ {
+ // start forward run at first entry
+ nNewHdl = 0;
+ }
+ }
+ else
+ {
+ if(nOldHdl == SAL_MAX_SIZE)
+ {
+ // start backward run at last entry
+ nNewHdl = maList.size() - 1;
+
+ }
+ else
+ {
+ if(nOldHdl == 0)
+ {
+ // end backward run
+ nNewHdl = SAL_MAX_SIZE;
+ }
+ else
+ {
+ // simply the previous handle
+ nNewHdl--;
+ }
+ }
+ }
+
+ // build new HdlNum
+ sal_uIntPtr nNewHdlNum(nNewHdl);
+
+ // look for old num in sorted array
+ if(nNewHdl != SAL_MAX_SIZE)
+ {
+ SdrHdl* pNew = pHdlAndIndex[nNewHdl].mpHdl;
+
+ for(size_t a = 0; a < maList.size(); ++a)
+ {
+ if(maList[a].get() == pNew)
+ {
+ nNewHdlNum = a;
+ break;
+ }
+ }
+ }
+
+ // take care of next handle
+ if(nOldHdlNum != nNewHdlNum)
+ {
+ mnFocusIndex = nNewHdlNum;
+ if (mnFocusIndex < GetHdlCount())
+ {
+ SdrHdl* pNew = GetHdl(mnFocusIndex);
+ pNew->Touch();
+ }
+ }
+}
+
+SdrHdl* SdrHdlList::GetFocusHdl() const
+{
+ if(mnFocusIndex < GetHdlCount())
+ return GetHdl(mnFocusIndex);
+ else
+ return nullptr;
+}
+
+void SdrHdlList::SetFocusHdl(SdrHdl* pNew)
+{
+ if(!pNew)
+ return;
+
+ SdrHdl* pActual = GetFocusHdl();
+
+ if(pActual && pActual == pNew)
+ return;
+
+ const size_t nNewHdlNum = GetHdlNum(pNew);
+
+ if(nNewHdlNum != SAL_MAX_SIZE)
+ {
+ mnFocusIndex = nNewHdlNum;
+
+ if(pActual)
+ {
+ pActual->Touch();
+ }
+
+ pNew->Touch();
+ }
+}
+
+void SdrHdlList::ResetFocusHdl()
+{
+ SdrHdl* pHdl = GetFocusHdl();
+
+ mnFocusIndex = SAL_MAX_SIZE;
+
+ if(pHdl)
+ {
+ pHdl->Touch();
+ }
+}
+
+
+SdrHdlList::SdrHdlList(SdrMarkView* pV)
+: mnFocusIndex(SAL_MAX_SIZE),
+ pView(pV)
+{
+ nHdlSize = 3;
+ bRotateShear = false;
+ bMoveOutside = false;
+ bDistortShear = false;
+}
+
+SdrHdlList::~SdrHdlList()
+{
+ Clear();
+}
+
+void SdrHdlList::SetHdlSize(sal_uInt16 nSiz)
+{
+ if(nHdlSize != nSiz)
+ {
+ // remember new value
+ nHdlSize = nSiz;
+
+ // propagate change to IAOs
+ for(size_t i=0; i<GetHdlCount(); ++i)
+ {
+ SdrHdl* pHdl = GetHdl(i);
+ pHdl->Touch();
+ }
+ }
+}
+
+void SdrHdlList::SetMoveOutside(bool bOn)
+{
+ if(bMoveOutside != bOn)
+ {
+ // remember new value
+ bMoveOutside = bOn;
+
+ // propagate change to IAOs
+ for(size_t i=0; i<GetHdlCount(); ++i)
+ {
+ SdrHdl* pHdl = GetHdl(i);
+ pHdl->Touch();
+ }
+ }
+}
+
+void SdrHdlList::SetRotateShear(bool bOn)
+{
+ bRotateShear = bOn;
+}
+
+void SdrHdlList::SetDistortShear(bool bOn)
+{
+ bDistortShear = bOn;
+}
+
+std::unique_ptr<SdrHdl> SdrHdlList::RemoveHdl(size_t nNum)
+{
+ std::unique_ptr<SdrHdl> pRetval = std::move(maList[nNum]);
+ maList.erase(maList.begin() + nNum);
+
+ return pRetval;
+}
+
+void SdrHdlList::RemoveAllByKind(SdrHdlKind eKind)
+{
+ maList.erase(std::remove_if(maList.begin(), maList.end(),
+ [&eKind](std::unique_ptr<SdrHdl>& rItem) { return rItem->GetKind() == eKind; }),
+ maList.end());
+}
+
+void SdrHdlList::Clear()
+{
+ maList.clear();
+
+ bRotateShear=false;
+ bDistortShear=false;
+}
+
+void SdrHdlList::Sort()
+{
+ // remember currently focused handle
+ SdrHdl* pPrev = GetFocusHdl();
+
+ std::sort( maList.begin(), maList.end(), ImpSdrHdlListSorter );
+
+ // get now and compare
+ SdrHdl* pNow = GetFocusHdl();
+
+ if(pPrev == pNow)
+ return;
+
+ if(pPrev)
+ {
+ pPrev->Touch();
+ }
+
+ if(pNow)
+ {
+ pNow->Touch();
+ }
+}
+
+size_t SdrHdlList::GetHdlNum(const SdrHdl* pHdl) const
+{
+ if (pHdl==nullptr)
+ return SAL_MAX_SIZE;
+ auto it = std::find_if( maList.begin(), maList.end(),
+ [&](const std::unique_ptr<SdrHdl> & p) { return p.get() == pHdl; });
+ assert(it != maList.end());
+ if( it == maList.end() )
+ return SAL_MAX_SIZE;
+ return it - maList.begin();
+}
+
+void SdrHdlList::AddHdl(std::unique_ptr<SdrHdl> pHdl)
+{
+ assert(pHdl);
+ pHdl->SetHdlList(this);
+ maList.push_back(std::move(pHdl));
+}
+
+SdrHdl* SdrHdlList::IsHdlListHit(const Point& rPnt) const
+{
+ SdrHdl* pRet=nullptr;
+ const size_t nCount=GetHdlCount();
+ size_t nNum=nCount;
+ while (nNum>0 && pRet==nullptr)
+ {
+ nNum--;
+ SdrHdl* pHdl=GetHdl(nNum);
+ if (pHdl->IsHdlHit(rPnt))
+ pRet=pHdl;
+ }
+ return pRet;
+}
+
+SdrHdl* SdrHdlList::GetHdl(SdrHdlKind eKind1) const
+{
+ SdrHdl* pRet=nullptr;
+ for (size_t i=0; i<GetHdlCount() && pRet==nullptr; ++i)
+ {
+ SdrHdl* pHdl=GetHdl(i);
+ if (pHdl->GetKind()==eKind1)
+ pRet=pHdl;
+ }
+ return pRet;
+}
+
+void SdrHdlList::MoveTo(SdrHdlList& rOther)
+{
+ for (auto & pHdl : maList)
+ pHdl->SetHdlList(&rOther);
+ rOther.maList.insert(rOther.maList.end(),
+ std::make_move_iterator(maList.begin()), std::make_move_iterator(maList.end()));
+ maList.clear();
+}
+
+SdrCropHdl::SdrCropHdl(
+ const Point& rPnt,
+ SdrHdlKind eNewKind,
+ double fShearX,
+ double fRotation)
+: SdrHdl(rPnt, eNewKind),
+ mfShearX(fShearX),
+ mfRotation(fRotation)
+{
+}
+
+
+BitmapEx SdrCropHdl::GetBitmapForHandle( const BitmapEx& rBitmap, int nSize )
+{
+ int nPixelSize = 0, nX = 0, nY = 0, nOffset = 0;
+
+ if( nSize <= 3 )
+ {
+ nPixelSize = 13;
+ nOffset = 0;
+ }
+ else if( nSize <=4 )
+ {
+ nPixelSize = 17;
+ nOffset = 39;
+ }
+ else
+ {
+ nPixelSize = 21;
+ nOffset = 90;
+ }
+
+ switch( eKind )
+ {
+ case SdrHdlKind::UpperLeft: nX = 0; nY = 0; break;
+ case SdrHdlKind::Upper: nX = 1; nY = 0; break;
+ case SdrHdlKind::UpperRight: nX = 2; nY = 0; break;
+ case SdrHdlKind::Left: nX = 0; nY = 1; break;
+ case SdrHdlKind::Right: nX = 2; nY = 1; break;
+ case SdrHdlKind::LowerLeft: nX = 0; nY = 2; break;
+ case SdrHdlKind::Lower: nX = 1; nY = 2; break;
+ case SdrHdlKind::LowerRight: nX = 2; nY = 2; break;
+ default: break;
+ }
+
+ tools::Rectangle aSourceRect( Point( nX * nPixelSize + nOffset, nY * nPixelSize), Size(nPixelSize, nPixelSize) );
+
+ BitmapEx aRetval(rBitmap);
+ aRetval.Crop(aSourceRect);
+ return aRetval;
+}
+
+
+void SdrCropHdl::CreateB2dIAObject()
+{
+ // first throw away old one
+ GetRidOfIAObject();
+
+ SdrMarkView* pView = pHdlList ? pHdlList->GetView() : nullptr;
+ SdrPageView* pPageView = pView ? pView->GetSdrPageView() : nullptr;
+
+ if( !pPageView || pView->areMarkHandlesHidden() )
+ return;
+
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ int nHdlSize = pHdlList->GetHdlSize();
+
+ const BitmapEx aHandlesBitmap(SIP_SA_CROP_MARKERS);
+ BitmapEx aBmpEx1( GetBitmapForHandle( aHandlesBitmap, nHdlSize ) );
+
+ for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
+ {
+ const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);
+
+ if(rPageWindow.GetPaintWindow().OutputToWindow())
+ {
+ const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager();
+ if (xManager.is())
+ {
+ basegfx::B2DPoint aPosition(aPos.X(), aPos.Y());
+
+ std::unique_ptr<sdr::overlay::OverlayObject> pOverlayObject;
+
+ // animate focused handles
+ if(IsFocusHdl() && (pHdlList->GetFocusHdl() == this))
+ {
+ if( nHdlSize >= 2 )
+ nHdlSize = 1;
+
+ BitmapEx aBmpEx2( GetBitmapForHandle( aHandlesBitmap, nHdlSize + 1 ) );
+
+ const sal_uInt64 nBlinkTime = rStyleSettings.GetCursorBlinkTime();
+
+ pOverlayObject.reset(new sdr::overlay::OverlayAnimatedBitmapEx(
+ aPosition,
+ aBmpEx1,
+ aBmpEx2,
+ nBlinkTime,
+ static_cast<sal_uInt16>(aBmpEx1.GetSizePixel().Width() - 1) >> 1,
+ static_cast<sal_uInt16>(aBmpEx1.GetSizePixel().Height() - 1) >> 1,
+ static_cast<sal_uInt16>(aBmpEx2.GetSizePixel().Width() - 1) >> 1,
+ static_cast<sal_uInt16>(aBmpEx2.GetSizePixel().Height() - 1) >> 1,
+ mfShearX,
+ mfRotation));
+ }
+ else
+ {
+ // create centered handle as default
+ pOverlayObject.reset(new sdr::overlay::OverlayBitmapEx(
+ aPosition,
+ aBmpEx1,
+ static_cast<sal_uInt16>(aBmpEx1.GetSizePixel().Width() - 1) >> 1,
+ static_cast<sal_uInt16>(aBmpEx1.GetSizePixel().Height() - 1) >> 1,
+ 0.0,
+ mfShearX,
+ mfRotation));
+ }
+
+ // OVERLAYMANAGER
+ insertNewlyCreatedOverlayObjectForSdrHdl(
+ std::move(pOverlayObject),
+ rPageWindow.GetObjectContact(),
+ *xManager);
+ }
+ }
+ }
+}
+
+
+// with the correction of crop handling I could get rid of the extra mirroring flag, adapted stuff
+// accordingly
+
+SdrCropViewHdl::SdrCropViewHdl(
+ const basegfx::B2DHomMatrix& rObjectTransform,
+ const Graphic& rGraphic,
+ double fCropLeft,
+ double fCropTop,
+ double fCropRight,
+ double fCropBottom)
+: SdrHdl(Point(), SdrHdlKind::User),
+ maObjectTransform(rObjectTransform),
+ maGraphic(rGraphic),
+ mfCropLeft(fCropLeft),
+ mfCropTop(fCropTop),
+ mfCropRight(fCropRight),
+ mfCropBottom(fCropBottom)
+{
+}
+
+namespace {
+
+void translateRotationToMirroring(basegfx::B2DVector & scale, double * rotate) {
+ assert(rotate != nullptr);
+
+ // detect 180 degree rotation, this is the same as mirrored in X and Y,
+ // thus change to mirroring. Prefer mirroring here. Use the equal call
+ // with getSmallValue here, the original which uses rtl::math::approxEqual
+ // is too correct here. Maybe this changes with enhanced precision in aw080
+ // to the better so that this can be reduced to the more precise call again
+ if(basegfx::fTools::equal(fabs(*rotate), M_PI, 0.000000001))
+ {
+ scale.setX(scale.getX() * -1.0);
+ scale.setY(scale.getY() * -1.0);
+ *rotate = 0.0;
+ }
+}
+
+}
+
+void SdrCropViewHdl::CreateB2dIAObject()
+{
+ GetRidOfIAObject();
+ SdrMarkView* pView = pHdlList ? pHdlList->GetView() : nullptr;
+ SdrPageView* pPageView = pView ? pView->GetSdrPageView() : nullptr;
+
+ if(!pPageView || pView->areMarkHandlesHidden())
+ {
+ return;
+ }
+
+ // decompose to have current translate and scale
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+
+ maObjectTransform.decompose(aScale, aTranslate, fRotate, fShearX);
+
+ if(aScale.equalZero())
+ {
+ return;
+ }
+
+ translateRotationToMirroring(aScale, &fRotate);
+
+ // remember mirroring, reset at Scale and adapt crop values for usage;
+ // mirroring can stay in the object transformation, so do not have to
+ // cope with it here (except later for the CroppedImage transformation,
+ // see below)
+ const bool bMirroredX(aScale.getX() < 0.0);
+ const bool bMirroredY(aScale.getY() < 0.0);
+ double fCropLeft(mfCropLeft);
+ double fCropTop(mfCropTop);
+ double fCropRight(mfCropRight);
+ double fCropBottom(mfCropBottom);
+
+ if(bMirroredX)
+ {
+ aScale.setX(-aScale.getX());
+ }
+
+ if(bMirroredY)
+ {
+ aScale.setY(-aScale.getY());
+ }
+
+ // create target translate and scale
+ const basegfx::B2DVector aTargetScale(
+ aScale.getX() + fCropRight + fCropLeft,
+ aScale.getY() + fCropBottom + fCropTop);
+ const basegfx::B2DVector aTargetTranslate(
+ aTranslate.getX() - fCropLeft,
+ aTranslate.getY() - fCropTop);
+
+ // create ranges to make comparisons
+ const basegfx::B2DRange aCurrentForCompare(
+ aTranslate.getX(), aTranslate.getY(),
+ aTranslate.getX() + aScale.getX(), aTranslate.getY() + aScale.getY());
+ basegfx::B2DRange aCropped(
+ aTargetTranslate.getX(), aTargetTranslate.getY(),
+ aTargetTranslate.getX() + aTargetScale.getX(), aTargetTranslate.getY() + aTargetScale.getY());
+
+ if(aCropped.isEmpty())
+ {
+ // nothing to return since cropped content is completely empty
+ return;
+ }
+
+ if(aCurrentForCompare.equal(aCropped))
+ {
+ // no crop at all
+ return;
+ }
+
+ // back-transform to have values in unit coordinates
+ basegfx::B2DHomMatrix aBackToUnit;
+ aBackToUnit.translate(-aTranslate.getX(), -aTranslate.getY());
+ aBackToUnit.scale(
+ basegfx::fTools::equalZero(aScale.getX()) ? 1.0 : 1.0 / aScale.getX(),
+ basegfx::fTools::equalZero(aScale.getY()) ? 1.0 : 1.0 / aScale.getY());
+
+ // transform cropped back to unit coordinates
+ aCropped.transform(aBackToUnit);
+
+ // prepare crop PolyPolygon
+ basegfx::B2DPolygon aGraphicOutlinePolygon(
+ basegfx::utils::createPolygonFromRect(
+ aCropped));
+ basegfx::B2DPolyPolygon aCropPolyPolygon(aGraphicOutlinePolygon);
+
+ // current range is unit range
+ basegfx::B2DRange aOverlap(0.0, 0.0, 1.0, 1.0);
+
+ aOverlap.intersect(aCropped);
+
+ if(!aOverlap.isEmpty())
+ {
+ aCropPolyPolygon.append(
+ basegfx::utils::createPolygonFromRect(
+ aOverlap));
+ }
+
+ // transform to object coordinates to prepare for clip
+ aCropPolyPolygon.transform(maObjectTransform);
+ aGraphicOutlinePolygon.transform(maObjectTransform);
+
+ // create cropped transformation
+ basegfx::B2DHomMatrix aCroppedTransform;
+
+ aCroppedTransform.scale(
+ aCropped.getWidth(),
+ aCropped.getHeight());
+ aCroppedTransform.translate(
+ aCropped.getMinX(),
+ aCropped.getMinY());
+ aCroppedTransform = maObjectTransform * aCroppedTransform;
+
+ // prepare graphic primitive (transformed)
+ const drawinglayer::primitive2d::Primitive2DReference aGraphic(
+ new drawinglayer::primitive2d::GraphicPrimitive2D(
+ aCroppedTransform,
+ maGraphic));
+
+ // prepare outline polygon for whole graphic
+ const basegfx::BColor aHilightColor(SvtOptionsDrawinglayer::getHilightColor().getBColor());
+ const drawinglayer::primitive2d::Primitive2DReference aGraphicOutline(
+ new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
+ aGraphicOutlinePolygon,
+ aHilightColor));
+
+ // combine these
+ drawinglayer::primitive2d::Primitive2DContainer aCombination(2);
+ aCombination[0] = aGraphic;
+ aCombination[1] = aGraphicOutline;
+
+ // embed to MaskPrimitive2D
+ const drawinglayer::primitive2d::Primitive2DReference aMaskedGraphic(
+ new drawinglayer::primitive2d::MaskPrimitive2D(
+ aCropPolyPolygon,
+ std::move(aCombination)));
+
+ // embed to UnifiedTransparencePrimitive2D
+ const drawinglayer::primitive2d::Primitive2DReference aTransparenceMaskedGraphic(
+ new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
+ drawinglayer::primitive2d::Primitive2DContainer { aMaskedGraphic },
+ 0.8));
+
+ const drawinglayer::primitive2d::Primitive2DContainer aSequence { aTransparenceMaskedGraphic };
+
+ for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
+ {
+ // const SdrPageViewWinRec& rPageViewWinRec = rPageViewWinList[b];
+ const SdrPageWindow& rPageWindow = *(pPageView->GetPageWindow(b));
+
+ if(rPageWindow.GetPaintWindow().OutputToWindow())
+ {
+ const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager();
+ if(xManager.is())
+ {
+ std::unique_ptr<sdr::overlay::OverlayObject> pNew(new sdr::overlay::OverlayPrimitive2DSequenceObject(drawinglayer::primitive2d::Primitive2DContainer(aSequence)));
+
+ // only informative object, no hit
+ pNew->setHittable(false);
+
+ // OVERLAYMANAGER
+ insertNewlyCreatedOverlayObjectForSdrHdl(
+ std::move(pNew),
+ rPageWindow.GetObjectContact(),
+ *xManager);
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdhlpln.cxx b/svx/source/svdraw/svdhlpln.cxx
new file mode 100644
index 000000000..0d515191e
--- /dev/null
+++ b/svx/source/svdraw/svdhlpln.cxx
@@ -0,0 +1,109 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <svx/svdhlpln.hxx>
+
+#include <vcl/outdev.hxx>
+#include <vcl/ptrstyle.hxx>
+
+
+PointerStyle SdrHelpLine::GetPointer() const
+{
+ switch (eKind) {
+ case SdrHelpLineKind::Vertical : return PointerStyle::ESize;
+ case SdrHelpLineKind::Horizontal: return PointerStyle::SSize;
+ default : return PointerStyle::Move;
+ } // switch
+}
+
+bool SdrHelpLine::IsHit(const Point& rPnt, sal_uInt16 nTolLog, const OutputDevice& rOut) const
+{
+ Size a1Pix(rOut.PixelToLogic(Size(1,1)));
+ bool bXHit=rPnt.X()>=aPos.X()-nTolLog && rPnt.X()<=aPos.X()+nTolLog+a1Pix.Width();
+ bool bYHit=rPnt.Y()>=aPos.Y()-nTolLog && rPnt.Y()<=aPos.Y()+nTolLog+a1Pix.Height();
+ switch (eKind) {
+ case SdrHelpLineKind::Vertical : return bXHit;
+ case SdrHelpLineKind::Horizontal: return bYHit;
+ case SdrHelpLineKind::Point: {
+ if (bXHit || bYHit) {
+ Size aRad(rOut.PixelToLogic(Size(SDRHELPLINE_POINT_PIXELSIZE,SDRHELPLINE_POINT_PIXELSIZE)));
+ return rPnt.X()>=aPos.X()-aRad.Width() && rPnt.X()<=aPos.X()+aRad.Width()+a1Pix.Width() &&
+ rPnt.Y()>=aPos.Y()-aRad.Height() && rPnt.Y()<=aPos.Y()+aRad.Height()+a1Pix.Height();
+ }
+ } break;
+ } // switch
+ return false;
+}
+
+tools::Rectangle SdrHelpLine::GetBoundRect(const OutputDevice& rOut) const
+{
+ tools::Rectangle aRet(aPos,aPos);
+ Point aOfs(rOut.GetMapMode().GetOrigin());
+ Size aSiz(rOut.GetOutputSize());
+ switch (eKind) {
+ case SdrHelpLineKind::Vertical : aRet.SetTop(-aOfs.Y() ); aRet.SetBottom(-aOfs.Y()+aSiz.Height() ); break;
+ case SdrHelpLineKind::Horizontal: aRet.SetLeft(-aOfs.X() ); aRet.SetRight(-aOfs.X()+aSiz.Width() ); break;
+ case SdrHelpLineKind::Point : {
+ Size aRad(rOut.PixelToLogic(Size(SDRHELPLINE_POINT_PIXELSIZE,SDRHELPLINE_POINT_PIXELSIZE)));
+ aRet.AdjustLeft( -(aRad.Width()) );
+ aRet.AdjustRight(aRad.Width() );
+ aRet.AdjustTop( -(aRad.Height()) );
+ aRet.AdjustBottom(aRad.Height() );
+ } break;
+ } // switch
+ return aRet;
+}
+
+SdrHelpLineList& SdrHelpLineList::operator=(const SdrHelpLineList& rSrcList)
+{
+ aList.clear();
+ sal_uInt16 nCount=rSrcList.GetCount();
+ for (sal_uInt16 i=0; i<nCount; i++) {
+ Insert(rSrcList[i]);
+ }
+ return *this;
+}
+
+bool SdrHelpLineList::operator==(const SdrHelpLineList& rSrcList) const
+{
+ bool bEqual = false;
+ sal_uInt16 nCount=GetCount();
+ if (nCount==rSrcList.GetCount()) {
+ bEqual = true;
+ for (sal_uInt16 i=0; i<nCount && bEqual; i++) {
+ if (*aList[i]!=*rSrcList.aList[i]) {
+ bEqual = false;
+ }
+ }
+ }
+ return bEqual;
+}
+
+sal_uInt16 SdrHelpLineList::HitTest(const Point& rPnt, sal_uInt16 nTolLog, const OutputDevice& rOut) const
+{
+ sal_uInt16 nCount=GetCount();
+ for (sal_uInt16 i=nCount; i>0;) {
+ i--;
+ if (aList[i]->IsHit(rPnt,nTolLog,rOut)) return i;
+ }
+ return SDRHELPLINE_NOTFOUND;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svditer.cxx b/svx/source/svdraw/svditer.cxx
new file mode 100644
index 000000000..eaca2b335
--- /dev/null
+++ b/svx/source/svdraw/svditer.cxx
@@ -0,0 +1,140 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/svditer.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdmark.hxx>
+#include <osl/diagnose.h>
+
+SdrObjListIter::SdrObjListIter(const SdrObjList* pObjList, SdrIterMode eMode, bool bReverse)
+: mnIndex(0),
+ mbReverse(bReverse),
+ mbUseZOrder(true)
+{
+ if(nullptr != pObjList)
+ {
+ ImpProcessObjectList(*pObjList, eMode);
+ }
+
+ Reset();
+}
+
+SdrObjListIter::SdrObjListIter(const SdrObjList* pObjList, bool bUseZOrder, SdrIterMode eMode, bool bReverse)
+: mnIndex(0),
+ mbReverse(bReverse),
+ mbUseZOrder(bUseZOrder)
+{
+ if(nullptr != pObjList)
+ {
+ // correct when we have no ObjectNavigationOrder
+ if(!mbUseZOrder && !pObjList->HasObjectNavigationOrder())
+ {
+ mbUseZOrder = false;
+ }
+
+ ImpProcessObjectList(*pObjList, eMode);
+ }
+
+ Reset();
+}
+
+SdrObjListIter::SdrObjListIter(const SdrObject& rSdrObject, SdrIterMode eMode, bool bReverse)
+: mnIndex(0),
+ mbReverse(bReverse),
+ mbUseZOrder(true)
+{
+ ImpProcessObj(rSdrObject, eMode);
+ Reset();
+}
+
+SdrObjListIter::SdrObjListIter(const SdrPage* pSdrPage, SdrIterMode eMode, bool bReverse)
+: mnIndex(0),
+ mbReverse(bReverse),
+ mbUseZOrder(true)
+{
+ if (pSdrPage)
+ ImpProcessObjectList(*pSdrPage, eMode);
+ Reset();
+}
+
+SdrObjListIter::SdrObjListIter( const SdrMarkList& rMarkList, SdrIterMode eMode )
+: mnIndex(0),
+ mbReverse(false),
+ mbUseZOrder(true)
+{
+ ImpProcessMarkList(rMarkList, eMode);
+ Reset();
+}
+
+void SdrObjListIter::ImpProcessObjectList(const SdrObjList& rObjList, SdrIterMode eMode)
+{
+ for(size_t nIdx(0), nCount(rObjList.GetObjCount()); nIdx < nCount; ++nIdx)
+ {
+ const SdrObject* pSdrObject(mbUseZOrder
+ ? rObjList.GetObj(nIdx)
+ : rObjList.GetObjectForNavigationPosition(nIdx));
+
+ if(nullptr == pSdrObject)
+ {
+ OSL_ENSURE(false, "SdrObjListIter: corrupted SdrObjList (!)");
+ }
+ else
+ {
+ ImpProcessObj(*pSdrObject, eMode);
+ }
+ }
+}
+
+void SdrObjListIter::ImpProcessMarkList(const SdrMarkList& rMarkList, SdrIterMode eMode)
+{
+ for( size_t nIdx = 0, nCount = rMarkList.GetMarkCount(); nIdx < nCount; ++nIdx )
+ {
+ if( SdrObject* pObj = rMarkList.GetMark( nIdx )->GetMarkedSdrObj() )
+ {
+ ImpProcessObj(*pObj, eMode);
+ }
+ }
+}
+
+void SdrObjListIter::ImpProcessObj(const SdrObject& rSdrObject, SdrIterMode eMode)
+{
+ // TTTT: Note: The behaviour has changed here, it will now deep-iterate
+ // for SdrObjGroup and E3dScene. Old version only deep-dived for SdrObjGroup,
+ // E3dScene was just added flat. This is now more correct, but potentially
+ // there will exist code in the 3D area that *self-iterates* with local
+ // functions/methods due to this iterator was not doing the expected thing.
+ // These will be difficult to find, but in most cases should do no harm,
+ // but cost runtime. Will need to have an eye on this aspect on continued
+ // changes...
+ const SdrObjList* pChildren(rSdrObject.getChildrenOfSdrObject());
+ const bool bIsGroup(nullptr != pChildren);
+
+ if(!bIsGroup || (SdrIterMode::DeepNoGroups != eMode))
+ {
+ maObjList.push_back(&rSdrObject);
+ }
+
+ if(bIsGroup && (SdrIterMode::Flat != eMode))
+ {
+ ImpProcessObjectList(*pChildren, eMode);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdlayer.cxx b/svx/source/svdraw/svdlayer.cxx
new file mode 100644
index 000000000..3146d62f7
--- /dev/null
+++ b/svx/source/svdraw/svdlayer.cxx
@@ -0,0 +1,361 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/uno/Sequence.hxx>
+
+#include <svx/svdlayer.hxx>
+#include <svx/svdmodel.hxx>
+
+#include <algorithm>
+
+bool SdrLayerIDSet::IsEmpty() const
+{
+ for(sal_uInt8 i : aData)
+ {
+ if(i != 0)
+ return false;
+ }
+
+ return true;
+}
+
+void SdrLayerIDSet::operator&=(const SdrLayerIDSet& r2ndSet)
+{
+ for(sal_uInt16 i(0); i < 32; i++)
+ {
+ aData[i] &= r2ndSet.aData[i];
+ }
+}
+
+/** initialize this set with a UNO sequence of sal_Int8 (e.g. as stored in settings.xml)
+*/
+void SdrLayerIDSet::PutValue( const css::uno::Any & rAny )
+{
+ css::uno::Sequence< sal_Int8 > aSeq;
+ if( !(rAny >>= aSeq) )
+ return;
+
+ sal_Int16 nCount = static_cast<sal_Int16>(aSeq.getLength());
+ if( nCount > 32 )
+ nCount = 32;
+
+ sal_Int16 nIndex;
+ for( nIndex = 0; nIndex < nCount; nIndex++ )
+ {
+ aData[nIndex] = static_cast<sal_uInt8>(aSeq[nIndex]);
+ }
+
+ for( ; nIndex < 32; nIndex++ )
+ {
+ aData[nIndex] = 0;
+ }
+}
+
+SdrLayer::SdrLayer(SdrLayerID nNewID, const OUString& rNewName) :
+ maName(rNewName), pModel(nullptr), nID(nNewID)
+{
+ // ODF default values
+ mbVisibleODF = true;
+ mbPrintableODF = true;
+ mbLockedODF = false;
+}
+
+void SdrLayer::SetName(const OUString& rNewName)
+{
+ if (rNewName == maName)
+ return;
+
+ maName = rNewName;
+
+ if (pModel)
+ {
+ SdrHint aHint(SdrHintKind::LayerChange);
+ pModel->Broadcast(aHint);
+ pModel->SetChanged();
+ }
+}
+
+bool SdrLayer::operator==(const SdrLayer& rCmpLayer) const
+{
+ return (nID == rCmpLayer.nID
+ && maName == rCmpLayer.maName);
+}
+
+SdrLayerAdmin::SdrLayerAdmin(SdrLayerAdmin* pNewParent):
+ pParent(pNewParent),
+ pModel(nullptr),
+ maControlLayerName("controls")
+{
+}
+
+SdrLayerAdmin::SdrLayerAdmin(const SdrLayerAdmin& rSrcLayerAdmin):
+ pParent(nullptr),
+ pModel(nullptr),
+ maControlLayerName("controls")
+{
+ *this = rSrcLayerAdmin;
+}
+
+SdrLayerAdmin::~SdrLayerAdmin()
+{
+}
+
+void SdrLayerAdmin::ClearLayers()
+{
+ maLayers.clear();
+}
+
+SdrLayerAdmin& SdrLayerAdmin::operator=(const SdrLayerAdmin& rSrcLayerAdmin)
+{
+ if (this != &rSrcLayerAdmin)
+ {
+ maLayers.clear();
+ pParent=rSrcLayerAdmin.pParent;
+ sal_uInt16 i;
+ sal_uInt16 nCount=rSrcLayerAdmin.GetLayerCount();
+ for (i=0; i<nCount; i++) {
+ maLayers.emplace_back(new SdrLayer(*rSrcLayerAdmin.GetLayer(i)));
+ }
+ }
+ return *this;
+}
+
+void SdrLayerAdmin::SetModel(SdrModel* pNewModelel)
+{
+ if (pNewModelel!=pModel) {
+ pModel=pNewModelel;
+ sal_uInt16 nCount=GetLayerCount();
+ sal_uInt16 i;
+ for (i=0; i<nCount; i++) {
+ GetLayer(i)->SetModel(pNewModelel);
+ }
+ }
+}
+
+void SdrLayerAdmin::Broadcast() const
+{
+ if (pModel!=nullptr) {
+ SdrHint aHint(SdrHintKind::LayerOrderChange);
+ pModel->Broadcast(aHint);
+ pModel->SetChanged();
+ }
+}
+
+void SdrLayerAdmin::InsertLayer(std::unique_ptr<SdrLayer> pLayer, sal_uInt16 nPos)
+{
+ pLayer->SetModel(pModel);
+ if(nPos==0xFFFF)
+ maLayers.push_back(std::move(pLayer));
+ else
+ maLayers.insert(maLayers.begin() + nPos, std::move(pLayer));
+ Broadcast();
+}
+
+std::unique_ptr<SdrLayer> SdrLayerAdmin::RemoveLayer(sal_uInt16 nPos)
+{
+ std::unique_ptr<SdrLayer> pRetLayer = std::move(maLayers[nPos]);
+ maLayers.erase(maLayers.begin()+nPos);
+ Broadcast();
+ return pRetLayer;
+}
+
+SdrLayer* SdrLayerAdmin::NewLayer(const OUString& rName, sal_uInt16 nPos)
+{
+ SdrLayerID nID=GetUniqueLayerID();
+ SdrLayer* pLay=new SdrLayer(nID,rName);
+ pLay->SetModel(pModel);
+ if(nPos==0xFFFF)
+ maLayers.push_back(std::unique_ptr<SdrLayer>(pLay));
+ else
+ maLayers.insert(maLayers.begin() + nPos, std::unique_ptr<SdrLayer>(pLay));
+ Broadcast();
+ return pLay;
+}
+
+sal_uInt16 SdrLayerAdmin::GetLayerPos(const SdrLayer* pLayer) const
+{
+ sal_uInt16 nRet=SDRLAYERPOS_NOTFOUND;
+ if (pLayer!=nullptr) {
+ auto it = std::find_if(maLayers.begin(), maLayers.end(),
+ [&](const std::unique_ptr<SdrLayer> & p) { return p.get() == pLayer; });
+ if (it!=maLayers.end()) {
+ nRet=it - maLayers.begin();
+ }
+ }
+ return nRet;
+}
+
+SdrLayer* SdrLayerAdmin::GetLayer(const OUString& rName)
+{
+ return const_cast<SdrLayer*>(const_cast<const SdrLayerAdmin*>(this)->GetLayer(rName));
+}
+
+const SdrLayer* SdrLayerAdmin::GetLayer(const OUString& rName) const
+{
+ sal_uInt16 i(0);
+ const SdrLayer* pLay = nullptr;
+
+ while(i < GetLayerCount() && !pLay)
+ {
+ if (rName == GetLayer(i)->GetName())
+ pLay = GetLayer(i);
+ else
+ i++;
+ }
+
+ if(!pLay && pParent)
+ {
+ pLay = pParent->GetLayer(rName);
+ }
+
+ return pLay;
+}
+
+SdrLayerID SdrLayerAdmin::GetLayerID(const OUString& rName) const
+{
+ SdrLayerID nRet=SDRLAYER_NOTFOUND;
+ const SdrLayer* pLay=GetLayer(rName);
+ if (pLay!=nullptr) nRet=pLay->GetID();
+ return nRet;
+}
+
+const SdrLayer* SdrLayerAdmin::GetLayerPerID(SdrLayerID nID) const
+{
+ for (auto const & pLayer : maLayers)
+ if (pLayer->GetID() == nID)
+ return pLayer.get();
+ return nullptr;
+}
+
+// Global LayerIDs begin at 0 and increase,
+// local LayerIDs begin at 254 and decrease;
+// 255 is reserved for SDRLAYER_NOTFOUND.
+
+SdrLayerID SdrLayerAdmin::GetUniqueLayerID() const
+{
+ SdrLayerIDSet aSet;
+ for (sal_uInt16 j=0; j<GetLayerCount(); j++)
+ {
+ aSet.Set(GetLayer(j)->GetID());
+ }
+ sal_uInt8 i;
+ if (pParent != nullptr)
+ {
+ i = 254;
+ while (i && aSet.IsSet(SdrLayerID(i)))
+ --i;
+ assert(i != 0);
+ if (i == 0)
+ i = 254;
+ }
+ else
+ {
+ i = 0;
+ while (i<=254 && aSet.IsSet(SdrLayerID(i)))
+ i++;
+ assert(i <= 254);
+ if (i>254)
+ i = 0;
+ }
+ return SdrLayerID(i);
+}
+
+void SdrLayerAdmin::SetControlLayerName(const OUString& rNewName)
+{
+ maControlLayerName = rNewName;
+}
+
+void SdrLayerAdmin::getVisibleLayersODF( SdrLayerIDSet& rOutSet) const
+{
+ rOutSet.ClearAll();
+ for( auto & pCurrentLayer : maLayers )
+ {
+ if ( pCurrentLayer->IsVisibleODF() )
+ rOutSet.Set( pCurrentLayer->GetID() );
+ }
+}
+
+void SdrLayerAdmin::getPrintableLayersODF( SdrLayerIDSet& rOutSet) const
+{
+ rOutSet.ClearAll();
+ for( auto & pCurrentLayer : maLayers )
+ {
+ if ( pCurrentLayer->IsPrintableODF() )
+ rOutSet.Set( pCurrentLayer->GetID() );
+ }
+}
+
+void SdrLayerAdmin::getLockedLayersODF( SdrLayerIDSet& rOutSet) const
+{
+ rOutSet.ClearAll();
+ for( auto& pCurrentLayer : maLayers )
+ {
+ if ( pCurrentLayer->IsLockedODF() )
+ rOutSet.Set( pCurrentLayer->GetID() );
+ }
+}
+
+ // Generates a bitfield for settings.xml from the SdrLayerIDSet.
+ // Output is a UNO sequence of BYTE (which is 'short' in API).
+void SdrLayerAdmin::QueryValue(const SdrLayerIDSet& rViewLayerSet, css::uno::Any& rAny)
+{
+ // tdf#119392 The SdrLayerIDSet in a view is ordered according LayerID, but in file
+ // the bitfield is interpreted in order of layers in <draw:layer-set>.
+ // First generate a new bitfield based on rViewLayerSet in the needed order.
+ sal_uInt8 aTmp[32]; // 256 bits in settings.xml makes byte 0 to 31
+ for (auto nIndex = 0; nIndex <32; nIndex++)
+ {
+ aTmp[nIndex] = 0;
+ }
+ sal_uInt8 nByteIndex = 0;
+ sal_uInt8 nBitpos = 0;
+ sal_uInt16 nLayerPos = 0; // Position of the layer in member aLayer and in <draw:layer-set> in file
+ sal_uInt16 nLayerIndex = 0;
+ for( const auto& pCurrentLayer : maLayers )
+ {
+ SdrLayerID nCurrentID = pCurrentLayer->GetID();
+ if ( rViewLayerSet.IsSet(nCurrentID) )
+ {
+ nLayerPos = nLayerIndex;
+ nByteIndex = nLayerPos / 8;
+ if (nByteIndex > 31)
+ continue; // skip position, if too large for bitfield
+ nBitpos = nLayerPos % 8;
+ aTmp[nByteIndex] |= (1 << nBitpos);
+ }
+ ++nLayerIndex;
+ }
+
+ // Second transform the bitfield to byte sequence, same as in previous version of QueryValue
+ sal_uInt8 nNumBytesSet = 0;
+ for( auto nIndex = 31; nIndex >= 0; nIndex--)
+ {
+ if( 0 != aTmp[nIndex] )
+ {
+ nNumBytesSet = nIndex + 1;
+ break;
+ }
+ }
+ css::uno::Sequence< sal_Int8 > aSeq( nNumBytesSet );
+ std::transform(aTmp, aTmp + nNumBytesSet, aSeq.getArray(),
+ [](const sal_uInt8 b) { return static_cast<sal_Int8>(b); });
+ rAny <<= aSeq;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdmark.cxx b/svx/source/svdraw/svdmark.cxx
new file mode 100644
index 000000000..e3c77bda1
--- /dev/null
+++ b/svx/source/svdraw/svdmark.cxx
@@ -0,0 +1,791 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <osl/time.h>
+#include <svx/svdmark.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+
+
+#include <svx/obj3d.hxx>
+#include <svx/scene3d.hxx>
+#include <svl/SfxBroadcaster.hxx>
+#include <svx/svdoedge.hxx>
+#include <osl/diagnose.h>
+
+#include <cassert>
+
+void SdrMark::setTime()
+{
+ TimeValue aNow;
+ osl_getSystemTime(&aNow);
+ mnTimeStamp = sal_Int64(aNow.Seconds) * 1000000000 + aNow.Nanosec;
+}
+
+SdrMark::SdrMark(SdrObject* pNewObj, SdrPageView* pNewPageView)
+: mpSelectedSdrObject(pNewObj),
+ mpPageView(pNewPageView),
+ mbCon1(false),
+ mbCon2(false),
+ mnUser(0)
+{
+ if(mpSelectedSdrObject)
+ {
+ mpSelectedSdrObject->AddObjectUser( *this );
+ }
+ setTime();
+}
+
+SdrMark::SdrMark(const SdrMark& rMark)
+: ObjectUser(),
+ mnTimeStamp(0),
+ mpSelectedSdrObject(nullptr),
+ mpPageView(nullptr),
+ mbCon1(false),
+ mbCon2(false),
+ mnUser(0)
+{
+ *this = rMark;
+}
+
+SdrMark::~SdrMark()
+{
+ if (mpSelectedSdrObject)
+ {
+ mpSelectedSdrObject->RemoveObjectUser( *this );
+ }
+}
+
+void SdrMark::ObjectInDestruction(const SdrObject& rObject)
+{
+ (void) rObject; // avoid warnings
+ OSL_ENSURE(mpSelectedSdrObject && mpSelectedSdrObject == &rObject, "SdrMark::ObjectInDestruction: called from object different from hosted one (!)");
+ OSL_ENSURE(mpSelectedSdrObject, "SdrMark::ObjectInDestruction: still selected SdrObject is deleted, deselect first (!)");
+ mpSelectedSdrObject = nullptr;
+}
+
+void SdrMark::SetMarkedSdrObj(SdrObject* pNewObj)
+{
+ if(mpSelectedSdrObject)
+ {
+ mpSelectedSdrObject->RemoveObjectUser( *this );
+ }
+
+ mpSelectedSdrObject = pNewObj;
+
+ if(mpSelectedSdrObject)
+ {
+ mpSelectedSdrObject->AddObjectUser( *this );
+ }
+}
+
+SdrMark& SdrMark::operator=(const SdrMark& rMark)
+{
+ SetMarkedSdrObj(rMark.mpSelectedSdrObject);
+
+ mnTimeStamp = rMark.mnTimeStamp;
+ mpPageView = rMark.mpPageView;
+ mbCon1 = rMark.mbCon1;
+ mbCon2 = rMark.mbCon2;
+ mnUser = rMark.mnUser;
+ maPoints = rMark.maPoints;
+ maGluePoints = rMark.maGluePoints;
+
+ return *this;
+}
+
+static bool ImpSdrMarkListSorter(std::unique_ptr<SdrMark> const& lhs, std::unique_ptr<SdrMark> const& rhs)
+{
+ SdrObject* pObj1 = lhs->GetMarkedSdrObj();
+ SdrObject* pObj2 = rhs->GetMarkedSdrObj();
+ SdrObjList* pOL1 = pObj1 ? pObj1->getParentSdrObjListFromSdrObject() : nullptr;
+ SdrObjList* pOL2 = pObj2 ? pObj2->getParentSdrObjListFromSdrObject() : nullptr;
+
+ if (pOL1 == pOL2)
+ {
+ // AF: Note that I reverted a change from sal_uInt32 to sal_uLong (made
+ // for 64bit compliance, #i78198#) because internally in SdrObject
+ // both nOrdNum and mnNavigationPosition are stored as sal_uInt32.
+ sal_uInt32 nObjOrd1(pObj1 ? pObj1->GetNavigationPosition() : 0);
+ sal_uInt32 nObjOrd2(pObj2 ? pObj2->GetNavigationPosition() : 0);
+
+ return nObjOrd1 < nObjOrd2;
+ }
+ else
+ {
+ return pOL1 < pOL2;
+ }
+}
+
+
+void SdrMarkList::ForceSort() const
+{
+ if(!mbSorted)
+ {
+ const_cast<SdrMarkList*>(this)->ImpForceSort();
+ }
+}
+
+void SdrMarkList::ImpForceSort()
+{
+ if(mbSorted)
+ return;
+
+ mbSorted = true;
+ size_t nCount = maList.size();
+
+ // remove invalid
+ if(nCount > 0 )
+ {
+ maList.erase(std::remove_if(maList.begin(), maList.end(),
+ [](std::unique_ptr<SdrMark>& rItem) { return rItem->GetMarkedSdrObj() == nullptr; }),
+ maList.end());
+ nCount = maList.size();
+ }
+
+ if(nCount <= 1)
+ return;
+
+ std::sort(maList.begin(), maList.end(), ImpSdrMarkListSorter);
+
+ // remove duplicates
+ if(maList.size() <= 1)
+ return;
+
+ SdrMark* pCurrent = maList.back().get();
+ for (size_t count = maList.size() - 1; count; --count)
+ {
+ size_t i = count - 1;
+ SdrMark* pCmp = maList[i].get();
+ assert(pCurrent->GetMarkedSdrObj());
+ if(pCurrent->GetMarkedSdrObj() == pCmp->GetMarkedSdrObj())
+ {
+ // Con1/Con2 Merging
+ if(pCmp->IsCon1())
+ pCurrent->SetCon1(true);
+
+ if(pCmp->IsCon2())
+ pCurrent->SetCon2(true);
+
+ // delete pCmp
+ maList.erase(maList.begin() + i);
+ }
+ else
+ {
+ pCurrent = pCmp;
+ }
+ }
+}
+
+void SdrMarkList::Clear()
+{
+ maList.clear();
+ mbSorted = true; //we're empty, so can be considered sorted
+ SetNameDirty();
+}
+
+SdrMarkList& SdrMarkList::operator=(const SdrMarkList& rLst)
+{
+ if (this != &rLst)
+ {
+ Clear();
+
+ for(size_t i = 0; i < rLst.GetMarkCount(); ++i)
+ {
+ SdrMark* pMark = rLst.GetMark(i);
+ maList.emplace_back(new SdrMark(*pMark));
+ }
+
+ maMarkName = rLst.maMarkName;
+ mbNameOk = rLst.mbNameOk;
+ maPointName = rLst.maPointName;
+ mbPointNameOk = rLst.mbPointNameOk;
+ maGluePointName = rLst.maGluePointName;
+ mbSorted = rLst.mbSorted;
+ }
+ return *this;
+}
+
+SdrMark* SdrMarkList::GetMark(size_t nNum) const
+{
+ return (nNum < maList.size()) ? maList[nNum].get() : nullptr;
+}
+
+size_t SdrMarkList::FindObject(const SdrObject* pObj) const
+{
+ // Since relying on OrdNums is not allowed for the selection because objects in the
+ // selection may not be inserted in a list if they are e.g. modified ATM, i changed
+ // this loop to just look if the object pointer is in the selection.
+
+ // Problem is that GetOrdNum() which is const, internally casts to non-const and
+ // hardly sets the OrdNum member of the object (nOrdNum) to 0 (ZERO) if the object
+ // is not inserted in an object list.
+ // Since this may be by purpose and necessary somewhere else i decided that it is
+ // less dangerous to change this method then changing SdrObject::GetOrdNum().
+ if(pObj)
+ {
+ for(size_t a = 0; a < maList.size(); ++a)
+ {
+ if(maList[a]->GetMarkedSdrObj() == pObj)
+ {
+ return a;
+ }
+ }
+ }
+
+ return SAL_MAX_SIZE;
+}
+
+void SdrMarkList::InsertEntry(const SdrMark& rMark, bool bChkSort)
+{
+ SetNameDirty();
+ const size_t nCount(maList.size());
+
+ if(!bChkSort || !mbSorted || nCount == 0)
+ {
+ if(!bChkSort)
+ mbSorted = false;
+
+ maList.emplace_back(new SdrMark(rMark));
+ }
+ else
+ {
+ SdrMark* pLast = GetMark(nCount - 1);
+ const SdrObject* pLastObj = pLast->GetMarkedSdrObj();
+ const SdrObject* pNewObj = rMark.GetMarkedSdrObj();
+
+ if(pLastObj == pNewObj)
+ {
+ // This one already exists.
+ // Con1/Con2 Merging
+ if(rMark.IsCon1())
+ pLast->SetCon1(true);
+
+ if(rMark.IsCon2())
+ pLast->SetCon2(true);
+ }
+ else
+ {
+ maList.emplace_back(new SdrMark(rMark));
+
+ // now check if the sort is ok
+ const SdrObjList* pLastOL = pLastObj!=nullptr ? pLastObj->getParentSdrObjListFromSdrObject() : nullptr;
+ const SdrObjList* pNewOL = pNewObj !=nullptr ? pNewObj->getParentSdrObjListFromSdrObject() : nullptr;
+
+ if(pLastOL == pNewOL)
+ {
+ const sal_uLong nLastNum(pLastObj!=nullptr ? pLastObj->GetOrdNum() : 0);
+ const sal_uLong nNewNum(pNewObj !=nullptr ? pNewObj ->GetOrdNum() : 0);
+
+ if(nNewNum < nLastNum)
+ {
+ // at some point, we have to sort
+ mbSorted = false;
+ }
+ }
+ else
+ {
+ // at some point, we have to sort
+ mbSorted = false;
+ }
+ }
+ }
+}
+
+void SdrMarkList::DeleteMark(size_t nNum)
+{
+ SdrMark* pMark = GetMark(nNum);
+ DBG_ASSERT(pMark!=nullptr,"DeleteMark: MarkEntry not found.");
+
+ if(pMark)
+ {
+ maList.erase(maList.begin() + nNum);
+ if (maList.empty())
+ mbSorted = true; //we're empty, so can be considered sorted
+ SetNameDirty();
+ }
+}
+
+void SdrMarkList::ReplaceMark(const SdrMark& rNewMark, size_t nNum)
+{
+ SdrMark* pMark = GetMark(nNum);
+ DBG_ASSERT(pMark!=nullptr,"ReplaceMark: MarkEntry not found.");
+
+ if(pMark)
+ {
+ SetNameDirty();
+ maList[nNum].reset(new SdrMark(rNewMark));
+ mbSorted = false;
+ }
+}
+
+void SdrMarkList::Merge(const SdrMarkList& rSrcList, bool bReverse)
+{
+ const size_t nCount(rSrcList.maList.size());
+
+ if(rSrcList.mbSorted)
+ {
+ // merge without forcing a Sort in rSrcList
+ bReverse = false;
+ }
+
+ if(!bReverse)
+ {
+ for(size_t i = 0; i < nCount; ++i)
+ {
+ SdrMark* pM = rSrcList.maList[i].get();
+ InsertEntry(*pM);
+ }
+ }
+ else
+ {
+ for(size_t i = nCount; i > 0;)
+ {
+ --i;
+ SdrMark* pM = rSrcList.maList[i].get();
+ InsertEntry(*pM);
+ }
+ }
+}
+
+bool SdrMarkList::DeletePageView(const SdrPageView& rPV)
+{
+ bool bChgd(false);
+
+ for(auto it = maList.begin(); it != maList.end(); )
+ {
+ SdrMark* pMark = it->get();
+
+ if(pMark->GetPageView()==&rPV)
+ {
+ it = maList.erase(it);
+ SetNameDirty();
+ bChgd = true;
+ }
+ else
+ ++it;
+ }
+
+ return bChgd;
+}
+
+bool SdrMarkList::InsertPageView(const SdrPageView& rPV)
+{
+ bool bChgd(false);
+ DeletePageView(rPV); // delete all of them, then append the entire page
+ const SdrObjList* pOL = rPV.GetObjList();
+ const size_t nObjCount(pOL->GetObjCount());
+
+ for(size_t nO = 0; nO < nObjCount; ++nO)
+ {
+ SdrObject* pObj = pOL->GetObj(nO);
+ bool bDoIt(rPV.IsObjMarkable(pObj));
+
+ if(bDoIt)
+ {
+ maList.emplace_back(new SdrMark(pObj, const_cast<SdrPageView*>(&rPV)));
+ SetNameDirty();
+ bChgd = true;
+ }
+ }
+
+ return bChgd;
+}
+
+const OUString& SdrMarkList::GetMarkDescription() const
+{
+ const size_t nCount(GetMarkCount());
+
+ if(mbNameOk && 1 == nCount)
+ {
+ // if it's a single selection, cache only text frame
+ const SdrObject* pObj = GetMark(0)->GetMarkedSdrObj();
+ const SdrTextObj* pTextObj = dynamic_cast<const SdrTextObj*>( pObj );
+
+ if(!pTextObj || !pTextObj->IsTextFrame())
+ {
+ const_cast<SdrMarkList*>(this)->mbNameOk = false;
+ }
+ }
+
+ if(!mbNameOk)
+ {
+ SdrMark* pMark = GetMark(0);
+ OUString aNam;
+
+ if(!nCount)
+ {
+ const_cast<SdrMarkList*>(this)->maMarkName = SvxResId(STR_ObjNameNoObj);
+ }
+ else if(1 == nCount)
+ {
+ if(pMark->GetMarkedSdrObj())
+ {
+ aNam = pMark->GetMarkedSdrObj()->TakeObjNameSingul();
+ }
+ }
+ else
+ {
+ if(pMark->GetMarkedSdrObj())
+ {
+ aNam = pMark->GetMarkedSdrObj()->TakeObjNamePlural();
+ bool bEq(true);
+
+ for(size_t i = 1; i < GetMarkCount() && bEq; ++i)
+ {
+ SdrMark* pMark2 = GetMark(i);
+ OUString aStr1(pMark2->GetMarkedSdrObj()->TakeObjNamePlural());
+ bEq = aNam == aStr1;
+ }
+
+ if(!bEq)
+ {
+ aNam = SvxResId(STR_ObjNamePlural);
+ }
+ }
+
+ aNam = OUString::number( nCount ) + " " + aNam;
+ }
+
+ const_cast<SdrMarkList*>(this)->maMarkName = aNam;
+ const_cast<SdrMarkList*>(this)->mbNameOk = true;
+ }
+
+ return maMarkName;
+}
+
+const OUString& SdrMarkList::GetPointMarkDescription(bool bGlue) const
+{
+ bool& rNameOk = const_cast<bool&>(bGlue ? mbGluePointNameOk : mbPointNameOk);
+ OUString& rName = const_cast<OUString&>(bGlue ? maGluePointName : maPointName);
+ const size_t nMarkCount(GetMarkCount());
+ size_t nMarkPtCnt(0);
+ size_t nMarkPtObjCnt(0);
+ size_t n1stMarkNum(SAL_MAX_SIZE);
+
+ for(size_t nMarkNum = 0; nMarkNum < nMarkCount; ++nMarkNum)
+ {
+ const SdrMark* pMark = GetMark(nMarkNum);
+ const SdrUShortCont& rPts = bGlue ? pMark->GetMarkedGluePoints() : pMark->GetMarkedPoints();
+
+ if (!rPts.empty())
+ {
+ if(n1stMarkNum == SAL_MAX_SIZE)
+ {
+ n1stMarkNum = nMarkNum;
+ }
+
+ nMarkPtCnt += rPts.size();
+ nMarkPtObjCnt++;
+ }
+
+ if(nMarkPtObjCnt > 1 && rNameOk)
+ {
+ // preliminary decision
+ return rName;
+ }
+ }
+
+ if(rNameOk && 1 == nMarkPtObjCnt)
+ {
+ // if it's a single selection, cache only text frame
+ const SdrObject* pObj = GetMark(0)->GetMarkedSdrObj();
+ const SdrTextObj* pTextObj = dynamic_cast<const SdrTextObj*>( pObj );
+
+ if(!pTextObj || !pTextObj->IsTextFrame())
+ {
+ rNameOk = false;
+ }
+ }
+
+ if(!nMarkPtObjCnt)
+ {
+ rName.clear();
+ rNameOk = true;
+ }
+ else if(!rNameOk)
+ {
+ const SdrMark* pMark = GetMark(n1stMarkNum);
+ OUString aNam;
+
+ if(1 == nMarkPtObjCnt)
+ {
+ if(pMark->GetMarkedSdrObj())
+ {
+ aNam = pMark->GetMarkedSdrObj()->TakeObjNameSingul();
+ }
+ }
+ else
+ {
+ if(pMark->GetMarkedSdrObj())
+ {
+ aNam = pMark->GetMarkedSdrObj()->TakeObjNamePlural();
+ }
+
+ bool bEq(true);
+
+ for(size_t i = n1stMarkNum + 1; i < GetMarkCount() && bEq; ++i)
+ {
+ const SdrMark* pMark2 = GetMark(i);
+ const SdrUShortCont& rPts = bGlue ? pMark2->GetMarkedGluePoints() : pMark2->GetMarkedPoints();
+
+ if (!rPts.empty() && pMark2->GetMarkedSdrObj())
+ {
+ OUString aStr1(pMark2->GetMarkedSdrObj()->TakeObjNamePlural());
+ bEq = aNam == aStr1;
+ }
+ }
+
+ if(!bEq)
+ {
+ aNam = SvxResId(STR_ObjNamePlural);
+ }
+
+ aNam = OUString::number( nMarkPtObjCnt ) + " " + aNam;
+ }
+
+ OUString aStr1;
+
+ if(1 == nMarkPtCnt)
+ {
+ aStr1 = SvxResId(bGlue ? STR_ViewMarkedGluePoint : STR_ViewMarkedPoint);
+ }
+ else
+ {
+ aStr1 = SvxResId(bGlue ? STR_ViewMarkedGluePoints : STR_ViewMarkedPoints);
+ aStr1 = aStr1.replaceFirst("%2", OUString::number( nMarkPtCnt ));
+ }
+
+ aStr1 = aStr1.replaceFirst("%1", aNam);
+ rName = aStr1;
+ rNameOk = true;
+ }
+
+ return rName;
+}
+
+bool SdrMarkList::TakeBoundRect(SdrPageView const * pPV, tools::Rectangle& rRect) const
+{
+ bool bFnd(false);
+ tools::Rectangle aR;
+
+ for(size_t i = 0; i < GetMarkCount(); ++i)
+ {
+ SdrMark* pMark = GetMark(i);
+
+ if(!pPV || pMark->GetPageView() == pPV)
+ {
+ if(pMark->GetMarkedSdrObj())
+ {
+ aR = pMark->GetMarkedSdrObj()->GetCurrentBoundRect();
+
+ if(bFnd)
+ {
+ rRect.Union(aR);
+ }
+ else
+ {
+ rRect = aR;
+ bFnd = true;
+ }
+ }
+ }
+ }
+
+ return bFnd;
+}
+
+bool SdrMarkList::TakeSnapRect(SdrPageView const * pPV, tools::Rectangle& rRect) const
+{
+ bool bFnd(false);
+
+ for(size_t i = 0; i < GetMarkCount(); ++i)
+ {
+ SdrMark* pMark = GetMark(i);
+
+ if(!pPV || pMark->GetPageView() == pPV)
+ {
+ if(pMark->GetMarkedSdrObj())
+ {
+ tools::Rectangle aR(pMark->GetMarkedSdrObj()->GetSnapRect());
+
+ if(bFnd)
+ {
+ rRect.Union(aR);
+ }
+ else
+ {
+ rRect = aR;
+ bFnd = true;
+ }
+ }
+ }
+ }
+
+ return bFnd;
+}
+
+
+namespace sdr
+{
+ ViewSelection::ViewSelection()
+ : mbEdgesOfMarkedNodesDirty(false)
+ {
+ }
+
+ void ViewSelection::SetEdgesOfMarkedNodesDirty()
+ {
+ if(!mbEdgesOfMarkedNodesDirty)
+ {
+ mbEdgesOfMarkedNodesDirty = true;
+ maEdgesOfMarkedNodes.Clear();
+ maMarkedEdgesOfMarkedNodes.Clear();
+ maAllMarkedObjects.clear();
+ }
+ }
+
+ const SdrMarkList& ViewSelection::GetEdgesOfMarkedNodes() const
+ {
+ if(mbEdgesOfMarkedNodesDirty)
+ {
+ const_cast<ViewSelection*>(this)->ImpForceEdgesOfMarkedNodes();
+ }
+
+ return maEdgesOfMarkedNodes;
+ }
+
+ const SdrMarkList& ViewSelection::GetMarkedEdgesOfMarkedNodes() const
+ {
+ if(mbEdgesOfMarkedNodesDirty)
+ {
+ const_cast<ViewSelection*>(this)->ImpForceEdgesOfMarkedNodes();
+ }
+
+ return maMarkedEdgesOfMarkedNodes;
+ }
+
+ const std::vector<SdrObject*>& ViewSelection::GetAllMarkedObjects() const
+ {
+ if(mbEdgesOfMarkedNodesDirty)
+ const_cast<ViewSelection*>(this)->ImpForceEdgesOfMarkedNodes();
+
+ return maAllMarkedObjects;
+ }
+
+ void ViewSelection::ImplCollectCompleteSelection(SdrObject* pObj)
+ {
+ if(!pObj)
+ return;
+
+ bool bIsGroup(pObj->IsGroupObject());
+
+ if(bIsGroup && dynamic_cast< const E3dObject* >(pObj) != nullptr && dynamic_cast< const E3dScene* >(pObj) == nullptr)
+ {
+ bIsGroup = false;
+ }
+
+ if(bIsGroup)
+ {
+ SdrObjList* pList = pObj->GetSubList();
+
+ for(size_t a = 0; a < pList->GetObjCount(); ++a)
+ {
+ SdrObject* pObj2 = pList->GetObj(a);
+ ImplCollectCompleteSelection(pObj2);
+ }
+ }
+
+ maAllMarkedObjects.push_back(pObj);
+ }
+
+ void ViewSelection::ImpForceEdgesOfMarkedNodes()
+ {
+ if(!mbEdgesOfMarkedNodesDirty)
+ return;
+
+ mbEdgesOfMarkedNodesDirty = false;
+ maMarkedObjectList.ForceSort();
+ maEdgesOfMarkedNodes.Clear();
+ maMarkedEdgesOfMarkedNodes.Clear();
+ maAllMarkedObjects.clear();
+
+ // GetMarkCount after ForceSort
+ const size_t nMarkCount(maMarkedObjectList.GetMarkCount());
+
+ for(size_t a = 0; a < nMarkCount; ++a)
+ {
+ SdrObject* pCandidate = maMarkedObjectList.GetMark(a)->GetMarkedSdrObj();
+
+ if(pCandidate)
+ {
+ // build transitive hull
+ ImplCollectCompleteSelection(pCandidate);
+
+ // travel over broadcaster/listener to access edges connected to the selected object
+ const SfxBroadcaster* pBC = pCandidate->GetBroadcaster();
+
+ if(pBC)
+ {
+ const size_t nLstCnt(pBC->GetSizeOfVector());
+
+ for(size_t nl=0; nl < nLstCnt; ++nl)
+ {
+ SfxListener* pLst = pBC->GetListener(nl);
+ SdrEdgeObj* pEdge = dynamic_cast<SdrEdgeObj*>( pLst );
+
+ if(pEdge && pEdge->IsInserted() && pEdge->getSdrPageFromSdrObject() == pCandidate->getSdrPageFromSdrObject())
+ {
+ SdrMark aM(pEdge, maMarkedObjectList.GetMark(a)->GetPageView());
+
+ if(pEdge->GetConnectedNode(true) == pCandidate)
+ {
+ aM.SetCon1(true);
+ }
+
+ if(pEdge->GetConnectedNode(false) == pCandidate)
+ {
+ aM.SetCon2(true);
+ }
+
+ if(SAL_MAX_SIZE == maMarkedObjectList.FindObject(pEdge))
+ {
+ // check if it itself is selected
+ maEdgesOfMarkedNodes.InsertEntry(aM);
+ }
+ else
+ {
+ maMarkedEdgesOfMarkedNodes.InsertEntry(aM);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ maEdgesOfMarkedNodes.ForceSort();
+ maMarkedEdgesOfMarkedNodes.ForceSort();
+ }
+} // end of namespace sdr
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdmodel.cxx b/svx/source/svdraw/svdmodel.cxx
new file mode 100644
index 000000000..acf05bd94
--- /dev/null
+++ b/svx/source/svdraw/svdmodel.cxx
@@ -0,0 +1,1941 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/svdmodel.hxx>
+#include <cassert>
+#include <math.h>
+#include <sal/log.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/document/XStorageBasedDocument.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <unotools/configmgr.hxx>
+#include <unotools/pathoptions.hxx>
+#include <svl/whiter.hxx>
+#include <svl/asiancfg.hxx>
+#include <svx/xbtmpit.hxx>
+#include <svx/xlndsit.hxx>
+#include <svx/xlnedit.hxx>
+#include <svx/xflgrit.hxx>
+#include <svx/xflftrit.hxx>
+#include <svx/xflhtit.hxx>
+#include <svx/xlnstit.hxx>
+#include <editeng/editeng.hxx>
+#include <svx/xtable.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdlayer.hxx>
+#include <svx/svdundo.hxx>
+#include <svx/svdpool.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdotext.hxx>
+#include <textchain.hxx>
+#include <svx/svdetc.hxx>
+#include <svx/svdoutl.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <svdoutlinercache.hxx>
+#include <svx/sdasitm.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <svl/style.hxx>
+#include <editeng/forbiddencharacterstable.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <unotools/syslocale.hxx>
+#include <editeng/eeitem.hxx>
+#include <svl/itemset.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <memory>
+#include <libxml/xmlwriter.h>
+#include <sfx2/viewsh.hxx>
+#include <o3tl/enumrange.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/UnitConversion.hxx>
+#include <svx/ColorSets.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+
+
+struct SdrModelImpl
+{
+ SfxUndoManager* mpUndoManager;
+ SdrUndoFactory* mpUndoFactory;
+ bool mbAnchoredTextOverflowLegacy; // tdf#99729 compatibility flag
+ std::unique_ptr<svx::Theme> mpTheme;
+
+ SdrModelImpl()
+ : mpUndoManager(nullptr)
+ , mpUndoFactory(nullptr)
+ , mbAnchoredTextOverflowLegacy(false)
+ {}
+};
+
+
+SdrModel::SdrModel(SfxItemPool* pPool, comphelper::IEmbeddedHelper* pEmbeddedHelper, bool bDisablePropertyFiles)
+ : m_aObjUnit(SdrEngineDefaults::GetMapFraction())
+ , m_eObjUnit(SdrEngineDefaults::GetMapUnit())
+ , m_eUIUnit(FieldUnit::MM)
+ , m_aUIScale(Fraction(1,1))
+ , m_nUIUnitDecimalMark(0)
+ , m_pLayerAdmin(new SdrLayerAdmin)
+ , m_pItemPool(pPool)
+ , m_pEmbeddedHelper(pEmbeddedHelper)
+ , mnDefTextHgt(SdrEngineDefaults::GetFontHeight())
+ , m_pRefOutDev(nullptr)
+ , m_pDefaultStyleSheet(nullptr)
+ , mpDefaultStyleSheetForSdrGrafObjAndSdrOle2Obj(nullptr)
+ , m_pLinkManager(nullptr)
+ , m_nUndoLevel(0)
+ , m_bIsWriter(true)
+ , mbUndoEnabled(true)
+ , mbChanged(false)
+ , m_bPagNumsDirty(false)
+ , m_bMPgNumsDirty(false)
+ , m_bTransportContainer(false)
+ , m_bReadOnly(false)
+ , m_bTransparentTextFrames(false)
+ , m_bSwapGraphics(false)
+ , m_bPasteResize(false)
+ , m_bStarDrawPreviewMode(false)
+ , mbDisableTextEditUsesCommonUndoManager(false)
+ , mbVOCInvalidationIsReliable(false)
+ , m_nDefaultTabulator(0)
+ , m_nMaxUndoCount(16)
+ , m_pTextChain(new TextChain)
+ , mpImpl(new SdrModelImpl)
+ , mnCharCompressType(CharCompressType::NONE)
+ , mnHandoutPageCount(0)
+ , mbModelLocked(false)
+ , mbKernAsianPunctuation(false)
+ , mbAddExtLeading(false)
+ , mbInDestruction(false)
+{
+ if (!utl::ConfigManager::IsFuzzing())
+ {
+ mnCharCompressType = static_cast<CharCompressType>(
+ officecfg::Office::Common::AsianLayout::CompressCharacterDistance::get());
+ }
+
+ if (m_pItemPool == nullptr)
+ {
+ m_pItemPool = new SdrItemPool(nullptr);
+ // Outliner doesn't have its own Pool, so use the EditEngine's
+ rtl::Reference<SfxItemPool> pOutlPool=EditEngine::CreatePool();
+ // OutlinerPool as SecondaryPool of SdrPool
+ m_pItemPool->SetSecondaryPool(pOutlPool.get());
+ // remember that I created both pools myself
+ m_bIsWriter = false;
+ }
+ m_pItemPool->SetDefaultMetric(m_eObjUnit);
+
+// using static SdrEngineDefaults only if default SvxFontHeight item is not available
+ const SfxPoolItem* pPoolItem = m_pItemPool->GetPoolDefaultItem( EE_CHAR_FONTHEIGHT );
+ if (pPoolItem)
+ mnDefTextHgt = static_cast<const SvxFontHeightItem*>(pPoolItem)->GetHeight();
+
+ m_pItemPool->SetPoolDefaultItem( makeSdrTextWordWrapItem( false ) );
+
+ SetTextDefaults();
+ m_pLayerAdmin->SetModel(this);
+ ImpSetUIUnit();
+
+ // can't create DrawOutliner OnDemand, because I can't get the Pool,
+ // then (only from 302 onwards!)
+ m_pDrawOutliner = SdrMakeOutliner(OutlinerMode::TextObject, *this);
+ ImpSetOutlinerDefaults(m_pDrawOutliner.get(), true);
+
+ m_pHitTestOutliner = SdrMakeOutliner(OutlinerMode::TextObject, *this);
+ ImpSetOutlinerDefaults(m_pHitTestOutliner.get(), true);
+
+ /* Start Text Chaining related code */
+ // Initialize Chaining Outliner
+ m_pChainingOutliner = SdrMakeOutliner( OutlinerMode::TextObject, *this );
+ ImpSetOutlinerDefaults(m_pChainingOutliner.get(), true);
+
+ ImpCreateTables(bDisablePropertyFiles || utl::ConfigManager::IsFuzzing());
+}
+
+SdrModel::~SdrModel()
+{
+ mbInDestruction = true;
+
+ Broadcast(SdrHint(SdrHintKind::ModelCleared));
+
+ mpOutlinerCache.reset();
+
+ ClearUndoBuffer();
+#ifdef DBG_UTIL
+ SAL_WARN_IF(m_pCurrentUndoGroup, "svx", "In the Dtor of the SdrModel there is an open Undo left: \""
+ << m_pCurrentUndoGroup->GetComment() << '\"');
+#endif
+ m_pCurrentUndoGroup.reset();
+
+ ClearModel(true);
+
+#ifdef DBG_UTIL
+ // SdrObjectLifetimeWatchDog:
+ if(!maAllIncarnatedObjects.empty())
+ {
+ SAL_WARN("svx","SdrModel::~SdrModel: Not all incarnations of SdrObjects deleted, possible memory leak (!)");
+ const std::vector<const SdrObject*> maRemainingObjects(maAllIncarnatedObjects.begin(),
+ maAllIncarnatedObjects.end());
+ for (auto pSdrObject : maRemainingObjects)
+ {
+ SdrObject* pCandidate(const_cast<SdrObject*>(pSdrObject));
+ // calling SdrObject::Free will change maAllIncarnatedObjects, and potentially remove
+ // more than one, so check if the candidate is still in the updated list before Free
+ if (maAllIncarnatedObjects.find(pSdrObject) != maAllIncarnatedObjects.end())
+ SdrObject::Free(pCandidate);
+ }
+ }
+#endif
+
+ m_pLayerAdmin.reset();
+
+ m_pTextChain.reset();
+ // Delete DrawOutliner only after deleting ItemPool, because ItemPool
+ // references Items of the DrawOutliner!
+ m_pChainingOutliner.reset();
+ m_pHitTestOutliner.reset();
+ m_pDrawOutliner.reset();
+
+ // delete StyleSheetPool, derived classes should not do this since
+ // the DrawingEngine may need it in its destructor
+ if( mxStyleSheetPool.is() )
+ {
+ Reference< XComponent > xComponent( static_cast< cppu::OWeakObject* >( mxStyleSheetPool.get() ), UNO_QUERY );
+ if( xComponent.is() ) try
+ {
+ xComponent->dispose();
+ }
+ catch( RuntimeException& )
+ {
+ }
+ mxStyleSheetPool.clear();
+ }
+
+ mpForbiddenCharactersTable.reset();
+
+ delete mpImpl->mpUndoFactory;
+}
+
+void SdrModel::SetSwapGraphics()
+{
+ m_bSwapGraphics = true;
+}
+
+bool SdrModel::IsReadOnly() const
+{
+ return m_bReadOnly;
+}
+
+void SdrModel::SetReadOnly(bool bYes)
+{
+ m_bReadOnly=bYes;
+}
+
+
+void SdrModel::SetMaxUndoActionCount(sal_uInt32 nCount)
+{
+ if (nCount<1) nCount=1;
+ m_nMaxUndoCount=nCount;
+ while (m_aUndoStack.size()>m_nMaxUndoCount)
+ m_aUndoStack.pop_back();
+}
+
+void SdrModel::ClearUndoBuffer()
+{
+ m_aUndoStack.clear();
+ m_aRedoStack.clear();
+}
+
+bool SdrModel::HasUndoActions() const
+{
+ return !m_aUndoStack.empty();
+}
+
+bool SdrModel::HasRedoActions() const
+{
+ return !m_aRedoStack.empty();
+}
+
+void SdrModel::Undo()
+{
+ if( mpImpl->mpUndoManager )
+ {
+ OSL_FAIL("svx::SdrModel::Undo(), method not supported with application undo manager!");
+ }
+ else
+ {
+ if(HasUndoActions())
+ {
+ SfxUndoAction* pDo = m_aUndoStack.front().get();
+ const bool bWasUndoEnabled = mbUndoEnabled;
+ mbUndoEnabled = false;
+ pDo->Undo();
+ std::unique_ptr<SfxUndoAction> p = std::move(m_aUndoStack.front());
+ m_aUndoStack.pop_front();
+ m_aRedoStack.emplace_front(std::move(p));
+ mbUndoEnabled = bWasUndoEnabled;
+ }
+ }
+}
+
+void SdrModel::Redo()
+{
+ if( mpImpl->mpUndoManager )
+ {
+ OSL_FAIL("svx::SdrModel::Redo(), method not supported with application undo manager!");
+ }
+ else
+ {
+ if(HasRedoActions())
+ {
+ SfxUndoAction* pDo = m_aRedoStack.front().get();
+ const bool bWasUndoEnabled = mbUndoEnabled;
+ mbUndoEnabled = false;
+ pDo->Redo();
+ std::unique_ptr<SfxUndoAction> p = std::move(m_aRedoStack.front());
+ m_aRedoStack.pop_front();
+ m_aUndoStack.emplace_front(std::move(p));
+ mbUndoEnabled = bWasUndoEnabled;
+ }
+ }
+}
+
+void SdrModel::Repeat(SfxRepeatTarget& rView)
+{
+ if( mpImpl->mpUndoManager )
+ {
+ OSL_FAIL("svx::SdrModel::Redo(), method not supported with application undo manager!");
+ }
+ else
+ {
+ if(HasUndoActions())
+ {
+ SfxUndoAction* pDo = m_aUndoStack.front().get();
+ if(pDo->CanRepeat(rView))
+ {
+ pDo->Repeat(rView);
+ }
+ }
+ }
+}
+
+void SdrModel::ImpPostUndoAction(std::unique_ptr<SdrUndoAction> pUndo)
+{
+ DBG_ASSERT( mpImpl->mpUndoManager == nullptr, "svx::SdrModel::ImpPostUndoAction(), method not supported with application undo manager!" );
+ if( !IsUndoEnabled() )
+ return;
+
+ if (m_aUndoLink)
+ {
+ m_aUndoLink(std::move(pUndo));
+ }
+ else
+ {
+ m_aUndoStack.emplace_front(std::move(pUndo));
+ while (m_aUndoStack.size()>m_nMaxUndoCount)
+ {
+ m_aUndoStack.pop_back();
+ }
+ m_aRedoStack.clear();
+ }
+}
+
+void SdrModel::BegUndo()
+{
+ if( mpImpl->mpUndoManager )
+ {
+ ViewShellId nViewShellId(-1);
+ if (SfxViewShell* pViewShell = SfxViewShell::Current())
+ nViewShellId = pViewShell->GetViewShellId();
+ mpImpl->mpUndoManager->EnterListAction("","",0,nViewShellId);
+ m_nUndoLevel++;
+ }
+ else if( IsUndoEnabled() )
+ {
+ if(!m_pCurrentUndoGroup)
+ {
+ m_pCurrentUndoGroup.reset(new SdrUndoGroup(*this));
+ m_nUndoLevel=1;
+ }
+ else
+ {
+ m_nUndoLevel++;
+ }
+ }
+}
+
+void SdrModel::BegUndo(const OUString& rComment)
+{
+ if( mpImpl->mpUndoManager )
+ {
+ ViewShellId nViewShellId(-1);
+ if (SfxViewShell* pViewShell = SfxViewShell::Current())
+ nViewShellId = pViewShell->GetViewShellId();
+ mpImpl->mpUndoManager->EnterListAction( rComment, "", 0, nViewShellId );
+ m_nUndoLevel++;
+ }
+ else if( IsUndoEnabled() )
+ {
+ BegUndo();
+ if (m_nUndoLevel==1)
+ {
+ m_pCurrentUndoGroup->SetComment(rComment);
+ }
+ }
+}
+
+void SdrModel::BegUndo(const OUString& rComment, const OUString& rObjDescr, SdrRepeatFunc eFunc)
+{
+ if( mpImpl->mpUndoManager )
+ {
+ OUString aComment(rComment);
+ if( !aComment.isEmpty() && !rObjDescr.isEmpty() )
+ {
+ aComment = aComment.replaceFirst("%1", rObjDescr);
+ }
+ ViewShellId nViewShellId(-1);
+ if (SfxViewShell* pViewShell = SfxViewShell::Current())
+ nViewShellId = pViewShell->GetViewShellId();
+ mpImpl->mpUndoManager->EnterListAction( aComment,"",0,nViewShellId );
+ m_nUndoLevel++;
+ }
+ else if( IsUndoEnabled() )
+ {
+ BegUndo();
+ if (m_nUndoLevel==1)
+ {
+ m_pCurrentUndoGroup->SetComment(rComment);
+ m_pCurrentUndoGroup->SetObjDescription(rObjDescr);
+ m_pCurrentUndoGroup->SetRepeatFunction(eFunc);
+ }
+ }
+}
+
+void SdrModel::EndUndo()
+{
+ DBG_ASSERT(m_nUndoLevel!=0,"SdrModel::EndUndo(): UndoLevel is already 0!");
+ if( mpImpl->mpUndoManager )
+ {
+ if( m_nUndoLevel )
+ {
+ m_nUndoLevel--;
+ mpImpl->mpUndoManager->LeaveListAction();
+ }
+ }
+ else
+ {
+ if(m_pCurrentUndoGroup!=nullptr && IsUndoEnabled())
+ {
+ m_nUndoLevel--;
+ if(m_nUndoLevel==0)
+ {
+ if(m_pCurrentUndoGroup->GetActionCount()!=0)
+ {
+ ImpPostUndoAction(std::move(m_pCurrentUndoGroup));
+ }
+ else
+ {
+ // was empty
+ m_pCurrentUndoGroup.reset();
+ }
+ }
+ }
+ }
+}
+
+void SdrModel::SetUndoComment(const OUString& rComment)
+{
+ DBG_ASSERT(m_nUndoLevel!=0,"SdrModel::SetUndoComment(): UndoLevel is already 0!");
+
+ if( mpImpl->mpUndoManager )
+ {
+ OSL_FAIL("svx::SdrModel::SetUndoComment(), method not supported with application undo manager!" );
+ }
+ else if( IsUndoEnabled() && m_nUndoLevel==1)
+ {
+ m_pCurrentUndoGroup->SetComment(rComment);
+ }
+}
+
+void SdrModel::SetUndoComment(const OUString& rComment, const OUString& rObjDescr)
+{
+ DBG_ASSERT(m_nUndoLevel!=0,"SdrModel::SetUndoComment(): UndoLevel is already 0!");
+ if( mpImpl->mpUndoManager )
+ {
+ OSL_FAIL("svx::SdrModel::SetUndoComment(), method not supported with application undo manager!" );
+ }
+ else
+ {
+ if (m_nUndoLevel==1)
+ {
+ m_pCurrentUndoGroup->SetComment(rComment);
+ m_pCurrentUndoGroup->SetObjDescription(rObjDescr);
+ }
+ }
+}
+
+void SdrModel::AddUndo(std::unique_ptr<SdrUndoAction> pUndo)
+{
+ if( mpImpl->mpUndoManager )
+ {
+ mpImpl->mpUndoManager->AddUndoAction( std::move(pUndo) );
+ }
+ else if( IsUndoEnabled() )
+ {
+ if (m_pCurrentUndoGroup)
+ {
+ m_pCurrentUndoGroup->AddAction(std::move(pUndo));
+ }
+ else
+ {
+ ImpPostUndoAction(std::move(pUndo));
+ }
+ }
+}
+
+void SdrModel::EnableUndo( bool bEnable )
+{
+ if( mpImpl->mpUndoManager )
+ {
+ mpImpl->mpUndoManager->EnableUndo( bEnable );
+ }
+ else
+ {
+ mbUndoEnabled = bEnable;
+ }
+}
+
+bool SdrModel::IsUndoEnabled() const
+{
+ if( mpImpl->mpUndoManager )
+ {
+ return mpImpl->mpUndoManager->IsUndoEnabled();
+ }
+ else
+ {
+ return mbUndoEnabled;
+ }
+}
+
+void SdrModel::ImpCreateTables(bool bDisablePropertyFiles)
+{
+ // use standard path for initial construction
+ const OUString aTablePath(!bDisablePropertyFiles ? SvtPathOptions().GetPalettePath() : "");
+
+ for( auto i : o3tl::enumrange<XPropertyListType>() )
+ {
+ maProperties[i] = XPropertyList::CreatePropertyList(i, aTablePath, ""/*TODO?*/ );
+ }
+}
+
+void SdrModel::ClearModel(bool bCalledFromDestructor)
+{
+ if(bCalledFromDestructor)
+ {
+ mbInDestruction = true;
+ }
+
+ sal_Int32 i;
+ // delete all drawing pages
+ sal_Int32 nCount=GetPageCount();
+ for (i=nCount-1; i>=0; i--)
+ {
+ DeletePage( static_cast<sal_uInt16>(i) );
+ }
+ maPages.clear();
+ PageListChanged();
+
+ // delete all Masterpages
+ nCount=GetMasterPageCount();
+ for(i=nCount-1; i>=0; i--)
+ {
+ DeleteMasterPage( static_cast<sal_uInt16>(i) );
+ }
+ maMasterPages.clear();
+ MasterPageListChanged();
+
+ m_pLayerAdmin->ClearLayers();
+}
+
+SdrModel* SdrModel::AllocModel() const
+{
+ SdrModel* pModel=new SdrModel();
+ pModel->SetScaleUnit(m_eObjUnit,m_aObjUnit);
+ return pModel;
+}
+
+rtl::Reference<SdrPage> SdrModel::AllocPage(bool bMasterPage)
+{
+ return new SdrPage(*this,bMasterPage);
+}
+
+void SdrModel::SetTextDefaults() const
+{
+ SetTextDefaults( m_pItemPool.get(), mnDefTextHgt );
+}
+
+void SdrModel::SetTextDefaults( SfxItemPool* pItemPool, sal_Int32 nDefTextHgt )
+{
+ // set application-language specific dynamic pool language defaults
+ SvxFontItem aSvxFontItem( EE_CHAR_FONTINFO) ;
+ SvxFontItem aSvxFontItemCJK(EE_CHAR_FONTINFO_CJK);
+ SvxFontItem aSvxFontItemCTL(EE_CHAR_FONTINFO_CTL);
+ LanguageType nLanguage;
+ if (!utl::ConfigManager::IsFuzzing())
+ nLanguage = Application::GetSettings().GetLanguageTag().getLanguageType();
+ else
+ nLanguage = LANGUAGE_ENGLISH_US;
+
+ // get DEFAULTFONT_LATIN_TEXT and set at pool as dynamic default
+ vcl::Font aFont(OutputDevice::GetDefaultFont(DefaultFontType::LATIN_TEXT, nLanguage, GetDefaultFontFlags::OnlyOne));
+ aSvxFontItem.SetFamily(aFont.GetFamilyType());
+ aSvxFontItem.SetFamilyName(aFont.GetFamilyName());
+ aSvxFontItem.SetStyleName(OUString());
+ aSvxFontItem.SetPitch( aFont.GetPitch());
+ aSvxFontItem.SetCharSet( aFont.GetCharSet() );
+ pItemPool->SetPoolDefaultItem(aSvxFontItem);
+
+ // get DEFAULTFONT_CJK_TEXT and set at pool as dynamic default
+ vcl::Font aFontCJK(OutputDevice::GetDefaultFont(DefaultFontType::CJK_TEXT, nLanguage, GetDefaultFontFlags::OnlyOne));
+ aSvxFontItemCJK.SetFamily( aFontCJK.GetFamilyType());
+ aSvxFontItemCJK.SetFamilyName(aFontCJK.GetFamilyName());
+ aSvxFontItemCJK.SetStyleName(OUString());
+ aSvxFontItemCJK.SetPitch( aFontCJK.GetPitch());
+ aSvxFontItemCJK.SetCharSet( aFontCJK.GetCharSet());
+ pItemPool->SetPoolDefaultItem(aSvxFontItemCJK);
+
+ // get DEFAULTFONT_CTL_TEXT and set at pool as dynamic default
+ vcl::Font aFontCTL(OutputDevice::GetDefaultFont(DefaultFontType::CTL_TEXT, nLanguage, GetDefaultFontFlags::OnlyOne));
+ aSvxFontItemCTL.SetFamily(aFontCTL.GetFamilyType());
+ aSvxFontItemCTL.SetFamilyName(aFontCTL.GetFamilyName());
+ aSvxFontItemCTL.SetStyleName(OUString());
+ aSvxFontItemCTL.SetPitch( aFontCTL.GetPitch() );
+ aSvxFontItemCTL.SetCharSet( aFontCTL.GetCharSet());
+ pItemPool->SetPoolDefaultItem(aSvxFontItemCTL);
+
+ // set dynamic FontHeight defaults
+ pItemPool->SetPoolDefaultItem( SvxFontHeightItem(nDefTextHgt, 100, EE_CHAR_FONTHEIGHT ) );
+ pItemPool->SetPoolDefaultItem( SvxFontHeightItem(nDefTextHgt, 100, EE_CHAR_FONTHEIGHT_CJK ) );
+ pItemPool->SetPoolDefaultItem( SvxFontHeightItem(nDefTextHgt, 100, EE_CHAR_FONTHEIGHT_CTL ) );
+
+ // set FontColor defaults
+ pItemPool->SetPoolDefaultItem( SvxColorItem(SdrEngineDefaults::GetFontColor(), EE_CHAR_COLOR) );
+}
+
+SdrOutliner& SdrModel::GetDrawOutliner(const SdrTextObj* pObj) const
+{
+ m_pDrawOutliner->SetTextObj(pObj);
+ return *m_pDrawOutliner;
+}
+
+SdrOutliner& SdrModel::GetChainingOutliner(const SdrTextObj* pObj) const
+{
+ m_pChainingOutliner->SetTextObj(pObj);
+ return *m_pChainingOutliner;
+}
+
+const SdrTextObj* SdrModel::GetFormattingTextObj() const
+{
+ if (m_pDrawOutliner!=nullptr) {
+ return m_pDrawOutliner->GetTextObj();
+ }
+ return nullptr;
+}
+
+void SdrModel::ImpSetOutlinerDefaults( SdrOutliner* pOutliner, bool bInit )
+{
+ // Initialization of the Outliners for drawing text and HitTest
+ if( bInit )
+ {
+ pOutliner->EraseVirtualDevice();
+ pOutliner->SetUpdateLayout(false);
+ pOutliner->SetEditTextObjectPool(m_pItemPool.get());
+ pOutliner->SetDefTab(m_nDefaultTabulator);
+ }
+
+ pOutliner->SetRefDevice(GetRefDevice());
+ Outliner::SetForbiddenCharsTable(GetForbiddenCharsTable());
+ pOutliner->SetAsianCompressionMode( mnCharCompressType );
+ pOutliner->SetKernAsianPunctuation( IsKernAsianPunctuation() );
+ pOutliner->SetAddExtLeading( IsAddExtLeading() );
+
+ if ( !GetRefDevice() )
+ {
+ MapMode aMapMode(m_eObjUnit, Point(0,0), m_aObjUnit, m_aObjUnit);
+ pOutliner->SetRefMapMode(aMapMode);
+ }
+}
+
+void SdrModel::SetRefDevice(OutputDevice* pDev)
+{
+ m_pRefOutDev=pDev;
+ ImpSetOutlinerDefaults( m_pDrawOutliner.get() );
+ ImpSetOutlinerDefaults( m_pHitTestOutliner.get() );
+ RefDeviceChanged();
+}
+
+void SdrModel::ImpReformatAllTextObjects()
+{
+ if( isLocked() )
+ return;
+
+ sal_uInt16 nCount=GetMasterPageCount();
+ sal_uInt16 nNum;
+ for (nNum=0; nNum<nCount; nNum++) {
+ GetMasterPage(nNum)->ReformatAllTextObjects();
+ }
+ nCount=GetPageCount();
+ for (nNum=0; nNum<nCount; nNum++) {
+ GetPage(nNum)->ReformatAllTextObjects();
+ }
+}
+
+/* steps over all available pages and sends notify messages to
+ all edge objects that are connected to other objects so that
+ they may reposition themselves
+*/
+void SdrModel::ImpReformatAllEdgeObjects()
+{
+ if( isLocked() )
+ return;
+
+ sal_uInt16 nCount=GetMasterPageCount();
+ sal_uInt16 nNum;
+ for (nNum=0; nNum<nCount; nNum++)
+ {
+ GetMasterPage(nNum)->ReformatAllEdgeObjects();
+ }
+ nCount=GetPageCount();
+ for (nNum=0; nNum<nCount; nNum++)
+ {
+ GetPage(nNum)->ReformatAllEdgeObjects();
+ }
+}
+
+uno::Reference<embed::XStorage> SdrModel::GetDocumentStorage() const
+{
+ uno::Reference<document::XStorageBasedDocument> const xSBD(
+ const_cast<SdrModel*>(this)->getUnoModel(), uno::UNO_QUERY);
+ if (!xSBD.is())
+ {
+ SAL_WARN("svx", "no UNO model");
+ return nullptr;
+ }
+ return xSBD->getDocumentStorage();
+}
+
+uno::Reference<io::XInputStream>
+SdrModel::GetDocumentStream( OUString const& rURL,
+ ::comphelper::LifecycleProxy const & rProxy) const
+{
+ uno::Reference<embed::XStorage> const xStorage(GetDocumentStorage());
+ if (!xStorage.is())
+ {
+ SAL_WARN("svx", "no storage?");
+ return nullptr;
+ }
+ try {
+ uno::Reference<io::XStream> const xStream(
+ ::comphelper::OStorageHelper::GetStreamAtPackageURL(
+ xStorage, rURL, embed::ElementModes::READ, rProxy));
+ return (xStream.is()) ? xStream->getInputStream() : nullptr;
+ }
+ catch (container::NoSuchElementException const&)
+ {
+ SAL_INFO("svx", "not found");
+ }
+ catch (uno::Exception const&)
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+ return nullptr;
+}
+
+// convert template attributes from the string into "hard" attributes
+void SdrModel::BurnInStyleSheetAttributes()
+{
+ sal_uInt16 nCount=GetMasterPageCount();
+ sal_uInt16 nNum;
+ for (nNum=0; nNum<nCount; nNum++) {
+ GetMasterPage(nNum)->BurnInStyleSheetAttributes();
+ }
+ nCount=GetPageCount();
+ for (nNum=0; nNum<nCount; nNum++) {
+ GetPage(nNum)->BurnInStyleSheetAttributes();
+ }
+}
+
+void SdrModel::RefDeviceChanged()
+{
+ Broadcast(SdrHint(SdrHintKind::RefDeviceChange));
+ ImpReformatAllTextObjects();
+}
+
+void SdrModel::SetDefaultFontHeight(sal_Int32 nVal)
+{
+ if (nVal!=mnDefTextHgt) {
+ mnDefTextHgt=nVal;
+ ImpReformatAllTextObjects();
+ }
+}
+
+void SdrModel::SetDefaultTabulator(sal_uInt16 nVal)
+{
+ if (m_nDefaultTabulator!=nVal) {
+ m_nDefaultTabulator=nVal;
+ Outliner& rOutliner=GetDrawOutliner();
+ rOutliner.SetDefTab(nVal);
+ Broadcast(SdrHint(SdrHintKind::DefaultTabChange));
+ ImpReformatAllTextObjects();
+ }
+}
+
+void SdrModel::ImpSetUIUnit()
+{
+ if(0 == m_aUIScale.GetNumerator() || 0 == m_aUIScale.GetDenominator())
+ {
+ m_aUIScale = Fraction(1,1);
+ }
+
+ m_nUIUnitDecimalMark = 0;
+
+ o3tl::Length eFrom = MapToO3tlLength(m_eObjUnit, o3tl::Length::invalid);
+ o3tl::Length eTo;
+
+ switch (m_eUIUnit)
+ {
+ case FieldUnit::CHAR:
+ case FieldUnit::LINE:
+ eTo = o3tl::Length::invalid;
+ break;
+ case FieldUnit::PERCENT:
+ m_nUIUnitDecimalMark += 2;
+ [[fallthrough]];
+ default:
+ eTo = FieldToO3tlLength(m_eUIUnit, o3tl::Length::invalid);
+ } // switch
+
+ sal_Int32 nMul = 1, nDiv = 1;
+ if (eFrom != o3tl::Length::invalid && eTo != o3tl::Length::invalid)
+ {
+ const auto& [mul, div] = o3tl::getConversionMulDiv(eFrom, eTo);
+ nMul = mul;
+ nDiv = div;
+ }
+ // #i89872# take Unit of Measurement into account
+ if(1 != m_aUIScale.GetDenominator() || 1 != m_aUIScale.GetNumerator())
+ {
+ // divide by UIScale
+ nMul *= m_aUIScale.GetDenominator();
+ nDiv *= m_aUIScale.GetNumerator();
+ }
+
+ // shorten trailing zeros for dividend
+ while(0 == (nMul % 10))
+ {
+ m_nUIUnitDecimalMark--;
+ nMul /= 10;
+ }
+
+ // shorten trailing zeros for divisor
+ while(0 == (nDiv % 10))
+ {
+ m_nUIUnitDecimalMark++;
+ nDiv /= 10;
+ }
+
+ // end preparations, set member values
+ m_aUIUnitFact = Fraction(sal_Int32(nMul), sal_Int32(nDiv));
+ m_aUIUnitStr = GetUnitString(m_eUIUnit);
+}
+
+void SdrModel::SetScaleUnit(MapUnit eMap, const Fraction& rFrac)
+{
+ if (m_eObjUnit!=eMap || m_aObjUnit!=rFrac) {
+ m_eObjUnit=eMap;
+ m_aObjUnit=rFrac;
+ m_pItemPool->SetDefaultMetric(m_eObjUnit);
+ ImpSetUIUnit();
+ ImpSetOutlinerDefaults( m_pDrawOutliner.get() );
+ ImpSetOutlinerDefaults( m_pHitTestOutliner.get() );
+ ImpReformatAllTextObjects();
+ }
+}
+
+void SdrModel::SetScaleUnit(MapUnit eMap)
+{
+ if (m_eObjUnit!=eMap) {
+ m_eObjUnit=eMap;
+ m_pItemPool->SetDefaultMetric(m_eObjUnit);
+ ImpSetUIUnit();
+ ImpSetOutlinerDefaults( m_pDrawOutliner.get() );
+ ImpSetOutlinerDefaults( m_pHitTestOutliner.get() );
+ ImpReformatAllTextObjects();
+ }
+}
+
+void SdrModel::SetScaleFraction(const Fraction& rFrac)
+{
+ if (m_aObjUnit!=rFrac) {
+ m_aObjUnit=rFrac;
+ ImpSetUIUnit();
+ ImpSetOutlinerDefaults( m_pDrawOutliner.get() );
+ ImpSetOutlinerDefaults( m_pHitTestOutliner.get() );
+ ImpReformatAllTextObjects();
+ }
+}
+
+void SdrModel::SetUIUnit(FieldUnit eUnit)
+{
+ if (m_eUIUnit!=eUnit) {
+ m_eUIUnit=eUnit;
+ ImpSetUIUnit();
+ ImpReformatAllTextObjects();
+ }
+}
+
+void SdrModel::SetUIScale(const Fraction& rScale)
+{
+ if (m_aUIScale!=rScale) {
+ m_aUIScale=rScale;
+ ImpSetUIUnit();
+ ImpReformatAllTextObjects();
+ }
+}
+
+void SdrModel::SetUIUnit(FieldUnit eUnit, const Fraction& rScale)
+{
+ if (m_eUIUnit!=eUnit || m_aUIScale!=rScale) {
+ m_eUIUnit=eUnit;
+ m_aUIScale=rScale;
+ ImpSetUIUnit();
+ ImpReformatAllTextObjects();
+ }
+}
+
+OUString SdrModel::GetUnitString(FieldUnit eUnit)
+{
+ switch(eUnit)
+ {
+ default:
+ case FieldUnit::NONE :
+ case FieldUnit::CUSTOM :
+ return OUString();
+ case FieldUnit::MM_100TH:
+ return OUString{"/100mm"};
+ case FieldUnit::MM :
+ return OUString{"mm"};
+ case FieldUnit::CM :
+ return OUString{"cm"};
+ case FieldUnit::M :
+ return OUString{"m"};
+ case FieldUnit::KM :
+ return OUString{"km"};
+ case FieldUnit::TWIP :
+ return OUString{"twip"};
+ case FieldUnit::POINT :
+ return OUString{"pt"};
+ case FieldUnit::PICA :
+ return OUString{"pica"};
+ case FieldUnit::INCH :
+ return OUString{"\""};
+ case FieldUnit::FOOT :
+ return OUString{"ft"};
+ case FieldUnit::MILE :
+ return OUString{"mile(s)"};
+ case FieldUnit::PERCENT:
+ return OUString{"%"};
+ }
+}
+
+OUString SdrModel::GetMetricString(tools::Long nVal, bool bNoUnitChars, sal_Int32 nNumDigits) const
+{
+ // #i22167#
+ // change to double precision usage to not lose decimal places
+ const bool bNegative(nVal < 0);
+ SvtSysLocale aSysLoc;
+ const LocaleDataWrapper& rLoc(aSysLoc.GetLocaleData());
+ double fLocalValue(double(nVal) * double(m_aUIUnitFact));
+
+ if(bNegative)
+ {
+ fLocalValue = -fLocalValue;
+ }
+
+ if( -1 == nNumDigits )
+ {
+ nNumDigits = LocaleDataWrapper::getNumDigits();
+ }
+
+ sal_Int32 nDecimalMark(m_nUIUnitDecimalMark);
+
+ if(nDecimalMark > nNumDigits)
+ {
+ const sal_Int32 nDiff(nDecimalMark - nNumDigits);
+ const double fFactor(pow(10.0, static_cast<int>(nDiff)));
+
+ fLocalValue /= fFactor;
+ nDecimalMark = nNumDigits;
+ }
+ else if(nDecimalMark < nNumDigits)
+ {
+ const sal_Int32 nDiff(nNumDigits - nDecimalMark);
+ const double fFactor(pow(10.0, static_cast<int>(nDiff)));
+
+ fLocalValue *= fFactor;
+ nDecimalMark = nNumDigits;
+ }
+
+ OUStringBuffer aBuf;
+ aBuf.append(static_cast<sal_Int32>(fLocalValue + 0.5));
+
+ if(nDecimalMark < 0)
+ {
+ // negative nDecimalMark (decimal point) means: add zeros
+ sal_Int32 nCount(-nDecimalMark);
+
+ for(sal_Int32 i=0; i<nCount; i++)
+ aBuf.append('0');
+
+ nDecimalMark = 0;
+ }
+
+ // the second condition needs to be <= since inside this loop
+ // also the leading zero is inserted.
+ if (nDecimalMark > 0 && aBuf.getLength() <= nDecimalMark)
+ {
+ // if necessary, add zeros before the decimal point
+ sal_Int32 nCount = nDecimalMark - aBuf.getLength();
+
+ if(nCount >= 0 && LocaleDataWrapper::isNumLeadingZero())
+ nCount++;
+
+ for(sal_Int32 i=0; i<nCount; i++)
+ aBuf.insert(0, '0');
+ }
+
+ const sal_Unicode cDec( rLoc.getNumDecimalSep()[0] );
+
+ // insert the decimal mark character
+ sal_Int32 nBeforeDecimalMark = aBuf.getLength() - nDecimalMark;
+
+ if(nDecimalMark > 0)
+ aBuf.insert(nBeforeDecimalMark, cDec);
+
+ if(!LocaleDataWrapper::isNumTrailingZeros())
+ {
+ sal_Int32 aPos=aBuf.getLength()-1;
+
+ // Remove all trailing zeros.
+ while (aPos>=0 && aBuf[aPos]=='0')
+ --aPos;
+
+ // Remove decimal if it's the last character.
+ if (aPos>=0 && aBuf[aPos]==cDec)
+ --aPos;
+
+ // Adjust aPos to index first char to be truncated, if any
+ if (++aPos<aBuf.getLength())
+ aBuf.truncate(aPos);
+ }
+
+ // if necessary, add separators before every third digit
+ if( nBeforeDecimalMark > 3 )
+ {
+ const OUString& aThoSep( rLoc.getNumThousandSep() );
+ if ( !aThoSep.isEmpty() )
+ {
+ sal_Unicode cTho( aThoSep[0] );
+ sal_Int32 i(nBeforeDecimalMark - 3);
+
+ while(i > 0)
+ {
+ aBuf.insert(i, cTho);
+ i -= 3;
+ }
+ }
+ }
+
+ if (aBuf.isEmpty())
+ aBuf.append("0");
+
+ if(bNegative)
+ {
+ aBuf.insert(0, "-");
+ }
+
+ if(!bNoUnitChars)
+ aBuf.append(m_aUIUnitStr);
+
+ return aBuf.makeStringAndClear();
+}
+
+OUString SdrModel::GetAngleString(Degree100 nAngle)
+{
+ bool bNeg = nAngle < 0_deg100;
+
+ if(bNeg)
+ nAngle = -nAngle;
+
+ OUStringBuffer aBuf;
+ aBuf.append(static_cast<sal_Int32>(nAngle));
+
+ SvtSysLocale aSysLoc;
+ const LocaleDataWrapper& rLoc = aSysLoc.GetLocaleData();
+ sal_Int32 nCount = 2;
+
+ if(LocaleDataWrapper::isNumLeadingZero())
+ nCount++;
+
+ while(aBuf.getLength() < nCount)
+ aBuf.insert(0, '0');
+
+ aBuf.insert(aBuf.getLength()-2, rLoc.getNumDecimalSep()[0]);
+
+ if(bNeg)
+ aBuf.insert(0, '-');
+
+ aBuf.append(DEGREE_CHAR);
+
+ return aBuf.makeStringAndClear();
+}
+
+OUString SdrModel::GetPercentString(const Fraction& rVal)
+{
+ sal_Int32 nMul(rVal.GetNumerator());
+ sal_Int32 nDiv(rVal.GetDenominator());
+ bool bNeg {false};
+
+ if (nDiv < 0)
+ {
+ bNeg = !bNeg;
+ nDiv = -nDiv;
+ }
+
+ if (nMul < 0)
+ {
+ bNeg = !bNeg;
+ nMul = -nMul;
+ }
+
+ sal_Int32 nPct = ((nMul*100) + nDiv/2)/nDiv;
+
+ if (bNeg)
+ nPct = -nPct;
+
+ return OUString::number(nPct) + "%";
+}
+
+void SdrModel::SetChanged(bool bFlg)
+{
+ mbChanged = bFlg;
+}
+
+void SdrModel::RecalcPageNums(bool bMaster)
+{
+ if(bMaster)
+ {
+ sal_uInt16 nCount=sal_uInt16(maMasterPages.size());
+ sal_uInt16 i;
+ for (i=0; i<nCount; i++) {
+ SdrPage* pPg = maMasterPages[i].get();
+ pPg->SetPageNum(i);
+ }
+ m_bMPgNumsDirty=false;
+ }
+ else
+ {
+ sal_uInt16 nCount=sal_uInt16(maPages.size());
+ sal_uInt16 i;
+ for (i=0; i<nCount; i++) {
+ SdrPage* pPg = maPages[i].get();
+ pPg->SetPageNum(i);
+ }
+ m_bPagNumsDirty=false;
+ }
+}
+
+void SdrModel::InsertPage(SdrPage* pPage, sal_uInt16 nPos)
+{
+ sal_uInt16 nCount = GetPageCount();
+ if (nPos > nCount)
+ nPos = nCount;
+
+ maPages.insert(maPages.begin() + nPos, pPage);
+ PageListChanged();
+ pPage->SetInserted();
+ pPage->SetPageNum(nPos);
+
+ if (mbMakePageObjectsNamesUnique)
+ pPage->MakePageObjectsNamesUnique();
+
+ if (nPos<nCount) m_bPagNumsDirty=true;
+ SetChanged();
+ SdrHint aHint(SdrHintKind::PageOrderChange, pPage);
+ Broadcast(aHint);
+}
+
+void SdrModel::DeletePage(sal_uInt16 nPgNum)
+{
+ RemovePage(nPgNum);
+}
+
+rtl::Reference<SdrPage> SdrModel::RemovePage(sal_uInt16 nPgNum)
+{
+ rtl::Reference<SdrPage> pPg = maPages[nPgNum];
+ maPages.erase(maPages.begin()+nPgNum);
+ PageListChanged();
+ if (pPg) {
+ pPg->SetInserted(false);
+ }
+ m_bPagNumsDirty=true;
+ SetChanged();
+ SdrHint aHint(SdrHintKind::PageOrderChange, pPg.get());
+ Broadcast(aHint);
+ return pPg;
+}
+
+void SdrModel::MovePage(sal_uInt16 nPgNum, sal_uInt16 nNewPos)
+{
+ rtl::Reference<SdrPage> pPg = std::move(maPages[nPgNum]);
+ if (pPg) {
+ maPages.erase(maPages.begin()+nPgNum); // shortcut to avoid two broadcasts
+ PageListChanged();
+ pPg->SetInserted(false);
+ InsertPage(pPg.get(), nNewPos);
+ }
+ else
+ RemovePage(nPgNum);
+}
+
+void SdrModel::InsertMasterPage(SdrPage* pPage, sal_uInt16 nPos)
+{
+ sal_uInt16 nCount=GetMasterPageCount();
+ if (nPos>nCount) nPos=nCount;
+ maMasterPages.insert(maMasterPages.begin()+nPos,pPage);
+ MasterPageListChanged();
+ pPage->SetInserted();
+ pPage->SetPageNum(nPos);
+
+ if (nPos<nCount) {
+ m_bMPgNumsDirty=true;
+ }
+
+ SetChanged();
+ SdrHint aHint(SdrHintKind::PageOrderChange, pPage);
+ Broadcast(aHint);
+}
+
+void SdrModel::DeleteMasterPage(sal_uInt16 nPgNum)
+{
+ RemoveMasterPage(nPgNum);
+}
+
+rtl::Reference<SdrPage> SdrModel::RemoveMasterPage(sal_uInt16 nPgNum)
+{
+ rtl::Reference<SdrPage> pRetPg = std::move(maMasterPages[nPgNum]);
+ maMasterPages.erase(maMasterPages.begin()+nPgNum);
+ MasterPageListChanged();
+
+ if(pRetPg)
+ {
+ // Now delete the links from the normal drawing pages to the deleted master page.
+ sal_uInt16 nPageCnt(GetPageCount());
+
+ for(sal_uInt16 np(0); np < nPageCnt; np++)
+ {
+ GetPage(np)->TRG_ImpMasterPageRemoved(*pRetPg);
+ }
+
+ pRetPg->SetInserted(false);
+ }
+
+ m_bMPgNumsDirty=true;
+ SetChanged();
+ SdrHint aHint(SdrHintKind::PageOrderChange, pRetPg.get());
+ Broadcast(aHint);
+ return pRetPg;
+}
+
+void SdrModel::MoveMasterPage(sal_uInt16 nPgNum, sal_uInt16 nNewPos)
+{
+ rtl::Reference<SdrPage> pPg = std::move(maMasterPages[nPgNum]);
+ maMasterPages.erase(maMasterPages.begin()+nPgNum);
+ MasterPageListChanged();
+ if (pPg) {
+ pPg->SetInserted(false);
+ maMasterPages.insert(maMasterPages.begin()+nNewPos,pPg);
+ MasterPageListChanged();
+ }
+ m_bMPgNumsDirty=true;
+ SetChanged();
+ SdrHint aHint(SdrHintKind::PageOrderChange, pPg.get());
+ Broadcast(aHint);
+}
+
+
+void SdrModel::CopyPages(sal_uInt16 nFirstPageNum, sal_uInt16 nLastPageNum,
+ sal_uInt16 nDestPos,
+ bool bUndo, bool bMoveNoCopy)
+{
+ if( bUndo && !IsUndoEnabled() )
+ bUndo = false;
+
+ if( bUndo )
+ BegUndo(SvxResId(STR_UndoMergeModel));
+
+ sal_uInt16 nPageCnt=GetPageCount();
+ sal_uInt16 nMaxPage=nPageCnt;
+
+ if (nMaxPage!=0)
+ nMaxPage--;
+ if (nFirstPageNum>nMaxPage)
+ nFirstPageNum=nMaxPage;
+ if (nLastPageNum>nMaxPage)
+ nLastPageNum =nMaxPage;
+ bool bReverse=nLastPageNum<nFirstPageNum;
+ if (nDestPos>nPageCnt)
+ nDestPos=nPageCnt;
+
+ // at first, save the pointers of the affected pages in an array
+ sal_uInt16 nPageNum=nFirstPageNum;
+ sal_uInt16 nCopyCnt=((!bReverse)?(nLastPageNum-nFirstPageNum):(nFirstPageNum-nLastPageNum))+1;
+ std::unique_ptr<SdrPage*[]> pPagePtrs(new SdrPage*[nCopyCnt]);
+ sal_uInt16 nCopyNum;
+ for(nCopyNum=0; nCopyNum<nCopyCnt; nCopyNum++)
+ {
+ pPagePtrs[nCopyNum]=GetPage(nPageNum);
+ if (bReverse)
+ nPageNum--;
+ else
+ nPageNum++;
+ }
+
+ // now copy the pages
+ sal_uInt16 nDestNum=nDestPos;
+ for (nCopyNum=0; nCopyNum<nCopyCnt; nCopyNum++)
+ {
+ rtl::Reference<SdrPage> pPg = pPagePtrs[nCopyNum];
+ sal_uInt16 nPageNum2=pPg->GetPageNum();
+ if (!bMoveNoCopy)
+ {
+ const SdrPage* pPg1=GetPage(nPageNum2);
+
+ // Clone to local model
+ pPg = pPg1->CloneSdrPage(*this);
+
+ InsertPage(pPg.get(), nDestNum);
+ if (bUndo)
+ AddUndo(GetSdrUndoFactory().CreateUndoCopyPage(*pPg));
+ nDestNum++;
+ }
+ else
+ {
+ // TODO: Move is untested!
+ if (nDestNum>nPageNum2)
+ nDestNum--;
+
+ if(bUndo)
+ AddUndo(GetSdrUndoFactory().CreateUndoSetPageNum(*GetPage(nPageNum2),nPageNum2,nDestNum));
+
+ pPg=RemovePage(nPageNum2);
+ InsertPage(pPg.get(), nDestNum);
+ nDestNum++;
+ }
+
+ if(bReverse)
+ nPageNum2--;
+ else
+ nPageNum2++;
+ }
+
+ pPagePtrs.reset();
+ if(bUndo)
+ EndUndo();
+}
+
+void SdrModel::Merge(SdrModel& rSourceModel,
+ sal_uInt16 nFirstPageNum, sal_uInt16 nLastPageNum,
+ sal_uInt16 nDestPos,
+ bool bMergeMasterPages, bool bAllMasterPages,
+ bool bUndo, bool bTreadSourceAsConst)
+{
+ if (&rSourceModel==this)
+ {
+ CopyPages(nFirstPageNum,nLastPageNum,nDestPos,bUndo,!bTreadSourceAsConst);
+ return;
+ }
+
+ if( bUndo && !IsUndoEnabled() )
+ bUndo = false;
+
+ if (bUndo)
+ BegUndo(SvxResId(STR_UndoMergeModel));
+
+ sal_uInt16 nSrcPageCnt=rSourceModel.GetPageCount();
+ sal_uInt16 nSrcMasterPageCnt=rSourceModel.GetMasterPageCount();
+ sal_uInt16 nDstMasterPageCnt=GetMasterPageCount();
+ bool bInsPages=(nFirstPageNum<nSrcPageCnt || nLastPageNum<nSrcPageCnt);
+ sal_uInt16 nMaxSrcPage=nSrcPageCnt; if (nMaxSrcPage!=0) nMaxSrcPage--;
+ if (nFirstPageNum>nMaxSrcPage) nFirstPageNum=nMaxSrcPage;
+ if (nLastPageNum>nMaxSrcPage) nLastPageNum =nMaxSrcPage;
+ bool bReverse=nLastPageNum<nFirstPageNum;
+
+ std::unique_ptr<sal_uInt16[]> pMasterMap;
+ std::unique_ptr<bool[]> pMasterNeed;
+ sal_uInt16 nMasterNeed=0;
+ if (bMergeMasterPages && nSrcMasterPageCnt!=0) {
+ // determine which MasterPages from rSrcModel we need
+ pMasterMap.reset(new sal_uInt16[nSrcMasterPageCnt]);
+ pMasterNeed.reset(new bool[nSrcMasterPageCnt]);
+ memset(pMasterMap.get(),0xFF,nSrcMasterPageCnt*sizeof(sal_uInt16));
+ if (bAllMasterPages) {
+ memset(pMasterNeed.get(), true, nSrcMasterPageCnt * sizeof(bool));
+ } else {
+ memset(pMasterNeed.get(), false, nSrcMasterPageCnt * sizeof(bool));
+ sal_uInt16 nStart= bReverse ? nLastPageNum : nFirstPageNum;
+ sal_uInt16 nEnd= bReverse ? nFirstPageNum : nLastPageNum;
+ for (sal_uInt16 i=nStart; i<=nEnd; i++) {
+ const SdrPage* pPg=rSourceModel.GetPage(i);
+ if(pPg->TRG_HasMasterPage())
+ {
+ SdrPage& rMasterPage = pPg->TRG_GetMasterPage();
+ sal_uInt16 nMPgNum(rMasterPage.GetPageNum());
+
+ if(nMPgNum < nSrcMasterPageCnt)
+ {
+ pMasterNeed[nMPgNum] = true;
+ }
+ }
+ }
+ }
+ // now determine the Mapping of the MasterPages
+ sal_uInt16 nCurrentMaPagNum=nDstMasterPageCnt;
+ for (sal_uInt16 i=0; i<nSrcMasterPageCnt; i++) {
+ if (pMasterNeed[i]) {
+ pMasterMap[i]=nCurrentMaPagNum;
+ nCurrentMaPagNum++;
+ nMasterNeed++;
+ }
+ }
+ }
+
+ // get the MasterPages
+ if (pMasterMap && pMasterNeed && nMasterNeed!=0) {
+ for (sal_uInt16 i=nSrcMasterPageCnt; i>0;) {
+ i--;
+ if (pMasterNeed[i])
+ {
+ // Always Clone to new model
+ const SdrPage* pPg1(rSourceModel.GetMasterPage(i));
+ rtl::Reference<SdrPage> pPg = pPg1->CloneSdrPage(*this);
+
+ if(!bTreadSourceAsConst)
+ {
+ // if requested, delete original/modify original model
+ rSourceModel.RemoveMasterPage(i);
+ }
+
+ if (pPg!=nullptr) {
+ // Now append all of them to the end of the DstModel.
+ // Don't use InsertMasterPage(), because everything is
+ // inconsistent until all are in.
+ maMasterPages.insert(maMasterPages.begin()+nDstMasterPageCnt, pPg);
+ MasterPageListChanged();
+ pPg->SetInserted();
+ m_bMPgNumsDirty=true;
+ if (bUndo) AddUndo(GetSdrUndoFactory().CreateUndoNewPage(*pPg));
+ } else {
+ OSL_FAIL("SdrModel::Merge(): MasterPage not found in SourceModel.");
+ }
+ }
+ }
+ }
+
+ // get the drawing pages
+ if (bInsPages) {
+ sal_uInt16 nSourcePos=nFirstPageNum;
+ sal_uInt16 nMergeCount=sal_uInt16(std::abs(static_cast<tools::Long>(static_cast<tools::Long>(nFirstPageNum)-nLastPageNum))+1);
+ if (nDestPos>GetPageCount()) nDestPos=GetPageCount();
+ while (nMergeCount>0)
+ {
+ // Always Clone to new model
+ const SdrPage* pPg1(rSourceModel.GetPage(nSourcePos));
+ rtl::Reference<SdrPage> pPg = pPg1->CloneSdrPage(*this);
+
+ if(!bTreadSourceAsConst)
+ {
+ // if requested, delete original/modify original model
+ rSourceModel.RemovePage(nSourcePos);
+ }
+
+ if (pPg!=nullptr) {
+ InsertPage(pPg.get(),nDestPos);
+ if (bUndo) AddUndo(GetSdrUndoFactory().CreateUndoNewPage(*pPg));
+
+ if(pPg->TRG_HasMasterPage())
+ {
+ SdrPage& rMasterPage = pPg->TRG_GetMasterPage();
+ sal_uInt16 nMaPgNum(rMasterPage.GetPageNum());
+
+ if (bMergeMasterPages)
+ {
+ sal_uInt16 nNewNum(0xFFFF);
+
+ if(pMasterMap)
+ {
+ nNewNum = pMasterMap[nMaPgNum];
+ }
+
+ if(nNewNum != 0xFFFF)
+ {
+ // tdf#90357 here pPg and the to-be-set new masterpage are parts of the new model
+ // already, but the currently set masterpage is part of the old model. Remove master
+ // page from already cloned page to prevent creating wrong undo action that can
+ // eventually crash the app.
+ // Do *not* remove it directly after cloning - the old masterpage is still needed
+ // later to find the new to-be-set masterpage.
+ pPg->TRG_ClearMasterPage();
+
+ if(bUndo)
+ {
+ AddUndo(GetSdrUndoFactory().CreateUndoPageChangeMasterPage(*pPg));
+ }
+
+ pPg->TRG_SetMasterPage(*GetMasterPage(nNewNum));
+ }
+ DBG_ASSERT(nNewNum!=0xFFFF,"SdrModel::Merge(): Something is crooked with the mapping of the MasterPages.");
+ } else {
+ if (nMaPgNum>=nDstMasterPageCnt) {
+ // This is outside of the original area of the MasterPage of the DstModel.
+ pPg->TRG_ClearMasterPage();
+ }
+ }
+ }
+
+ } else {
+ OSL_FAIL("SdrModel::Merge(): Drawing page not found in SourceModel.");
+ }
+ nDestPos++;
+ if (bReverse) nSourcePos--;
+ else if (bTreadSourceAsConst) nSourcePos++;
+ nMergeCount--;
+ }
+ }
+
+ pMasterMap.reset();
+ pMasterNeed.reset();
+
+ m_bMPgNumsDirty=true;
+ m_bPagNumsDirty=true;
+
+ SetChanged();
+ // TODO: Missing: merging and mapping of layers
+ // at the objects as well as at the MasterPageDescriptors
+ if (bUndo) EndUndo();
+}
+
+void SdrModel::SetStarDrawPreviewMode(bool bPreview)
+{
+ if (!bPreview && m_bStarDrawPreviewMode && GetPageCount())
+ {
+ // Resetting is not allowed, because the Model might not be loaded completely
+ SAL_WARN("svx", "SdrModel::SetStarDrawPreviewMode(): Resetting not allowed, because Model might not be complete.");
+ }
+ else
+ {
+ m_bStarDrawPreviewMode = bPreview;
+ }
+}
+
+void SdrModel::SetTheme(std::unique_ptr<svx::Theme> pTheme) { mpImpl->mpTheme = std::move(pTheme); }
+
+svx::Theme* SdrModel::GetTheme() { return mpImpl->mpTheme.get(); }
+
+uno::Reference< uno::XInterface > const & SdrModel::getUnoModel()
+{
+ if( !mxUnoModel.is() )
+ mxUnoModel = createUnoModel();
+
+ return mxUnoModel;
+}
+
+void SdrModel::setUnoModel( const css::uno::Reference< css::uno::XInterface >& xModel )
+{
+ mxUnoModel = xModel;
+}
+
+void SdrModel::adaptSizeAndBorderForAllPages(
+ const Size& /*rNewSize*/,
+ tools::Long /*nLeft*/,
+ tools::Long /*nRight*/,
+ tools::Long /*nUpper*/,
+ tools::Long /*nLower*/)
+{
+ // base implementation does currently nothing. It may be added if needed,
+ // but we are on SdrModel level here, thus probably have not enough information
+ // to do this for higher-level (derived) Models (e.g. Draw/Impress)
+}
+
+uno::Reference< uno::XInterface > SdrModel::createUnoModel()
+{
+ OSL_FAIL( "SdrModel::createUnoModel() - base implementation should not be called!" );
+ css::uno::Reference< css::uno::XInterface > xInt;
+ return xInt;
+}
+
+void SdrModel::setLock( bool bLock )
+{
+ if( mbModelLocked != bLock )
+ {
+ // #i120437# need to set first, else ImpReformatAllEdgeObjects will do nothing
+ mbModelLocked = bLock;
+
+ if( !bLock )
+ {
+ ImpReformatAllEdgeObjects();
+ }
+ }
+}
+
+
+void SdrModel::MigrateItemSet( const SfxItemSet* pSourceSet, SfxItemSet* pDestSet, SdrModel* pNewModelel )
+{
+ assert(pNewModelel != nullptr);
+ if( !(pSourceSet && pDestSet && (pSourceSet != pDestSet )) )
+ return;
+
+ SfxWhichIter aWhichIter(*pSourceSet);
+ sal_uInt16 nWhich(aWhichIter.FirstWhich());
+ const SfxPoolItem *pPoolItem;
+
+ while(nWhich)
+ {
+ if(SfxItemState::SET == aWhichIter.GetItemState(false, &pPoolItem))
+ {
+ std::unique_ptr<SfxPoolItem> pResultItem;
+
+ switch( nWhich )
+ {
+ case XATTR_FILLBITMAP:
+ pResultItem = static_cast<const XFillBitmapItem*>(pPoolItem)->checkForUniqueItem( pNewModelel );
+ break;
+ case XATTR_LINEDASH:
+ pResultItem = static_cast<const XLineDashItem*>(pPoolItem)->checkForUniqueItem( pNewModelel );
+ break;
+ case XATTR_LINESTART:
+ pResultItem = static_cast<const XLineStartItem*>(pPoolItem)->checkForUniqueItem( pNewModelel );
+ break;
+ case XATTR_LINEEND:
+ pResultItem = static_cast<const XLineEndItem*>(pPoolItem)->checkForUniqueItem( pNewModelel );
+ break;
+ case XATTR_FILLGRADIENT:
+ pResultItem = static_cast<const XFillGradientItem*>(pPoolItem)->checkForUniqueItem( pNewModelel );
+ break;
+ case XATTR_FILLFLOATTRANSPARENCE:
+ // allow all kinds of XFillFloatTransparenceItem to be set
+ pResultItem = static_cast<const XFillFloatTransparenceItem*>(pPoolItem)->checkForUniqueItem( pNewModelel );
+ break;
+ case XATTR_FILLHATCH:
+ pResultItem = static_cast<const XFillHatchItem*>(pPoolItem)->checkForUniqueItem( pNewModelel );
+ break;
+ }
+
+ // set item
+ if( pResultItem )
+ pDestSet->Put(std::move(pResultItem));
+ else
+ pDestSet->Put(*pPoolItem);
+ }
+ nWhich = aWhichIter.NextWhich();
+ }
+}
+
+
+void SdrModel::SetForbiddenCharsTable(const std::shared_ptr<SvxForbiddenCharactersTable>& xForbiddenChars)
+{
+ mpForbiddenCharactersTable = xForbiddenChars;
+
+ ImpSetOutlinerDefaults( m_pDrawOutliner.get() );
+ ImpSetOutlinerDefaults( m_pHitTestOutliner.get() );
+}
+
+
+void SdrModel::SetCharCompressType( CharCompressType nType )
+{
+ if( nType != mnCharCompressType )
+ {
+ mnCharCompressType = nType;
+ ImpSetOutlinerDefaults( m_pDrawOutliner.get() );
+ ImpSetOutlinerDefaults( m_pHitTestOutliner.get() );
+ }
+}
+
+void SdrModel::SetKernAsianPunctuation( bool bEnabled )
+{
+ if( mbKernAsianPunctuation != bEnabled )
+ {
+ mbKernAsianPunctuation = bEnabled;
+ ImpSetOutlinerDefaults( m_pDrawOutliner.get() );
+ ImpSetOutlinerDefaults( m_pHitTestOutliner.get() );
+ }
+}
+
+void SdrModel::SetAddExtLeading( bool bEnabled )
+{
+ if( mbAddExtLeading != bEnabled )
+ {
+ mbAddExtLeading = bEnabled;
+ ImpSetOutlinerDefaults( m_pDrawOutliner.get() );
+ ImpSetOutlinerDefaults( m_pHitTestOutliner.get() );
+ }
+}
+
+void SdrModel::SetAnchoredTextOverflowLegacy(bool bEnabled)
+{
+ mpImpl->mbAnchoredTextOverflowLegacy = bEnabled;
+}
+
+bool SdrModel::IsAnchoredTextOverflowLegacy() const
+{
+ return mpImpl->mbAnchoredTextOverflowLegacy;
+}
+
+void SdrModel::ReformatAllTextObjects()
+{
+ ImpReformatAllTextObjects();
+}
+
+std::unique_ptr<SdrOutliner> SdrModel::createOutliner( OutlinerMode nOutlinerMode )
+{
+ if( !mpOutlinerCache )
+ mpOutlinerCache.reset(new SdrOutlinerCache(this));
+
+ return mpOutlinerCache->createOutliner( nOutlinerMode );
+}
+
+std::vector<SdrOutliner*> SdrModel::GetActiveOutliners() const
+{
+ std::vector< SdrOutliner* > aRet(mpOutlinerCache ? mpOutlinerCache->GetActiveOutliners() : std::vector< SdrOutliner* >());
+ aRet.push_back(m_pDrawOutliner.get());
+ aRet.push_back(m_pHitTestOutliner.get());
+
+ return aRet;
+}
+
+void SdrModel::disposeOutliner( std::unique_ptr<SdrOutliner> pOutliner )
+{
+ if( mpOutlinerCache )
+ mpOutlinerCache->disposeOutliner( std::move(pOutliner) );
+}
+
+SvxNumType SdrModel::GetPageNumType() const
+{
+ return SVX_NUM_ARABIC;
+}
+
+void SdrModel::ReadUserDataSequenceValue(const css::beans::PropertyValue* pValue)
+{
+ if (pValue->Name == "AnchoredTextOverflowLegacy")
+ {
+ bool bBool = false;
+ if (pValue->Value >>= bBool)
+ {
+ mpImpl->mbAnchoredTextOverflowLegacy = bBool;
+ }
+ }
+}
+
+template <typename T>
+static void addPair(std::vector< std::pair< OUString, Any > >& aUserData, const OUString& name, const T val)
+{
+ aUserData.push_back(std::pair< OUString, Any >(name, css::uno::Any(val)));
+}
+
+void SdrModel::WriteUserDataSequence(css::uno::Sequence < css::beans::PropertyValue >& rValues)
+{
+ std::vector< std::pair< OUString, Any > > aUserData;
+ addPair(aUserData, "AnchoredTextOverflowLegacy", IsAnchoredTextOverflowLegacy());
+
+ const sal_Int32 nOldLength = rValues.getLength();
+ rValues.realloc(nOldLength + aUserData.size());
+
+ css::beans::PropertyValue* pValue = &(rValues.getArray()[nOldLength]);
+
+ for (const auto &aIter : aUserData)
+ {
+ pValue->Name = aIter.first;
+ pValue->Value = aIter.second;
+ ++pValue;
+ }
+}
+
+const SdrPage* SdrModel::GetPage(sal_uInt16 nPgNum) const
+{
+ return nPgNum < maPages.size() ? maPages[nPgNum].get() : nullptr;
+}
+
+SdrPage* SdrModel::GetPage(sal_uInt16 nPgNum)
+{
+ return nPgNum < maPages.size() ? maPages[nPgNum].get() : nullptr;
+}
+
+sal_uInt16 SdrModel::GetPageCount() const
+{
+ return sal_uInt16(maPages.size());
+}
+
+void SdrModel::PageListChanged()
+{
+}
+
+TextChain *SdrModel::GetTextChain() const
+{
+ return m_pTextChain.get();
+}
+
+const SdrPage* SdrModel::GetMasterPage(sal_uInt16 nPgNum) const
+{
+ DBG_ASSERT(nPgNum < maMasterPages.size(), "SdrModel::GetMasterPage: Access out of range (!)");
+ return maMasterPages[nPgNum].get();
+}
+
+SdrPage* SdrModel::GetMasterPage(sal_uInt16 nPgNum)
+{
+ DBG_ASSERT(nPgNum < maMasterPages.size(), "SdrModel::GetMasterPage: Access out of range (!)");
+ return maMasterPages[nPgNum].get();
+}
+
+sal_uInt16 SdrModel::GetMasterPageCount() const
+{
+ return sal_uInt16(maMasterPages.size());
+}
+
+void SdrModel::MasterPageListChanged()
+{
+}
+
+void SdrModel::SetSdrUndoManager( SfxUndoManager* pUndoManager )
+{
+ mpImpl->mpUndoManager = pUndoManager;
+}
+
+SfxUndoManager* SdrModel::GetSdrUndoManager() const
+{
+ return mpImpl->mpUndoManager;
+}
+
+SdrUndoFactory& SdrModel::GetSdrUndoFactory() const
+{
+ if( !mpImpl->mpUndoFactory )
+ mpImpl->mpUndoFactory = new SdrUndoFactory;
+ return *mpImpl->mpUndoFactory;
+}
+
+void SdrModel::SetSdrUndoFactory( SdrUndoFactory* pUndoFactory )
+{
+ if( pUndoFactory && (pUndoFactory != mpImpl->mpUndoFactory) )
+ {
+ delete mpImpl->mpUndoFactory;
+ mpImpl->mpUndoFactory = pUndoFactory;
+ }
+}
+
+void SdrModel::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SdrModel"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("maMasterPages"));
+ for (size_t i = 0; i < maMasterPages.size(); ++i)
+ {
+ if (const SdrPage* pPage = maMasterPages[i].get())
+ {
+ pPage->dumpAsXml(pWriter);
+ }
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("maPages"));
+ for (size_t i = 0; i < maPages.size(); ++i)
+ {
+ if (const SdrPage* pPage = maPages[i].get())
+ {
+ pPage->dumpAsXml(pWriter);
+ }
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+
+ if (mpImpl->mpTheme)
+ {
+ mpImpl->mpTheme->dumpAsXml(pWriter);
+ }
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+const css::uno::Sequence< sal_Int8 >& SdrModel::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theSdrModelUnoTunnelImplementationId;
+ return theSdrModelUnoTunnelImplementationId.getSeq();
+}
+
+
+SdrHint::SdrHint(SdrHintKind eNewHint)
+: SfxHint(SfxHintId::ThisIsAnSdrHint),
+ meHint(eNewHint),
+ mpObj(nullptr),
+ mpPage(nullptr)
+{
+}
+
+SdrHint::SdrHint(SdrHintKind eNewHint, const SdrObject& rNewObj)
+: SfxHint(SfxHintId::ThisIsAnSdrHint),
+ meHint(eNewHint),
+ mpObj(&rNewObj),
+ mpPage(rNewObj.getSdrPageFromSdrObject())
+{
+}
+
+SdrHint::SdrHint(SdrHintKind eNewHint, const SdrPage* pPage)
+: SfxHint(SfxHintId::ThisIsAnSdrHint),
+ meHint(eNewHint),
+ mpObj(nullptr),
+ mpPage(pPage)
+{
+}
+
+SdrHint::SdrHint(SdrHintKind eNewHint, const SdrObject& rNewObj, const SdrPage* pPage)
+: SfxHint(SfxHintId::ThisIsAnSdrHint),
+ meHint(eNewHint),
+ mpObj(&rNewObj),
+ mpPage(pPage)
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdmrkv.cxx b/svx/source/svdraw/svdmrkv.cxx
new file mode 100644
index 000000000..c298e1925
--- /dev/null
+++ b/svx/source/svdraw/svdmrkv.cxx
@@ -0,0 +1,2733 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <svx/svdmrkv.hxx>
+#include <svx/svdview.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdotable.hxx>
+
+#include <osl/diagnose.h>
+#include <osl/thread.h>
+#include <rtl/strbuf.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/xgrad.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflgrit.hxx>
+#include "gradtrns.hxx"
+#include <svx/xflftrit.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <svx/svdundo.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/scene3d.hxx>
+#include <svx/svdovirt.hxx>
+#include <sdr/overlay/overlayrollingrectangle.hxx>
+#include <svx/sdr/contact/objectcontact.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <svx/sdr/overlay/overlayselection.hxx>
+#include <svx/sdr/contact/viewcontact.hxx>
+#include <svx/sdr/contact/viewobjectcontact.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <svx/sdrpagewindow.hxx>
+#include <svx/sdrhittesthelper.hxx>
+#include <vcl/uitest/logger.hxx>
+#include <vcl/uitest/eventdescription.hxx>
+#include <vcl/window.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <comphelper/lok.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <sfx2/lokcomponenthelpers.hxx>
+#include <sfx2/viewsh.hxx>
+#include <svtools/optionsdrawinglayer.hxx>
+
+#include <array>
+
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+
+#include <boost/property_tree/json_parser.hpp>
+
+using namespace com::sun::star;
+
+// Migrate Marking of Objects, Points and GluePoints
+
+class ImplMarkingOverlay
+{
+ // The OverlayObjects
+ sdr::overlay::OverlayObjectList maObjects;
+
+ // The remembered second position in logical coordinates
+ basegfx::B2DPoint maSecondPosition;
+
+ // A flag to remember if the action is for unmarking.
+ bool mbUnmarking : 1;
+
+public:
+ ImplMarkingOverlay(const SdrPaintView& rView, const basegfx::B2DPoint& rStartPos, bool bUnmarking);
+
+ // The OverlayObjects are cleared using the destructor of OverlayObjectList.
+ // That destructor calls clear() at the list which removes all objects from the
+ // OverlayManager and deletes them.
+
+ void SetSecondPosition(const basegfx::B2DPoint& rNewPosition);
+ bool IsUnmarking() const { return mbUnmarking; }
+};
+
+ImplMarkingOverlay::ImplMarkingOverlay(const SdrPaintView& rView, const basegfx::B2DPoint& rStartPos, bool bUnmarking)
+: maSecondPosition(rStartPos),
+ mbUnmarking(bUnmarking)
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ return; // We do client-side object manipulation with the Kit API
+
+ for(sal_uInt32 a(0); a < rView.PaintWindowCount(); a++)
+ {
+ SdrPaintWindow* pCandidate = rView.GetPaintWindow(a);
+ const rtl::Reference< sdr::overlay::OverlayManager >& xTargetOverlay = pCandidate->GetOverlayManager();
+
+ if (xTargetOverlay.is())
+ {
+ std::unique_ptr<sdr::overlay::OverlayRollingRectangleStriped> pNew(new sdr::overlay::OverlayRollingRectangleStriped(
+ rStartPos, rStartPos, false));
+ xTargetOverlay->add(*pNew);
+ maObjects.append(std::move(pNew));
+ }
+ }
+}
+
+void ImplMarkingOverlay::SetSecondPosition(const basegfx::B2DPoint& rNewPosition)
+{
+ if(rNewPosition != maSecondPosition)
+ {
+ // apply to OverlayObjects
+ for(sal_uInt32 a(0); a < maObjects.count(); a++)
+ {
+ sdr::overlay::OverlayRollingRectangleStriped& rCandidate = static_cast< sdr::overlay::OverlayRollingRectangleStriped&>(maObjects.getOverlayObject(a));
+ rCandidate.setSecondPosition(rNewPosition);
+ }
+
+ // remember new position
+ maSecondPosition = rNewPosition;
+ }
+}
+
+class MarkingSubSelectionOverlay
+{
+ sdr::overlay::OverlayObjectList maObjects;
+
+public:
+ MarkingSubSelectionOverlay(const SdrPaintView& rView, std::vector<basegfx::B2DRectangle> const & rSelections)
+ {
+ if (comphelper::LibreOfficeKit::isActive())
+ return; // We do client-side object manipulation with the Kit API
+
+ for (sal_uInt32 a(0); a < rView.PaintWindowCount(); a++)
+ {
+ SdrPaintWindow* pCandidate = rView.GetPaintWindow(a);
+ const rtl::Reference<sdr::overlay::OverlayManager>& xTargetOverlay = pCandidate->GetOverlayManager();
+
+ if (xTargetOverlay.is())
+ {
+ const Color aHighlightColor = SvtOptionsDrawinglayer::getHilightColor();
+
+ std::unique_ptr<sdr::overlay::OverlaySelection> pNew =
+ std::make_unique<sdr::overlay::OverlaySelection>(
+ sdr::overlay::OverlayType::Transparent,
+ aHighlightColor, std::vector(rSelections), false);
+
+ xTargetOverlay->add(*pNew);
+ maObjects.append(std::move(pNew));
+ }
+ }
+ }
+};
+
+SdrMarkView::SdrMarkView(SdrModel& rSdrModel, OutputDevice* pOut)
+ : SdrSnapView(rSdrModel, pOut)
+ , mpMarkedObj(nullptr)
+ , mpMarkedPV(nullptr)
+ , maHdlList(this)
+ , meDragMode(SdrDragMode::Move)
+ , meEditMode(SdrViewEditMode::Edit)
+ , meEditMode0(SdrViewEditMode::Edit)
+ , mbDesignMode(false)
+ , mbForceFrameHandles(false)
+ , mbPlusHdlAlways(false)
+ , mbInsPolyPoint(false)
+ , mbMarkedObjRectDirty(false)
+ , mbMrkPntDirty(false)
+ , mbMarkedPointsRectsDirty(false)
+ , mbMarkHandlesHidden(false)
+ , mbNegativeX(false)
+{
+
+ BrkMarkObj();
+ BrkMarkPoints();
+ BrkMarkGluePoints();
+
+ StartListening(rSdrModel);
+}
+
+SdrMarkView::~SdrMarkView()
+{
+ // Migrate selections
+ BrkMarkObj();
+ BrkMarkPoints();
+ BrkMarkGluePoints();
+}
+
+void SdrMarkView::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
+{
+ if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint)
+ {
+ const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint);
+ SdrHintKind eKind=pSdrHint->GetKind();
+ if (eKind==SdrHintKind::ObjectChange || eKind==SdrHintKind::ObjectInserted || eKind==SdrHintKind::ObjectRemoved)
+ {
+ mbMarkedObjRectDirty=true;
+ mbMarkedPointsRectsDirty=true;
+ }
+ }
+ SdrSnapView::Notify(rBC,rHint);
+}
+
+void SdrMarkView::ModelHasChanged()
+{
+ SdrPaintView::ModelHasChanged();
+ GetMarkedObjectListWriteAccess().SetNameDirty();
+ mbMarkedObjRectDirty=true;
+ mbMarkedPointsRectsDirty=true;
+ // Example: Obj is selected and maMarkedObjectList is sorted.
+ // In another View 2, the ObjOrder is changed (e. g. MovToTop())
+ // Then we need to re-sort MarkList.
+ GetMarkedObjectListWriteAccess().SetUnsorted();
+ SortMarkedObjects();
+ mbMrkPntDirty=true;
+ UndirtyMrkPnt();
+ SdrView* pV=static_cast<SdrView*>(this);
+ if (pV!=nullptr && !pV->IsDragObj() && !pV->IsInsObjPoint()) {
+ AdjustMarkHdl();
+ }
+
+ if (comphelper::LibreOfficeKit::isActive())
+ modelHasChangedLOKit();
+}
+
+void SdrMarkView::modelHasChangedLOKit()
+{
+ if (GetMarkedObjectCount() <= 0)
+ return;
+
+ //TODO: Is MarkedObjRect valid at this point?
+ tools::Rectangle aSelection(GetMarkedObjRect());
+ tools::Rectangle* pResultSelection;
+ if (aSelection.IsEmpty())
+ pResultSelection = nullptr;
+ else
+ {
+ sal_uInt32 nTotalPaintWindows = this->PaintWindowCount();
+ if (nTotalPaintWindows == 1)
+ {
+ const OutputDevice* pOut = this->GetFirstOutputDevice();
+ const vcl::Window* pWin = pOut ? pOut->GetOwnerWindow() : nullptr;
+ if (pWin && pWin->IsChart())
+ {
+ const vcl::Window* pViewShellWindow = GetSfxViewShell()->GetEditWindowForActiveOLEObj();
+ if (pViewShellWindow && pViewShellWindow->IsAncestorOf(*pWin))
+ {
+ Point aOffsetPx = pWin->GetOffsetPixelFrom(*pViewShellWindow);
+ Point aLogicOffset = pWin->PixelToLogic(aOffsetPx);
+ aSelection.Move(aLogicOffset.getX(), aLogicOffset.getY());
+ }
+ }
+ }
+
+ // In case the map mode is in 100th MM, then need to convert the coordinates over to twips for LOK.
+ if (mpMarkedPV)
+ {
+ if (OutputDevice* pOutputDevice = mpMarkedPV->GetView().GetFirstOutputDevice())
+ {
+ if (pOutputDevice->GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
+ aSelection = o3tl::convert(aSelection, o3tl::Length::mm100, o3tl::Length::twip);
+ }
+ }
+
+ pResultSelection = &aSelection;
+
+ if (mbNegativeX)
+ {
+ // Convert to positive X doc-coordinates
+ tools::Long nTmp = aSelection.Left();
+ aSelection.SetLeft(-aSelection.Right());
+ aSelection.SetRight(-nTmp);
+ }
+ }
+
+ if (SfxViewShell* pViewShell = GetSfxViewShell())
+ SfxLokHelper::notifyInvalidation(pViewShell, pResultSelection);
+}
+
+bool SdrMarkView::IsAction() const
+{
+ return SdrSnapView::IsAction() || IsMarkObj() || IsMarkPoints() || IsMarkGluePoints();
+}
+
+void SdrMarkView::MovAction(const Point& rPnt)
+{
+ SdrSnapView::MovAction(rPnt);
+
+ if(IsMarkObj())
+ {
+ MovMarkObj(rPnt);
+ }
+ else if(IsMarkPoints())
+ {
+ MovMarkPoints(rPnt);
+ }
+ else if(IsMarkGluePoints())
+ {
+ MovMarkGluePoints(rPnt);
+ }
+}
+
+void SdrMarkView::EndAction()
+{
+ if(IsMarkObj())
+ {
+ EndMarkObj();
+ }
+ else if(IsMarkPoints())
+ {
+ EndMarkPoints();
+ }
+ else if(IsMarkGluePoints())
+ {
+ EndMarkGluePoints();
+ }
+
+ SdrSnapView::EndAction();
+}
+
+void SdrMarkView::BckAction()
+{
+ SdrSnapView::BckAction();
+ BrkMarkObj();
+ BrkMarkPoints();
+ BrkMarkGluePoints();
+}
+
+void SdrMarkView::BrkAction()
+{
+ SdrSnapView::BrkAction();
+ BrkMarkObj();
+ BrkMarkPoints();
+ BrkMarkGluePoints();
+}
+
+void SdrMarkView::TakeActionRect(tools::Rectangle& rRect) const
+{
+ if(IsMarkObj() || IsMarkPoints() || IsMarkGluePoints())
+ {
+ rRect = tools::Rectangle(maDragStat.GetStart(), maDragStat.GetNow());
+ }
+ else
+ {
+ SdrSnapView::TakeActionRect(rRect);
+ }
+}
+
+
+void SdrMarkView::ClearPageView()
+{
+ UnmarkAllObj();
+ SdrSnapView::ClearPageView();
+}
+
+void SdrMarkView::HideSdrPage()
+{
+ bool bMrkChg(false);
+
+ SdrPageView* pPageView = GetSdrPageView();
+ if (pPageView)
+ {
+ // break all creation actions when hiding page (#75081#)
+ BrkAction();
+
+ // Discard all selections on this page
+ bMrkChg = GetMarkedObjectListWriteAccess().DeletePageView(*pPageView);
+ }
+
+ SdrSnapView::HideSdrPage();
+
+ if(bMrkChg)
+ {
+ MarkListHasChanged();
+ AdjustMarkHdl();
+ }
+}
+
+
+void SdrMarkView::BegMarkObj(const Point& rPnt, bool bUnmark)
+{
+ BrkAction();
+
+ DBG_ASSERT(!mpMarkObjOverlay, "SdrMarkView::BegMarkObj: There exists a mpMarkObjOverlay (!)");
+
+ basegfx::B2DPoint aStartPos(rPnt.X(), rPnt.Y());
+ mpMarkObjOverlay.reset(new ImplMarkingOverlay(*this, aStartPos, bUnmark));
+
+ maDragStat.Reset(rPnt);
+ maDragStat.NextPoint();
+ maDragStat.SetMinMove(mnMinMovLog);
+}
+
+void SdrMarkView::MovMarkObj(const Point& rPnt)
+{
+ if(IsMarkObj() && maDragStat.CheckMinMoved(rPnt))
+ {
+ maDragStat.NextMove(rPnt);
+ DBG_ASSERT(mpMarkObjOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
+ basegfx::B2DPoint aNewPos(rPnt.X(), rPnt.Y());
+ mpMarkObjOverlay->SetSecondPosition(aNewPos);
+ }
+}
+
+bool SdrMarkView::EndMarkObj()
+{
+ bool bRetval(false);
+
+ if(IsMarkObj())
+ {
+ if(maDragStat.IsMinMoved())
+ {
+ tools::Rectangle aRect(maDragStat.GetStart(), maDragStat.GetNow());
+ aRect.Justify();
+ MarkObj(aRect, mpMarkObjOverlay->IsUnmarking());
+ bRetval = true;
+ }
+
+ // cleanup
+ BrkMarkObj();
+ }
+
+ return bRetval;
+}
+
+void SdrMarkView::BrkMarkObj()
+{
+ if(IsMarkObj())
+ {
+ DBG_ASSERT(mpMarkObjOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
+ mpMarkObjOverlay.reset();
+ }
+}
+
+
+bool SdrMarkView::BegMarkPoints(const Point& rPnt, bool bUnmark)
+{
+ if(HasMarkablePoints())
+ {
+ BrkAction();
+
+ DBG_ASSERT(!mpMarkPointsOverlay, "SdrMarkView::BegMarkObj: There exists a mpMarkPointsOverlay (!)");
+ basegfx::B2DPoint aStartPos(rPnt.X(), rPnt.Y());
+ mpMarkPointsOverlay.reset(new ImplMarkingOverlay(*this, aStartPos, bUnmark));
+
+ maDragStat.Reset(rPnt);
+ maDragStat.NextPoint();
+ maDragStat.SetMinMove(mnMinMovLog);
+
+ return true;
+ }
+
+ return false;
+}
+
+void SdrMarkView::MovMarkPoints(const Point& rPnt)
+{
+ if(IsMarkPoints() && maDragStat.CheckMinMoved(rPnt))
+ {
+ maDragStat.NextMove(rPnt);
+
+ DBG_ASSERT(mpMarkPointsOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
+ basegfx::B2DPoint aNewPos(rPnt.X(), rPnt.Y());
+ mpMarkPointsOverlay->SetSecondPosition(aNewPos);
+ }
+}
+
+bool SdrMarkView::EndMarkPoints()
+{
+ bool bRetval(false);
+
+ if(IsMarkPoints())
+ {
+ if(maDragStat.IsMinMoved())
+ {
+ tools::Rectangle aRect(maDragStat.GetStart(), maDragStat.GetNow());
+ aRect.Justify();
+ MarkPoints(&aRect, mpMarkPointsOverlay->IsUnmarking());
+
+ bRetval = true;
+ }
+
+ // cleanup
+ BrkMarkPoints();
+ }
+
+ return bRetval;
+}
+
+void SdrMarkView::BrkMarkPoints()
+{
+ if(IsMarkPoints())
+ {
+ DBG_ASSERT(mpMarkPointsOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
+ mpMarkPointsOverlay.reset();
+ }
+}
+
+
+bool SdrMarkView::BegMarkGluePoints(const Point& rPnt, bool bUnmark)
+{
+ if(HasMarkableGluePoints())
+ {
+ BrkAction();
+
+ DBG_ASSERT(!mpMarkGluePointsOverlay, "SdrMarkView::BegMarkObj: There exists a mpMarkGluePointsOverlay (!)");
+
+ basegfx::B2DPoint aStartPos(rPnt.X(), rPnt.Y());
+ mpMarkGluePointsOverlay.reset(new ImplMarkingOverlay(*this, aStartPos, bUnmark));
+ maDragStat.Reset(rPnt);
+ maDragStat.NextPoint();
+ maDragStat.SetMinMove(mnMinMovLog);
+
+ return true;
+ }
+
+ return false;
+}
+
+void SdrMarkView::MovMarkGluePoints(const Point& rPnt)
+{
+ if(IsMarkGluePoints() && maDragStat.CheckMinMoved(rPnt))
+ {
+ maDragStat.NextMove(rPnt);
+
+ DBG_ASSERT(mpMarkGluePointsOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
+ basegfx::B2DPoint aNewPos(rPnt.X(), rPnt.Y());
+ mpMarkGluePointsOverlay->SetSecondPosition(aNewPos);
+ }
+}
+
+void SdrMarkView::EndMarkGluePoints()
+{
+ if(IsMarkGluePoints())
+ {
+ if(maDragStat.IsMinMoved())
+ {
+ tools::Rectangle aRect(maDragStat.GetStart(),maDragStat.GetNow());
+ aRect.Justify();
+ MarkGluePoints(&aRect, mpMarkGluePointsOverlay->IsUnmarking());
+ }
+
+ // cleanup
+ BrkMarkGluePoints();
+ }
+}
+
+void SdrMarkView::BrkMarkGluePoints()
+{
+ if(IsMarkGluePoints())
+ {
+ DBG_ASSERT(mpMarkGluePointsOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
+ mpMarkGluePointsOverlay.reset();
+ }
+}
+
+bool SdrMarkView::MarkableObjectsExceed( int n ) const
+{
+ SdrPageView* pPV = GetSdrPageView();
+ if (!pPV)
+ return false;
+
+ SdrObjList* pOL=pPV->GetObjList();
+ const size_t nObjCount = pOL->GetObjCount();
+ for (size_t nObjNum=0; nObjNum<nObjCount; ++nObjNum) {
+ SdrObject* pObj=pOL->GetObj(nObjNum);
+ if (IsObjMarkable(pObj,pPV) && --n<0)
+ return true;
+ }
+
+ return false;
+}
+
+void SdrMarkView::hideMarkHandles()
+{
+ if(!mbMarkHandlesHidden)
+ {
+ mbMarkHandlesHidden = true;
+ AdjustMarkHdl();
+ }
+}
+
+void SdrMarkView::showMarkHandles()
+{
+ if(mbMarkHandlesHidden)
+ {
+ mbMarkHandlesHidden = false;
+ AdjustMarkHdl();
+ }
+}
+
+bool SdrMarkView::ImpIsFrameHandles() const
+{
+ const size_t nMarkCount=GetMarkedObjectCount();
+ bool bFrmHdl=nMarkCount>static_cast<size_t>(mnFrameHandlesLimit) || mbForceFrameHandles;
+ bool bStdDrag=meDragMode==SdrDragMode::Move;
+ if (nMarkCount==1 && bStdDrag && bFrmHdl)
+ {
+ const SdrObject* pObj=GetMarkedObjectByIndex(0);
+ if (pObj && pObj->GetObjInventor()==SdrInventor::Default)
+ {
+ SdrObjKind nIdent=pObj->GetObjIdentifier();
+ if (nIdent==SdrObjKind::Line || nIdent==SdrObjKind::Edge || nIdent==SdrObjKind::Caption || nIdent==SdrObjKind::Measure || nIdent==SdrObjKind::CustomShape || nIdent==SdrObjKind::Table )
+ {
+ bFrmHdl=false;
+ }
+ }
+ }
+ if (!bStdDrag && !bFrmHdl) {
+ // all other drag modes only with FrameHandles
+ bFrmHdl=true;
+ if (meDragMode==SdrDragMode::Rotate) {
+ // when rotating, use ObjOwn drag, if there's at least 1 PolyObj
+ for (size_t nMarkNum=0; nMarkNum<nMarkCount && bFrmHdl; ++nMarkNum) {
+ const SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
+ const SdrObject* pObj=pM->GetMarkedSdrObj();
+ bFrmHdl=!pObj->IsPolyObj();
+ }
+ }
+ }
+ if (!bFrmHdl) {
+ // FrameHandles, if at least 1 Obj can't do SpecialDrag
+ for (size_t nMarkNum=0; nMarkNum<nMarkCount && !bFrmHdl; ++nMarkNum) {
+ const SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
+ const SdrObject* pObj=pM->GetMarkedSdrObj();
+ bFrmHdl=!pObj->hasSpecialDrag();
+ }
+ }
+
+ // no FrameHdl for crop
+ if(bFrmHdl && SdrDragMode::Crop == meDragMode)
+ {
+ bFrmHdl = false;
+ }
+
+ return bFrmHdl;
+}
+
+namespace
+{
+std::u16string_view lcl_getDragMethodServiceName( std::u16string_view rCID )
+{
+ std::u16string_view aRet;
+
+ size_t nIndexStart = rCID.find( u"DragMethod=" );
+ if( nIndexStart != std::u16string_view::npos )
+ {
+ nIndexStart = rCID.find( '=', nIndexStart );
+ if( nIndexStart != std::u16string_view::npos )
+ {
+ nIndexStart++;
+ size_t nNextSlash = rCID.find( '/', nIndexStart );
+ if( nNextSlash != std::u16string_view::npos )
+ {
+ sal_Int32 nIndexEnd = nNextSlash;
+ size_t nNextColon = rCID.find( ':', nIndexStart );
+ if( nNextColon == std::u16string_view::npos || nNextColon < nNextSlash )
+ nIndexEnd = nNextColon;
+ aRet = rCID.substr(nIndexStart,nIndexEnd-nIndexStart);
+ }
+ }
+ }
+ return aRet;
+}
+
+std::u16string_view lcl_getDragParameterString( std::u16string_view rCID )
+{
+ std::u16string_view aRet;
+
+ size_t nIndexStart = rCID.find( u"DragParameter=" );
+ if( nIndexStart != std::u16string_view::npos )
+ {
+ nIndexStart = rCID.find( '=', nIndexStart );
+ if( nIndexStart != std::u16string_view::npos )
+ {
+ nIndexStart++;
+ size_t nNextSlash = rCID.find( '/', nIndexStart );
+ if( nNextSlash != std::u16string_view::npos )
+ {
+ sal_Int32 nIndexEnd = nNextSlash;
+ size_t nNextColon = rCID.find( ':', nIndexStart );
+ if( nNextColon == std::u16string_view::npos || nNextColon < nNextSlash )
+ nIndexEnd = nNextColon;
+ aRet = rCID.substr(nIndexStart,nIndexEnd-nIndexStart);
+ }
+ }
+ }
+ return aRet;
+}
+} // anonymous namespace
+
+bool SdrMarkView::dumpGluePointsToJSON(boost::property_tree::ptree& rTree)
+{
+ bool result = false;
+ tools::Long nSignX = mbNegativeX ? -1 : 1;
+ if (OutputDevice* pOutDev = mpMarkedPV ? mpMarkedPV->GetView().GetFirstOutputDevice() : nullptr)
+ {
+ bool bConvertUnit = false;
+ if (pOutDev->GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
+ bConvertUnit = true;
+ const SdrObjList* pOL = mpMarkedPV->GetObjList();
+ if (!pOL)
+ return false;
+ const size_t nObjCount = pOL->GetObjCount();
+ boost::property_tree::ptree elements;
+ for (size_t nObjNum = 0; nObjNum < nObjCount; ++nObjNum)
+ {
+ SdrObject* pObj = pOL->GetObj(nObjNum);
+ if (!pObj)
+ continue;
+ if (pObj == GetMarkedObjectByIndex(0))
+ continue;
+ const SdrGluePointList* pGPL = pObj->GetGluePointList();
+ bool VertexObject = !(pGPL && pGPL->GetCount());
+ const size_t count = !VertexObject ? pGPL->GetCount() : 4;
+ boost::property_tree::ptree object;
+ boost::property_tree::ptree points;
+ for (size_t i = 0; i < count; ++i)
+ {
+ boost::property_tree::ptree node;
+ boost::property_tree::ptree point;
+ const SdrGluePoint& rGP = !VertexObject ? (*pGPL)[i] : pObj->GetVertexGluePoint(i);
+ Point rPoint = rGP.GetAbsolutePos(*pObj);
+ if (bConvertUnit)
+ {
+ rPoint = o3tl::convert(rPoint, o3tl::Length::mm100, o3tl::Length::twip);
+ }
+ point.put("x", nSignX * rPoint.getX());
+ point.put("y", rPoint.getY());
+ node.add_child("point", point);
+ points.push_back(std::make_pair("", node));
+ }
+ basegfx::B2DVector aGridOffset(0.0, 0.0);
+ Point objLogicRectTopLeft = pObj->GetLogicRect().TopLeft();
+ if(getPossibleGridOffsetForPosition(aGridOffset, basegfx::B2DPoint(objLogicRectTopLeft.X(), objLogicRectTopLeft.Y()), GetSdrPageView()))
+ {
+ Point p(aGridOffset.getX(), aGridOffset.getY());
+ if (bConvertUnit)
+ {
+ p = o3tl::convert(p, o3tl::Length::mm100, o3tl::Length::twip);
+ }
+ boost::property_tree::ptree gridOffset;
+ gridOffset.put("x", nSignX * p.getX());
+ gridOffset.put("y", p.getY());
+ object.add_child("gridoffset", gridOffset);
+ }
+ object.put("ordnum", pObj->GetOrdNum());
+ object.add_child("gluepoints", points);
+ elements.push_back(std::make_pair("", object));
+ result = true;
+ }
+ rTree.add_child("shapes", elements);
+ }
+ return result;
+}
+
+void SdrMarkView::SetMarkHandlesForLOKit(tools::Rectangle const & rRect, const SfxViewShell* pOtherShell)
+{
+ SfxViewShell* pViewShell = GetSfxViewShell();
+
+ tools::Rectangle aSelection(rRect);
+ tools::Long nSignX = mbNegativeX ? -1 : 1;
+ bool bIsChart = false;
+ Point addLogicOffset(0, 0);
+ bool convertMapMode = false;
+ if (!rRect.IsEmpty())
+ {
+ sal_uInt32 nTotalPaintWindows = this->PaintWindowCount();
+ if (nTotalPaintWindows == 1)
+ {
+ const OutputDevice* pOut = this->GetFirstOutputDevice();
+ const vcl::Window* pWin = pOut ? pOut->GetOwnerWindow() : nullptr;
+ if (pWin && pWin->IsChart())
+ {
+ bIsChart = true;
+ const vcl::Window* pViewShellWindow = GetSfxViewShell()->GetEditWindowForActiveOLEObj();
+ if (pViewShellWindow && pViewShellWindow->IsAncestorOf(*pWin))
+ {
+ Point aOffsetPx = pWin->GetOffsetPixelFrom(*pViewShellWindow);
+ if (mbNegativeX && AllSettings::GetLayoutRTL())
+ {
+ // mbNegativeX is set only for Calc in RTL mode.
+ // If global RTL flag is set, vcl-window X offset of chart window is
+ // mirrored w.r.t parent window rectangle. This needs to be reverted.
+ aOffsetPx.setX(pViewShellWindow->GetOutOffXPixel() + pViewShellWindow->GetSizePixel().Width()
+ - pWin->GetOutOffXPixel() - pWin->GetSizePixel().Width());
+ }
+ Point aLogicOffset = pWin->PixelToLogic(aOffsetPx);
+ addLogicOffset = aLogicOffset;
+ aSelection.Move(aLogicOffset.getX(), aLogicOffset.getY());
+ }
+ }
+ }
+ }
+
+ if (!aSelection.IsEmpty())
+ {
+ // In case the map mode is in 100th MM, then need to convert the coordinates over to twips for LOK.
+ if (mpMarkedPV)
+ {
+ if (OutputDevice* pOutputDevice = mpMarkedPV->GetView().GetFirstOutputDevice())
+ {
+ if (pOutputDevice->GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
+ {
+ aSelection = o3tl::convert(aSelection, o3tl::Length::mm100, o3tl::Length::twip);
+ convertMapMode = true;
+ }
+ }
+ }
+
+ // hide the text selection too
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, "");
+ }
+
+ {
+ OString sSelectionText;
+ OString sSelectionTextView;
+ boost::property_tree::ptree aTableJsonTree;
+ boost::property_tree::ptree aGluePointsTree;
+ bool bTableSelection = false;
+ bool bConnectorSelection = false;
+
+ if (mpMarkedObj && mpMarkedObj->GetObjIdentifier() == SdrObjKind::Table)
+ {
+ auto& rTableObject = dynamic_cast<sdr::table::SdrTableObj&>(*mpMarkedObj);
+ bTableSelection = rTableObject.createTableEdgesJson(aTableJsonTree);
+ }
+ if (mpMarkedObj && mpMarkedObj->GetObjIdentifier() == SdrObjKind::Edge)
+ {
+ bConnectorSelection = dumpGluePointsToJSON(aGluePointsTree);
+ }
+ if (GetMarkedObjectCount())
+ {
+ SdrMark* pM = GetSdrMarkByIndex(0);
+ SdrObject* pO = pM->GetMarkedSdrObj();
+ Degree100 nRotAngle = pO->GetRotateAngle();
+ // true if we are dealing with a RotGrfFlyFrame
+ // (SwVirtFlyDrawObj with a SwGrfNode)
+ bool bWriterGraphic = pO->HasLimitedRotation();
+
+ OStringBuffer aExtraInfo;
+ OString handleArrayStr;
+
+ aExtraInfo.append("{\"id\":\"");
+ aExtraInfo.append(reinterpret_cast<sal_IntPtr>(pO));
+ aExtraInfo.append("\",\"type\":");
+ aExtraInfo.append(static_cast<sal_Int32>(pO->GetObjIdentifier()));
+
+ // In core, the gridOffset is calculated based on the LogicRect's TopLeft coordinate
+ // In online, we have the SnapRect and we calculate it based on its TopLeft coordinate
+ // SnapRect's TopLeft and LogicRect's TopLeft match unless there is rotation
+ // but the rotation is not applied to the LogicRect. Therefore,
+ // what we calculate in online does not match with the core in case of the rotation.
+ // Here we can send the correct gridOffset in the selection callback, this way
+ // whether the shape is rotated or not, we will always have the correct gridOffset
+ // Note that the gridOffset is calculated from the first selected obj
+ basegfx::B2DVector aGridOffset(0.0, 0.0);
+ if(getPossibleGridOffsetForSdrObject(aGridOffset, GetMarkedObjectByIndex(0), GetSdrPageView()))
+ {
+ Point p(aGridOffset.getX(), aGridOffset.getY());
+ if (convertMapMode)
+ p = o3tl::convert(p, o3tl::Length::mm100, o3tl::Length::twip);
+ aExtraInfo.append(",\"gridOffsetX\":");
+ aExtraInfo.append(nSignX * p.getX());
+ aExtraInfo.append(",\"gridOffsetY\":");
+ aExtraInfo.append(p.getY());
+ }
+
+ if (bWriterGraphic)
+ {
+ aExtraInfo.append(", \"isWriterGraphic\": true");
+ }
+ else if (bIsChart)
+ {
+ LokChartHelper aChartHelper(pViewShell);
+ css::uno::Reference<css::frame::XController>& xChartController = aChartHelper.GetXController();
+ css::uno::Reference<css::view::XSelectionSupplier> xSelectionSupplier( xChartController, uno::UNO_QUERY);
+ if (xSelectionSupplier.is())
+ {
+ uno::Any aSel = xSelectionSupplier->getSelection();
+ OUString aValue;
+ if (aSel >>= aValue)
+ {
+ OString aObjectCID(aValue.getStr(), aValue.getLength(), osl_getThreadTextEncoding());
+ const std::vector<OString> aProps{"Draggable", "Resizable", "Rotatable"};
+ for (const auto& rProp: aProps)
+ {
+ sal_Int32 nPos = aObjectCID.indexOf(rProp);
+ if (nPos == -1) continue;
+ nPos += rProp.getLength() + 1; // '='
+ if (aExtraInfo.getLength() > 2) // != "{ "
+ aExtraInfo.append(", ");
+ aExtraInfo.append("\"is");
+ aExtraInfo.append(rProp);
+ aExtraInfo.append("\": ");
+ aExtraInfo.append(OString::boolean(aObjectCID[nPos] == '1'));
+ }
+
+ std::u16string_view sDragMethod = lcl_getDragMethodServiceName(aValue);
+ if (sDragMethod == u"PieSegmentDragging")
+ {
+ // old initial offset inside the CID returned by xSelectionSupplier->getSelection()
+ // after a pie segment dragging; using SdrObject::GetName for getting a CID with the updated offset
+ aValue = pO->GetName();
+ std::u16string_view sDragParameters = lcl_getDragParameterString(aValue);
+ if (!sDragParameters.empty())
+ {
+ aExtraInfo.append(", \"dragInfo\": { ");
+ aExtraInfo.append("\"dragMethod\": \"");
+ aExtraInfo.append(OUString(sDragMethod).toUtf8());
+ aExtraInfo.append("\"");
+
+ sal_Int32 nStartIndex = 0;
+ std::array<int, 5> aDragParameters;
+ for (auto& rParam : aDragParameters)
+ {
+ std::u16string_view sParam = o3tl::getToken(sDragParameters, 0, ',', nStartIndex);
+ if (sParam.empty())
+ break;
+ rParam = o3tl::toInt32(sParam);
+ }
+
+ // initial offset in %
+ if (aDragParameters[0] < 0)
+ aDragParameters[0] = 0;
+ else if (aDragParameters[0] > 100)
+ aDragParameters[0] = 100;
+
+ aExtraInfo.append(", \"initialOffset\": ");
+ aExtraInfo.append(static_cast<sal_Int32>(aDragParameters[0]));
+
+ // drag direction constraint
+ Point aMinPos(aDragParameters[1], aDragParameters[2]);
+ Point aMaxPos(aDragParameters[3], aDragParameters[4]);
+ Point aDragDirection = aMaxPos - aMinPos;
+ aDragDirection = o3tl::convert(aDragDirection, o3tl::Length::mm100, o3tl::Length::twip);
+
+ aExtraInfo.append(", \"dragDirection\": [");
+ aExtraInfo.append(aDragDirection.toString());
+ aExtraInfo.append("]");
+
+ // polygon approximating the pie segment or donut segment
+ if (pO->GetObjIdentifier() == SdrObjKind::PathFill)
+ {
+ const basegfx::B2DPolyPolygon aPolyPolygon(pO->TakeXorPoly());
+ if (aPolyPolygon.count() == 1)
+ {
+ const basegfx::B2DPolygon aPolygon = aPolyPolygon.getB2DPolygon(0);
+ if (sal_uInt32 nPolySize = aPolygon.count())
+ {
+ const OutputDevice* pOut = this->GetFirstOutputDevice();
+ const vcl::Window* pWin = pOut ? pOut->GetOwnerWindow() : nullptr;
+ const vcl::Window* pViewShellWindow = pViewShell->GetEditWindowForActiveOLEObj();
+ if (pWin && pViewShellWindow && pViewShellWindow->IsAncestorOf(*pWin))
+ {
+ // in the following code escaping sequences used inside raw literal strings
+ // are for making them understandable by the JSON parser
+
+ Point aOffsetPx = pWin->GetOffsetPixelFrom(*pViewShellWindow);
+ Point aLogicOffset = pWin->PixelToLogic(aOffsetPx);
+ OString sPolygonElem("<polygon points=\\\"");
+ for (sal_uInt32 nIndex = 0; nIndex < nPolySize; ++nIndex)
+ {
+ const basegfx::B2DPoint aB2Point = aPolygon.getB2DPoint(nIndex);
+ Point aPoint(aB2Point.getX(), aB2Point.getY());
+ aPoint.Move(aLogicOffset.getX(), aLogicOffset.getY());
+ if (mbNegativeX)
+ aPoint.setX(-aPoint.X());
+ if (nIndex > 0)
+ sPolygonElem += " ";
+ sPolygonElem += aPoint.toString();
+ }
+ sPolygonElem += R"elem(\" style=\"stroke: none; fill: rgb(114,159,207); fill-opacity: 0.8\"/>)elem";
+
+ OString sSVGElem = R"elem(<svg version=\"1.2\" width=\")elem" +
+ OString::number(aSelection.GetWidth() / 100.0) +
+ R"elem(mm\" height=\")elem" +
+ OString::number(aSelection.GetHeight() / 100.0) +
+ R"elem(mm\" viewBox=\")elem" +
+ aSelection.toString() +
+ R"elem(\" preserveAspectRatio=\"xMidYMid\" xmlns=\"http://www.w3.org/2000/svg\">)elem";
+
+ aExtraInfo.append(", \"svg\": \"");
+ aExtraInfo.append(sSVGElem);
+ aExtraInfo.append("\\n ");
+ aExtraInfo.append(sPolygonElem);
+ aExtraInfo.append("\\n</svg>");
+ aExtraInfo.append("\""); // svg
+ }
+ }
+ }
+ }
+ aExtraInfo.append("}"); // dragInfo
+ }
+ }
+ }
+ }
+ }
+ if (!bTableSelection && !pOtherShell && maHdlList.GetHdlCount())
+ {
+ boost::property_tree::ptree responseJSON;
+ boost::property_tree::ptree others;
+ boost::property_tree::ptree anchor;
+ boost::property_tree::ptree rectangle;
+ boost::property_tree::ptree poly;
+ boost::property_tree::ptree custom;
+ boost::property_tree::ptree nodes;
+ for (size_t i = 0; i < maHdlList.GetHdlCount(); i++)
+ {
+ SdrHdl *pHdl = maHdlList.GetHdl(i);
+ boost::property_tree::ptree child;
+ boost::property_tree::ptree point;
+ sal_Int32 kind = static_cast<sal_Int32>(pHdl->GetKind());
+ child.put("id", pHdl->GetObjHdlNum());
+ child.put("kind", kind);
+ child.put("pointer", static_cast<sal_Int32>(pHdl->GetPointer()));
+ Point pHdlPos = pHdl->GetPos();
+ pHdlPos.Move(addLogicOffset.getX(), addLogicOffset.getY());
+ if (convertMapMode)
+ {
+ pHdlPos = o3tl::convert(pHdlPos, o3tl::Length::mm100, o3tl::Length::twip);
+ }
+ point.put("x", pHdlPos.getX());
+ point.put("y", pHdlPos.getY());
+ child.add_child("point", point);
+ const auto node = std::make_pair("", child);
+ boost::property_tree::ptree* selectedNode = nullptr;
+ if (kind >= static_cast<sal_Int32>(SdrHdlKind::UpperLeft) && kind <= static_cast<sal_Int32>(SdrHdlKind::LowerRight))
+ {
+ selectedNode = &rectangle;
+ }
+ else if (kind == static_cast<sal_Int32>(SdrHdlKind::Poly))
+ {
+ selectedNode = &poly;
+ }
+ else if (kind == static_cast<sal_Int32>(SdrHdlKind::CustomShape1))
+ {
+ selectedNode = &custom;
+ }
+ else if (kind == static_cast<sal_Int32>(SdrHdlKind::Anchor) || kind == static_cast<sal_Int32>(SdrHdlKind::Anchor_TR))
+ {
+ if (getSdrModelFromSdrView().IsWriter())
+ selectedNode = &anchor;
+ else
+ // put it to others as we don't render them except in writer
+ selectedNode = &others;
+ }
+ else
+ {
+ selectedNode = &others;
+ }
+ std::string sKind = std::to_string(kind);
+ boost::optional< boost::property_tree::ptree& > kindNode = selectedNode->get_child_optional(sKind.c_str());
+ if (!kindNode)
+ {
+ boost::property_tree::ptree newChild;
+ newChild.push_back(node);
+ selectedNode->add_child(sKind.c_str(), newChild);
+ }
+ else
+ kindNode.get().push_back(node);
+ }
+ nodes.add_child("rectangle", rectangle);
+ nodes.add_child("poly", poly);
+ nodes.add_child("custom", custom);
+ nodes.add_child("anchor", anchor);
+ nodes.add_child("others", others);
+ responseJSON.add_child("kinds", nodes);
+ std::stringstream aStream;
+ boost::property_tree::write_json(aStream, responseJSON, /*pretty=*/ false);
+ handleArrayStr = ", \"handles\":";
+ handleArrayStr = handleArrayStr + aStream.str().c_str();
+ if (bConnectorSelection)
+ {
+ aStream.str("");
+ boost::property_tree::write_json(aStream, aGluePointsTree, /*pretty=*/ false);
+ handleArrayStr = handleArrayStr + ", \"GluePoints\":";
+ handleArrayStr = handleArrayStr + aStream.str().c_str();
+ }
+ }
+
+ if (mbNegativeX)
+ {
+ tools::Rectangle aNegatedRect(aSelection);
+ aNegatedRect.SetLeft(-aNegatedRect.Left());
+ aNegatedRect.SetRight(-aNegatedRect.Right());
+ aNegatedRect.Justify();
+ sSelectionText = aNegatedRect.toString() +
+ ", " + OString::number(nRotAngle.get());
+ }
+ else
+ {
+ sSelectionText = aSelection.toString() +
+ ", " + OString::number(nRotAngle.get());
+ }
+ if (!aExtraInfo.isEmpty())
+ {
+ sSelectionTextView = sSelectionText + ", " + aExtraInfo + "}";
+ aExtraInfo.append(handleArrayStr);
+ aExtraInfo.append("}");
+ sSelectionText += ", " + aExtraInfo;
+ aExtraInfo.setLength(0);
+ }
+ }
+
+ if (sSelectionText.isEmpty())
+ {
+ sSelectionText = "EMPTY";
+ sSelectionTextView = "EMPTY";
+ }
+
+ if (bTableSelection)
+ {
+ boost::property_tree::ptree aTableRectangle;
+ aTableRectangle.put("x", aSelection.Left());
+ aTableRectangle.put("y", aSelection.Top());
+ aTableRectangle.put("width", aSelection.GetWidth());
+ aTableRectangle.put("height", aSelection.GetHeight());
+ aTableJsonTree.push_back(std::make_pair("rectangle", aTableRectangle));
+
+ std::stringstream aStream;
+ boost::property_tree::write_json(aStream, aTableJsonTree);
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TABLE_SELECTED, aStream.str().c_str());
+ }
+ else if (!getSdrModelFromSdrView().IsWriter())
+ {
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TABLE_SELECTED, "{}");
+ }
+
+ if (pOtherShell)
+ {
+ // Another shell wants to know about our existing
+ // selection.
+ if (pViewShell != pOtherShell)
+ SfxLokHelper::notifyOtherView(pViewShell, pOtherShell, LOK_CALLBACK_GRAPHIC_VIEW_SELECTION, "selection", sSelectionTextView);
+ }
+ else
+ {
+ // We have a new selection, so both pViewShell and the
+ // other views want to know about it.
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_GRAPHIC_SELECTION, sSelectionText.getStr());
+ SfxLokHelper::notifyOtherViews(pViewShell, LOK_CALLBACK_GRAPHIC_VIEW_SELECTION, "selection", sSelectionTextView);
+ }
+ }
+}
+
+void SdrMarkView::SetMarkHandles(SfxViewShell* pOtherShell)
+{
+ // remember old focus handle values to search for it again
+ const SdrHdl* pSaveOldFocusHdl = maHdlList.GetFocusHdl();
+ bool bSaveOldFocus(false);
+ sal_uInt32 nSavePolyNum(0), nSavePointNum(0);
+ SdrHdlKind eSaveKind(SdrHdlKind::Move);
+ SdrObject* pSaveObj = nullptr;
+
+ mpMarkingSubSelectionOverlay.reset();
+
+ if(pSaveOldFocusHdl
+ && pSaveOldFocusHdl->GetObj()
+ && dynamic_cast<const SdrPathObj*>(pSaveOldFocusHdl->GetObj()) != nullptr
+ && (pSaveOldFocusHdl->GetKind() == SdrHdlKind::Poly || pSaveOldFocusHdl->GetKind() == SdrHdlKind::BezierWeight))
+ {
+ bSaveOldFocus = true;
+ nSavePolyNum = pSaveOldFocusHdl->GetPolyNum();
+ nSavePointNum = pSaveOldFocusHdl->GetPointNum();
+ pSaveObj = pSaveOldFocusHdl->GetObj();
+ eSaveKind = pSaveOldFocusHdl->GetKind();
+ }
+
+ // delete/clear all handles. This will always be done, even with areMarkHandlesHidden()
+ maHdlList.Clear();
+ maHdlList.SetRotateShear(meDragMode==SdrDragMode::Rotate);
+ maHdlList.SetDistortShear(meDragMode==SdrDragMode::Shear);
+ mpMarkedObj=nullptr;
+ mpMarkedPV=nullptr;
+
+ // are handles enabled at all? Create only then
+ if(areMarkHandlesHidden())
+ return;
+
+ // There can be multiple mark views, but we're only interested in the one that has a window associated.
+ const bool bTiledRendering = comphelper::LibreOfficeKit::isActive() && GetFirstOutputDevice() && GetFirstOutputDevice()->GetOutDevType() == OUTDEV_WINDOW;
+
+ const size_t nMarkCount=GetMarkedObjectCount();
+ bool bStdDrag=meDragMode==SdrDragMode::Move;
+ bool bSingleTextObjMark=false;
+ bool bLimitedRotation(false);
+
+ if (nMarkCount==1)
+ {
+ mpMarkedObj=GetMarkedObjectByIndex(0);
+
+ if(nullptr != mpMarkedObj)
+ {
+ bSingleTextObjMark =
+ dynamic_cast<const SdrTextObj*>( mpMarkedObj) != nullptr &&
+ static_cast<SdrTextObj*>(mpMarkedObj)->IsTextFrame();
+
+ // RotGrfFlyFrame: we may have limited rotation
+ bLimitedRotation = SdrDragMode::Rotate == meDragMode && mpMarkedObj->HasLimitedRotation();
+ }
+ }
+
+ bool bFrmHdl=ImpIsFrameHandles();
+
+ if (nMarkCount>0)
+ {
+ mpMarkedPV=GetSdrPageViewOfMarkedByIndex(0);
+
+ for (size_t nMarkNum=0; nMarkNum<nMarkCount && (mpMarkedPV!=nullptr || !bFrmHdl); ++nMarkNum)
+ {
+ const SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
+
+ if (mpMarkedPV!=pM->GetPageView())
+ {
+ mpMarkedPV=nullptr;
+ }
+ }
+ }
+
+ SfxViewShell* pViewShell = GetSfxViewShell();
+
+ // check if text edit or ole is active and handles need to be suppressed. This may be the case
+ // when a single object is selected
+ // Using a strict return statement is okay here; no handles means *no* handles.
+ if(mpMarkedObj)
+ {
+ // formerly #i33755#: If TextEdit is active the EditEngine will directly paint
+ // to the window, so suppress Overlay and handles completely; a text frame for
+ // the active text edit will be painted by the repaint mechanism in
+ // SdrObjEditView::ImpPaintOutlinerView in this case. This needs to be reworked
+ // in the future
+ // Also formerly #122142#: Pretty much the same for SdrCaptionObj's in calc.
+ if(static_cast<SdrView*>(this)->IsTextEdit())
+ {
+ const SdrTextObj* pSdrTextObj = dynamic_cast< const SdrTextObj* >(mpMarkedObj);
+
+ if (pSdrTextObj && pSdrTextObj->IsInEditMode())
+ {
+ if (!bTiledRendering)
+ return;
+ }
+ }
+
+ // formerly #i118524#: if inplace activated OLE is selected, suppress handles
+ const SdrOle2Obj* pSdrOle2Obj = dynamic_cast< const SdrOle2Obj* >(mpMarkedObj);
+
+ if(pSdrOle2Obj && (pSdrOle2Obj->isInplaceActive() || pSdrOle2Obj->isUiActive()))
+ {
+ return;
+ }
+
+ if (!maSubSelectionList.empty())
+ {
+ mpMarkingSubSelectionOverlay = std::make_unique<MarkingSubSelectionOverlay>(*this, maSubSelectionList);
+ }
+ }
+
+ tools::Rectangle aRect(GetMarkedObjRect());
+
+ if (bFrmHdl)
+ {
+ if(!aRect.IsEmpty())
+ {
+ // otherwise nothing is found
+ const size_t nSiz0(maHdlList.GetHdlCount());
+
+ if( bSingleTextObjMark )
+ {
+ mpMarkedObj->AddToHdlList(maHdlList);
+ }
+ else
+ {
+ const bool bWdt0(aRect.Left() == aRect.Right());
+ const bool bHgt0(aRect.Top() == aRect.Bottom());
+
+ if (bWdt0 && bHgt0)
+ {
+ maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.TopLeft(), SdrHdlKind::UpperLeft));
+ }
+ else if (!bStdDrag && (bWdt0 || bHgt0))
+ {
+ maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.TopLeft(), SdrHdlKind::UpperLeft));
+ maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.BottomRight(), SdrHdlKind::LowerRight));
+ }
+ else
+ {
+ if (!bWdt0 && !bHgt0)
+ {
+ maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.TopLeft(), SdrHdlKind::UpperLeft));
+ }
+
+ if (!bLimitedRotation && !bHgt0)
+ {
+ maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.TopCenter(), SdrHdlKind::Upper));
+ }
+
+ if (!bWdt0 && !bHgt0)
+ {
+ maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.TopRight(), SdrHdlKind::UpperRight));
+ }
+
+ if (!bLimitedRotation && !bWdt0)
+ {
+ maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.LeftCenter(), SdrHdlKind::Left ));
+ maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.RightCenter(), SdrHdlKind::Right));
+ }
+
+ if (!bWdt0 && !bHgt0)
+ {
+ maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.BottomLeft(), SdrHdlKind::LowerLeft));
+ }
+
+ if (!bLimitedRotation && !bHgt0)
+ {
+ maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.BottomCenter(), SdrHdlKind::Lower));
+ }
+
+ if (!bWdt0 && !bHgt0)
+ {
+ maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.BottomRight(), SdrHdlKind::LowerRight));
+ }
+ }
+ }
+
+ // Diagram selection visualization support
+ // Caution: CppunitTest_sd_tiledrendering shows that mpMarkedObj *can* actually be nullptr (!)
+ if(nullptr != mpMarkedObj && mpMarkedObj->isDiagram())
+ {
+ mpMarkedObj->AddToHdlList(maHdlList);
+ }
+
+ const size_t nSiz1(maHdlList.GetHdlCount());
+
+ // moved setting the missing parameters at SdrHdl here from the
+ // single loop above (bSingleTextObjMark), this was missing all
+ // the time. Setting SdrObject is now required to correctly get
+ // the View-Dependent evtl. GridOffset adapted
+ for (size_t i=nSiz0; i<nSiz1; ++i)
+ {
+ SdrHdl* pHdl=maHdlList.GetHdl(i);
+ pHdl->SetObj(mpMarkedObj);
+ pHdl->SetPageView(mpMarkedPV);
+ pHdl->SetObjHdlNum(sal_uInt16(i-nSiz0));
+ }
+ }
+ }
+ else
+ {
+ bool bDone(false);
+
+ // moved crop handling to non-frame part and the handle creation to SdrGrafObj
+ if(1 == nMarkCount && mpMarkedObj && SdrDragMode::Crop == meDragMode)
+ {
+ // Default addCropHandles from SdrObject does nothing. When pMarkedObj is SdrGrafObj, previous
+ // behaviour occurs (code in svx/source/svdraw/svdograf.cxx). When pMarkedObj is SwVirtFlyDrawObj
+ // writer takes the responsibility of adding handles (code in sw/source/core/draw/dflyobj.cxx)
+ const size_t nSiz0(maHdlList.GetHdlCount());
+ mpMarkedObj->addCropHandles(maHdlList);
+ const size_t nSiz1(maHdlList.GetHdlCount());
+
+ // Was missing: Set infos at SdrCropHdl
+ for (size_t i=nSiz0; i<nSiz1; ++i)
+ {
+ SdrHdl* pHdl=maHdlList.GetHdl(i);
+ pHdl->SetObj(mpMarkedObj);
+ pHdl->SetPageView(mpMarkedPV);
+ pHdl->SetObjHdlNum(sal_uInt16(i-nSiz0));
+ }
+
+ bDone = true;
+ }
+
+ if(!bDone)
+ {
+ for (size_t nMarkNum=0; nMarkNum<nMarkCount; ++nMarkNum)
+ {
+ const SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ SdrPageView* pPV=pM->GetPageView();
+ const size_t nSiz0=maHdlList.GetHdlCount();
+ pObj->AddToHdlList(maHdlList);
+ const size_t nSiz1=maHdlList.GetHdlCount();
+ bool bPoly=pObj->IsPolyObj();
+ const SdrUShortCont& rMrkPnts = pM->GetMarkedPoints();
+ for (size_t i=nSiz0; i<nSiz1; ++i)
+ {
+ SdrHdl* pHdl=maHdlList.GetHdl(i);
+ pHdl->SetObj(pObj);
+ pHdl->SetPageView(pPV);
+ pHdl->SetObjHdlNum(sal_uInt16(i-nSiz0));
+
+ if (bPoly)
+ {
+ bool bSelected= rMrkPnts.find( sal_uInt16(i-nSiz0) ) != rMrkPnts.end();
+ pHdl->SetSelected(bSelected);
+ if (mbPlusHdlAlways || bSelected)
+ {
+ SdrHdlList plusList(nullptr);
+ pObj->AddToPlusHdlList(plusList, *pHdl);
+ sal_uInt32 nPlusHdlCnt=plusList.GetHdlCount();
+ for (sal_uInt32 nPlusNum=0; nPlusNum<nPlusHdlCnt; nPlusNum++)
+ {
+ SdrHdl* pPlusHdl=plusList.GetHdl(nPlusNum);
+ pPlusHdl->SetObj(pObj);
+ pPlusHdl->SetPageView(pPV);
+ pPlusHdl->SetPlusHdl(true);
+ }
+ plusList.MoveTo(maHdlList);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // GluePoint handles
+ for (size_t nMarkNum=0; nMarkNum<nMarkCount; ++nMarkNum)
+ {
+ const SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ const SdrGluePointList* pGPL=pObj->GetGluePointList();
+ if (!pGPL)
+ continue;
+
+ SdrPageView* pPV=pM->GetPageView();
+ const SdrUShortCont& rMrkGlue=pM->GetMarkedGluePoints();
+ for (sal_uInt16 nId : rMrkGlue)
+ {
+ //nNum changed to nNumGP because already used in for loop
+ sal_uInt16 nNumGP=pGPL->FindGluePoint(nId);
+ if (nNumGP!=SDRGLUEPOINT_NOTFOUND)
+ {
+ const SdrGluePoint& rGP=(*pGPL)[nNumGP];
+ Point aPos(rGP.GetAbsolutePos(*pObj));
+ std::unique_ptr<SdrHdl> pGlueHdl(new SdrHdl(aPos,SdrHdlKind::Glue));
+ pGlueHdl->SetObj(pObj);
+ pGlueHdl->SetPageView(pPV);
+ pGlueHdl->SetObjHdlNum(nId);
+ maHdlList.AddHdl(std::move(pGlueHdl));
+ }
+ }
+ }
+
+ // rotation point/axis of reflection
+ if(!bLimitedRotation)
+ {
+ AddDragModeHdl(meDragMode);
+ }
+
+ // sort handles
+ maHdlList.Sort();
+
+ // add custom handles (used by other apps, e.g. AnchorPos)
+ AddCustomHdl();
+
+ // moved it here to access all the handles for callback.
+ if (bTiledRendering && pViewShell)
+ {
+ SetMarkHandlesForLOKit(aRect, pOtherShell);
+ }
+
+ // try to restore focus handle index from remembered values
+ if(!bSaveOldFocus)
+ return;
+
+ for(size_t a = 0; a < maHdlList.GetHdlCount(); ++a)
+ {
+ SdrHdl* pCandidate = maHdlList.GetHdl(a);
+
+ if(pCandidate->GetObj()
+ && pCandidate->GetObj() == pSaveObj
+ && pCandidate->GetKind() == eSaveKind
+ && pCandidate->GetPolyNum() == nSavePolyNum
+ && pCandidate->GetPointNum() == nSavePointNum)
+ {
+ maHdlList.SetFocusHdl(pCandidate);
+ break;
+ }
+ }
+}
+
+void SdrMarkView::AddCustomHdl()
+{
+ // add custom handles (used by other apps, e.g. AnchorPos)
+}
+
+void SdrMarkView::SetDragMode(SdrDragMode eMode)
+{
+ SdrDragMode eMode0=meDragMode;
+ meDragMode=eMode;
+ if (meDragMode==SdrDragMode::Resize) meDragMode=SdrDragMode::Move;
+ if (meDragMode!=eMode0) {
+ ForceRefToMarked();
+ SetMarkHandles(nullptr);
+ {
+ if (AreObjectsMarked()) MarkListHasChanged();
+ }
+ }
+}
+
+void SdrMarkView::AddDragModeHdl(SdrDragMode eMode)
+{
+ switch(eMode)
+ {
+ case SdrDragMode::Rotate:
+ {
+ // add rotation center
+ maHdlList.AddHdl(std::make_unique<SdrHdl>(maRef1, SdrHdlKind::Ref1));
+ break;
+ }
+ case SdrDragMode::Mirror:
+ {
+ // add axis of reflection
+ std::unique_ptr<SdrHdl> pHdl3(new SdrHdl(maRef2, SdrHdlKind::Ref2));
+ std::unique_ptr<SdrHdl> pHdl2(new SdrHdl(maRef1, SdrHdlKind::Ref1));
+ std::unique_ptr<SdrHdl> pHdl1(new SdrHdlLine(*pHdl2, *pHdl3, SdrHdlKind::MirrorAxis));
+
+ pHdl1->SetObjHdlNum(1); // for sorting
+ pHdl2->SetObjHdlNum(2); // for sorting
+ pHdl3->SetObjHdlNum(3); // for sorting
+
+ maHdlList.AddHdl(std::move(pHdl1)); // line comes first, so it is the last in HitTest
+ maHdlList.AddHdl(std::move(pHdl2));
+ maHdlList.AddHdl(std::move(pHdl3));
+
+ break;
+ }
+ case SdrDragMode::Transparence:
+ {
+ // add interactive transparency handle
+ const size_t nMarkCount = GetMarkedObjectCount();
+ if(nMarkCount == 1)
+ {
+ SdrObject* pObj = GetMarkedObjectByIndex(0);
+ SdrModel* pModel = GetModel();
+ const SfxItemSet& rSet = pObj->GetMergedItemSet();
+
+ if(SfxItemState::SET != rSet.GetItemState(XATTR_FILLFLOATTRANSPARENCE, false))
+ {
+ // add this item, it's not yet there
+ XFillFloatTransparenceItem aNewItem(rSet.Get(XATTR_FILLFLOATTRANSPARENCE));
+ XGradient aGrad = aNewItem.GetGradientValue();
+
+ aNewItem.SetEnabled(true);
+ aGrad.SetStartIntens(100);
+ aGrad.SetEndIntens(100);
+ aNewItem.SetGradientValue(aGrad);
+
+ // add undo to allow user to take back this step
+ if( pModel->IsUndoEnabled() )
+ {
+ pModel->BegUndo(SvxResId(SIP_XA_FILLTRANSPARENCE));
+ pModel->AddUndo(pModel->GetSdrUndoFactory().CreateUndoAttrObject(*pObj));
+ pModel->EndUndo();
+ }
+
+ SfxItemSet aNewSet(pModel->GetItemPool());
+ aNewSet.Put(aNewItem);
+ pObj->SetMergedItemSetAndBroadcast(aNewSet);
+ }
+
+ // set values and transform to vector set
+ GradTransVector aGradTransVector;
+ GradTransGradient aGradTransGradient;
+
+ aGradTransGradient.aGradient = rSet.Get(XATTR_FILLFLOATTRANSPARENCE).GetGradientValue();
+ GradTransformer::GradToVec(aGradTransGradient, aGradTransVector, pObj);
+
+ // build handles
+ const Point aTmpPos1(basegfx::fround(aGradTransVector.maPositionA.getX()), basegfx::fround(aGradTransVector.maPositionA.getY()));
+ const Point aTmpPos2(basegfx::fround(aGradTransVector.maPositionB.getX()), basegfx::fround(aGradTransVector.maPositionB.getY()));
+ std::unique_ptr<SdrHdlColor> pColHdl1(new SdrHdlColor(aTmpPos1, aGradTransVector.aCol1, SDR_HANDLE_COLOR_SIZE_NORMAL, true));
+ std::unique_ptr<SdrHdlColor> pColHdl2(new SdrHdlColor(aTmpPos2, aGradTransVector.aCol2, SDR_HANDLE_COLOR_SIZE_NORMAL, true));
+ std::unique_ptr<SdrHdlGradient> pGradHdl(new SdrHdlGradient(aTmpPos1, aTmpPos2, false));
+ DBG_ASSERT(pColHdl1 && pColHdl2 && pGradHdl, "Could not get all necessary handles!");
+
+ // link them
+ pGradHdl->SetColorHandles(pColHdl1.get(), pColHdl2.get());
+ pGradHdl->SetObj(pObj);
+ pColHdl1->SetColorChangeHdl(LINK(pGradHdl.get(), SdrHdlGradient, ColorChangeHdl));
+ pColHdl2->SetColorChangeHdl(LINK(pGradHdl.get(), SdrHdlGradient, ColorChangeHdl));
+
+ // insert them
+ maHdlList.AddHdl(std::move(pColHdl1));
+ maHdlList.AddHdl(std::move(pColHdl2));
+ maHdlList.AddHdl(std::move(pGradHdl));
+ }
+ break;
+ }
+ case SdrDragMode::Gradient:
+ {
+ // add interactive gradient handle
+ const size_t nMarkCount = GetMarkedObjectCount();
+ if(nMarkCount == 1)
+ {
+ SdrObject* pObj = GetMarkedObjectByIndex(0);
+ const SfxItemSet& rSet = pObj->GetMergedItemSet();
+ drawing::FillStyle eFillStyle = rSet.Get(XATTR_FILLSTYLE).GetValue();
+
+ if(eFillStyle == drawing::FillStyle_GRADIENT)
+ {
+ // set values and transform to vector set
+ GradTransVector aGradTransVector;
+ GradTransGradient aGradTransGradient;
+ Size aHdlSize(15, 15);
+
+ aGradTransGradient.aGradient = rSet.Get(XATTR_FILLGRADIENT).GetGradientValue();
+ GradTransformer::GradToVec(aGradTransGradient, aGradTransVector, pObj);
+
+ // build handles
+ const Point aTmpPos1(basegfx::fround(aGradTransVector.maPositionA.getX()), basegfx::fround(aGradTransVector.maPositionA.getY()));
+ const Point aTmpPos2(basegfx::fround(aGradTransVector.maPositionB.getX()), basegfx::fround(aGradTransVector.maPositionB.getY()));
+ std::unique_ptr<SdrHdlColor> pColHdl1(new SdrHdlColor(aTmpPos1, aGradTransVector.aCol1, aHdlSize, false));
+ std::unique_ptr<SdrHdlColor> pColHdl2(new SdrHdlColor(aTmpPos2, aGradTransVector.aCol2, aHdlSize, false));
+ std::unique_ptr<SdrHdlGradient> pGradHdl(new SdrHdlGradient(aTmpPos1, aTmpPos2, true));
+ DBG_ASSERT(pColHdl1 && pColHdl2 && pGradHdl, "Could not get all necessary handles!");
+
+ // link them
+ pGradHdl->SetColorHandles(pColHdl1.get(), pColHdl2.get());
+ pGradHdl->SetObj(pObj);
+ pColHdl1->SetColorChangeHdl(LINK(pGradHdl.get(), SdrHdlGradient, ColorChangeHdl));
+ pColHdl2->SetColorChangeHdl(LINK(pGradHdl.get(), SdrHdlGradient, ColorChangeHdl));
+
+ // insert them
+ maHdlList.AddHdl(std::move(pColHdl1));
+ maHdlList.AddHdl(std::move(pColHdl2));
+ maHdlList.AddHdl(std::move(pGradHdl));
+ }
+ }
+ break;
+ }
+ case SdrDragMode::Crop:
+ {
+ // TODO
+ break;
+ }
+ default: break;
+ }
+}
+
+/** handle mouse over effects for handles */
+bool SdrMarkView::MouseMove(const MouseEvent& rMEvt, OutputDevice* pWin)
+{
+ if(maHdlList.GetHdlCount())
+ {
+ SdrHdl* pMouseOverHdl = nullptr;
+ if( !rMEvt.IsLeaveWindow() && pWin )
+ {
+ Point aMDPos( pWin->PixelToLogic( rMEvt.GetPosPixel() ) );
+ pMouseOverHdl = PickHandle(aMDPos);
+ }
+
+ // notify last mouse over handle that he lost the mouse
+ const size_t nHdlCount = maHdlList.GetHdlCount();
+
+ for(size_t nHdl = 0; nHdl < nHdlCount; ++nHdl)
+ {
+ SdrHdl* pCurrentHdl = GetHdl(nHdl);
+ if( pCurrentHdl->mbMouseOver )
+ {
+ if( pCurrentHdl != pMouseOverHdl )
+ {
+ pCurrentHdl->mbMouseOver = false;
+ pCurrentHdl->onMouseLeave();
+ }
+ break;
+ }
+ }
+
+ // notify current mouse over handle
+ if( pMouseOverHdl )
+ {
+ pMouseOverHdl->mbMouseOver = true;
+ pMouseOverHdl->onMouseEnter(rMEvt);
+ }
+ }
+ return SdrSnapView::MouseMove(rMEvt, pWin);
+}
+
+bool SdrMarkView::RequestHelp(const HelpEvent& rHEvt)
+{
+ if (maHdlList.GetHdlCount())
+ {
+ const size_t nHdlCount = maHdlList.GetHdlCount();
+
+ for (size_t nHdl = 0; nHdl < nHdlCount; ++nHdl)
+ {
+ SdrHdl* pCurrentHdl = GetHdl(nHdl);
+ if (pCurrentHdl->mbMouseOver)
+ {
+ pCurrentHdl->onHelpRequest();
+ return true;
+ }
+ }
+ }
+ return SdrSnapView::RequestHelp(rHEvt);
+}
+
+void SdrMarkView::ForceRefToMarked()
+{
+ switch(meDragMode)
+ {
+ case SdrDragMode::Rotate:
+ {
+ tools::Rectangle aR(GetMarkedObjRect());
+ maRef1 = aR.Center();
+
+ break;
+ }
+
+ case SdrDragMode::Mirror:
+ {
+ // first calculate the length of the axis of reflection
+ tools::Long nOutMin=0;
+ tools::Long nOutMax=0;
+ tools::Long nMinLen=0;
+ tools::Long nObjDst=0;
+ tools::Long nOutHgt=0;
+ OutputDevice* pOut=GetFirstOutputDevice();
+ if (pOut!=nullptr) {
+ // minimum length: 50 pixels
+ nMinLen=pOut->PixelToLogic(Size(0,50)).Height();
+ // 20 pixels distance to the Obj for the reference point
+ nObjDst=pOut->PixelToLogic(Size(0,20)).Height();
+ // MinY/MaxY
+ // margin = minimum length = 10 pixels
+ tools::Long nDst=pOut->PixelToLogic(Size(0,10)).Height();
+ nOutMin=-pOut->GetMapMode().GetOrigin().Y();
+ nOutMax=pOut->GetOutputSize().Height()-1+nOutMin;
+ nOutMin+=nDst;
+ nOutMax-=nDst;
+ // absolute minimum length, however, is 10 pixels
+ if (nOutMax-nOutMin<nDst) {
+ nOutMin+=nOutMax+1;
+ nOutMin/=2;
+ nOutMin-=(nDst+1)/2;
+ nOutMax=nOutMin+nDst;
+ }
+ nOutHgt=nOutMax-nOutMin;
+ // otherwise minimum length = 1/4 OutHgt
+ tools::Long nTemp=nOutHgt/4;
+ if (nTemp>nMinLen) nMinLen=nTemp;
+ }
+
+ tools::Rectangle aR(GetMarkedObjBoundRect());
+ Point aCenter(aR.Center());
+ tools::Long nMarkHgt=aR.GetHeight()-1;
+ tools::Long nHgt=nMarkHgt+nObjDst*2; // 20 pixels overlapping above and below
+ if (nHgt<nMinLen) nHgt=nMinLen; // minimum length 50 pixels or 1/4 OutHgt, respectively
+
+ tools::Long nY1=aCenter.Y()-(nHgt+1)/2;
+ tools::Long nY2=nY1+nHgt;
+
+ if (pOut!=nullptr && nMinLen>nOutHgt) nMinLen=nOutHgt; // TODO: maybe shorten this a little
+
+ if (pOut!=nullptr) { // now move completely into the visible area
+ if (nY1<nOutMin) {
+ nY1=nOutMin;
+ if (nY2<nY1+nMinLen) nY2=nY1+nMinLen;
+ }
+ if (nY2>nOutMax) {
+ nY2=nOutMax;
+ if (nY1>nY2-nMinLen) nY1=nY2-nMinLen;
+ }
+ }
+
+ maRef1.setX(aCenter.X() );
+ maRef1.setY(nY1 );
+ maRef2.setX(aCenter.X() );
+ maRef2.setY(nY2 );
+
+ break;
+ }
+
+ case SdrDragMode::Transparence:
+ case SdrDragMode::Gradient:
+ case SdrDragMode::Crop:
+ {
+ tools::Rectangle aRect(GetMarkedObjBoundRect());
+ maRef1 = aRect.TopLeft();
+ maRef2 = aRect.BottomRight();
+ break;
+ }
+ default: break;
+ }
+}
+
+void SdrMarkView::SetRef1(const Point& rPt)
+{
+ if(meDragMode == SdrDragMode::Rotate || meDragMode == SdrDragMode::Mirror)
+ {
+ maRef1 = rPt;
+ SdrHdl* pH = maHdlList.GetHdl(SdrHdlKind::Ref1);
+ if(pH)
+ pH->SetPos(rPt);
+ }
+}
+
+void SdrMarkView::SetRef2(const Point& rPt)
+{
+ if(meDragMode == SdrDragMode::Mirror)
+ {
+ maRef2 = rPt;
+ SdrHdl* pH = maHdlList.GetHdl(SdrHdlKind::Ref2);
+ if(pH)
+ pH->SetPos(rPt);
+ }
+}
+
+SfxViewShell* SdrMarkView::GetSfxViewShell() const
+{
+ return SfxViewShell::Current();
+}
+
+void SdrMarkView::CheckMarked()
+{
+ for (size_t nm=GetMarkedObjectCount(); nm>0;) {
+ --nm;
+ SdrMark* pM = GetSdrMarkByIndex(nm);
+ SdrObject* pObj = pM->GetMarkedSdrObj();
+ SdrPageView* pPV = pM->GetPageView();
+ bool bRaus = !pObj || !pPV->IsObjMarkable(pObj);
+ if (bRaus)
+ {
+ GetMarkedObjectListWriteAccess().DeleteMark(nm);
+ }
+ else
+ {
+ if (!IsGluePointEditMode()) { // selected gluepoints only in GlueEditMode
+ SdrUShortCont& rPts = pM->GetMarkedGluePoints();
+ rPts.clear();
+ }
+ }
+ }
+
+ // at least reset the remembered BoundRect to prevent handle
+ // generation if bForceFrameHandles is TRUE.
+ mbMarkedObjRectDirty = true;
+}
+
+void SdrMarkView::SetMarkRects()
+{
+ SdrPageView* pPV = GetSdrPageView();
+
+ if(pPV)
+ {
+ pPV->SetHasMarkedObj(GetMarkedObjectList().TakeSnapRect(pPV, pPV->MarkSnap()));
+ GetMarkedObjectList().TakeBoundRect(pPV, pPV->MarkBound());
+ }
+}
+
+void SdrMarkView::SetFrameHandles(bool bOn)
+{
+ if (bOn!=mbForceFrameHandles) {
+ bool bOld=ImpIsFrameHandles();
+ mbForceFrameHandles=bOn;
+ bool bNew=ImpIsFrameHandles();
+ if (bNew!=bOld) {
+ AdjustMarkHdl();
+ MarkListHasChanged();
+ }
+ }
+}
+
+void SdrMarkView::SetEditMode(SdrViewEditMode eMode)
+{
+ if (eMode==meEditMode) return;
+
+ bool bGlue0=meEditMode==SdrViewEditMode::GluePointEdit;
+ bool bEdge0=static_cast<SdrCreateView*>(this)->IsEdgeTool();
+ meEditMode0=meEditMode;
+ meEditMode=eMode;
+ bool bGlue1=meEditMode==SdrViewEditMode::GluePointEdit;
+ bool bEdge1=static_cast<SdrCreateView*>(this)->IsEdgeTool();
+ // avoid flickering when switching between GlueEdit and EdgeTool
+ if (bGlue1 && !bGlue0) ImpSetGlueVisible2(bGlue1);
+ if (bEdge1!=bEdge0) ImpSetGlueVisible3(bEdge1);
+ if (!bGlue1 && bGlue0) ImpSetGlueVisible2(bGlue1);
+ if (bGlue0 && !bGlue1) UnmarkAllGluePoints();
+}
+
+
+bool SdrMarkView::IsObjMarkable(SdrObject const * pObj, SdrPageView const * pPV) const
+{
+ if (pObj)
+ {
+ if (pObj->IsMarkProtect() ||
+ (!mbDesignMode && pObj->IsUnoObj()))
+ {
+ // object not selectable or
+ // SdrUnoObj not in DesignMode
+ return false;
+ }
+ }
+ return pPV==nullptr || pPV->IsObjMarkable(pObj);
+}
+
+bool SdrMarkView::IsMarkedObjHit(const Point& rPnt, short nTol) const
+{
+ bool bRet=false;
+ nTol=ImpGetHitTolLogic(nTol,nullptr);
+ for (size_t nm=0; nm<GetMarkedObjectCount() && !bRet; ++nm) {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ bRet = nullptr != CheckSingleSdrObjectHit(rPnt,sal_uInt16(nTol),pM->GetMarkedSdrObj(),pM->GetPageView(),SdrSearchOptions::NONE,nullptr);
+ }
+ return bRet;
+}
+
+SdrHdl* SdrMarkView::PickHandle(const Point& rPnt) const
+{
+ if (mbSomeObjChgdFlag) { // recalculate handles, if necessary
+ FlushComeBackTimer();
+ }
+ return maHdlList.IsHdlListHit(rPnt);
+}
+
+bool SdrMarkView::MarkObj(const Point& rPnt, short nTol, bool bToggle, bool bDeep)
+{
+ SdrPageView* pPV;
+ nTol=ImpGetHitTolLogic(nTol,nullptr);
+ SdrSearchOptions nOptions=SdrSearchOptions::PICKMARKABLE;
+ if (bDeep) nOptions=nOptions|SdrSearchOptions::DEEP;
+ SdrObject* pObj = PickObj(rPnt, static_cast<sal_uInt16>(nTol), pPV, nOptions);
+ if (pObj) {
+ bool bUnmark=bToggle && IsObjMarked(pObj);
+ MarkObj(pObj,pPV,bUnmark);
+ }
+ return pObj != nullptr;
+}
+
+bool SdrMarkView::MarkNextObj(bool bPrev)
+{
+ SdrPageView* pPageView = GetSdrPageView();
+
+ if(!pPageView)
+ {
+ return false;
+ }
+
+ SortMarkedObjects();
+ const size_t nMarkCount=GetMarkedObjectCount();
+ size_t nChgMarkNum = SAL_MAX_SIZE; // number of the MarkEntry we want to replace
+ size_t nSearchObjNum = bPrev ? 0 : SAL_MAX_SIZE;
+ if (nMarkCount!=0) {
+ nChgMarkNum=bPrev ? 0 : nMarkCount-1;
+ SdrMark* pM=GetSdrMarkByIndex(nChgMarkNum);
+ OSL_ASSERT(pM!=nullptr);
+ if (pM->GetMarkedSdrObj() != nullptr)
+ nSearchObjNum = pM->GetMarkedSdrObj()->GetNavigationPosition();
+ }
+
+ SdrObject* pMarkObj=nullptr;
+ SdrObjList* pSearchObjList=pPageView->GetObjList();
+ const size_t nObjCount = pSearchObjList->GetObjCount();
+ if (nObjCount!=0) {
+ if (nSearchObjNum>nObjCount) nSearchObjNum=nObjCount;
+ while (pMarkObj==nullptr && ((!bPrev && nSearchObjNum>0) || (bPrev && nSearchObjNum<nObjCount)))
+ {
+ if (!bPrev)
+ nSearchObjNum--;
+ SdrObject* pSearchObj = pSearchObjList->GetObjectForNavigationPosition(nSearchObjNum);
+ if (IsObjMarkable(pSearchObj,pPageView))
+ {
+ if (TryToFindMarkedObject(pSearchObj)==SAL_MAX_SIZE)
+ {
+ pMarkObj=pSearchObj;
+ }
+ }
+ if (bPrev) nSearchObjNum++;
+ }
+ }
+
+ if(!pMarkObj)
+ {
+ return false;
+ }
+
+ if (nChgMarkNum!=SAL_MAX_SIZE)
+ {
+ GetMarkedObjectListWriteAccess().DeleteMark(nChgMarkNum);
+ }
+ MarkObj(pMarkObj,pPageView); // also calls MarkListHasChanged(), AdjustMarkHdl()
+ return true;
+}
+
+bool SdrMarkView::MarkNextObj(const Point& rPnt, short nTol, bool bPrev)
+{
+ SortMarkedObjects();
+ nTol=ImpGetHitTolLogic(nTol,nullptr);
+ SdrMark* pTopMarkHit=nullptr;
+ SdrMark* pBtmMarkHit=nullptr;
+ size_t nTopMarkHit=0;
+ size_t nBtmMarkHit=0;
+ // find topmost of the selected objects that is hit by rPnt
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nm=nMarkCount; nm>0 && pTopMarkHit==nullptr;) {
+ --nm;
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ if(CheckSingleSdrObjectHit(rPnt,sal_uInt16(nTol),pM->GetMarkedSdrObj(),pM->GetPageView(),SdrSearchOptions::NONE,nullptr))
+ {
+ pTopMarkHit=pM;
+ nTopMarkHit=nm;
+ }
+ }
+ // nothing found, in this case, just select an object
+ if (pTopMarkHit==nullptr) return MarkObj(rPnt,sal_uInt16(nTol));
+
+ SdrObject* pTopObjHit=pTopMarkHit->GetMarkedSdrObj();
+ SdrObjList* pObjList=pTopObjHit->getParentSdrObjListFromSdrObject();
+ SdrPageView* pPV=pTopMarkHit->GetPageView();
+ // find lowermost of the selected objects that is hit by rPnt
+ // and is placed on the same PageView as pTopMarkHit
+ for (size_t nm=0; nm<nMarkCount && pBtmMarkHit==nullptr; ++nm) {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrPageView* pPV2=pM->GetPageView();
+ if (pPV2==pPV && CheckSingleSdrObjectHit(rPnt,sal_uInt16(nTol),pM->GetMarkedSdrObj(),pPV2,SdrSearchOptions::NONE,nullptr))
+ {
+ pBtmMarkHit=pM;
+ nBtmMarkHit=nm;
+ }
+ }
+ if (pBtmMarkHit==nullptr) { pBtmMarkHit=pTopMarkHit; nBtmMarkHit=nTopMarkHit; }
+ SdrObject* pBtmObjHit=pBtmMarkHit->GetMarkedSdrObj();
+ const size_t nObjCount = pObjList->GetObjCount();
+
+ size_t nSearchBeg(0);
+ E3dScene* pScene(nullptr);
+ SdrObject* pObjHit(bPrev ? pBtmObjHit : pTopObjHit);
+ bool bRemap =
+ nullptr != dynamic_cast< const E3dCompoundObject* >(pObjHit);
+ if (bRemap)
+ {
+ pScene = dynamic_cast< E3dScene* >(pObjHit->getParentSdrObjectFromSdrObject());
+ bRemap = nullptr != pScene;
+ }
+
+ if(bPrev)
+ {
+ sal_uInt32 nOrdNumBtm(pBtmObjHit->GetOrdNum());
+
+ if(bRemap)
+ {
+ nOrdNumBtm = pScene->RemapOrdNum(nOrdNumBtm);
+ }
+
+ nSearchBeg = nOrdNumBtm + 1;
+ }
+ else
+ {
+ sal_uInt32 nOrdNumTop(pTopObjHit->GetOrdNum());
+
+ if(bRemap)
+ {
+ nOrdNumTop = pScene->RemapOrdNum(nOrdNumTop);
+ }
+
+ nSearchBeg = nOrdNumTop;
+ }
+
+ size_t no=nSearchBeg;
+ SdrObject* pFndObj=nullptr;
+ while (pFndObj==nullptr && ((!bPrev && no>0) || (bPrev && no<nObjCount))) {
+ if (!bPrev) no--;
+ SdrObject* pObj;
+
+ if(bRemap)
+ {
+ pObj = pObjList->GetObj(pScene->RemapOrdNum(no));
+ }
+ else
+ {
+ pObj = pObjList->GetObj(no);
+ }
+
+ if (CheckSingleSdrObjectHit(rPnt,sal_uInt16(nTol),pObj,pPV,SdrSearchOptions::TESTMARKABLE,nullptr))
+ {
+ if (TryToFindMarkedObject(pObj)==SAL_MAX_SIZE) {
+ pFndObj=pObj;
+ } else {
+ // TODO: for performance reasons set on to Top or Btm, if necessary
+ }
+ }
+ if (bPrev) no++;
+ }
+ if (pFndObj!=nullptr)
+ {
+ GetMarkedObjectListWriteAccess().DeleteMark(bPrev?nBtmMarkHit:nTopMarkHit);
+ GetMarkedObjectListWriteAccess().InsertEntry(SdrMark(pFndObj,pPV));
+ MarkListHasChanged();
+ AdjustMarkHdl();
+ }
+ return pFndObj!=nullptr;
+}
+
+void SdrMarkView::MarkObj(const tools::Rectangle& rRect, bool bUnmark)
+{
+ bool bFnd=false;
+ tools::Rectangle aR(rRect);
+ SdrObjList* pObjList;
+ BrkAction();
+ SdrPageView* pPV = GetSdrPageView();
+
+ if(pPV)
+ {
+ pObjList=pPV->GetObjList();
+ tools::Rectangle aFrm1(aR);
+ const size_t nObjCount = pObjList->GetObjCount();
+ for (size_t nO=0; nO<nObjCount; ++nO) {
+ SdrObject* pObj=pObjList->GetObj(nO);
+ tools::Rectangle aRect(pObj->GetCurrentBoundRect());
+ if (aFrm1.Contains(aRect)) {
+ if (!bUnmark) {
+ if (IsObjMarkable(pObj,pPV))
+ {
+ GetMarkedObjectListWriteAccess().InsertEntry(SdrMark(pObj,pPV));
+ bFnd=true;
+ }
+ } else {
+ const size_t nPos=TryToFindMarkedObject(pObj);
+ if (nPos!=SAL_MAX_SIZE)
+ {
+ GetMarkedObjectListWriteAccess().DeleteMark(nPos);
+ bFnd=true;
+ }
+ }
+ }
+ }
+ }
+ if (bFnd) {
+ SortMarkedObjects();
+ MarkListHasChanged();
+ AdjustMarkHdl();
+ }
+}
+
+namespace {
+
+void collectUIInformation(const SdrObject* pObj)
+{
+ EventDescription aDescription;
+ aDescription.aAction = "SELECT";
+ aDescription.aParent = "MainWindow";
+ aDescription.aKeyWord = "CurrentApp";
+
+ if (!pObj->GetName().isEmpty())
+ aDescription.aParameters = {{"OBJECT", pObj->GetName()}};
+ else
+ aDescription.aParameters = {{"OBJECT", "Unnamed_Obj_" + OUString::number(pObj->GetOrdNum())}};
+
+ UITestLogger::getInstance().logEvent(aDescription);
+}
+
+}
+
+ void SdrMarkView::MarkObj(SdrObject* pObj, SdrPageView* pPV, bool bUnmark, bool bDoNoSetMarkHdl,
+ std::vector<basegfx::B2DRectangle> && rSubSelections)
+{
+ if (!(pObj!=nullptr && pPV!=nullptr && IsObjMarkable(pObj, pPV)))
+ return;
+
+ BrkAction();
+ if (!bUnmark)
+ {
+ GetMarkedObjectListWriteAccess().InsertEntry(SdrMark(pObj,pPV));
+ collectUIInformation(pObj);
+ }
+ else
+ {
+ const size_t nPos=TryToFindMarkedObject(pObj);
+ if (nPos!=SAL_MAX_SIZE)
+ {
+ GetMarkedObjectListWriteAccess().DeleteMark(nPos);
+ }
+ }
+
+ maSubSelectionList = std::move(rSubSelections);
+
+ if (!bDoNoSetMarkHdl) {
+ MarkListHasChanged();
+ AdjustMarkHdl();
+ }
+}
+
+bool SdrMarkView::IsObjMarked(SdrObject const * pObj) const
+{
+ return TryToFindMarkedObject(pObj)!=SAL_MAX_SIZE;
+}
+
+sal_uInt16 SdrMarkView::GetMarkHdlSizePixel() const
+{
+ return maHdlList.GetHdlSize()*2+1;
+}
+
+void SdrMarkView::SetMarkHdlSizePixel(sal_uInt16 nSiz)
+{
+ if (nSiz<3) nSiz=3;
+ nSiz/=2;
+ if (nSiz!=maHdlList.GetHdlSize()) {
+ maHdlList.SetHdlSize(nSiz);
+ }
+}
+
+bool SdrMarkView::getPossibleGridOffsetForSdrObject(
+ basegfx::B2DVector& rOffset,
+ const SdrObject* pObj,
+ const SdrPageView* pPV) const
+{
+ if(nullptr == pObj || nullptr == pPV)
+ {
+ return false;
+ }
+
+ const OutputDevice* pOutputDevice(GetFirstOutputDevice());
+
+ if(nullptr == pOutputDevice)
+ {
+ return false;
+ }
+
+ const SdrPageWindow* pSdrPageWindow(pPV->FindPageWindow(*pOutputDevice));
+
+ if(nullptr == pSdrPageWindow)
+ {
+ return false;
+ }
+
+ const sdr::contact::ObjectContact& rObjectContact(pSdrPageWindow->GetObjectContact());
+
+ if(!rObjectContact.supportsGridOffsets())
+ {
+ return false;
+ }
+
+ const sdr::contact::ViewObjectContact& rVOC(pObj->GetViewContact().GetViewObjectContact(
+ const_cast<sdr::contact::ObjectContact&>(rObjectContact)));
+
+ rOffset = rVOC.getGridOffset();
+
+ return !rOffset.equalZero();
+}
+
+bool SdrMarkView::getPossibleGridOffsetForPosition(
+ basegfx::B2DVector& rOffset,
+ const basegfx::B2DPoint& rPoint,
+ const SdrPageView* pPV) const
+{
+ if(nullptr == pPV)
+ {
+ return false;
+ }
+
+ const OutputDevice* pOutputDevice(GetFirstOutputDevice());
+
+ if(nullptr == pOutputDevice)
+ {
+ return false;
+ }
+
+ const SdrPageWindow* pSdrPageWindow(pPV->FindPageWindow(*pOutputDevice));
+
+ if(nullptr == pSdrPageWindow)
+ {
+ return false;
+ }
+
+ const sdr::contact::ObjectContact& rObjectContact(pSdrPageWindow->GetObjectContact());
+
+ if(!rObjectContact.supportsGridOffsets())
+ {
+ return false;
+ }
+
+ rObjectContact.calculateGridOffsetForB2DRange(rOffset, basegfx::B2DRange(rPoint));
+
+ return !rOffset.equalZero();
+}
+
+SdrObject* SdrMarkView::CheckSingleSdrObjectHit(const Point& rPnt, sal_uInt16 nTol, SdrObject* pObj, SdrPageView* pPV, SdrSearchOptions nOptions, const SdrLayerIDSet* pMVisLay) const
+{
+ if(((nOptions & SdrSearchOptions::IMPISMASTER) && pObj->IsNotVisibleAsMaster()) || (!pObj->IsVisible()))
+ {
+ return nullptr;
+ }
+
+ const bool bCheckIfMarkable(nOptions & SdrSearchOptions::TESTMARKABLE);
+ const bool bDeep(nOptions & SdrSearchOptions::DEEP);
+ const bool bOLE(dynamic_cast< const SdrOle2Obj* >(pObj) != nullptr);
+ auto pTextObj = dynamic_cast<const SdrTextObj*>( pObj);
+ const bool bTXT(pTextObj && pTextObj->IsTextFrame());
+ SdrObject* pRet=nullptr;
+ tools::Rectangle aRect(pObj->GetCurrentBoundRect());
+
+ // add possible GridOffset to up-to-now view-independent BoundRect data
+ basegfx::B2DVector aGridOffset(0.0, 0.0);
+ if(getPossibleGridOffsetForSdrObject(aGridOffset, pObj, pPV))
+ {
+ aRect += Point(
+ basegfx::fround(aGridOffset.getX()),
+ basegfx::fround(aGridOffset.getY()));
+ }
+
+ sal_uInt16 nTol2(nTol);
+
+ // double tolerance for OLE, text frames and objects in
+ // active text edit
+ if(bOLE || bTXT || pObj==static_cast<const SdrObjEditView*>(this)->GetTextEditObject())
+ {
+ nTol2*=2;
+ }
+
+ aRect.AdjustLeft( -nTol2 ); // add 1 tolerance for all objects
+ aRect.AdjustTop( -nTol2 );
+ aRect.AdjustRight(nTol2 );
+ aRect.AdjustBottom(nTol2 );
+
+ if (aRect.Contains(rPnt))
+ {
+ if (!bCheckIfMarkable || IsObjMarkable(pObj,pPV))
+ {
+ SdrObjList* pOL=pObj->GetSubList();
+
+ if (pOL!=nullptr && pOL->GetObjCount()!=0)
+ {
+ SdrObject* pTmpObj;
+ // adjustment hit point for virtual objects
+ Point aPnt( rPnt );
+
+ if ( auto pVirtObj = dynamic_cast<const SdrVirtObj*>( pObj) )
+ {
+ Point aOffset = pVirtObj->GetOffset();
+ aPnt.Move( -aOffset.X(), -aOffset.Y() );
+ }
+
+ pRet=CheckSingleSdrObjectHit(aPnt,nTol,pOL,pPV,nOptions,pMVisLay,pTmpObj);
+ }
+ else
+ {
+ if(!pMVisLay || pMVisLay->IsSet(pObj->GetLayer()))
+ {
+ pRet = SdrObjectPrimitiveHit(*pObj, rPnt, nTol2, *pPV, &pPV->GetVisibleLayers(), false);
+ }
+ }
+ }
+ }
+
+ if (!bDeep && pRet!=nullptr)
+ {
+ pRet=pObj;
+ }
+
+ return pRet;
+}
+
+SdrObject* SdrMarkView::CheckSingleSdrObjectHit(const Point& rPnt, sal_uInt16 nTol, SdrObjList const * pOL, SdrPageView* pPV, SdrSearchOptions nOptions, const SdrLayerIDSet* pMVisLay, SdrObject*& rpRootObj) const
+{
+ return (*this).CheckSingleSdrObjectHit(rPnt,nTol,pOL,pPV,nOptions,pMVisLay,rpRootObj,nullptr);
+}
+SdrObject* SdrMarkView::CheckSingleSdrObjectHit(const Point& rPnt, sal_uInt16 nTol, SdrObjList const * pOL, SdrPageView* pPV, SdrSearchOptions nOptions, const SdrLayerIDSet* pMVisLay, SdrObject*& rpRootObj,const SdrMarkList * pMarkList) const
+{
+ SdrObject* pRet=nullptr;
+ rpRootObj=nullptr;
+ if (pOL!=nullptr)
+ {
+ const bool bRemap(
+ nullptr != pOL->getSdrObjectFromSdrObjList()
+ && nullptr != dynamic_cast< const E3dScene* >(pOL->getSdrObjectFromSdrObjList()));
+ const E3dScene* pRemapScene(bRemap ? static_cast< E3dScene* >(pOL->getSdrObjectFromSdrObjList()) : nullptr);
+ const size_t nObjCount(pOL->GetObjCount());
+ size_t nObjNum(nObjCount);
+
+ while (pRet==nullptr && nObjNum>0)
+ {
+ nObjNum--;
+ SdrObject* pObj;
+
+ if(bRemap)
+ {
+ pObj = pOL->GetObj(pRemapScene->RemapOrdNum(nObjNum));
+ }
+ else
+ {
+ pObj = pOL->GetObj(nObjNum);
+ }
+ if (nOptions & SdrSearchOptions::BEFOREMARK)
+ {
+ if (pMarkList!=nullptr)
+ {
+ if ((*pMarkList).FindObject(pObj)!=SAL_MAX_SIZE)
+ {
+ return nullptr;
+ }
+ }
+ }
+ pRet=CheckSingleSdrObjectHit(rPnt,nTol,pObj,pPV,nOptions,pMVisLay);
+ if (pRet!=nullptr) rpRootObj=pObj;
+ }
+ }
+ return pRet;
+}
+
+SdrObject* SdrMarkView::PickObj(const Point& rPnt, short nTol, SdrPageView*& rpPV, SdrSearchOptions nOptions) const
+{
+ return PickObj(rPnt, nTol, rpPV, nOptions, nullptr);
+}
+
+SdrObject* SdrMarkView::PickObj(const Point& rPnt, short nTol, SdrPageView*& rpPV, SdrSearchOptions nOptions, SdrObject** ppRootObj, bool* pbHitPassDirect) const
+{ // TODO: lacks a Pass2,Pass3
+ SortMarkedObjects();
+ if (ppRootObj!=nullptr) *ppRootObj=nullptr;
+ if (pbHitPassDirect!=nullptr) *pbHitPassDirect=true;
+ SdrObject* pRet = nullptr;
+ rpPV=nullptr;
+ bool bMarked(nOptions & SdrSearchOptions::MARKED);
+ bool bMasters=!bMarked && bool(nOptions & SdrSearchOptions::ALSOONMASTER);
+ // nOptions & SdrSearchOptions::NEXT: n.i.
+ // nOptions & SdrSearchOptions::PASS2BOUND: n.i.
+ // nOptions & SdrSearchOptions::PASS3NEAREST// n.i.
+ if (nTol<0) nTol=ImpGetHitTolLogic(nTol,nullptr);
+ SdrObject* pObj=nullptr;
+ SdrObject* pHitObj=nullptr;
+ SdrPageView* pPV=nullptr;
+ if (static_cast<const SdrObjEditView*>(this)->IsTextEditFrameHit(rPnt)) {
+ pObj=static_cast<const SdrObjEditView*>(this)->GetTextEditObject();
+ pHitObj=pObj;
+ pPV=static_cast<const SdrObjEditView*>(this)->GetTextEditPageView();
+ }
+ if (bMarked) {
+ const size_t nMrkCnt=GetMarkedObjectCount();
+ size_t nMrkNum=nMrkCnt;
+ while (pHitObj==nullptr && nMrkNum>0) {
+ nMrkNum--;
+ SdrMark* pM=GetSdrMarkByIndex(nMrkNum);
+ pObj=pM->GetMarkedSdrObj();
+ pPV=pM->GetPageView();
+ pHitObj=CheckSingleSdrObjectHit(rPnt,nTol,pObj,pPV,nOptions,nullptr);
+ }
+ }
+ else
+ {
+ pPV = GetSdrPageView();
+
+ if(pPV)
+ {
+ SdrPage* pPage=pPV->GetPage();
+ sal_uInt16 nPgCount=1;
+
+ if(bMasters && pPage->TRG_HasMasterPage())
+ {
+ nPgCount++;
+ }
+ bool bWholePage(nOptions & SdrSearchOptions::WHOLEPAGE);
+ bool bExtraPassForWholePage=bWholePage && pPage!=pPV->GetObjList();
+ if (bExtraPassForWholePage) nPgCount++; // First search in AktObjList, then on the entire page
+ sal_uInt16 nPgNum=nPgCount;
+ while (pHitObj==nullptr && nPgNum>0) {
+ SdrSearchOptions nTmpOptions=nOptions;
+ nPgNum--;
+ const SdrLayerIDSet* pMVisLay=nullptr;
+ SdrObjList* pObjList=nullptr;
+ if (pbHitPassDirect!=nullptr) *pbHitPassDirect = true;
+ if (nPgNum>=nPgCount-1 || (bExtraPassForWholePage && nPgNum>=nPgCount-2))
+ {
+ pObjList=pPV->GetObjList();
+ if (bExtraPassForWholePage && nPgNum==nPgCount-2) {
+ pObjList=pPage;
+ if (pbHitPassDirect!=nullptr) *pbHitPassDirect = false;
+ }
+ }
+ else
+ {
+ // otherwise MasterPage
+ SdrPage& rMasterPage = pPage->TRG_GetMasterPage();
+ pMVisLay = &pPage->TRG_GetMasterPageVisibleLayers();
+ pObjList = &rMasterPage;
+
+ if (pbHitPassDirect!=nullptr) *pbHitPassDirect = false;
+ nTmpOptions=nTmpOptions | SdrSearchOptions::IMPISMASTER;
+ }
+ pHitObj=CheckSingleSdrObjectHit(rPnt,nTol,pObjList,pPV,nTmpOptions,pMVisLay,pObj,&(GetMarkedObjectList()));
+ }
+ }
+ }
+ if (pHitObj!=nullptr) {
+ if (ppRootObj!=nullptr) *ppRootObj=pObj;
+ if (nOptions & SdrSearchOptions::DEEP) pObj=pHitObj;
+ if (nOptions & SdrSearchOptions::TESTTEXTEDIT) {
+ if (!pObj->HasTextEdit() || pPV->GetLockedLayers().IsSet(pObj->GetLayer())) {
+ pObj=nullptr;
+ }
+ }
+ if (pObj!=nullptr && (nOptions & SdrSearchOptions::TESTMACRO)) {
+ SdrObjMacroHitRec aHitRec;
+ aHitRec.aPos=rPnt;
+ aHitRec.nTol=nTol;
+ aHitRec.pVisiLayer=&pPV->GetVisibleLayers();
+ aHitRec.pPageView=pPV;
+ if (!pObj->HasMacro() || !pObj->IsMacroHit(aHitRec)) pObj=nullptr;
+ }
+ if (pObj!=nullptr) {
+ pRet=pObj;
+ rpPV=pPV;
+ }
+ }
+ return pRet;
+}
+
+bool SdrMarkView::PickMarkedObj(const Point& rPnt, SdrObject*& rpObj, SdrPageView*& rpPV, SdrSearchOptions nOptions) const
+{
+ SortMarkedObjects();
+ const bool bBoundCheckOn2ndPass(nOptions & SdrSearchOptions::PASS2BOUND);
+ rpObj=nullptr;
+ rpPV=nullptr;
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nMarkNum=nMarkCount; nMarkNum>0;) {
+ --nMarkNum;
+ SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
+ SdrPageView* pPV=pM->GetPageView();
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ if (CheckSingleSdrObjectHit(rPnt,mnHitTolLog,pObj,pPV,SdrSearchOptions::TESTMARKABLE,nullptr)) {
+ rpObj=pObj;
+ rpPV=pPV;
+ return true;
+ }
+ }
+ if (bBoundCheckOn2ndPass) {
+ for (size_t nMarkNum=nMarkCount; nMarkNum>0;) {
+ --nMarkNum;
+ SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
+ SdrPageView* pPV=pM->GetPageView();
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ tools::Rectangle aRect(pObj->GetCurrentBoundRect());
+ aRect.AdjustLeft( -mnHitTolLog );
+ aRect.AdjustTop( -mnHitTolLog );
+ aRect.AdjustRight(mnHitTolLog );
+ aRect.AdjustBottom(mnHitTolLog );
+ if (aRect.Contains(rPnt)) {
+ rpObj=pObj;
+ rpPV=pPV;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+
+void SdrMarkView::UnmarkAllObj(SdrPageView const * pPV)
+{
+ if (GetMarkedObjectCount()==0)
+ return;
+
+ BrkAction();
+ if (pPV!=nullptr)
+ {
+ GetMarkedObjectListWriteAccess().DeletePageView(*pPV);
+ }
+ else
+ {
+ GetMarkedObjectListWriteAccess().Clear();
+ }
+ mpMarkedObj=nullptr;
+ mpMarkedPV=nullptr;
+ MarkListHasChanged();
+ AdjustMarkHdl();
+}
+
+void SdrMarkView::MarkAllObj(SdrPageView* pPV)
+{
+ BrkAction();
+
+ if(!pPV)
+ {
+ pPV = GetSdrPageView();
+ }
+
+ // #i69171# pPV may still be NULL if there is no SDrPageView (!), e.g. when inserting
+ // other files
+ if(pPV)
+ {
+ const bool bMarkChg(GetMarkedObjectListWriteAccess().InsertPageView(*pPV));
+
+ if(bMarkChg)
+ {
+ MarkListHasChanged();
+ }
+ }
+
+ if(GetMarkedObjectCount())
+ {
+ AdjustMarkHdl();
+ }
+}
+
+void SdrMarkView::AdjustMarkHdl(SfxViewShell* pOtherShell)
+{
+ CheckMarked();
+ SetMarkRects();
+ SetMarkHandles(pOtherShell);
+}
+
+// BoundRect in model coordinates, no GridOffset added
+tools::Rectangle SdrMarkView::GetMarkedObjBoundRect() const
+{
+ tools::Rectangle aRect;
+ for (size_t nm=0; nm<GetMarkedObjectCount(); ++nm) {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pO=pM->GetMarkedSdrObj();
+ tools::Rectangle aR1(pO->GetCurrentBoundRect());
+ if (aRect.IsEmpty()) aRect=aR1;
+ else aRect.Union(aR1);
+ }
+ return aRect;
+}
+
+// ObjRect in model coordinates, no GridOffset added
+const tools::Rectangle& SdrMarkView::GetMarkedObjRect() const
+{
+ if (mbMarkedObjRectDirty) {
+ const_cast<SdrMarkView*>(this)->mbMarkedObjRectDirty=false;
+ tools::Rectangle aRect;
+ for (size_t nm=0; nm<GetMarkedObjectCount(); ++nm) {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pO = pM->GetMarkedSdrObj();
+ if (!pO)
+ continue;
+ tools::Rectangle aR1(pO->GetSnapRect());
+ if (aRect.IsEmpty()) aRect=aR1;
+ else aRect.Union(aR1);
+ }
+ const_cast<SdrMarkView*>(this)->maMarkedObjRect=aRect;
+ }
+ return maMarkedObjRect;
+}
+
+
+OUString SdrMarkView::ImpGetDescriptionString(TranslateId pStrCacheID, ImpGetDescriptionOptions nOpt) const
+{
+ OUString sStr = SvxResId(pStrCacheID);
+ const sal_Int32 nPos = sStr.indexOf("%1");
+
+ if(nPos != -1)
+ {
+ if(nOpt == ImpGetDescriptionOptions::POINTS)
+ {
+ sStr = sStr.replaceAt(nPos, 2, GetDescriptionOfMarkedPoints());
+ }
+ else if(nOpt == ImpGetDescriptionOptions::GLUEPOINTS)
+ {
+ sStr = sStr.replaceAt(nPos, 2, GetDescriptionOfMarkedGluePoints());
+ }
+ else
+ {
+ sStr = sStr.replaceAt(nPos, 2, GetDescriptionOfMarkedObjects());
+ }
+ }
+
+ return sStr.replaceFirst("%2", "0");
+}
+
+
+void SdrMarkView::EnterMarkedGroup()
+{
+ // We enter only the first group found (in only one PageView), because
+ // PageView::EnterGroup calls an AdjustMarkHdl.
+ // TODO: I'll have to prevent that via a flag.
+ SdrPageView* pPV = GetSdrPageView();
+
+ if(!pPV)
+ return;
+
+ bool bEnter=false;
+ for (size_t nm = GetMarkedObjectCount(); nm > 0 && !bEnter;)
+ {
+ --nm;
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ if (pM->GetPageView()==pPV) {
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ if (pObj->IsGroupObject()) {
+ if (pPV->EnterGroup(pObj)) {
+ bEnter=true;
+ }
+ }
+ }
+ }
+}
+
+
+void SdrMarkView::MarkListHasChanged()
+{
+ GetMarkedObjectListWriteAccess().SetNameDirty();
+ maSdrViewSelection.SetEdgesOfMarkedNodesDirty();
+
+ mbMarkedObjRectDirty=true;
+ mbMarkedPointsRectsDirty=true;
+ bool bOneEdgeMarked=false;
+ if (GetMarkedObjectCount()==1) {
+ const SdrObject* pObj=GetMarkedObjectByIndex(0);
+ if (pObj->GetObjInventor()==SdrInventor::Default) {
+ bOneEdgeMarked = pObj->GetObjIdentifier() == SdrObjKind::Edge;
+ }
+ }
+ ImpSetGlueVisible4(bOneEdgeMarked);
+}
+
+
+void SdrMarkView::SetMoveOutside(bool bOn)
+{
+ maHdlList.SetMoveOutside(bOn);
+}
+
+void SdrMarkView::SetDesignMode( bool bOn )
+{
+ if ( mbDesignMode != bOn )
+ {
+ mbDesignMode = bOn;
+ SdrPageView* pPageView = GetSdrPageView();
+ if ( pPageView )
+ pPageView->SetDesignMode( bOn );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdmrkv1.cxx b/svx/source/svdraw/svdmrkv1.cxx
new file mode 100644
index 000000000..9c732262b
--- /dev/null
+++ b/svx/source/svdraw/svdmrkv1.cxx
@@ -0,0 +1,547 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <svx/svdmrkv.hxx>
+#include <svx/svdpagv.hxx>
+#include <osl/diagnose.h>
+
+
+// Point Selection
+
+
+bool SdrMarkView::HasMarkablePoints() const
+{
+ ForceUndirtyMrkPnt();
+ bool bRet=false;
+ if (!ImpIsFrameHandles()) {
+ const size_t nMarkCount=GetMarkedObjectCount();
+ if (nMarkCount<=static_cast<size_t>(mnFrameHandlesLimit)) {
+ for (size_t nMarkNum=0; nMarkNum<nMarkCount && !bRet; ++nMarkNum) {
+ const SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
+ const SdrObject* pObj=pM->GetMarkedSdrObj();
+ bRet=pObj->IsPolyObj();
+ }
+ }
+ }
+ return bRet;
+}
+
+sal_Int32 SdrMarkView::GetMarkablePointCount() const
+{
+ ForceUndirtyMrkPnt();
+ sal_Int32 nCount=0;
+ if (!ImpIsFrameHandles()) {
+ const size_t nMarkCount=GetMarkedObjectCount();
+ if (nMarkCount<=static_cast<size_t>(mnFrameHandlesLimit)) {
+ for (size_t nMarkNum=0; nMarkNum<nMarkCount; ++nMarkNum) {
+ const SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
+ const SdrObject* pObj=pM->GetMarkedSdrObj();
+ if (pObj->IsPolyObj()) {
+ nCount+=pObj->GetPointCount();
+ }
+ }
+ }
+ }
+ return nCount;
+}
+
+bool SdrMarkView::HasMarkedPoints() const
+{
+ ForceUndirtyMrkPnt();
+ bool bRet=false;
+ if (!ImpIsFrameHandles()) {
+ const size_t nMarkCount=GetMarkedObjectCount();
+ if (nMarkCount<=static_cast<size_t>(mnFrameHandlesLimit)) {
+ for (size_t nMarkNum=0; nMarkNum<nMarkCount && !bRet; ++nMarkNum) {
+ const SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
+ const SdrUShortCont& rPts = pM->GetMarkedPoints();
+ bRet = !rPts.empty();
+ }
+ }
+ }
+ return bRet;
+}
+
+bool SdrMarkView::IsPointMarkable(const SdrHdl& rHdl) const
+{
+ return !ImpIsFrameHandles() && !rHdl.IsPlusHdl() && rHdl.GetKind()!=SdrHdlKind::Glue && rHdl.GetKind()!=SdrHdlKind::SmartTag && rHdl.GetObj()!=nullptr && rHdl.GetObj()->IsPolyObj();
+}
+
+bool SdrMarkView::MarkPointHelper(SdrHdl* pHdl, SdrMark* pMark, bool bUnmark)
+{
+ return ImpMarkPoint( pHdl, pMark, bUnmark );
+}
+
+bool SdrMarkView::ImpMarkPoint(SdrHdl* pHdl, SdrMark* pMark, bool bUnmark)
+{
+ if (pHdl==nullptr || pHdl->IsPlusHdl() || pHdl->GetKind()==SdrHdlKind::Glue)
+ return false;
+
+ if (pHdl->IsSelected() != bUnmark)
+ return false;
+
+ SdrObject* pObj=pHdl->GetObj();
+ if (pObj==nullptr || !pObj->IsPolyObj())
+ return false;
+
+ if (pMark==nullptr)
+ {
+ const size_t nMarkNum=TryToFindMarkedObject(pObj);
+ if (nMarkNum==SAL_MAX_SIZE)
+ return false;
+ pMark=GetSdrMarkByIndex(nMarkNum);
+ }
+ const sal_uInt32 nHdlNum(pHdl->GetObjHdlNum());
+ SdrUShortCont& rPts=pMark->GetMarkedPoints();
+ if (!bUnmark)
+ {
+ rPts.insert(static_cast<sal_uInt16>(nHdlNum));
+ }
+ else
+ {
+ SdrUShortCont::const_iterator it = rPts.find( static_cast<sal_uInt16>(nHdlNum) );
+ if (it != rPts.end())
+ {
+ rPts.erase(it);
+ }
+ else
+ {
+ return false; // error case!
+ }
+ }
+
+ pHdl->SetSelected(!bUnmark);
+ if (!mbPlusHdlAlways)
+ {
+ if (!bUnmark)
+ {
+ SdrHdlList plusList(nullptr);
+ pObj->AddToPlusHdlList(plusList, *pHdl);
+ sal_uInt32 nCount(plusList.GetHdlCount());
+ for (sal_uInt32 i=0; i<nCount; i++)
+ {
+ SdrHdl* pPlusHdl=plusList.GetHdl(i);
+ pPlusHdl->SetObj(pObj);
+ pPlusHdl->SetPageView(pMark->GetPageView());
+ pPlusHdl->SetPlusHdl(true);
+ }
+ plusList.MoveTo(maHdlList);
+ }
+ else
+ {
+ for (size_t i = maHdlList.GetHdlCount(); i>0;)
+ {
+ --i;
+ SdrHdl* pPlusHdl=maHdlList.GetHdl(i);
+ if (pPlusHdl->IsPlusHdl() && pPlusHdl->GetSourceHdlNum()==nHdlNum)
+ {
+ maHdlList.RemoveHdl(i);
+ }
+ }
+ }
+ }
+
+ maHdlList.Sort();
+
+ return true;
+}
+
+
+bool SdrMarkView::MarkPoint(SdrHdl& rHdl, bool bUnmark)
+{
+ ForceUndirtyMrkPnt();
+ bool bRet=false;
+ const SdrObject* pObj=rHdl.GetObj();
+ if (IsPointMarkable(rHdl) && rHdl.IsSelected()==bUnmark) {
+ const size_t nMarkNum=TryToFindMarkedObject(pObj);
+ if (nMarkNum!=SAL_MAX_SIZE) {
+ SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
+ if (ImpMarkPoint(&rHdl,pM,bUnmark)) {
+ MarkListHasChanged();
+ bRet=true;
+ }
+ }
+ }
+
+ return bRet;
+}
+
+bool SdrMarkView::MarkPoints(const tools::Rectangle* pRect, bool bUnmark)
+{
+ ForceUndirtyMrkPnt();
+ bool bChgd=false;
+ SortMarkedObjects();
+ const SdrObject* pObj0=nullptr;
+ const SdrPageView* pPV0=nullptr;
+ SdrMark* pM=nullptr;
+ maHdlList.Sort();
+ const size_t nHdlCnt=maHdlList.GetHdlCount();
+ for (size_t nHdlNum=nHdlCnt; nHdlNum>0;) {
+ --nHdlNum;
+ SdrHdl* pHdl=maHdlList.GetHdl(nHdlNum);
+ if (IsPointMarkable(*pHdl) && pHdl->IsSelected()==bUnmark) {
+ const SdrObject* pObj=pHdl->GetObj();
+ const SdrPageView* pPV=pHdl->GetPageView();
+ if (pObj!=pObj0 || pPV!=pPV0 || pM==nullptr) { // This section is for optimization,
+ const size_t nMarkNum=TryToFindMarkedObject(pObj); // so ImpMarkPoint() doesn't always
+ if (nMarkNum!=SAL_MAX_SIZE) { // have to search the object in the MarkList.
+ pM=GetSdrMarkByIndex(nMarkNum);
+ pObj0=pObj;
+ pPV0=pPV;
+ } else {
+#ifdef DBG_UTIL
+ if (pObj->IsInserted()) {
+ OSL_FAIL("SdrMarkView::MarkPoints(const Rectangle* pRect): Selected object not found.");
+ }
+#endif
+ pM=nullptr;
+ }
+ }
+ Point aPos(pHdl->GetPos());
+ if (pM!=nullptr && (pRect==nullptr || pRect->Contains(aPos))) {
+ if (ImpMarkPoint(pHdl,pM,bUnmark)) bChgd=true;
+ }
+ }
+ }
+ if (bChgd) {
+ MarkListHasChanged();
+ }
+
+ return bChgd;
+}
+
+void SdrMarkView::MarkNextPoint()
+{
+ ForceUndirtyMrkPnt();
+ SortMarkedObjects();
+}
+
+const tools::Rectangle& SdrMarkView::GetMarkedPointsRect() const
+{
+ ForceUndirtyMrkPnt();
+ if (mbMarkedPointsRectsDirty) ImpSetPointsRects();
+ return maMarkedPointsRect;
+}
+
+void SdrMarkView::SetPlusHandlesAlwaysVisible(bool bOn)
+{ // TODO: Optimize HandlePaint!
+ ForceUndirtyMrkPnt();
+ if (bOn!=mbPlusHdlAlways) {
+ mbPlusHdlAlways=bOn;
+ SetMarkHandles(nullptr);
+ MarkListHasChanged();
+ }
+}
+
+
+// ImpSetPointsRects() is for PolyPoints and GluePoints!
+
+
+void SdrMarkView::ImpSetPointsRects() const
+{
+ tools::Rectangle aPnts;
+ tools::Rectangle aGlue;
+ const size_t nHdlCnt=maHdlList.GetHdlCount();
+ for (size_t nHdlNum=0; nHdlNum<nHdlCnt; ++nHdlNum) {
+ const SdrHdl* pHdl=maHdlList.GetHdl(nHdlNum);
+ SdrHdlKind eKind=pHdl->GetKind();
+ if ((eKind==SdrHdlKind::Poly && pHdl->IsSelected()) || eKind==SdrHdlKind::Glue) {
+ Point aPt(pHdl->GetPos());
+ tools::Rectangle& rR=eKind==SdrHdlKind::Glue ? aGlue : aPnts;
+ if (rR.IsEmpty()) {
+ rR=tools::Rectangle(aPt,aPt);
+ } else {
+ if (aPt.X()<rR.Left ()) rR.SetLeft(aPt.X() );
+ if (aPt.X()>rR.Right ()) rR.SetRight(aPt.X() );
+ if (aPt.Y()<rR.Top ()) rR.SetTop(aPt.Y() );
+ if (aPt.Y()>rR.Bottom()) rR.SetBottom(aPt.Y() );
+ }
+ }
+ }
+ const_cast<SdrMarkView*>(this)->maMarkedPointsRect=aPnts;
+ const_cast<SdrMarkView*>(this)->maMarkedGluePointsRect=aGlue;
+ const_cast<SdrMarkView*>(this)->mbMarkedPointsRectsDirty=false;
+}
+
+
+// UndirtyMrkPnt() is for PolyPoints and GluePoints!
+
+
+void SdrMarkView::UndirtyMrkPnt() const
+{
+ bool bChg=false;
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nMarkNum=0; nMarkNum<nMarkCount; ++nMarkNum) {
+ SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
+ const SdrObject* pObj=pM->GetMarkedSdrObj();
+ // PolyPoints
+ {
+ SdrUShortCont& rPts = pM->GetMarkedPoints();
+ if (pObj->IsPolyObj()) {
+ // Remove invalid selected points, that is, all
+ // entries above the number of points in the object.
+ sal_uInt32 nMax(pObj->GetPointCount());
+
+ SdrUShortCont::const_iterator it = rPts.lower_bound(nMax);
+ if( it != rPts.end() )
+ {
+ rPts.erase(it, rPts.end());
+ bChg = true;
+ }
+ }
+ else
+ {
+ if (!rPts.empty())
+ {
+ // only fail *if* there are marked points
+ OSL_FAIL("SdrMarkView::UndirtyMrkPnt(): Selected points on an object that is not a PolyObj!");
+ rPts.clear();
+ bChg = true;
+ }
+ }
+ }
+
+ // GluePoints
+ {
+ SdrUShortCont& rPts = pM->GetMarkedGluePoints();
+ const SdrGluePointList* pGPL=pObj->GetGluePointList();
+ if (pGPL!=nullptr) {
+ // Remove invalid selected gluepoints, that is, all entries
+ // (IDs) that aren't contained in the GluePointList of the
+ // object
+ for(SdrUShortCont::const_iterator it = rPts.begin(); it != rPts.end(); )
+ {
+ sal_uInt16 nId=*it;
+ if (pGPL->FindGluePoint(nId)==SDRGLUEPOINT_NOTFOUND) {
+ it = rPts.erase(it);
+ bChg=true;
+ }
+ else
+ ++it;
+ }
+ } else {
+ if (!rPts.empty()) {
+ rPts.clear(); // object doesn't have any gluepoints (any more)
+ bChg=true;
+ }
+ }
+ }
+ }
+ if (bChg) const_cast<SdrMarkView*>(this)->mbMarkedPointsRectsDirty=true;
+ const_cast<SdrMarkView*>(this)->mbMrkPntDirty=false;
+}
+
+
+bool SdrMarkView::HasMarkableGluePoints() const
+{
+ bool bRet=false;
+ if (IsGluePointEditMode()) {
+ ForceUndirtyMrkPnt();
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nMarkNum=0; nMarkNum<nMarkCount && !bRet; ++nMarkNum) {
+ const SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
+ const SdrObject* pObj=pM->GetMarkedSdrObj();
+ const SdrGluePointList* pGPL=pObj->GetGluePointList();
+
+ // #i38892#
+ if(pGPL && pGPL->GetCount())
+ {
+ for(sal_uInt16 a(0); !bRet && a < pGPL->GetCount(); a++)
+ {
+ if((*pGPL)[a].IsUserDefined())
+ {
+ bRet = true;
+ }
+ }
+ }
+ }
+ }
+ return bRet;
+}
+
+bool SdrMarkView::HasMarkedGluePoints() const
+{
+ ForceUndirtyMrkPnt();
+ bool bRet=false;
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nMarkNum=0; nMarkNum<nMarkCount && !bRet; ++nMarkNum) {
+ const SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
+ const SdrUShortCont& rPts = pM->GetMarkedGluePoints();
+ bRet = !rPts.empty();
+ }
+ return bRet;
+}
+
+bool SdrMarkView::MarkGluePoints(const tools::Rectangle* pRect, bool bUnmark)
+{
+ if (!IsGluePointEditMode() && !bUnmark) return false;
+ ForceUndirtyMrkPnt();
+ bool bChgd=false;
+ SortMarkedObjects();
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nMarkNum=0; nMarkNum<nMarkCount; ++nMarkNum) {
+ SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
+ const SdrObject* pObj=pM->GetMarkedSdrObj();
+ const SdrGluePointList* pGPL=pObj->GetGluePointList();
+ SdrUShortCont& rPts = pM->GetMarkedGluePoints();
+ if (bUnmark && pRect==nullptr) { // UnmarkAll
+ if (!rPts.empty()) {
+ rPts.clear();
+ bChgd=true;
+ }
+ } else {
+ if (pGPL!=nullptr) {
+ sal_uInt16 nGluePointCnt=pGPL->GetCount();
+ for (sal_uInt16 nGPNum=0; nGPNum<nGluePointCnt; nGPNum++) {
+ const SdrGluePoint& rGP=(*pGPL)[nGPNum];
+
+ // #i38892#
+ if(rGP.IsUserDefined())
+ {
+ Point aPos(rGP.GetAbsolutePos(*pObj));
+ if (pRect==nullptr || pRect->Contains(aPos)) {
+ bool bContains = rPts.find( rGP.GetId() ) != rPts.end();
+ if (!bUnmark && !bContains) {
+ bChgd=true;
+ rPts.insert(rGP.GetId());
+ }
+ if (bUnmark && bContains) {
+ bChgd=true;
+ rPts.erase(rGP.GetId());
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if (bChgd) {
+ AdjustMarkHdl();
+ MarkListHasChanged();
+ }
+ return bChgd;
+}
+
+bool SdrMarkView::PickGluePoint(const Point& rPnt, SdrObject*& rpObj, sal_uInt16& rnId, SdrPageView*& rpPV) const
+{
+ rpObj=nullptr; rpPV=nullptr; rnId=0;
+ if (!IsGluePointEditMode()) return false;
+ OutputDevice* pOut=mpActualOutDev.get();
+ if (pOut==nullptr) pOut=GetFirstOutputDevice();
+ if (pOut==nullptr) return false;
+ SortMarkedObjects();
+ const size_t nMarkCount=GetMarkedObjectCount();
+ size_t nMarkNum=nMarkCount;
+ while (nMarkNum>0) {
+ nMarkNum--;
+ const SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ SdrPageView* pPV=pM->GetPageView();
+ const SdrGluePointList* pGPL=pObj->GetGluePointList();
+ if (pGPL!=nullptr) {
+ sal_uInt16 nNum=pGPL->HitTest(rPnt,*pOut,pObj);
+ if (nNum!=SDRGLUEPOINT_NOTFOUND)
+ {
+ // #i38892#
+ const SdrGluePoint& rCandidate = (*pGPL)[nNum];
+
+ if(rCandidate.IsUserDefined())
+ {
+ rpObj=pObj;
+ rnId=(*pGPL)[nNum].GetId();
+ rpPV=pPV;
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool SdrMarkView::MarkGluePoint(const SdrObject* pObj, sal_uInt16 nId, bool bUnmark)
+{
+ if (!IsGluePointEditMode()) return false;
+ ForceUndirtyMrkPnt();
+ bool bChgd=false;
+ if (pObj!=nullptr) {
+ const size_t nMarkPos=TryToFindMarkedObject(pObj);
+ if (nMarkPos!=SAL_MAX_SIZE) {
+ SdrMark* pM=GetSdrMarkByIndex(nMarkPos);
+ SdrUShortCont& rPts = pM->GetMarkedGluePoints();
+ bool bContains = rPts.find( nId ) != rPts.end();
+ if (!bUnmark && !bContains) {
+ bChgd=true;
+ rPts.insert(nId);
+ }
+ if (bUnmark && bContains) {
+ bChgd=true;
+ rPts.erase(nId);
+ }
+ } else {
+ // TODO: implement implicit selection of objects
+ }
+ }
+ if (bChgd) {
+ AdjustMarkHdl();
+ MarkListHasChanged();
+ }
+ return bChgd;
+}
+
+bool SdrMarkView::IsGluePointMarked(const SdrObject* pObj, sal_uInt16 nId) const
+{
+ ForceUndirtyMrkPnt();
+ bool bRet=false;
+ const size_t nPos=TryToFindMarkedObject(pObj); // casting to NonConst
+ if (nPos!=SAL_MAX_SIZE) {
+ const SdrMark* pM=GetSdrMarkByIndex(nPos);
+ const SdrUShortCont& rPts = pM->GetMarkedGluePoints();
+ bRet = rPts.find( nId ) != rPts.end();
+ }
+ return bRet;
+}
+
+SdrHdl* SdrMarkView::GetGluePointHdl(const SdrObject* pObj, sal_uInt16 nId) const
+{
+ ForceUndirtyMrkPnt();
+ const size_t nHdlCnt=maHdlList.GetHdlCount();
+ for (size_t nHdlNum=0; nHdlNum<nHdlCnt; ++nHdlNum) {
+ SdrHdl* pHdl=maHdlList.GetHdl(nHdlNum);
+ if (pHdl->GetObj()==pObj &&
+ pHdl->GetKind()==SdrHdlKind::Glue &&
+ pHdl->GetObjHdlNum()==nId ) return pHdl;
+ }
+ return nullptr;
+}
+
+void SdrMarkView::MarkNextGluePoint()
+{
+ ForceUndirtyMrkPnt();
+ SortMarkedObjects();
+}
+
+const tools::Rectangle& SdrMarkView::GetMarkedGluePointsRect() const
+{
+ ForceUndirtyMrkPnt();
+ if (mbMarkedPointsRectsDirty) ImpSetPointsRects();
+ return maMarkedGluePointsRect;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdoashp.cxx b/svx/source/svdraw/svdoashp.cxx
new file mode 100644
index 000000000..a21c59415
--- /dev/null
+++ b/svx/source/svdraw/svdoashp.cxx
@@ -0,0 +1,3245 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/BitmapShadowFilter.hxx>
+#include <svx/svdoashp.hxx>
+#include <svx/unoapi.hxx>
+#include <com/sun/star/loader/CannotActivateFactoryException.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/drawing/XCustomShapeEngine.hpp>
+#include <com/sun/star/drawing/PolyPolygonBezierCoords.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/awt/Rectangle.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <com/sun/star/uno/Sequence.h>
+#include <tools/helpers.hxx>
+#include <svx/svddrag.hxx>
+#include <svx/svddrgmt.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svditer.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdtrans.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <editeng/eeitem.hxx>
+#include <editeng/editstat.hxx>
+#include <editeng/adjustitem.hxx>
+#include <svx/svdoutl.hxx>
+#include <editeng/outlobj.hxx>
+#include <svx/sdtfchim.hxx>
+#include <svx/EnhancedCustomShapeGeometry.hxx>
+#include <svx/EnhancedCustomShapeTypeNames.hxx>
+#include <svx/EnhancedCustomShape2d.hxx>
+#include <com/sun/star/beans/PropertyValues.hpp>
+#include <com/sun/star/drawing/EnhancedCustomShapeAdjustmentValue.hpp>
+#include <com/sun/star/drawing/EnhancedCustomShapeParameterPair.hpp>
+#include <com/sun/star/drawing/EnhancedCustomShapeTextFrame.hpp>
+#include <com/sun/star/drawing/EnhancedCustomShapeSegment.hpp>
+#include <com/sun/star/drawing/EnhancedCustomShapeSegmentCommand.hpp>
+#include <editeng/writingmodeitem.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/xlnclit.hxx>
+#include <sdr/properties/customshapeproperties.hxx>
+#include <sdr/contact/viewcontactofsdrobjcustomshape.hxx>
+#include <svx/xlntrit.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xfltrit.hxx>
+#include <svx/xflclit.hxx>
+#include <svx/xflgrit.hxx>
+#include <svx/xflhtit.hxx>
+#include <svx/xbtmpit.hxx>
+#include <vcl/virdev.hxx>
+#include <svx/svdview.hxx>
+#include <svx/sdmetitm.hxx>
+#include <svx/sdprcitm.hxx>
+#include <svx/sdshitm.hxx>
+#include <svx/sdsxyitm.hxx>
+#include <svx/sdtmfitm.hxx>
+#include <svx/sdasitm.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <svdobjplusdata.hxx>
+#include <sal/log.hxx>
+#include <o3tl/string_view.hxx>
+#include "presetooxhandleadjustmentrelations.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::drawing;
+
+static void lcl_ShapeSegmentFromBinary( EnhancedCustomShapeSegment& rSegInfo, sal_uInt16 nSDat )
+{
+ switch( nSDat >> 8 )
+ {
+ case 0x00 :
+ rSegInfo.Command = EnhancedCustomShapeSegmentCommand::LINETO;
+ rSegInfo.Count = nSDat & 0xff;
+ if ( !rSegInfo.Count )
+ rSegInfo.Count = 1;
+ break;
+ case 0x20 :
+ rSegInfo.Command = EnhancedCustomShapeSegmentCommand::CURVETO;
+ rSegInfo.Count = nSDat & 0xff;
+ if ( !rSegInfo.Count )
+ rSegInfo.Count = 1;
+ break;
+ case 0x40 :
+ rSegInfo.Command = EnhancedCustomShapeSegmentCommand::MOVETO;
+ rSegInfo.Count = nSDat & 0xff;
+ if ( !rSegInfo.Count )
+ rSegInfo.Count = 1;
+ break;
+ case 0x60 :
+ rSegInfo.Command = EnhancedCustomShapeSegmentCommand::CLOSESUBPATH;
+ rSegInfo.Count = 0;
+ break;
+ case 0x80 :
+ rSegInfo.Command = EnhancedCustomShapeSegmentCommand::ENDSUBPATH;
+ rSegInfo.Count = 0;
+ break;
+ case 0xa1 :
+ rSegInfo.Command = EnhancedCustomShapeSegmentCommand::ANGLEELLIPSETO;
+ rSegInfo.Count = ( nSDat & 0xff ) / 3;
+ break;
+ case 0xa2 :
+ rSegInfo.Command = EnhancedCustomShapeSegmentCommand::ANGLEELLIPSE;
+ rSegInfo.Count = ( nSDat & 0xff ) / 3;
+ break;
+ case 0xa3 :
+ rSegInfo.Command = EnhancedCustomShapeSegmentCommand::ARCTO;
+ rSegInfo.Count = ( nSDat & 0xff ) >> 2;
+ break;
+ case 0xa4 :
+ rSegInfo.Command = EnhancedCustomShapeSegmentCommand::ARC;
+ rSegInfo.Count = ( nSDat & 0xff ) >> 2;
+ break;
+ case 0xa5 :
+ rSegInfo.Command = EnhancedCustomShapeSegmentCommand::CLOCKWISEARCTO;
+ rSegInfo.Count = ( nSDat & 0xff ) >> 2;
+ break;
+ case 0xa6 :
+ rSegInfo.Command = EnhancedCustomShapeSegmentCommand::CLOCKWISEARC;
+ rSegInfo.Count = ( nSDat & 0xff ) >> 2;
+ break;
+ case 0xa7 :
+ rSegInfo.Command = EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTX;
+ rSegInfo.Count = nSDat & 0xff;
+ break;
+ case 0xa8 :
+ rSegInfo.Command = EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTY;
+ rSegInfo.Count = nSDat & 0xff;
+ break;
+ case 0xaa :
+ rSegInfo.Command = EnhancedCustomShapeSegmentCommand::NOFILL;
+ rSegInfo.Count = 0;
+ break;
+ case 0xab :
+ rSegInfo.Command = EnhancedCustomShapeSegmentCommand::NOSTROKE;
+ rSegInfo.Count = 0;
+ break;
+ default:
+ case 0xf8 :
+ rSegInfo.Command = EnhancedCustomShapeSegmentCommand::UNKNOWN;
+ rSegInfo.Count = nSDat;
+ break;
+ }
+}
+
+static MSO_SPT ImpGetCustomShapeType( const SdrObjCustomShape& rCustoShape )
+{
+ MSO_SPT eRetValue = mso_sptNil;
+
+ OUString aEngine( rCustoShape.GetMergedItem( SDRATTR_CUSTOMSHAPE_ENGINE ).GetValue() );
+ if ( aEngine.isEmpty() || aEngine == "com.sun.star.drawing.EnhancedCustomShapeEngine" )
+ {
+ OUString sShapeType;
+ const SdrCustomShapeGeometryItem& rGeometryItem( rCustoShape.GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
+ const Any* pAny = rGeometryItem.GetPropertyValueByName( "Type" );
+ if ( pAny && ( *pAny >>= sShapeType ) )
+ eRetValue = EnhancedCustomShapeTypeNames::Get( sShapeType );
+ }
+ return eRetValue;
+};
+
+static bool ImpVerticalSwitch( const SdrObjCustomShape& rCustoShape )
+{
+ bool bRet = false;
+ MSO_SPT eShapeType( ImpGetCustomShapeType( rCustoShape ) );
+ switch( eShapeType )
+ {
+ case mso_sptAccentBorderCallout90 : // 2 ortho
+ case mso_sptBorderCallout1 : // 2 diag
+ case mso_sptBorderCallout2 : // 3
+ {
+ bRet = true;
+ }
+ break;
+ default: break;
+ }
+ return bRet;
+}
+
+// #i37011# create a clone with all attributes changed to shadow attributes
+// and translation executed, too.
+static SdrObject* ImpCreateShadowObjectClone(const SdrObject& rOriginal, const SfxItemSet& rOriginalSet)
+{
+ SdrObject* pRetval = nullptr;
+ const bool bShadow(rOriginalSet.Get(SDRATTR_SHADOW).GetValue());
+
+ if(bShadow)
+ {
+ // create a shadow representing object
+ const sal_Int32 nXDist(rOriginalSet.Get(SDRATTR_SHADOWXDIST).GetValue());
+ const sal_Int32 nYDist(rOriginalSet.Get(SDRATTR_SHADOWYDIST).GetValue());
+ const ::Color aShadowColor(rOriginalSet.Get(SDRATTR_SHADOWCOLOR).GetColorValue());
+ const sal_uInt16 nShadowTransparence(rOriginalSet.Get(SDRATTR_SHADOWTRANSPARENCE).GetValue());
+ pRetval = rOriginal.CloneSdrObject(rOriginal.getSdrModelFromSdrObject());
+ DBG_ASSERT(pRetval, "ImpCreateShadowObjectClone: Could not clone object (!)");
+
+ // look for used stuff
+ SdrObjListIter aIterator(rOriginal);
+ bool bLineUsed(false);
+ bool bAllFillUsed(false);
+ bool bSolidFillUsed(false);
+ bool bGradientFillUsed(false);
+ bool bHatchFillUsed(false);
+ bool bBitmapFillUsed(false);
+
+ while(aIterator.IsMore())
+ {
+ SdrObject* pObj = aIterator.Next();
+ drawing::FillStyle eFillStyle = pObj->GetMergedItem(XATTR_FILLSTYLE).GetValue();
+
+ if(!bLineUsed)
+ {
+ drawing::LineStyle eLineStyle = pObj->GetMergedItem(XATTR_LINESTYLE).GetValue();
+
+ if(drawing::LineStyle_NONE != eLineStyle)
+ {
+ bLineUsed = true;
+ }
+ }
+
+ if(!bAllFillUsed)
+ {
+ if(!bSolidFillUsed && drawing::FillStyle_SOLID == eFillStyle)
+ {
+ bSolidFillUsed = true;
+ bAllFillUsed = (bSolidFillUsed && bGradientFillUsed && bHatchFillUsed && bBitmapFillUsed);
+ }
+ if(!bGradientFillUsed && drawing::FillStyle_GRADIENT == eFillStyle)
+ {
+ bGradientFillUsed = true;
+ bAllFillUsed = (bSolidFillUsed && bGradientFillUsed && bHatchFillUsed && bBitmapFillUsed);
+ }
+ if(!bHatchFillUsed && drawing::FillStyle_HATCH == eFillStyle)
+ {
+ bHatchFillUsed = true;
+ bAllFillUsed = (bSolidFillUsed && bGradientFillUsed && bHatchFillUsed && bBitmapFillUsed);
+ }
+ if(!bBitmapFillUsed && drawing::FillStyle_BITMAP == eFillStyle)
+ {
+ bBitmapFillUsed = true;
+ bAllFillUsed = (bSolidFillUsed && bGradientFillUsed && bHatchFillUsed && bBitmapFillUsed);
+ }
+ }
+ }
+
+ // translate to shadow coordinates
+ pRetval->NbcMove(Size(nXDist, nYDist));
+
+ // set items as needed
+ SfxItemSet aTempSet(rOriginalSet);
+
+ // if a SvxWritingModeItem (Top->Bottom) is set the text object
+ // is creating a paraobject, but paraobjects can not be created without model. So
+ // we are preventing the crash by setting the writing mode always left to right,
+ // this is not bad since our shadow geometry does not contain text.
+ aTempSet.Put( SvxWritingModeItem( css::text::WritingMode_LR_TB, SDRATTR_TEXTDIRECTION ) );
+
+ // no shadow
+ aTempSet.Put(makeSdrShadowItem(false));
+ aTempSet.Put(makeSdrShadowXDistItem(0));
+ aTempSet.Put(makeSdrShadowYDistItem(0));
+
+ // line color and transparency like shadow
+ if(bLineUsed)
+ {
+ aTempSet.Put(XLineColorItem(OUString(), aShadowColor));
+ aTempSet.Put(XLineTransparenceItem(nShadowTransparence));
+ }
+
+ // fill color and transparency like shadow
+ if(bSolidFillUsed)
+ {
+ aTempSet.Put(XFillColorItem(OUString(), aShadowColor));
+ aTempSet.Put(XFillTransparenceItem(nShadowTransparence));
+ }
+
+ // gradient and transparency like shadow
+ if(bGradientFillUsed)
+ {
+ XGradient aGradient(rOriginalSet.Get(XATTR_FILLGRADIENT).GetGradientValue());
+ sal_uInt8 nStartLuminance(aGradient.GetStartColor().GetLuminance());
+ sal_uInt8 nEndLuminance(aGradient.GetEndColor().GetLuminance());
+
+ if(aGradient.GetStartIntens() != 100)
+ {
+ nStartLuminance = static_cast<sal_uInt8>(nStartLuminance * (static_cast<double>(aGradient.GetStartIntens()) / 100.0));
+ }
+
+ if(aGradient.GetEndIntens() != 100)
+ {
+ nEndLuminance = static_cast<sal_uInt8>(nEndLuminance * (static_cast<double>(aGradient.GetEndIntens()) / 100.0));
+ }
+
+ ::Color aStartColor(
+ static_cast<sal_uInt8>((nStartLuminance * aShadowColor.GetRed()) / 256),
+ static_cast<sal_uInt8>((nStartLuminance * aShadowColor.GetGreen()) / 256),
+ static_cast<sal_uInt8>((nStartLuminance * aShadowColor.GetBlue()) / 256));
+
+ ::Color aEndColor(
+ static_cast<sal_uInt8>((nEndLuminance * aShadowColor.GetRed()) / 256),
+ static_cast<sal_uInt8>((nEndLuminance * aShadowColor.GetGreen()) / 256),
+ static_cast<sal_uInt8>((nEndLuminance * aShadowColor.GetBlue()) / 256));
+
+ aGradient.SetStartColor(aStartColor);
+ aGradient.SetEndColor(aEndColor);
+ aTempSet.Put(XFillGradientItem(aGradient));
+ aTempSet.Put(XFillTransparenceItem(nShadowTransparence));
+ }
+
+ // hatch and transparency like shadow
+ if(bHatchFillUsed)
+ {
+ XHatch aHatch(rOriginalSet.Get(XATTR_FILLHATCH).GetHatchValue());
+ aHatch.SetColor(aShadowColor);
+ aTempSet.Put(XFillHatchItem(aHatch));
+ aTempSet.Put(XFillTransparenceItem(nShadowTransparence));
+ }
+
+ // bitmap and transparency like shadow
+ if(bBitmapFillUsed)
+ {
+ GraphicObject aGraphicObject(rOriginalSet.Get(XATTR_FILLBITMAP).GetGraphicObject());
+ BitmapEx aBitmapEx(aGraphicObject.GetGraphic().GetBitmapEx());
+
+ if(!aBitmapEx.IsEmpty())
+ {
+ ScopedVclPtr<VirtualDevice> pVirDev(VclPtr<VirtualDevice>::Create());
+ pVirDev->SetOutputSizePixel(aBitmapEx.GetSizePixel());
+ BitmapFilter::Filter(aBitmapEx, BitmapShadowFilter(aShadowColor));
+ pVirDev->DrawBitmapEx(Point(), aBitmapEx);
+ aGraphicObject.SetGraphic(Graphic(pVirDev->GetBitmapEx(Point(0,0), aBitmapEx.GetSizePixel())));
+ }
+
+ aTempSet.Put(XFillBitmapItem(aGraphicObject));
+ aTempSet.Put(XFillTransparenceItem(nShadowTransparence));
+ }
+
+ // set attributes and paint shadow object
+ pRetval->SetMergedItemSet( aTempSet );
+ }
+ return pRetval;
+}
+
+
+Reference< XCustomShapeEngine > const & SdrObjCustomShape::GetCustomShapeEngine() const
+{
+ if (mxCustomShapeEngine.is())
+ return mxCustomShapeEngine;
+
+ Reference< XShape > aXShape = GetXShapeForSdrObject(const_cast<SdrObjCustomShape*>(this));
+ if ( !aXShape )
+ return mxCustomShapeEngine;
+
+ Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
+
+ OUString aEngine(GetMergedItem( SDRATTR_CUSTOMSHAPE_ENGINE ).GetValue());
+ static constexpr OUStringLiteral sEnhancedCustomShapeEngine = u"com.sun.star.drawing.EnhancedCustomShapeEngine";
+ if ( aEngine.isEmpty() )
+ aEngine = sEnhancedCustomShapeEngine;
+
+ {
+ static constexpr OUStringLiteral sCustomShape = u"CustomShape";
+ Sequence< PropertyValue > aPropValues{ comphelper::makePropertyValue(sCustomShape,
+ aXShape) };
+ Sequence< Any > aArgument{ Any(aPropValues) };
+ try
+ {
+ Reference<XInterface> xInterface(xContext->getServiceManager()->createInstanceWithArgumentsAndContext(aEngine, aArgument, xContext));
+ if (xInterface.is())
+ mxCustomShapeEngine.set( xInterface, UNO_QUERY );
+ }
+ catch (const css::loader::CannotActivateFactoryException&)
+ {
+ }
+ }
+
+ return mxCustomShapeEngine;
+}
+
+const SdrObject* SdrObjCustomShape::GetSdrObjectFromCustomShape() const
+{
+ if ( !mXRenderedCustomShape.is() )
+ {
+ Reference< XCustomShapeEngine > xCustomShapeEngine( GetCustomShapeEngine() );
+ if ( xCustomShapeEngine.is() )
+ const_cast<SdrObjCustomShape*>(this)->mXRenderedCustomShape = xCustomShapeEngine->render();
+ }
+ SdrObject* pRenderedCustomShape = mXRenderedCustomShape.is()
+ ? SdrObject::getSdrObjectFromXShape( mXRenderedCustomShape )
+ : nullptr;
+ return pRenderedCustomShape;
+}
+
+// #i37011# Shadow geometry creation
+const SdrObject* SdrObjCustomShape::GetSdrObjectShadowFromCustomShape() const
+{
+ if(!mpLastShadowGeometry)
+ {
+ const SdrObject* pSdrObject = GetSdrObjectFromCustomShape();
+ if(pSdrObject)
+ {
+ const SfxItemSet& rOriginalSet = GetObjectItemSet();
+ const bool bShadow(rOriginalSet.Get( SDRATTR_SHADOW ).GetValue());
+
+ if(bShadow)
+ {
+ // create a clone with all attributes changed to shadow attributes
+ // and translation executed, too.
+ const_cast<SdrObjCustomShape*>(this)->mpLastShadowGeometry =
+ ImpCreateShadowObjectClone(*pSdrObject, rOriginalSet);
+ }
+ }
+ }
+
+ return mpLastShadowGeometry;
+}
+
+bool SdrObjCustomShape::IsTextPath() const
+{
+ static const OUStringLiteral sTextPath( u"TextPath" );
+ bool bTextPathOn = false;
+ const SdrCustomShapeGeometryItem& rGeometryItem = GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY );
+ const Any* pAny = rGeometryItem.GetPropertyValueByName( sTextPath, sTextPath );
+ if ( pAny )
+ *pAny >>= bTextPathOn;
+ return bTextPathOn;
+}
+
+bool SdrObjCustomShape::UseNoFillStyle() const
+{
+ bool bRet = false;
+ OUString sShapeType;
+ static const OUStringLiteral sType( u"Type" );
+ const SdrCustomShapeGeometryItem& rGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
+ const Any* pAny = rGeometryItem.GetPropertyValueByName( sType );
+ if ( pAny )
+ *pAny >>= sShapeType;
+ bRet = !IsCustomShapeFilledByDefault( EnhancedCustomShapeTypeNames::Get( sType ) );
+
+ return bRet;
+}
+
+bool SdrObjCustomShape::IsMirroredX() const
+{
+ bool bMirroredX = false;
+ const SdrCustomShapeGeometryItem & rGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
+ const css::uno::Any* pAny = rGeometryItem.GetPropertyValueByName( "MirroredX" );
+ if ( pAny )
+ *pAny >>= bMirroredX;
+ return bMirroredX;
+}
+bool SdrObjCustomShape::IsMirroredY() const
+{
+ bool bMirroredY = false;
+ const SdrCustomShapeGeometryItem & rGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
+ const css::uno::Any* pAny = rGeometryItem.GetPropertyValueByName( "MirroredY" );
+ if ( pAny )
+ *pAny >>= bMirroredY;
+ return bMirroredY;
+}
+void SdrObjCustomShape::SetMirroredX( const bool bMirrorX )
+{
+ SdrCustomShapeGeometryItem aGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
+ PropertyValue aPropVal;
+ aPropVal.Name = "MirroredX";
+ aPropVal.Value <<= bMirrorX;
+ aGeometryItem.SetPropertyValue( aPropVal );
+ SetMergedItem( aGeometryItem );
+}
+void SdrObjCustomShape::SetMirroredY( const bool bMirrorY )
+{
+ SdrCustomShapeGeometryItem aGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
+ PropertyValue aPropVal;
+ aPropVal.Name = "MirroredY";
+ aPropVal.Value <<= bMirrorY;
+ aGeometryItem.SetPropertyValue( aPropVal );
+ SetMergedItem( aGeometryItem );
+}
+
+double SdrObjCustomShape::GetExtraTextRotation( const bool bPreRotation ) const
+{
+ const css::uno::Any* pAny;
+ const SdrCustomShapeGeometryItem& rGeometryItem = GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY );
+ pAny = rGeometryItem.GetPropertyValueByName( bPreRotation ? OUString( "TextPreRotateAngle" ) : OUString( "TextRotateAngle" ) );
+ double fExtraTextRotateAngle = 0.0;
+ if ( pAny )
+ *pAny >>= fExtraTextRotateAngle;
+ return fExtraTextRotateAngle;
+}
+
+bool SdrObjCustomShape::GetTextBounds( tools::Rectangle& rTextBound ) const
+{
+ bool bRet = false;
+
+ Reference< XCustomShapeEngine > xCustomShapeEngine( GetCustomShapeEngine() );
+ if ( xCustomShapeEngine.is() )
+ {
+ awt::Rectangle aR( xCustomShapeEngine->getTextBounds() );
+ if ( aR.Width > 1 && aR.Height > 1 )
+ {
+ rTextBound = tools::Rectangle( Point( aR.X, aR.Y ), Size( aR.Width, aR.Height ) );
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+basegfx::B2DPolyPolygon SdrObjCustomShape::GetLineGeometry( const bool bBezierAllowed ) const
+{
+ basegfx::B2DPolyPolygon aRetval;
+ Reference< XCustomShapeEngine > xCustomShapeEngine( GetCustomShapeEngine() );
+ if ( xCustomShapeEngine.is() )
+ {
+ css::drawing::PolyPolygonBezierCoords aBezierCoords = xCustomShapeEngine->getLineGeometry();
+ try
+ {
+ aRetval = basegfx::utils::UnoPolyPolygonBezierCoordsToB2DPolyPolygon( aBezierCoords );
+ if ( !bBezierAllowed && aRetval.areControlPointsUsed())
+ {
+ aRetval = basegfx::utils::adaptiveSubdivideByAngle(aRetval);
+ }
+ }
+ catch ( const css::lang::IllegalArgumentException & )
+ {
+ }
+ }
+ return aRetval;
+}
+
+std::vector< SdrCustomShapeInteraction > SdrObjCustomShape::GetInteractionHandles() const
+{
+ std::vector< SdrCustomShapeInteraction > aRet;
+ try
+ {
+ Reference< XCustomShapeEngine > xCustomShapeEngine( GetCustomShapeEngine() );
+ if ( xCustomShapeEngine.is() )
+ {
+ int i;
+ Sequence< Reference< XCustomShapeHandle > > xInteractionHandles( xCustomShapeEngine->getInteraction() );
+ for ( i = 0; i < xInteractionHandles.getLength(); i++ )
+ {
+ if ( xInteractionHandles[ i ].is() )
+ {
+ SdrCustomShapeInteraction aSdrCustomShapeInteraction;
+ aSdrCustomShapeInteraction.xInteraction = xInteractionHandles[ i ];
+ aSdrCustomShapeInteraction.aPosition = xInteractionHandles[ i ]->getPosition();
+
+ CustomShapeHandleModes nMode = CustomShapeHandleModes::NONE;
+ switch( ImpGetCustomShapeType( *this ) )
+ {
+ case mso_sptAccentBorderCallout90 : // 2 ortho
+ {
+ if (i == 0)
+ nMode |= CustomShapeHandleModes::RESIZE_FIXED | CustomShapeHandleModes::CREATE_FIXED;
+ else if (i == 1)
+ nMode |= CustomShapeHandleModes::RESIZE_ABSOLUTE_X | CustomShapeHandleModes::RESIZE_ABSOLUTE_Y | CustomShapeHandleModes::MOVE_SHAPE | CustomShapeHandleModes::ORTHO4;
+ }
+ break;
+
+ case mso_sptChevron :
+ case mso_sptHomePlate :
+ nMode |= CustomShapeHandleModes::RESIZE_ABSOLUTE_NEGX;
+ break;
+
+ case mso_sptWedgeRectCallout :
+ case mso_sptWedgeRRectCallout :
+ case mso_sptCloudCallout :
+ case mso_sptWedgeEllipseCallout :
+ {
+ if (i == 0)
+ nMode |= CustomShapeHandleModes::RESIZE_FIXED;
+ }
+ break;
+
+ case mso_sptBorderCallout1 : // 2 diag
+ {
+ if (i == 0)
+ nMode |= CustomShapeHandleModes::RESIZE_FIXED | CustomShapeHandleModes::CREATE_FIXED;
+ else if (i == 1)
+ nMode |= CustomShapeHandleModes::RESIZE_ABSOLUTE_X | CustomShapeHandleModes::RESIZE_ABSOLUTE_Y | CustomShapeHandleModes::MOVE_SHAPE;
+ }
+ break;
+ case mso_sptBorderCallout2 : // 3
+ {
+ if (i == 0)
+ nMode |= CustomShapeHandleModes::RESIZE_FIXED | CustomShapeHandleModes::CREATE_FIXED;
+ else if (i == 2)
+ nMode |= CustomShapeHandleModes::RESIZE_ABSOLUTE_X | CustomShapeHandleModes::RESIZE_ABSOLUTE_Y | CustomShapeHandleModes::MOVE_SHAPE;
+ }
+ break;
+ case mso_sptCallout90 :
+ case mso_sptAccentCallout90 :
+ case mso_sptBorderCallout90 :
+ case mso_sptCallout1 :
+ case mso_sptCallout2 :
+ case mso_sptCallout3 :
+ case mso_sptAccentCallout1 :
+ case mso_sptAccentCallout2 :
+ case mso_sptAccentCallout3 :
+ case mso_sptBorderCallout3 :
+ case mso_sptAccentBorderCallout1 :
+ case mso_sptAccentBorderCallout2 :
+ case mso_sptAccentBorderCallout3 :
+ {
+ if (i == 0)
+ nMode |= CustomShapeHandleModes::RESIZE_FIXED | CustomShapeHandleModes::CREATE_FIXED;
+ }
+ break;
+ default: break;
+ }
+ aSdrCustomShapeInteraction.nMode = nMode;
+ aRet.push_back( aSdrCustomShapeInteraction );
+ }
+ }
+ }
+ }
+ catch( const uno::RuntimeException& )
+ {
+ }
+ return aRet;
+}
+
+
+// BaseProperties section
+#define DEFAULT_MINIMUM_SIGNED_COMPARE (sal_Int32(0x80000000))
+#define DEFAULT_MAXIMUM_SIGNED_COMPARE (sal_Int32(0x7fffffff))
+
+static sal_Int32 GetNumberOfProperties ( const SvxMSDffHandle* pData )
+{
+ sal_Int32 nPropertiesNeeded=1; // position is always needed
+ SvxMSDffHandleFlags nFlags = pData->nFlags;
+
+ if ( nFlags & SvxMSDffHandleFlags::MIRRORED_X )
+ nPropertiesNeeded++;
+ if ( nFlags & SvxMSDffHandleFlags::MIRRORED_Y )
+ nPropertiesNeeded++;
+ if ( nFlags & SvxMSDffHandleFlags::SWITCHED )
+ nPropertiesNeeded++;
+ if ( nFlags & SvxMSDffHandleFlags::POLAR )
+ {
+ nPropertiesNeeded++;
+ if ( nFlags & SvxMSDffHandleFlags::RADIUS_RANGE )
+ {
+ if ( pData->nRangeXMin != DEFAULT_MINIMUM_SIGNED_COMPARE )
+ nPropertiesNeeded++;
+ if ( pData->nRangeXMax != DEFAULT_MAXIMUM_SIGNED_COMPARE )
+ nPropertiesNeeded++;
+ }
+ }
+ else if ( nFlags & SvxMSDffHandleFlags::RANGE )
+ {
+ if ( pData->nRangeXMin != DEFAULT_MINIMUM_SIGNED_COMPARE )
+ nPropertiesNeeded++;
+ if ( pData->nRangeXMax != DEFAULT_MAXIMUM_SIGNED_COMPARE )
+ nPropertiesNeeded++;
+ if ( pData->nRangeYMin != DEFAULT_MINIMUM_SIGNED_COMPARE )
+ nPropertiesNeeded++;
+ if ( pData->nRangeYMax != DEFAULT_MAXIMUM_SIGNED_COMPARE )
+ nPropertiesNeeded++;
+ }
+
+ return nPropertiesNeeded;
+}
+
+static void lcl_ShapePropertiesFromDFF( const SvxMSDffHandle* pData, css::beans::PropertyValues& rPropValues )
+{
+ SvxMSDffHandleFlags nFlags = pData->nFlags;
+ sal_Int32 n=0;
+ auto pPropValues = rPropValues.getArray();
+
+ // POSITION
+ {
+ css::drawing::EnhancedCustomShapeParameterPair aPosition;
+ EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aPosition.First, pData->nPositionX, true, true );
+ EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aPosition.Second, pData->nPositionY, true, false );
+ pPropValues[ n ].Name = "Position";
+ pPropValues[ n++ ].Value <<= aPosition;
+ }
+ if ( nFlags & SvxMSDffHandleFlags::MIRRORED_X )
+ {
+ pPropValues[ n ].Name = "MirroredX";
+ pPropValues[ n++ ].Value <<= true;
+ }
+ if ( nFlags & SvxMSDffHandleFlags::MIRRORED_Y )
+ {
+ pPropValues[ n ].Name = "MirroredY";
+ pPropValues[ n++ ].Value <<= true;
+ }
+ if ( nFlags & SvxMSDffHandleFlags::SWITCHED )
+ {
+ pPropValues[ n ].Name = "Switched";
+ pPropValues[ n++ ].Value <<= true;
+ }
+ if ( nFlags & SvxMSDffHandleFlags::POLAR )
+ {
+ css::drawing::EnhancedCustomShapeParameterPair aCenter;
+ EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aCenter.First, pData->nCenterX,
+ bool( nFlags & SvxMSDffHandleFlags::CENTER_X_IS_SPECIAL ), true );
+ EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aCenter.Second, pData->nCenterY,
+ bool( nFlags & SvxMSDffHandleFlags::CENTER_Y_IS_SPECIAL ), false );
+ pPropValues[ n ].Name = "Polar";
+ pPropValues[ n++ ].Value <<= aCenter;
+ if ( nFlags & SvxMSDffHandleFlags::RADIUS_RANGE )
+ {
+ if ( pData->nRangeXMin != DEFAULT_MINIMUM_SIGNED_COMPARE )
+ {
+ css::drawing::EnhancedCustomShapeParameter aRadiusRangeMinimum;
+ EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRadiusRangeMinimum, pData->nRangeXMin,
+ bool( nFlags & SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL ), true );
+ pPropValues[ n ].Name = "RadiusRangeMinimum";
+ pPropValues[ n++ ].Value <<= aRadiusRangeMinimum;
+ }
+ if ( pData->nRangeXMax != DEFAULT_MAXIMUM_SIGNED_COMPARE )
+ {
+ css::drawing::EnhancedCustomShapeParameter aRadiusRangeMaximum;
+ EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRadiusRangeMaximum, pData->nRangeXMax,
+ bool( nFlags & SvxMSDffHandleFlags::RANGE_X_MAX_IS_SPECIAL ), false );
+ pPropValues[ n ].Name = "RadiusRangeMaximum";
+ pPropValues[ n++ ].Value <<= aRadiusRangeMaximum;
+ }
+ }
+ }
+ else if ( nFlags & SvxMSDffHandleFlags::RANGE )
+ {
+ if ( pData->nRangeXMin != DEFAULT_MINIMUM_SIGNED_COMPARE )
+ {
+ css::drawing::EnhancedCustomShapeParameter aRangeXMinimum;
+ EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRangeXMinimum, pData->nRangeXMin,
+ bool( nFlags & SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL ), true );
+ pPropValues[ n ].Name = "RangeXMinimum";
+ pPropValues[ n++ ].Value <<= aRangeXMinimum;
+ }
+ if ( pData->nRangeXMax != DEFAULT_MAXIMUM_SIGNED_COMPARE )
+ {
+ css::drawing::EnhancedCustomShapeParameter aRangeXMaximum;
+ EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRangeXMaximum, pData->nRangeXMax,
+ bool( nFlags & SvxMSDffHandleFlags::RANGE_X_MAX_IS_SPECIAL ), false );
+ pPropValues[ n ].Name = "RangeXMaximum";
+ pPropValues[ n++ ].Value <<= aRangeXMaximum;
+ }
+ if ( pData->nRangeYMin != DEFAULT_MINIMUM_SIGNED_COMPARE )
+ {
+ css::drawing::EnhancedCustomShapeParameter aRangeYMinimum;
+ EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRangeYMinimum, pData->nRangeYMin,
+ bool( nFlags & SvxMSDffHandleFlags::RANGE_Y_MIN_IS_SPECIAL ), true );
+ pPropValues[ n ].Name = "RangeYMinimum";
+ pPropValues[ n++ ].Value <<= aRangeYMinimum;
+ }
+ if ( pData->nRangeYMax != DEFAULT_MAXIMUM_SIGNED_COMPARE )
+ {
+ css::drawing::EnhancedCustomShapeParameter aRangeYMaximum;
+ EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRangeYMaximum, pData->nRangeYMax,
+ bool( nFlags & SvxMSDffHandleFlags::RANGE_Y_MAX_IS_SPECIAL ), false );
+ pPropValues[ n ].Name = "RangeYMaximum";
+ pPropValues[ n++ ].Value <<= aRangeYMaximum;
+ }
+ }
+}
+
+std::unique_ptr<sdr::properties::BaseProperties> SdrObjCustomShape::CreateObjectSpecificProperties()
+{
+ return std::make_unique<sdr::properties::CustomShapeProperties>(*this);
+}
+
+SdrObjCustomShape::SdrObjCustomShape(SdrModel& rSdrModel)
+: SdrTextObj(rSdrModel)
+ , fObjectRotation(0.0)
+ , mbAdjustingTextFrameWidthAndHeight(false)
+ , mpLastShadowGeometry(nullptr)
+{
+ m_bClosedObj = true; // custom shapes may be filled
+ mbTextFrame = true;
+}
+
+SdrObjCustomShape::SdrObjCustomShape(SdrModel& rSdrModel, SdrObjCustomShape const & rSource)
+: SdrTextObj(rSdrModel, rSource)
+ , fObjectRotation(0.0)
+ , mbAdjustingTextFrameWidthAndHeight(false)
+ , mpLastShadowGeometry(nullptr)
+{
+ m_bClosedObj = true; // custom shapes may be filled
+ mbTextFrame = true;
+
+ fObjectRotation = rSource.fObjectRotation;
+ mbAdjustingTextFrameWidthAndHeight = rSource.mbAdjustingTextFrameWidthAndHeight;
+ assert(!mbAdjustingTextFrameWidthAndHeight);
+ InvalidateRenderGeometry();
+}
+
+SdrObjCustomShape::~SdrObjCustomShape()
+{
+ // delete buffered display geometry
+ InvalidateRenderGeometry();
+}
+
+void SdrObjCustomShape::MergeDefaultAttributes( const OUString* pType )
+{
+ PropertyValue aPropVal;
+ OUString sShapeType;
+ static const OUStringLiteral sType( u"Type" );
+ SdrCustomShapeGeometryItem aGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
+ if ( pType && !pType->isEmpty() )
+ {
+ sal_Int32 nType = pType->toInt32();
+ if ( nType )
+ sShapeType = EnhancedCustomShapeTypeNames::Get( static_cast< MSO_SPT >( nType ) );
+ else
+ sShapeType = *pType;
+
+ aPropVal.Name = sType;
+ aPropVal.Value <<= sShapeType;
+ aGeometryItem.SetPropertyValue( aPropVal );
+ }
+ else
+ {
+ Any *pAny = aGeometryItem.GetPropertyValueByName( sType );
+ if ( pAny )
+ *pAny >>= sShapeType;
+ }
+ MSO_SPT eSpType = EnhancedCustomShapeTypeNames::Get( sShapeType );
+
+ const sal_Int32* pDefData = nullptr;
+ const mso_CustomShape* pDefCustomShape = GetCustomShapeContent( eSpType );
+ if ( pDefCustomShape )
+ pDefData = pDefCustomShape->pDefData;
+
+ css::uno::Sequence< css::drawing::EnhancedCustomShapeAdjustmentValue > seqAdjustmentValues;
+
+
+ // AdjustmentValues
+
+ static const OUStringLiteral sAdjustmentValues( u"AdjustmentValues" );
+ const Any* pAny = aGeometryItem.GetPropertyValueByName( sAdjustmentValues );
+ if ( pAny )
+ *pAny >>= seqAdjustmentValues;
+ if ( pDefCustomShape && pDefData ) // now check if we have to default some adjustment values
+ {
+ // first check if there are adjustment values are to be appended
+ sal_Int32 i, nAdjustmentValues = seqAdjustmentValues.getLength();
+ sal_Int32 nAdjustmentDefaults = *pDefData++;
+ if ( nAdjustmentDefaults > nAdjustmentValues )
+ seqAdjustmentValues.realloc( nAdjustmentDefaults );
+ auto pseqAdjustmentValues = seqAdjustmentValues.getArray();
+ for ( i = nAdjustmentValues; i < nAdjustmentDefaults; i++ )
+ {
+ pseqAdjustmentValues[ i ].Value <<= pDefData[ i ];
+ pseqAdjustmentValues[ i ].State = css::beans::PropertyState_DIRECT_VALUE;
+ }
+ // check if there are defaulted adjustment values that should be filled the hard coded defaults (pDefValue)
+ sal_Int32 nCount = std::min(nAdjustmentValues, nAdjustmentDefaults);
+ for ( i = 0; i < nCount; i++ )
+ {
+ if ( seqAdjustmentValues[ i ].State != css::beans::PropertyState_DIRECT_VALUE )
+ {
+ pseqAdjustmentValues[ i ].Value <<= pDefData[ i ];
+ pseqAdjustmentValues[ i ].State = css::beans::PropertyState_DIRECT_VALUE;
+ }
+ }
+ }
+ aPropVal.Name = sAdjustmentValues;
+ aPropVal.Value <<= seqAdjustmentValues;
+ aGeometryItem.SetPropertyValue( aPropVal );
+
+
+ // Coordsize
+
+ static const OUStringLiteral sViewBox( u"ViewBox" );
+ const Any* pViewBox = aGeometryItem.GetPropertyValueByName( sViewBox );
+ css::awt::Rectangle aViewBox;
+ if ( !pViewBox || !(*pViewBox >>= aViewBox ) )
+ {
+ if ( pDefCustomShape )
+ {
+ aViewBox.X = 0;
+ aViewBox.Y = 0;
+ aViewBox.Width = pDefCustomShape->nCoordWidth;
+ aViewBox.Height= pDefCustomShape->nCoordHeight;
+ aPropVal.Name = sViewBox;
+ aPropVal.Value <<= aViewBox;
+ aGeometryItem.SetPropertyValue( aPropVal );
+ }
+ }
+
+ static const OUStringLiteral sPath( u"Path" );
+
+
+ // Path/Coordinates
+
+ static const OUStringLiteral sCoordinates( u"Coordinates" );
+ pAny = aGeometryItem.GetPropertyValueByName( sPath, sCoordinates );
+ if ( !pAny && pDefCustomShape && pDefCustomShape->nVertices && pDefCustomShape->pVertices )
+ {
+ sal_Int32 i, nCount = pDefCustomShape->nVertices;
+ css::uno::Sequence< css::drawing::EnhancedCustomShapeParameterPair> seqCoordinates( nCount );
+ auto pseqCoordinates = seqCoordinates.getArray();
+ for ( i = 0; i < nCount; i++ )
+ {
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqCoordinates[ i ].First, pDefCustomShape->pVertices[ i ].nValA );
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqCoordinates[ i ].Second, pDefCustomShape->pVertices[ i ].nValB );
+ }
+ aPropVal.Name = sCoordinates;
+ aPropVal.Value <<= seqCoordinates;
+ aGeometryItem.SetPropertyValue( sPath, aPropVal );
+ }
+
+ // Path/GluePoints
+ static const OUStringLiteral sGluePoints( u"GluePoints" );
+ pAny = aGeometryItem.GetPropertyValueByName( sPath, sGluePoints );
+ if ( !pAny && pDefCustomShape && pDefCustomShape->nGluePoints && pDefCustomShape->pGluePoints )
+ {
+ sal_Int32 i, nCount = pDefCustomShape->nGluePoints;
+ css::uno::Sequence< css::drawing::EnhancedCustomShapeParameterPair> seqGluePoints( nCount );
+ auto pseqGluePoints = seqGluePoints.getArray();
+ for ( i = 0; i < nCount; i++ )
+ {
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqGluePoints[ i ].First, pDefCustomShape->pGluePoints[ i ].nValA );
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqGluePoints[ i ].Second, pDefCustomShape->pGluePoints[ i ].nValB );
+ }
+ aPropVal.Name = sGluePoints;
+ aPropVal.Value <<= seqGluePoints;
+ aGeometryItem.SetPropertyValue( sPath, aPropVal );
+ }
+
+ // Path/Segments
+ static const OUStringLiteral sSegments( u"Segments" );
+ pAny = aGeometryItem.GetPropertyValueByName( sPath, sSegments );
+ if ( !pAny && pDefCustomShape && pDefCustomShape->nElements && pDefCustomShape->pElements )
+ {
+ sal_Int32 i, nCount = pDefCustomShape->nElements;
+ css::uno::Sequence< css::drawing::EnhancedCustomShapeSegment > seqSegments( nCount );
+ auto pseqSegments = seqSegments.getArray();
+ for ( i = 0; i < nCount; i++ )
+ {
+ EnhancedCustomShapeSegment& rSegInfo = pseqSegments[ i ];
+ sal_uInt16 nSDat = pDefCustomShape->pElements[ i ];
+ lcl_ShapeSegmentFromBinary( rSegInfo, nSDat );
+ }
+ aPropVal.Name = sSegments;
+ aPropVal.Value <<= seqSegments;
+ aGeometryItem.SetPropertyValue( sPath, aPropVal );
+ }
+
+ // Path/StretchX
+ static const OUStringLiteral sStretchX( u"StretchX" );
+ pAny = aGeometryItem.GetPropertyValueByName( sPath, sStretchX );
+ if ( !pAny && pDefCustomShape )
+ {
+ sal_Int32 nXRef = pDefCustomShape->nXRef;
+ if ( nXRef != DEFAULT_MINIMUM_SIGNED_COMPARE )
+ {
+ aPropVal.Name = sStretchX;
+ aPropVal.Value <<= nXRef;
+ aGeometryItem.SetPropertyValue( sPath, aPropVal );
+ }
+ }
+
+ // Path/StretchY
+ static const OUStringLiteral sStretchY( u"StretchY" );
+ pAny = aGeometryItem.GetPropertyValueByName( sPath, sStretchY );
+ if ( !pAny && pDefCustomShape )
+ {
+ sal_Int32 nYRef = pDefCustomShape->nYRef;
+ if ( nYRef != DEFAULT_MINIMUM_SIGNED_COMPARE )
+ {
+ aPropVal.Name = sStretchY;
+ aPropVal.Value <<= nYRef;
+ aGeometryItem.SetPropertyValue( sPath, aPropVal );
+ }
+ }
+
+ // Path/TextFrames
+ static const OUStringLiteral sTextFrames( u"TextFrames" );
+ pAny = aGeometryItem.GetPropertyValueByName( sPath, sTextFrames );
+ if ( !pAny && pDefCustomShape && pDefCustomShape->nTextRect && pDefCustomShape->pTextRect )
+ {
+ sal_Int32 i, nCount = pDefCustomShape->nTextRect;
+ css::uno::Sequence< css::drawing::EnhancedCustomShapeTextFrame > seqTextFrames( nCount );
+ auto pseqTextFrames = seqTextFrames.getArray();
+ const SvxMSDffTextRectangles* pRectangles = pDefCustomShape->pTextRect;
+ for ( i = 0; i < nCount; i++, pRectangles++ )
+ {
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqTextFrames[ i ].TopLeft.First, pRectangles->nPairA.nValA );
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqTextFrames[ i ].TopLeft.Second, pRectangles->nPairA.nValB );
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqTextFrames[ i ].BottomRight.First, pRectangles->nPairB.nValA );
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqTextFrames[ i ].BottomRight.Second, pRectangles->nPairB.nValB );
+ }
+ aPropVal.Name = sTextFrames;
+ aPropVal.Value <<= seqTextFrames;
+ aGeometryItem.SetPropertyValue( sPath, aPropVal );
+ }
+
+ // Equations
+ static const OUStringLiteral sEquations( u"Equations" );
+ pAny = aGeometryItem.GetPropertyValueByName( sEquations );
+ if ( !pAny && pDefCustomShape && pDefCustomShape->nCalculation && pDefCustomShape->pCalculation )
+ {
+ sal_Int32 i, nCount = pDefCustomShape->nCalculation;
+ css::uno::Sequence< OUString > seqEquations( nCount );
+ auto pseqEquations = seqEquations.getArray();
+ const SvxMSDffCalculationData* pData = pDefCustomShape->pCalculation;
+ for ( i = 0; i < nCount; i++, pData++ )
+ pseqEquations[ i ] = EnhancedCustomShape2d::GetEquation( pData->nFlags, pData->nVal[ 0 ], pData->nVal[ 1 ], pData->nVal[ 2 ] );
+ aPropVal.Name = sEquations;
+ aPropVal.Value <<= seqEquations;
+ aGeometryItem.SetPropertyValue( aPropVal );
+ }
+
+ // Handles
+ static const OUStringLiteral sHandles( u"Handles" );
+ pAny = aGeometryItem.GetPropertyValueByName( sHandles );
+ if ( !pAny && pDefCustomShape && pDefCustomShape->nHandles && pDefCustomShape->pHandles )
+ {
+ sal_Int32 i, nCount = pDefCustomShape->nHandles;
+ const SvxMSDffHandle* pData = pDefCustomShape->pHandles;
+ css::uno::Sequence< css::beans::PropertyValues > seqHandles( nCount );
+ auto pseqHandles = seqHandles.getArray();
+ for ( i = 0; i < nCount; i++, pData++ )
+ {
+ sal_Int32 nPropertiesNeeded;
+ css::beans::PropertyValues& rPropValues = pseqHandles[ i ];
+ nPropertiesNeeded = GetNumberOfProperties( pData );
+ rPropValues.realloc( nPropertiesNeeded );
+ lcl_ShapePropertiesFromDFF( pData, rPropValues );
+ }
+ aPropVal.Name = sHandles;
+ aPropVal.Value <<= seqHandles;
+ aGeometryItem.SetPropertyValue( aPropVal );
+ }
+ else if (pAny && sShapeType.startsWith("ooxml-") && sShapeType != "ooxml-non-primitive")
+ {
+ // ODF is not able to store the ooxml way of connecting handle to an adjustment
+ // value by name, e.g. attribute RefX="adj". So the information is lost, when exporting
+ // a pptx to odp, for example. This part reconstructs this information for the
+ // ooxml preset shapes from their definition.
+ css::uno::Sequence<css::beans::PropertyValues> seqHandles;
+ *pAny >>= seqHandles;
+ auto seqHandlesRange = asNonConstRange(seqHandles);
+ bool bChanged(false);
+ for (sal_Int32 i = 0; i < seqHandles.getLength(); i++)
+ {
+ comphelper::SequenceAsHashMap aHandleProps(seqHandles[i]);
+ OUString sFirstRefType;
+ sal_Int32 nFirstAdjRef;
+ OUString sSecondRefType;
+ sal_Int32 nSecondAdjRef;
+ PresetOOXHandleAdj::GetOOXHandleAdjRelation(sShapeType, i, sFirstRefType, nFirstAdjRef,
+ sSecondRefType, nSecondAdjRef);
+ if (sFirstRefType != "na" && 0 <= nFirstAdjRef
+ && nFirstAdjRef < seqAdjustmentValues.getLength())
+ {
+ bChanged |= aHandleProps.createItemIfMissing(sFirstRefType, nFirstAdjRef);
+ }
+ if (sSecondRefType != "na" && 0 <= nSecondAdjRef
+ && nSecondAdjRef < seqAdjustmentValues.getLength())
+ {
+ bChanged |= aHandleProps.createItemIfMissing(sSecondRefType, nSecondAdjRef);
+ }
+ aHandleProps >> seqHandlesRange[i];
+ }
+ if (bChanged)
+ {
+ aPropVal.Name = sHandles;
+ aPropVal.Value <<= seqHandles;
+ aGeometryItem.SetPropertyValue(aPropVal);
+ }
+ }
+
+ SetMergedItem( aGeometryItem );
+}
+
+bool SdrObjCustomShape::IsDefaultGeometry( const DefaultType eDefaultType ) const
+{
+ bool bIsDefaultGeometry = false;
+
+ OUString sShapeType;
+ const SdrCustomShapeGeometryItem & rGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
+
+ const Any *pAny = rGeometryItem.GetPropertyValueByName( "Type" );
+ if ( pAny )
+ *pAny >>= sShapeType;
+
+ MSO_SPT eSpType = EnhancedCustomShapeTypeNames::Get( sShapeType );
+
+ const mso_CustomShape* pDefCustomShape = GetCustomShapeContent( eSpType );
+ static const OUStringLiteral sPath( u"Path" );
+ switch( eDefaultType )
+ {
+ case DefaultType::Viewbox :
+ {
+ const Any* pViewBox = rGeometryItem.GetPropertyValueByName( "ViewBox" );
+ css::awt::Rectangle aViewBox;
+ if (pViewBox && (*pViewBox >>= aViewBox) && pDefCustomShape)
+ {
+ if ( ( aViewBox.Width == pDefCustomShape->nCoordWidth )
+ && ( aViewBox.Height == pDefCustomShape->nCoordHeight ) )
+ bIsDefaultGeometry = true;
+ }
+ }
+ break;
+
+ case DefaultType::Path :
+ {
+ pAny = rGeometryItem.GetPropertyValueByName( sPath, "Coordinates" );
+ if ( pAny && pDefCustomShape && pDefCustomShape->nVertices && pDefCustomShape->pVertices )
+ {
+ css::uno::Sequence< css::drawing::EnhancedCustomShapeParameterPair> seqCoordinates1;
+ if ( *pAny >>= seqCoordinates1 )
+ {
+ sal_Int32 i, nCount = pDefCustomShape->nVertices;
+ css::uno::Sequence< css::drawing::EnhancedCustomShapeParameterPair> seqCoordinates2( nCount );
+ auto pseqCoordinates2 = seqCoordinates2.getArray();
+ for ( i = 0; i < nCount; i++ )
+ {
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqCoordinates2[ i ].First, pDefCustomShape->pVertices[ i ].nValA );
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqCoordinates2[ i ].Second, pDefCustomShape->pVertices[ i ].nValB );
+ }
+ if ( seqCoordinates1 == seqCoordinates2 )
+ bIsDefaultGeometry = true;
+ }
+ }
+ else if ( pDefCustomShape && ( ( pDefCustomShape->nVertices == 0 ) || ( pDefCustomShape->pVertices == nullptr ) ) )
+ bIsDefaultGeometry = true;
+ }
+ break;
+
+ case DefaultType::Gluepoints :
+ {
+ pAny = rGeometryItem.GetPropertyValueByName( sPath, "GluePoints" );
+ if ( pAny && pDefCustomShape && pDefCustomShape->nGluePoints && pDefCustomShape->pGluePoints )
+ {
+ css::uno::Sequence< css::drawing::EnhancedCustomShapeParameterPair> seqGluePoints1;
+ if ( *pAny >>= seqGluePoints1 )
+ {
+ sal_Int32 i, nCount = pDefCustomShape->nGluePoints;
+ css::uno::Sequence< css::drawing::EnhancedCustomShapeParameterPair> seqGluePoints2( nCount );
+ auto pseqGluePoints2 = seqGluePoints2.getArray();
+ for ( i = 0; i < nCount; i++ )
+ {
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqGluePoints2[ i ].First, pDefCustomShape->pGluePoints[ i ].nValA );
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqGluePoints2[ i ].Second, pDefCustomShape->pGluePoints[ i ].nValB );
+ }
+ if ( seqGluePoints1 == seqGluePoints2 )
+ bIsDefaultGeometry = true;
+ }
+ }
+ else if ( pDefCustomShape && ( pDefCustomShape->nGluePoints == 0 ) )
+ bIsDefaultGeometry = true;
+ }
+ break;
+
+ case DefaultType::Segments :
+ {
+ // Path/Segments
+ pAny = rGeometryItem.GetPropertyValueByName( sPath, "Segments" );
+ if ( pAny )
+ {
+ css::uno::Sequence< css::drawing::EnhancedCustomShapeSegment > seqSegments1;
+ if ( *pAny >>= seqSegments1 )
+ {
+ if ( pDefCustomShape && pDefCustomShape->nElements && pDefCustomShape->pElements )
+ {
+ sal_Int32 i, nCount = pDefCustomShape->nElements;
+ if ( nCount )
+ {
+ css::uno::Sequence< css::drawing::EnhancedCustomShapeSegment > seqSegments2( nCount );
+ auto pseqSegments2 = seqSegments2.getArray();
+ for ( i = 0; i < nCount; i++ )
+ {
+ EnhancedCustomShapeSegment& rSegInfo = pseqSegments2[ i ];
+ sal_uInt16 nSDat = pDefCustomShape->pElements[ i ];
+ lcl_ShapeSegmentFromBinary( rSegInfo, nSDat );
+ }
+ if ( seqSegments1 == seqSegments2 )
+ bIsDefaultGeometry = true;
+ }
+ }
+ else
+ {
+ // check if it's the default segment description ( M L Z N )
+ if ( seqSegments1.getLength() == 4 )
+ {
+ if ( ( seqSegments1[ 0 ].Command == EnhancedCustomShapeSegmentCommand::MOVETO )
+ && ( seqSegments1[ 1 ].Command == EnhancedCustomShapeSegmentCommand::LINETO )
+ && ( seqSegments1[ 2 ].Command == EnhancedCustomShapeSegmentCommand::CLOSESUBPATH )
+ && ( seqSegments1[ 3 ].Command == EnhancedCustomShapeSegmentCommand::ENDSUBPATH ) )
+ bIsDefaultGeometry = true;
+ }
+ }
+ }
+ }
+ else if ( pDefCustomShape && ( ( pDefCustomShape->nElements == 0 ) || ( pDefCustomShape->pElements == nullptr ) ) )
+ bIsDefaultGeometry = true;
+ }
+ break;
+
+ case DefaultType::StretchX :
+ {
+ pAny = rGeometryItem.GetPropertyValueByName( sPath, "StretchX" );
+ if ( pAny && pDefCustomShape )
+ {
+ sal_Int32 nStretchX = 0;
+ if ( *pAny >>= nStretchX )
+ {
+ if ( pDefCustomShape->nXRef == nStretchX )
+ bIsDefaultGeometry = true;
+ }
+ }
+ else if ( pDefCustomShape && ( pDefCustomShape->nXRef == DEFAULT_MINIMUM_SIGNED_COMPARE ) )
+ bIsDefaultGeometry = true;
+ }
+ break;
+
+ case DefaultType::StretchY :
+ {
+ pAny = rGeometryItem.GetPropertyValueByName( sPath, "StretchY" );
+ if ( pAny && pDefCustomShape )
+ {
+ sal_Int32 nStretchY = 0;
+ if ( *pAny >>= nStretchY )
+ {
+ if ( pDefCustomShape->nYRef == nStretchY )
+ bIsDefaultGeometry = true;
+ }
+ }
+ else if ( pDefCustomShape && ( pDefCustomShape->nYRef == DEFAULT_MINIMUM_SIGNED_COMPARE ) )
+ bIsDefaultGeometry = true;
+ }
+ break;
+
+ case DefaultType::Equations :
+ {
+ pAny = rGeometryItem.GetPropertyValueByName( "Equations" );
+ if ( pAny && pDefCustomShape && pDefCustomShape->nCalculation && pDefCustomShape->pCalculation )
+ {
+ css::uno::Sequence< OUString > seqEquations1;
+ if ( *pAny >>= seqEquations1 )
+ {
+ sal_Int32 i, nCount = pDefCustomShape->nCalculation;
+ css::uno::Sequence< OUString > seqEquations2( nCount );
+ auto pseqEquations2 = seqEquations2.getArray();
+
+ const SvxMSDffCalculationData* pData = pDefCustomShape->pCalculation;
+ for ( i = 0; i < nCount; i++, pData++ )
+ pseqEquations2[ i ] = EnhancedCustomShape2d::GetEquation( pData->nFlags, pData->nVal[ 0 ], pData->nVal[ 1 ], pData->nVal[ 2 ] );
+
+ if ( seqEquations1 == seqEquations2 )
+ bIsDefaultGeometry = true;
+ }
+ }
+ else if ( pDefCustomShape && ( ( pDefCustomShape->nCalculation == 0 ) || ( pDefCustomShape->pCalculation == nullptr ) ) )
+ bIsDefaultGeometry = true;
+ }
+ break;
+
+ case DefaultType::TextFrames :
+ {
+ pAny = rGeometryItem.GetPropertyValueByName( sPath, "TextFrames" );
+ if ( pAny && pDefCustomShape && pDefCustomShape->nTextRect && pDefCustomShape->pTextRect )
+ {
+ css::uno::Sequence< css::drawing::EnhancedCustomShapeTextFrame > seqTextFrames1;
+ if ( *pAny >>= seqTextFrames1 )
+ {
+ sal_Int32 i, nCount = pDefCustomShape->nTextRect;
+ css::uno::Sequence< css::drawing::EnhancedCustomShapeTextFrame > seqTextFrames2( nCount );
+ auto pseqTextFrames2 = seqTextFrames2.getArray();
+ const SvxMSDffTextRectangles* pRectangles = pDefCustomShape->pTextRect;
+ for ( i = 0; i < nCount; i++, pRectangles++ )
+ {
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqTextFrames2[ i ].TopLeft.First, pRectangles->nPairA.nValA );
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqTextFrames2[ i ].TopLeft.Second, pRectangles->nPairA.nValB );
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqTextFrames2[ i ].BottomRight.First, pRectangles->nPairB.nValA );
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqTextFrames2[ i ].BottomRight.Second, pRectangles->nPairB.nValB );
+ }
+ if ( seqTextFrames1 == seqTextFrames2 )
+ bIsDefaultGeometry = true;
+ }
+ }
+ else if ( pDefCustomShape && ( ( pDefCustomShape->nTextRect == 0 ) || ( pDefCustomShape->pTextRect == nullptr ) ) )
+ bIsDefaultGeometry = true;
+ }
+ break;
+ }
+ return bIsDefaultGeometry;
+}
+
+void SdrObjCustomShape::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
+{
+ rInfo.bResizeFreeAllowed=fObjectRotation == 0.0;
+ rInfo.bResizePropAllowed=true;
+ rInfo.bRotateFreeAllowed=true;
+ rInfo.bRotate90Allowed =true;
+ rInfo.bMirrorFreeAllowed=true;
+ rInfo.bMirror45Allowed =true;
+ rInfo.bMirror90Allowed =true;
+ rInfo.bTransparenceAllowed = false;
+ rInfo.bShearAllowed =true;
+ rInfo.bEdgeRadiusAllowed=false;
+ rInfo.bNoContortion =true;
+
+ // #i37011#
+ if ( !mXRenderedCustomShape.is() )
+ return;
+
+ const SdrObject* pRenderedCustomShape = SdrObject::getSdrObjectFromXShape( mXRenderedCustomShape );
+ if ( !pRenderedCustomShape )
+ return;
+
+ // #i37262#
+ // Iterate self over the contained objects, since there are combinations of
+ // polygon and curve objects. In that case, aInfo.bCanConvToPath and
+ // aInfo.bCanConvToPoly would be false. What is needed here is an or, not an and.
+ SdrObjListIter aIterator(*pRenderedCustomShape);
+ while(aIterator.IsMore())
+ {
+ SdrObject* pCandidate = aIterator.Next();
+ SdrObjTransformInfoRec aInfo;
+ pCandidate->TakeObjInfo(aInfo);
+
+ // set path and poly conversion if one is possible since
+ // this object will first be broken
+ const bool bCanConvToPathOrPoly(aInfo.bCanConvToPath || aInfo.bCanConvToPoly);
+ if(rInfo.bCanConvToPath != bCanConvToPathOrPoly)
+ {
+ rInfo.bCanConvToPath = bCanConvToPathOrPoly;
+ }
+
+ if(rInfo.bCanConvToPoly != bCanConvToPathOrPoly)
+ {
+ rInfo.bCanConvToPoly = bCanConvToPathOrPoly;
+ }
+
+ if(rInfo.bCanConvToContour != aInfo.bCanConvToContour)
+ {
+ rInfo.bCanConvToContour = aInfo.bCanConvToContour;
+ }
+
+ if(rInfo.bShearAllowed != aInfo.bShearAllowed)
+ {
+ rInfo.bShearAllowed = aInfo.bShearAllowed;
+ }
+ }
+}
+
+SdrObjKind SdrObjCustomShape::GetObjIdentifier() const
+{
+ return SdrObjKind::CustomShape;
+}
+
+// #115391# This implementation is based on the TextFrame size of the CustomShape and the
+// state of the ResizeShapeToFitText flag to correctly set TextMinFrameWidth/Height
+void SdrObjCustomShape::AdaptTextMinSize()
+{
+ if (getSdrModelFromSdrObject().IsCreatingDataObj() || getSdrModelFromSdrObject().IsPasteResize())
+ return;
+
+ // check if we need to change anything before creating an SfxItemSet, because that is expensive
+ const bool bResizeShapeToFitText(GetObjectItem(SDRATTR_TEXT_AUTOGROWHEIGHT).GetValue());
+ tools::Rectangle aTextBound(maRect);
+ bool bChanged(false);
+ if(bResizeShapeToFitText)
+ bChanged = true;
+ else if(GetTextBounds(aTextBound))
+ bChanged = true;
+ if (!bChanged)
+ return;
+
+ SfxItemSetFixed<SDRATTR_TEXT_MINFRAMEHEIGHT, SDRATTR_TEXT_AUTOGROWHEIGHT,
+ SDRATTR_TEXT_MINFRAMEWIDTH, SDRATTR_TEXT_AUTOGROWWIDTH> // contains SDRATTR_TEXT_MAXFRAMEWIDTH
+ aSet(*GetObjectItemSet().GetPool());
+
+ if(bResizeShapeToFitText)
+ {
+ // always reset MinWidthHeight to zero to only rely on text size and frame size
+ // to allow resizing being completely dependent on text size only
+ aSet.Put(makeSdrTextMinFrameWidthItem(0));
+ aSet.Put(makeSdrTextMinFrameHeightItem(0));
+ }
+ else
+ {
+ // recreate from CustomShape-specific TextBounds
+ const tools::Long nHDist(GetTextLeftDistance() + GetTextRightDistance());
+ const tools::Long nVDist(GetTextUpperDistance() + GetTextLowerDistance());
+ const tools::Long nTWdt(std::max(tools::Long(0), static_cast<tools::Long>(aTextBound.GetWidth() - 1 - nHDist)));
+ const tools::Long nTHgt(std::max(tools::Long(0), static_cast<tools::Long>(aTextBound.GetHeight() - 1 - nVDist)));
+
+ aSet.Put(makeSdrTextMinFrameWidthItem(nTWdt));
+ aSet.Put(makeSdrTextMinFrameHeightItem(nTHgt));
+ }
+
+ SetObjectItemSet(aSet);
+}
+
+void SdrObjCustomShape::NbcSetSnapRect( const tools::Rectangle& rRect )
+{
+ maRect = rRect;
+ ImpJustifyRect(maRect);
+ InvalidateRenderGeometry();
+
+ AdaptTextMinSize();
+
+ ImpCheckShear();
+ SetBoundAndSnapRectsDirty();
+ SetChanged();
+}
+
+void SdrObjCustomShape::SetSnapRect( const tools::Rectangle& rRect )
+{
+ tools::Rectangle aBoundRect0;
+ if ( m_pUserCall )
+ aBoundRect0 = GetLastBoundRect();
+ NbcSetSnapRect( rRect );
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+}
+
+void SdrObjCustomShape::NbcSetLogicRect( const tools::Rectangle& rRect )
+{
+ maRect = rRect;
+ ImpJustifyRect(maRect);
+ InvalidateRenderGeometry();
+
+ AdaptTextMinSize();
+
+ SetBoundAndSnapRectsDirty();
+ SetChanged();
+}
+
+void SdrObjCustomShape::SetLogicRect( const tools::Rectangle& rRect )
+{
+ tools::Rectangle aBoundRect0;
+ if ( m_pUserCall )
+ aBoundRect0 = GetLastBoundRect();
+ NbcSetLogicRect(rRect);
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+}
+
+void SdrObjCustomShape::Move( const Size& rSiz )
+{
+ if ( rSiz.Width() || rSiz.Height() )
+ {
+ tools::Rectangle aBoundRect0;
+ if ( m_pUserCall )
+ aBoundRect0 = GetLastBoundRect();
+ NbcMove(rSiz);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::MoveOnly,aBoundRect0);
+ }
+}
+void SdrObjCustomShape::NbcMove( const Size& rSiz )
+{
+ SdrTextObj::NbcMove( rSiz );
+ if ( mXRenderedCustomShape.is() )
+ {
+ SdrObject* pRenderedCustomShape = SdrObject::getSdrObjectFromXShape(mXRenderedCustomShape);
+ if ( pRenderedCustomShape )
+ {
+ // #i97149# the visualisation shape needs to be informed
+ // about change, too
+ pRenderedCustomShape->ActionChanged();
+ pRenderedCustomShape->NbcMove( rSiz );
+ }
+ }
+
+ // #i37011# adapt geometry shadow
+ if(mpLastShadowGeometry)
+ {
+ mpLastShadowGeometry->NbcMove( rSiz );
+ }
+}
+
+void SdrObjCustomShape::NbcResize( const Point& rRef, const Fraction& rxFact, const Fraction& ryFact )
+{
+ // taking care of handles that should not been changed
+ tools::Rectangle aOld( maRect );
+ std::vector< SdrCustomShapeInteraction > aInteractionHandles( GetInteractionHandles() );
+
+ SdrTextObj::NbcResize( rRef, rxFact, ryFact );
+
+ if ( ( rxFact.GetNumerator() != rxFact.GetDenominator() )
+ || ( ryFact.GetNumerator()!= ryFact.GetDenominator() ) )
+ {
+ if ( ( ( rxFact.GetNumerator() < 0 ) && ( rxFact.GetDenominator() > 0 ) ) ||
+ ( ( rxFact.GetNumerator() > 0 ) && ( rxFact.GetDenominator() < 0 ) ) )
+ {
+ SetMirroredX( !IsMirroredX() );
+ }
+ if ( ( ( ryFact.GetNumerator() < 0 ) && ( ryFact.GetDenominator() > 0 ) ) ||
+ ( ( ryFact.GetNumerator() > 0 ) && ( ryFact.GetDenominator() < 0 ) ) )
+ {
+ SetMirroredY( !IsMirroredY() );
+ }
+ }
+
+ for (const auto& rInteraction : aInteractionHandles)
+ {
+ try
+ {
+ if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_FIXED )
+ rInteraction.xInteraction->setControllerPosition( rInteraction.aPosition );
+ if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_ABSOLUTE_X )
+ {
+ sal_Int32 nX = ( rInteraction.aPosition.X - aOld.Left() ) + maRect.Left();
+ rInteraction.xInteraction->setControllerPosition( css::awt::Point( nX, rInteraction.xInteraction->getPosition().Y ) );
+ }
+ else if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_ABSOLUTE_NEGX )
+ {
+ sal_Int32 nX = maRect.Right() - (aOld.Right() - rInteraction.aPosition.X);
+ rInteraction.xInteraction->setControllerPosition( css::awt::Point( nX, rInteraction.xInteraction->getPosition().Y ) );
+ }
+ if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_ABSOLUTE_Y )
+ {
+ sal_Int32 nY = ( rInteraction.aPosition.Y - aOld.Top() ) + maRect.Top();
+ rInteraction.xInteraction->setControllerPosition( css::awt::Point( rInteraction.xInteraction->getPosition().X, nY ) );
+ }
+ }
+ catch ( const uno::RuntimeException& )
+ {
+ }
+ }
+
+ // updating fObjectRotation
+ Degree100 nTextObjRotation = maGeo.nRotationAngle;
+ double fAngle = toDegrees(nTextObjRotation);
+ if (IsMirroredX())
+ {
+ if (IsMirroredY())
+ fObjectRotation = fAngle - 180.0;
+ else
+ fObjectRotation = -fAngle;
+ }
+ else
+ {
+ if (IsMirroredY())
+ fObjectRotation = 180.0 - fAngle;
+ else
+ fObjectRotation = fAngle;
+ }
+ while (fObjectRotation < 0)
+ fObjectRotation += 360.0;
+ while (fObjectRotation >= 360.0)
+ fObjectRotation -= 360.0;
+
+ InvalidateRenderGeometry();
+}
+
+void SdrObjCustomShape::NbcRotate( const Point& rRef, Degree100 nAngle, double sn, double cs )
+{
+ bool bMirroredX = IsMirroredX();
+ bool bMirroredY = IsMirroredY();
+
+ fObjectRotation = fmod( fObjectRotation, 360.0 );
+ if ( fObjectRotation < 0 )
+ fObjectRotation = 360 + fObjectRotation;
+
+ // the rotation angle for ashapes is stored in fObjectRotation, this rotation
+ // has to be applied to the text object (which is internally using maGeo.nAngle).
+ SdrTextObj::NbcRotate( maRect.TopLeft(), -maGeo.nRotationAngle, // retrieving the unrotated text object
+ -maGeo.mfSinRotationAngle,
+ maGeo.mfCosRotationAngle );
+ maGeo.nRotationAngle = 0_deg100; // resetting aGeo data
+ maGeo.RecalcSinCos();
+
+ Degree100 nW(static_cast<sal_Int32>( fObjectRotation * 100 )); // applying our object rotation
+ if ( bMirroredX )
+ nW = 36000_deg100 - nW;
+ if ( bMirroredY )
+ nW = 18000_deg100 - nW;
+ nW = nW % 36000_deg100;
+ if ( nW < 0_deg100 )
+ nW = 36000_deg100 + nW;
+ SdrTextObj::NbcRotate( maRect.TopLeft(), nW, // applying text rotation
+ sin( toRadians(nW) ),
+ cos( toRadians(nW) ) );
+
+ int nSwap = 0;
+ if ( bMirroredX )
+ nSwap ^= 1;
+ if ( bMirroredY )
+ nSwap ^= 1;
+
+ double fAngle = toDegrees(nAngle); // updating to our new object rotation
+ fObjectRotation = fmod( nSwap ? fObjectRotation - fAngle : fObjectRotation + fAngle, 360.0 );
+ if ( fObjectRotation < 0 )
+ fObjectRotation = 360 + fObjectRotation;
+
+ SdrTextObj::NbcRotate( rRef, nAngle, sn, cs ); // applying text rotation
+ InvalidateRenderGeometry();
+}
+
+void SdrObjCustomShape::NbcMirror( const Point& rRef1, const Point& rRef2 )
+{
+ // TTTT: Fix for old mirroring, can be removed again in aw080
+ // storing horizontal and vertical flipping without modifying the rotate angle
+ // decompose other flipping to rotation and MirrorX.
+ tools::Long ndx = rRef2.X()-rRef1.X();
+ tools::Long ndy = rRef2.Y()-rRef1.Y();
+
+ if(!ndx) // MirroredX
+ {
+ SetMirroredX(!IsMirroredX());
+ SdrTextObj::NbcMirror( rRef1, rRef2 );
+ }
+ else
+ {
+ if(!ndy) // MirroredY
+ {
+ SetMirroredY(!IsMirroredY());
+ SdrTextObj::NbcMirror( rRef1, rRef2 );
+ }
+ else // neither horizontal nor vertical
+ {
+ SetMirroredX(!IsMirroredX());
+
+ // call parent
+ SdrTextObj::NbcMirror( rRef1, rRef2 );
+
+ // update fObjectRotation
+ Degree100 nTextObjRotation = maGeo.nRotationAngle;
+ double fAngle = toDegrees(nTextObjRotation);
+
+ bool bSingleFlip = (IsMirroredX()!= IsMirroredY());
+
+ fObjectRotation = fmod( bSingleFlip ? -fAngle : fAngle, 360.0 );
+
+ if ( fObjectRotation < 0 )
+ {
+ fObjectRotation = 360.0 + fObjectRotation;
+ }
+ }
+ }
+
+ InvalidateRenderGeometry();
+}
+
+void SdrObjCustomShape::Shear( const Point& rRef, Degree100 nAngle, double tn, bool bVShear )
+{
+ SdrTextObj::Shear( rRef, nAngle, tn, bVShear );
+ InvalidateRenderGeometry();
+}
+void SdrObjCustomShape::NbcShear( const Point& rRef, Degree100 nAngle, double tn, bool bVShear )
+{
+ // TTTT: Fix for old mirroring, can be removed again in aw080
+ SdrTextObj::NbcShear(rRef,nAngle,tn,bVShear);
+
+ // updating fObjectRotation
+ Degree100 nTextObjRotation = maGeo.nRotationAngle;
+ double fAngle = toDegrees(nTextObjRotation);
+ if (IsMirroredX())
+ {
+ if (IsMirroredY())
+ fObjectRotation = fAngle - 180.0;
+ else
+ fObjectRotation = -fAngle;
+ }
+ else
+ {
+ if (IsMirroredY())
+ fObjectRotation = 180.0 - fAngle;
+ else
+ fObjectRotation = fAngle;
+ }
+ while (fObjectRotation < 0)
+ fObjectRotation += 360.0;
+ while (fObjectRotation >= 360.0)
+ fObjectRotation -= 360.0;
+
+ InvalidateRenderGeometry();
+}
+
+SdrGluePoint SdrObjCustomShape::GetVertexGluePoint(sal_uInt16 nPosNum) const
+{
+ sal_Int32 nWdt = ImpGetLineWdt(); // #i25616#
+
+ // #i25616#
+ if(!LineIsOutsideGeometry())
+ {
+ nWdt++;
+ nWdt /= 2;
+ }
+
+ Point aPt;
+ switch (nPosNum) {
+ case 0: aPt=maRect.TopCenter(); aPt.AdjustY( -nWdt ); break;
+ case 1: aPt=maRect.RightCenter(); aPt.AdjustX(nWdt ); break;
+ case 2: aPt=maRect.BottomCenter(); aPt.AdjustY(nWdt ); break;
+ case 3: aPt=maRect.LeftCenter(); aPt.AdjustX( -nWdt ); break;
+ }
+ if (maGeo.nShearAngle != 0_deg100) ShearPoint(aPt, maRect.TopLeft(), maGeo.mfTanShearAngle);
+ if (maGeo.nRotationAngle != 0_deg100) RotatePoint(aPt, maRect.TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
+ aPt-=GetSnapRect().Center();
+ SdrGluePoint aGP(aPt);
+ aGP.SetPercent(false);
+ return aGP;
+}
+
+
+// #i38892#
+void SdrObjCustomShape::ImpCheckCustomGluePointsAreAdded()
+{
+ const SdrObject* pSdrObject = GetSdrObjectFromCustomShape();
+
+ if(!pSdrObject)
+ return;
+
+ const SdrGluePointList* pSource = pSdrObject->GetGluePointList();
+
+ if(!(pSource && pSource->GetCount()))
+ return;
+
+ if(!SdrTextObj::GetGluePointList())
+ {
+ SdrTextObj::ForceGluePointList();
+ }
+
+ const SdrGluePointList* pList = SdrTextObj::GetGluePointList();
+
+ if(!pList)
+ return;
+
+ SdrGluePointList aNewList;
+ sal_uInt16 a;
+
+ for(a = 0; a < pSource->GetCount(); a++)
+ {
+ SdrGluePoint aCopy((*pSource)[a]);
+ aCopy.SetUserDefined(false);
+ aNewList.Insert(aCopy);
+ }
+
+ bool bMirroredX = IsMirroredX();
+ bool bMirroredY = IsMirroredY();
+
+ Degree100 nShearAngle = maGeo.nShearAngle;
+ double fTan = maGeo.mfTanShearAngle;
+
+ if (maGeo.nRotationAngle || nShearAngle || bMirroredX || bMirroredY)
+ {
+ tools::Polygon aPoly( maRect );
+ if( nShearAngle )
+ {
+ sal_uInt16 nPointCount=aPoly.GetSize();
+ for (sal_uInt16 i=0; i<nPointCount; i++)
+ ShearPoint(aPoly[i],maRect.Center(), fTan );
+ }
+ if (maGeo.nRotationAngle)
+ aPoly.Rotate( maRect.Center(), to<Degree10>(maGeo.nRotationAngle) );
+
+ tools::Rectangle aBoundRect( aPoly.GetBoundRect() );
+ sal_Int32 nXDiff = aBoundRect.Left() - maRect.Left();
+ sal_Int32 nYDiff = aBoundRect.Top() - maRect.Top();
+
+ if (nShearAngle && bMirroredX != bMirroredY)
+ {
+ nShearAngle = -nShearAngle;
+ fTan = -fTan;
+ }
+
+ Point aRef( maRect.GetWidth() / 2, maRect.GetHeight() / 2 );
+ for ( a = 0; a < aNewList.GetCount(); a++ )
+ {
+ SdrGluePoint& rPoint = aNewList[ a ];
+ Point aGlue( rPoint.GetPos() );
+ if ( nShearAngle )
+ ShearPoint( aGlue, aRef, fTan );
+
+ RotatePoint(aGlue, aRef, sin(basegfx::deg2rad(fObjectRotation)),
+ cos(basegfx::deg2rad(fObjectRotation)));
+ if ( bMirroredX )
+ aGlue.setX( maRect.GetWidth() - aGlue.X() );
+ if ( bMirroredY )
+ aGlue.setY( maRect.GetHeight() - aGlue.Y() );
+ aGlue.AdjustX( -nXDiff );
+ aGlue.AdjustY( -nYDiff );
+ rPoint.SetPos( aGlue );
+ }
+ }
+
+ for(a = 0; a < pList->GetCount(); a++)
+ {
+ const SdrGluePoint& rCandidate = (*pList)[a];
+
+ if(rCandidate.IsUserDefined())
+ {
+ aNewList.Insert(rCandidate);
+ }
+ }
+
+ // copy new list to local. This is NOT very convenient behavior, the local
+ // GluePointList should not be set, but we delivered by using GetGluePointList(),
+ // maybe on demand. Since the local object is changed here, this is assumed to
+ // be a result of GetGluePointList and thus the list is copied
+ if(m_pPlusData)
+ {
+ m_pPlusData->SetGluePoints(aNewList);
+ }
+}
+
+// #i38892#
+const SdrGluePointList* SdrObjCustomShape::GetGluePointList() const
+{
+ const_cast<SdrObjCustomShape*>(this)->ImpCheckCustomGluePointsAreAdded();
+ return SdrTextObj::GetGluePointList();
+}
+
+// #i38892#
+SdrGluePointList* SdrObjCustomShape::ForceGluePointList()
+{
+ if(SdrTextObj::ForceGluePointList())
+ {
+ ImpCheckCustomGluePointsAreAdded();
+ return SdrTextObj::ForceGluePointList();
+ }
+ else
+ {
+ return nullptr;
+ }
+}
+
+
+sal_uInt32 SdrObjCustomShape::GetHdlCount() const
+{
+ const sal_uInt32 nBasicHdlCount(SdrTextObj::GetHdlCount());
+ return ( GetInteractionHandles().size() + nBasicHdlCount );
+}
+
+void SdrObjCustomShape::AddToHdlList(SdrHdlList& rHdlList) const
+{
+ SdrTextObj::AddToHdlList(rHdlList);
+
+ int nCustomShapeHdlNum = 0;
+ for (SdrCustomShapeInteraction const & rInteraction : GetInteractionHandles())
+ {
+ if ( rInteraction.xInteraction.is() )
+ {
+ try
+ {
+ css::awt::Point aPosition( rInteraction.xInteraction->getPosition() );
+ std::unique_ptr<SdrHdl> pH(new SdrHdl( Point( aPosition.X, aPosition.Y ), SdrHdlKind::CustomShape1 ));
+ pH->SetPointNum( nCustomShapeHdlNum );
+ pH->SetObj( const_cast<SdrObjCustomShape*>(this) );
+ rHdlList.AddHdl(std::move(pH));
+ }
+ catch ( const uno::RuntimeException& )
+ {
+ }
+ }
+ ++nCustomShapeHdlNum;
+ }
+}
+
+bool SdrObjCustomShape::hasSpecialDrag() const
+{
+ return true;
+}
+
+bool SdrObjCustomShape::beginSpecialDrag(SdrDragStat& rDrag) const
+{
+ const SdrHdl* pHdl = rDrag.GetHdl();
+
+ if(pHdl && SdrHdlKind::CustomShape1 == pHdl->GetKind())
+ {
+ rDrag.SetEndDragChangesAttributes(true);
+ rDrag.SetNoSnap();
+ }
+ else
+ {
+ const SdrHdl* pHdl2 = rDrag.GetHdl();
+ const SdrHdlKind eHdl((pHdl2 == nullptr) ? SdrHdlKind::Move : pHdl2->GetKind());
+
+ switch( eHdl )
+ {
+ case SdrHdlKind::UpperLeft :
+ case SdrHdlKind::Upper :
+ case SdrHdlKind::UpperRight :
+ case SdrHdlKind::Left :
+ case SdrHdlKind::Right :
+ case SdrHdlKind::LowerLeft :
+ case SdrHdlKind::Lower :
+ case SdrHdlKind::LowerRight :
+ case SdrHdlKind::Move :
+ {
+ break;
+ }
+ default:
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+void SdrObjCustomShape::DragResizeCustomShape( const tools::Rectangle& rNewRect )
+{
+ tools::Rectangle aOld( maRect );
+ bool bOldMirroredX( IsMirroredX() );
+ bool bOldMirroredY( IsMirroredY() );
+
+ tools::Rectangle aNewRect( rNewRect );
+ aNewRect.Justify();
+
+ std::vector< SdrCustomShapeInteraction > aInteractionHandles( GetInteractionHandles() );
+
+ GeoStat aGeoStat( GetGeoStat() );
+ if ( aNewRect.TopLeft()!= maRect.TopLeft() &&
+ ( maGeo.nRotationAngle || maGeo.nShearAngle ) )
+ {
+ Point aNewPos( aNewRect.TopLeft() );
+ if ( maGeo.nShearAngle ) ShearPoint( aNewPos, aOld.TopLeft(), aGeoStat.mfTanShearAngle );
+ if ( maGeo.nRotationAngle ) RotatePoint(aNewPos, aOld.TopLeft(), aGeoStat.mfSinRotationAngle, aGeoStat.mfCosRotationAngle );
+ aNewRect.SetPos( aNewPos );
+ }
+ if ( aNewRect == maRect )
+ return;
+
+ SetLogicRect( aNewRect );
+ InvalidateRenderGeometry();
+
+ if ( rNewRect.Left() > rNewRect.Right() )
+ {
+ Point aTop( ( GetSnapRect().Left() + GetSnapRect().Right() ) >> 1, GetSnapRect().Top() );
+ Point aBottom( aTop.X(), aTop.Y() + 1000 );
+ NbcMirror( aTop, aBottom );
+ }
+ if ( rNewRect.Top() > rNewRect.Bottom() )
+ {
+ Point aLeft( GetSnapRect().Left(), ( GetSnapRect().Top() + GetSnapRect().Bottom() ) >> 1 );
+ Point aRight( aLeft.X() + 1000, aLeft.Y() );
+ NbcMirror( aLeft, aRight );
+ }
+
+ for (const auto& rInteraction : aInteractionHandles)
+ {
+ try
+ {
+ if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_FIXED )
+ rInteraction.xInteraction->setControllerPosition( rInteraction.aPosition );
+ if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_ABSOLUTE_X ||
+ rInteraction.nMode & CustomShapeHandleModes::RESIZE_ABSOLUTE_NEGX )
+ {
+ if (rInteraction.nMode & CustomShapeHandleModes::RESIZE_ABSOLUTE_NEGX)
+ bOldMirroredX = !bOldMirroredX;
+
+ sal_Int32 nX;
+ if ( bOldMirroredX )
+ {
+ nX = ( rInteraction.aPosition.X - aOld.Right() );
+ if ( rNewRect.Left() > rNewRect.Right() )
+ nX = maRect.Left() - nX;
+ else
+ nX += maRect.Right();
+ }
+ else
+ {
+ nX = ( rInteraction.aPosition.X - aOld.Left() );
+ if ( rNewRect.Left() > rNewRect.Right() )
+ nX = maRect.Right() - nX;
+ else
+ nX += maRect.Left();
+ }
+ rInteraction.xInteraction->setControllerPosition( css::awt::Point( nX, rInteraction.xInteraction->getPosition().Y ) );
+ }
+ if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_ABSOLUTE_Y )
+ {
+ sal_Int32 nY;
+ if ( bOldMirroredY )
+ {
+ nY = ( rInteraction.aPosition.Y - aOld.Bottom() );
+ if ( rNewRect.Top() > rNewRect.Bottom() )
+ nY = maRect.Top() - nY;
+ else
+ nY += maRect.Bottom();
+ }
+ else
+ {
+ nY = ( rInteraction.aPosition.Y - aOld.Top() );
+ if ( rNewRect.Top() > rNewRect.Bottom() )
+ nY = maRect.Bottom() - nY;
+ else
+ nY += maRect.Top();
+ }
+ rInteraction.xInteraction->setControllerPosition( css::awt::Point( rInteraction.xInteraction->getPosition().X, nY ) );
+ }
+ }
+ catch ( const uno::RuntimeException& )
+ {
+ }
+ }
+}
+
+void SdrObjCustomShape::DragMoveCustomShapeHdl( const Point& rDestination,
+ const sal_uInt16 nCustomShapeHdlNum, bool bMoveCalloutRectangle )
+{
+ std::vector< SdrCustomShapeInteraction > aInteractionHandles( GetInteractionHandles() );
+ if ( nCustomShapeHdlNum >= aInteractionHandles.size() )
+ return;
+
+ SdrCustomShapeInteraction aInteractionHandle( aInteractionHandles[ nCustomShapeHdlNum ] );
+ if ( !aInteractionHandle.xInteraction.is() )
+ return;
+
+ try
+ {
+ css::awt::Point aPt( rDestination.X(), rDestination.Y() );
+ if ( aInteractionHandle.nMode & CustomShapeHandleModes::MOVE_SHAPE && bMoveCalloutRectangle )
+ {
+ sal_Int32 nXDiff = aPt.X - aInteractionHandle.aPosition.X;
+ sal_Int32 nYDiff = aPt.Y - aInteractionHandle.aPosition.Y;
+
+ maRect.Move( nXDiff, nYDiff );
+ m_aOutRect.Move( nXDiff, nYDiff );
+ maSnapRect.Move( nXDiff, nYDiff );
+ SetBoundAndSnapRectsDirty(/*bNotMyself*/true);
+ InvalidateRenderGeometry();
+
+ for (const auto& rInteraction : aInteractionHandles)
+ {
+ if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_FIXED )
+ {
+ if ( rInteraction.xInteraction.is() )
+ rInteraction.xInteraction->setControllerPosition( rInteraction.aPosition );
+ }
+ }
+ }
+ aInteractionHandle.xInteraction->setControllerPosition( aPt );
+ }
+ catch ( const uno::RuntimeException& )
+ {
+ }
+}
+
+bool SdrObjCustomShape::applySpecialDrag(SdrDragStat& rDrag)
+{
+ const SdrHdl* pHdl = rDrag.GetHdl();
+ const SdrHdlKind eHdl((pHdl == nullptr) ? SdrHdlKind::Move : pHdl->GetKind());
+
+ switch(eHdl)
+ {
+ case SdrHdlKind::CustomShape1 :
+ {
+ rDrag.SetEndDragChangesGeoAndAttributes(true);
+ DragMoveCustomShapeHdl( rDrag.GetNow(), static_cast<sal_uInt16>(pHdl->GetPointNum()), !rDrag.GetDragMethod()->IsShiftPressed() );
+ SetBoundAndSnapRectsDirty();
+ InvalidateRenderGeometry();
+ SetChanged();
+ break;
+ }
+
+ case SdrHdlKind::UpperLeft :
+ case SdrHdlKind::Upper :
+ case SdrHdlKind::UpperRight :
+ case SdrHdlKind::Left :
+ case SdrHdlKind::Right :
+ case SdrHdlKind::LowerLeft :
+ case SdrHdlKind::Lower :
+ case SdrHdlKind::LowerRight :
+ {
+ DragResizeCustomShape( ImpDragCalcRect(rDrag) );
+ break;
+ }
+ case SdrHdlKind::Move :
+ {
+ Move(Size(rDrag.GetDX(), rDrag.GetDY()));
+ break;
+ }
+ default: break;
+ }
+
+ return true;
+}
+
+
+void SdrObjCustomShape::DragCreateObject( SdrDragStat& rStat )
+{
+ tools::Rectangle aRect1;
+ rStat.TakeCreateRect( aRect1 );
+
+ std::vector< SdrCustomShapeInteraction > aInteractionHandles( GetInteractionHandles() );
+
+ constexpr sal_uInt32 nDefaultObjectSizeWidth = 3000; // default width from SDOptions ?
+ constexpr sal_uInt32 nDefaultObjectSizeHeight= 3000;
+
+ if ( ImpVerticalSwitch( *this ) )
+ {
+ SetMirroredX( aRect1.Left() > aRect1.Right() );
+
+ aRect1 = tools::Rectangle( rStat.GetNow(), Size( nDefaultObjectSizeWidth, nDefaultObjectSizeHeight ) );
+ // subtracting the horizontal difference of the latest handle from shape position
+ if ( !aInteractionHandles.empty() )
+ {
+ sal_Int32 nHandlePos = aInteractionHandles[ aInteractionHandles.size() - 1 ].xInteraction->getPosition().X;
+ aRect1.Move( maRect.Left() - nHandlePos, 0 );
+ }
+ }
+ ImpJustifyRect( aRect1 );
+ rStat.SetActionRect( aRect1 );
+ maRect = aRect1;
+ SetBoundAndSnapRectsDirty();
+
+ for (const auto& rInteraction : aInteractionHandles)
+ {
+ try
+ {
+ if ( rInteraction.nMode & CustomShapeHandleModes::CREATE_FIXED )
+ rInteraction.xInteraction->setControllerPosition( awt::Point( rStat.GetStart().X(), rStat.GetStart().Y() ) );
+ }
+ catch ( const uno::RuntimeException& )
+ {
+ }
+ }
+
+ SetBoundRectDirty();
+ m_bSnapRectDirty=true;
+}
+
+bool SdrObjCustomShape::MovCreate(SdrDragStat& rStat)
+{
+ SdrView* pView = rStat.GetView(); // #i37448#
+ if( pView && pView->IsSolidDragging() )
+ {
+ InvalidateRenderGeometry();
+ }
+ DragCreateObject( rStat );
+ SetBoundAndSnapRectsDirty();
+ return true;
+}
+
+bool SdrObjCustomShape::EndCreate( SdrDragStat& rStat, SdrCreateCmd eCmd )
+{
+ DragCreateObject( rStat );
+
+ AdaptTextMinSize();
+
+ SetBoundAndSnapRectsDirty();
+ return ( eCmd == SdrCreateCmd::ForceEnd || rStat.GetPointCount() >= 2 );
+}
+
+basegfx::B2DPolyPolygon SdrObjCustomShape::TakeCreatePoly(const SdrDragStat& /*rDrag*/) const
+{
+ return GetLineGeometry( false );
+}
+
+
+// in context with the SdrObjCustomShape the SdrTextAutoGrowHeightItem == true -> Resize Shape to fit text,
+// the SdrTextAutoGrowWidthItem == true -> Word wrap text in Shape
+bool SdrObjCustomShape::IsAutoGrowHeight() const
+{
+ const SfxItemSet& rSet = GetMergedItemSet();
+ bool bIsAutoGrowHeight = rSet.Get(SDRATTR_TEXT_AUTOGROWHEIGHT).GetValue();
+ if ( bIsAutoGrowHeight && IsVerticalWriting() )
+ bIsAutoGrowHeight = !rSet.Get(SDRATTR_TEXT_WORDWRAP).GetValue();
+ return bIsAutoGrowHeight;
+}
+bool SdrObjCustomShape::IsAutoGrowWidth() const
+{
+ const SfxItemSet& rSet = GetMergedItemSet();
+ bool bIsAutoGrowWidth = rSet.Get(SDRATTR_TEXT_AUTOGROWHEIGHT).GetValue();
+ if ( bIsAutoGrowWidth && !IsVerticalWriting() )
+ bIsAutoGrowWidth = !rSet.Get(SDRATTR_TEXT_WORDWRAP).GetValue();
+ return bIsAutoGrowWidth;
+}
+
+/* The following method is identical to the SdrTextObj::SetVerticalWriting method, the only difference
+ is that the SdrAutoGrowWidthItem and SdrAutoGrowHeightItem are not exchanged if the vertical writing
+ mode has been changed */
+
+void SdrObjCustomShape::SetVerticalWriting( bool bVertical )
+{
+ ForceOutlinerParaObject();
+
+ OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject();
+
+ DBG_ASSERT( pOutlinerParaObject, "SdrTextObj::SetVerticalWriting() without OutlinerParaObject!" );
+
+ if( !pOutlinerParaObject ||
+ (pOutlinerParaObject->IsEffectivelyVertical() == bVertical) )
+ return;
+
+ // get item settings
+ const SfxItemSet& rSet = GetObjectItemSet();
+
+ // Also exchange horizontal and vertical adjust items
+ SdrTextHorzAdjust eHorz = rSet.Get(SDRATTR_TEXT_HORZADJUST).GetValue();
+ SdrTextVertAdjust eVert = rSet.Get(SDRATTR_TEXT_VERTADJUST).GetValue();
+
+ // rescue object size, SetSnapRect below expects logic rect,
+ // not snap rect.
+ tools::Rectangle aObjectRect = GetLogicRect();
+
+ // prepare ItemSet to set exchanged width and height items
+ SfxItemSetFixed<SDRATTR_TEXT_AUTOGROWHEIGHT, SDRATTR_TEXT_AUTOGROWHEIGHT,
+ // Expanded item ranges to also support horizontal and vertical adjust.
+ SDRATTR_TEXT_VERTADJUST, SDRATTR_TEXT_VERTADJUST,
+ SDRATTR_TEXT_AUTOGROWWIDTH, SDRATTR_TEXT_HORZADJUST> aNewSet(*rSet.GetPool());
+
+ aNewSet.Put(rSet);
+
+ // Exchange horizontal and vertical adjusts
+ switch(eVert)
+ {
+ case SDRTEXTVERTADJUST_TOP: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_RIGHT)); break;
+ case SDRTEXTVERTADJUST_CENTER: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_CENTER)); break;
+ case SDRTEXTVERTADJUST_BOTTOM: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_LEFT)); break;
+ case SDRTEXTVERTADJUST_BLOCK: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_BLOCK)); break;
+ }
+ switch(eHorz)
+ {
+ case SDRTEXTHORZADJUST_LEFT: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_BOTTOM)); break;
+ case SDRTEXTHORZADJUST_CENTER: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_CENTER)); break;
+ case SDRTEXTHORZADJUST_RIGHT: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_TOP)); break;
+ case SDRTEXTHORZADJUST_BLOCK: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_BLOCK)); break;
+ }
+
+ pOutlinerParaObject = GetOutlinerParaObject();
+ if ( pOutlinerParaObject )
+ pOutlinerParaObject->SetVertical(bVertical);
+ SetObjectItemSet( aNewSet );
+
+ // restore object size
+ SetSnapRect(aObjectRect);
+}
+
+void SdrObjCustomShape::SuggestTextFrameSize(Size aSuggestedTextFrameSize)
+{
+ m_aSuggestedTextFrameSize = aSuggestedTextFrameSize;
+}
+
+bool SdrObjCustomShape::AdjustTextFrameWidthAndHeight(tools::Rectangle& rR, bool bHgt, bool bWdt) const
+{
+ // Either we have text or the application has native text and suggested its size to us.
+ bool bHasText = HasText() || !m_aSuggestedTextFrameSize.IsEmpty();
+ if ( bHasText && !rR.IsEmpty() )
+ {
+ bool bWdtGrow=bWdt && IsAutoGrowWidth();
+ bool bHgtGrow=bHgt && IsAutoGrowHeight();
+ if ( bWdtGrow || bHgtGrow )
+ {
+ tools::Rectangle aR0(rR);
+ tools::Long nHgt=0,nMinHgt=0,nMaxHgt=0;
+ tools::Long nWdt=0,nMinWdt=0,nMaxWdt=0;
+ Size aSiz(rR.GetSize()); aSiz.AdjustWidth( -1 ); aSiz.AdjustHeight( -1 );
+ Size aMaxSiz(100000,100000);
+ Size aTmpSiz(getSdrModelFromSdrObject().GetMaxObjSize());
+ if (aTmpSiz.Width()!=0) aMaxSiz.setWidth(aTmpSiz.Width() );
+ if (aTmpSiz.Height()!=0) aMaxSiz.setHeight(aTmpSiz.Height() );
+ if (bWdtGrow)
+ {
+ nMinWdt=GetMinTextFrameWidth();
+ nMaxWdt=GetMaxTextFrameWidth();
+ if (nMaxWdt==0 || nMaxWdt>aMaxSiz.Width()) nMaxWdt=aMaxSiz.Width();
+ if (nMinWdt<=0) nMinWdt=1;
+ aSiz.setWidth(nMaxWdt );
+ }
+ if (bHgtGrow)
+ {
+ nMinHgt=GetMinTextFrameHeight();
+ nMaxHgt=GetMaxTextFrameHeight();
+ if (nMaxHgt==0 || nMaxHgt>aMaxSiz.Height()) nMaxHgt=aMaxSiz.Height();
+ if (nMinHgt<=0) nMinHgt=1;
+ aSiz.setHeight(nMaxHgt );
+ }
+ tools::Long nHDist=GetTextLeftDistance()+GetTextRightDistance();
+ tools::Long nVDist=GetTextUpperDistance()+GetTextLowerDistance();
+ aSiz.AdjustWidth( -nHDist );
+ aSiz.AdjustHeight( -nVDist );
+ if ( aSiz.Width() < 2 )
+ aSiz.setWidth( 2 ); // minimum size=2
+ if ( aSiz.Height() < 2 )
+ aSiz.setHeight( 2 ); // minimum size=2
+
+ if (HasText())
+ {
+ if(mpEditingOutliner)
+ {
+ mpEditingOutliner->SetMaxAutoPaperSize( aSiz );
+ if (bWdtGrow)
+ {
+ Size aSiz2(mpEditingOutliner->CalcTextSize());
+ nWdt=aSiz2.Width()+1; // a little more tolerance
+ if (bHgtGrow) nHgt=aSiz2.Height()+1; // a little more tolerance
+ } else
+ {
+ nHgt=mpEditingOutliner->GetTextHeight()+1; // a little more tolerance
+ }
+ }
+ else
+ {
+ Outliner& rOutliner=ImpGetDrawOutliner();
+ rOutliner.SetPaperSize(aSiz);
+ rOutliner.SetUpdateLayout(true);
+ // TODO: add the optimization with bPortionInfoChecked again.
+ OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject();
+ if( pOutlinerParaObject != nullptr )
+ {
+ rOutliner.SetText(*pOutlinerParaObject);
+ rOutliner.SetFixedCellHeight(GetMergedItem(SDRATTR_TEXT_USEFIXEDCELLHEIGHT).GetValue());
+ }
+ if ( bWdtGrow )
+ {
+ Size aSiz2(rOutliner.CalcTextSize());
+ nWdt=aSiz2.Width()+1; // a little more tolerance
+ if ( bHgtGrow )
+ nHgt=aSiz2.Height()+1; // a little more tolerance
+ }
+ else
+ {
+ nHgt = rOutliner.GetTextHeight()+1; // a little more tolerance
+
+ sal_Int16 nColumns = GetMergedItem(SDRATTR_TEXTCOLUMNS_NUMBER).GetValue();
+ if (bHgtGrow && nColumns > 1)
+ {
+ // Both 'resize shape to fix text' and multiple columns are enabled. The
+ // first means a dynamic height, the second expects a fixed height.
+ // Resolve this conflict by going with the original height.
+ nHgt = rR.getHeight();
+ }
+ }
+ rOutliner.Clear();
+ }
+ }
+ else
+ {
+ nHgt = m_aSuggestedTextFrameSize.Height();
+ nWdt = m_aSuggestedTextFrameSize.Width();
+ }
+ if ( nWdt < nMinWdt )
+ nWdt = nMinWdt;
+ if ( nWdt > nMaxWdt )
+ nWdt = nMaxWdt;
+ nWdt += nHDist;
+ if ( nWdt < 1 )
+ nWdt = 1; // nHDist may also be negative
+ if ( nHgt < nMinHgt )
+ nHgt = nMinHgt;
+ if ( nHgt > nMaxHgt )
+ nHgt = nMaxHgt;
+ nHgt+=nVDist;
+ if ( nHgt < 1 )
+ nHgt = 1; // nVDist may also be negative
+ tools::Long nWdtGrow = nWdt-(rR.Right()-rR.Left());
+ tools::Long nHgtGrow = nHgt-(rR.Bottom()-rR.Top());
+ if ( nWdtGrow == 0 )
+ bWdtGrow = false;
+ if ( nHgtGrow == 0 )
+ bHgtGrow=false;
+ if ( bWdtGrow || bHgtGrow || !m_aSuggestedTextFrameSize.IsEmpty())
+ {
+ if ( bWdtGrow || m_aSuggestedTextFrameSize.Width() )
+ {
+ SdrTextHorzAdjust eHAdj=GetTextHorizontalAdjust();
+ if (m_aSuggestedTextFrameSize.Width())
+ {
+ rR.SetRight(rR.Left() + m_aSuggestedTextFrameSize.Width());
+ }
+ else if ( eHAdj == SDRTEXTHORZADJUST_LEFT )
+ rR.AdjustRight(nWdtGrow );
+ else if ( eHAdj == SDRTEXTHORZADJUST_RIGHT )
+ rR.AdjustLeft( -nWdtGrow );
+ else
+ {
+ tools::Long nWdtGrow2=nWdtGrow/2;
+ rR.AdjustLeft( -nWdtGrow2 );
+ rR.SetRight(rR.Left()+nWdt );
+ }
+ }
+ if ( bHgtGrow || m_aSuggestedTextFrameSize.Height() )
+ {
+ SdrTextVertAdjust eVAdj=GetTextVerticalAdjust();
+ if (m_aSuggestedTextFrameSize.Height())
+ {
+ rR.SetBottom(rR.Top() + m_aSuggestedTextFrameSize.Height());
+ }
+ else if ( eVAdj == SDRTEXTVERTADJUST_TOP )
+ rR.AdjustBottom(nHgtGrow );
+ else if ( eVAdj == SDRTEXTVERTADJUST_BOTTOM )
+ rR.AdjustTop( -nHgtGrow );
+ else
+ {
+ tools::Long nHgtGrow2=nHgtGrow/2;
+ rR.AdjustTop( -nHgtGrow2 );
+ rR.SetBottom(rR.Top()+nHgt );
+ }
+ }
+ if ( maGeo.nRotationAngle )
+ {
+ Point aD1(rR.TopLeft());
+ aD1-=aR0.TopLeft();
+ Point aD2(aD1);
+ RotatePoint(aD2,Point(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
+ aD2-=aD1;
+ rR.Move(aD2.X(),aD2.Y());
+ }
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+tools::Rectangle SdrObjCustomShape::ImpCalculateTextFrame( const bool bHgt, const bool bWdt )
+{
+ tools::Rectangle aReturnValue;
+
+ tools::Rectangle aOldTextRect( maRect ); // <- initial text rectangle
+
+ tools::Rectangle aNewTextRect( maRect ); // <- new text rectangle returned from the custom shape renderer,
+ GetTextBounds( aNewTextRect ); // it depends to the current logical shape size
+
+ tools::Rectangle aAdjustedTextRect( aNewTextRect ); // <- new text rectangle is being tested by AdjustTextFrameWidthAndHeight to ensure
+ if ( AdjustTextFrameWidthAndHeight( aAdjustedTextRect, bHgt, bWdt ) ) // that the new text rectangle is matching the current text size from the outliner
+ {
+ if (aAdjustedTextRect != aNewTextRect && aOldTextRect != aAdjustedTextRect &&
+ aNewTextRect.GetWidth() && aNewTextRect.GetHeight())
+ {
+ aReturnValue = maRect;
+ double fXScale = static_cast<double>(aOldTextRect.GetWidth()) / static_cast<double>(aNewTextRect.GetWidth());
+ double fYScale = static_cast<double>(aOldTextRect.GetHeight()) / static_cast<double>(aNewTextRect.GetHeight());
+ double fRightDiff = static_cast<double>( aAdjustedTextRect.Right() - aNewTextRect.Right() ) * fXScale;
+ double fLeftDiff = static_cast<double>( aAdjustedTextRect.Left() - aNewTextRect.Left() ) * fXScale;
+ double fTopDiff = static_cast<double>( aAdjustedTextRect.Top() - aNewTextRect.Top() ) * fYScale;
+ double fBottomDiff= static_cast<double>( aAdjustedTextRect.Bottom()- aNewTextRect.Bottom()) * fYScale;
+ aReturnValue.AdjustLeft(static_cast<sal_Int32>(fLeftDiff) );
+ aReturnValue.AdjustRight(static_cast<sal_Int32>(fRightDiff) );
+ aReturnValue.AdjustTop(static_cast<sal_Int32>(fTopDiff) );
+ aReturnValue.AdjustBottom(static_cast<sal_Int32>(fBottomDiff) );
+ }
+ }
+ return aReturnValue;
+}
+
+bool SdrObjCustomShape::NbcAdjustTextFrameWidthAndHeight(bool bHgt, bool bWdt)
+{
+ tools::Rectangle aNewTextRect = ImpCalculateTextFrame(bHgt, bWdt);
+ const bool bRet = !aNewTextRect.IsEmpty() && aNewTextRect != maRect;
+ if (bRet && !mbAdjustingTextFrameWidthAndHeight)
+ {
+ mbAdjustingTextFrameWidthAndHeight = true;
+
+ // taking care of handles that should not been changed
+ std::vector< SdrCustomShapeInteraction > aInteractionHandles( GetInteractionHandles() );
+
+ maRect = aNewTextRect;
+ SetBoundAndSnapRectsDirty();
+ SetChanged();
+
+ for (const auto& rInteraction : aInteractionHandles)
+ {
+ try
+ {
+ if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_FIXED )
+ rInteraction.xInteraction->setControllerPosition( rInteraction.aPosition );
+ }
+ catch ( const uno::RuntimeException& )
+ {
+ }
+ }
+ InvalidateRenderGeometry();
+
+ mbAdjustingTextFrameWidthAndHeight = false;
+ }
+ return bRet;
+}
+
+bool SdrObjCustomShape::AdjustTextFrameWidthAndHeight()
+{
+ tools::Rectangle aNewTextRect = ImpCalculateTextFrame( true/*bHgt*/, true/*bWdt*/ );
+ bool bRet = !aNewTextRect.IsEmpty() && ( aNewTextRect != maRect );
+ if ( bRet )
+ {
+ tools::Rectangle aBoundRect0;
+ if ( m_pUserCall )
+ aBoundRect0 = GetCurrentBoundRect();
+
+ // taking care of handles that should not been changed
+ std::vector< SdrCustomShapeInteraction > aInteractionHandles( GetInteractionHandles() );
+
+ maRect = aNewTextRect;
+ SetBoundAndSnapRectsDirty();
+
+ for (const auto& rInteraction : aInteractionHandles)
+ {
+ try
+ {
+ if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_FIXED )
+ rInteraction.xInteraction->setControllerPosition( rInteraction.aPosition );
+ }
+ catch ( const uno::RuntimeException& )
+ {
+ }
+ }
+
+ InvalidateRenderGeometry();
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+ }
+ return bRet;
+}
+void SdrObjCustomShape::TakeTextEditArea(Size* pPaperMin, Size* pPaperMax, tools::Rectangle* pViewInit, tools::Rectangle* pViewMin) const
+{
+ tools::Rectangle aViewInit;
+ TakeTextAnchorRect( aViewInit );
+ if (maGeo.nRotationAngle)
+ {
+ Point aCenter(aViewInit.Center());
+ aCenter-=aViewInit.TopLeft();
+ Point aCenter0(aCenter);
+ RotatePoint(aCenter, Point(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
+ aCenter-=aCenter0;
+ aViewInit.Move(aCenter.X(),aCenter.Y());
+ }
+ Size aAnkSiz(aViewInit.GetSize());
+ aAnkSiz.AdjustWidth( -1 ); aAnkSiz.AdjustHeight( -1 ); // because GetSize() adds 1
+ Size aMaxSiz(1000000,1000000);
+ {
+ Size aTmpSiz(getSdrModelFromSdrObject().GetMaxObjSize());
+ if (aTmpSiz.Width()!=0) aMaxSiz.setWidth(aTmpSiz.Width() );
+ if (aTmpSiz.Height()!=0) aMaxSiz.setHeight(aTmpSiz.Height() );
+ }
+ SdrTextHorzAdjust eHAdj(GetTextHorizontalAdjust());
+ SdrTextVertAdjust eVAdj(GetTextVerticalAdjust());
+
+ tools::Long nMinWdt = GetMinTextFrameWidth();
+ tools::Long nMinHgt = GetMinTextFrameHeight();
+ tools::Long nMaxWdt = GetMaxTextFrameWidth();
+ tools::Long nMaxHgt = GetMaxTextFrameHeight();
+ if (nMinWdt<1) nMinWdt=1;
+ if (nMinHgt<1) nMinHgt=1;
+ if ( nMaxWdt == 0 || nMaxWdt > aMaxSiz.Width() )
+ nMaxWdt = aMaxSiz.Width();
+ if ( nMaxHgt == 0 || nMaxHgt > aMaxSiz.Height() )
+ nMaxHgt=aMaxSiz.Height();
+
+ if (GetMergedItem(SDRATTR_TEXT_WORDWRAP).GetValue())
+ {
+ if ( IsVerticalWriting() )
+ {
+ nMaxHgt = aAnkSiz.Height();
+ nMinHgt = nMaxHgt;
+ }
+ else
+ {
+ nMaxWdt = aAnkSiz.Width();
+ nMinWdt = nMaxWdt;
+ }
+ }
+ Size aPaperMax(nMaxWdt, nMaxHgt);
+ Size aPaperMin(nMinWdt, nMinHgt);
+
+ if ( pViewMin )
+ {
+ *pViewMin = aViewInit;
+
+ tools::Long nXFree = aAnkSiz.Width() - aPaperMin.Width();
+ if ( eHAdj == SDRTEXTHORZADJUST_LEFT )
+ pViewMin->AdjustRight( -nXFree );
+ else if ( eHAdj == SDRTEXTHORZADJUST_RIGHT )
+ pViewMin->AdjustLeft(nXFree );
+ else { pViewMin->AdjustLeft(nXFree / 2 ); pViewMin->SetRight( pViewMin->Left() + aPaperMin.Width() ); }
+
+ tools::Long nYFree = aAnkSiz.Height() - aPaperMin.Height();
+ if ( eVAdj == SDRTEXTVERTADJUST_TOP )
+ pViewMin->AdjustBottom( -nYFree );
+ else if ( eVAdj == SDRTEXTVERTADJUST_BOTTOM )
+ pViewMin->AdjustTop(nYFree );
+ else { pViewMin->AdjustTop(nYFree / 2 ); pViewMin->SetBottom( pViewMin->Top() + aPaperMin.Height() ); }
+ }
+
+ if( IsVerticalWriting() )
+ aPaperMin.setWidth( 0 );
+ else
+ aPaperMin.setHeight( 0 );
+
+ if( eHAdj != SDRTEXTHORZADJUST_BLOCK )
+ aPaperMin.setWidth(0 );
+
+ // For complete vertical adjust support, set paper min height to 0, here.
+ if(SDRTEXTVERTADJUST_BLOCK != eVAdj )
+ aPaperMin.setHeight( 0 );
+
+ if (pPaperMin!=nullptr) *pPaperMin=aPaperMin;
+ if (pPaperMax!=nullptr) *pPaperMax=aPaperMax;
+ if (pViewInit!=nullptr) *pViewInit=aViewInit;
+}
+void SdrObjCustomShape::EndTextEdit( SdrOutliner& rOutl )
+{
+ SdrTextObj::EndTextEdit( rOutl );
+ InvalidateRenderGeometry();
+}
+void SdrObjCustomShape::TakeTextAnchorRect( tools::Rectangle& rAnchorRect ) const
+{
+ if ( GetTextBounds( rAnchorRect ) )
+ {
+ Point aRotateRef( maSnapRect.Center() );
+ AdjustRectToTextDistance(rAnchorRect);
+
+ if ( rAnchorRect.GetWidth() < 2 )
+ rAnchorRect.SetRight( rAnchorRect.Left() + 1 ); // minimal width is 2
+ if ( rAnchorRect.GetHeight() < 2 )
+ rAnchorRect.SetBottom( rAnchorRect.Top() + 1 ); // minimal height is 2
+ if (maGeo.nRotationAngle)
+ {
+ Point aP( rAnchorRect.TopLeft() );
+ RotatePoint(aP, aRotateRef, maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
+ rAnchorRect.SetPos( aP );
+ }
+ }
+ else
+ SdrTextObj::TakeTextAnchorRect( rAnchorRect );
+}
+void SdrObjCustomShape::TakeTextRect( SdrOutliner& rOutliner, tools::Rectangle& rTextRect, bool bNoEditText,
+ tools::Rectangle* pAnchorRect, bool /*bLineWidth*/) const
+{
+ tools::Rectangle aAnkRect; // Rect in which we anchor
+ TakeTextAnchorRect(aAnkRect);
+ SdrTextVertAdjust eVAdj=GetTextVerticalAdjust();
+ SdrTextHorzAdjust eHAdj=GetTextHorizontalAdjust();
+ EEControlBits nStat0=rOutliner.GetControlWord();
+ Size aNullSize;
+
+ rOutliner.SetControlWord(nStat0|EEControlBits::AUTOPAGESIZE);
+ rOutliner.SetMinAutoPaperSize(aNullSize);
+ sal_Int32 nMaxAutoPaperWidth = 1000000;
+ sal_Int32 nMaxAutoPaperHeight= 1000000;
+
+ tools::Long nAnkWdt=aAnkRect.GetWidth();
+ tools::Long nAnkHgt=aAnkRect.GetHeight();
+
+ if (GetMergedItem(SDRATTR_TEXT_WORDWRAP).GetValue())
+ {
+ if ( IsVerticalWriting() )
+ nMaxAutoPaperHeight = nAnkHgt;
+ else
+ nMaxAutoPaperWidth = nAnkWdt;
+ }
+ if(SDRTEXTHORZADJUST_BLOCK == eHAdj && !IsVerticalWriting())
+ {
+ rOutliner.SetMinAutoPaperSize(Size(nAnkWdt, 0));
+ }
+
+ if(SDRTEXTVERTADJUST_BLOCK == eVAdj && IsVerticalWriting())
+ {
+ rOutliner.SetMinAutoPaperSize(Size(0, nAnkHgt));
+ }
+ rOutliner.SetMaxAutoPaperSize( Size( nMaxAutoPaperWidth, nMaxAutoPaperHeight ) );
+ rOutliner.SetPaperSize( aNullSize );
+
+ // put text into the Outliner - if necessary the use the text from the EditOutliner
+ std::optional<OutlinerParaObject> pPara;
+ if (GetOutlinerParaObject())
+ pPara = *GetOutlinerParaObject();
+ if (mpEditingOutliner && !bNoEditText)
+ pPara=mpEditingOutliner->CreateParaObject();
+
+ if (pPara)
+ {
+ bool bHitTest(&getSdrModelFromSdrObject().GetHitTestOutliner() == &rOutliner);
+ const SdrTextObj* pTestObj = rOutliner.GetTextObj();
+
+ if( !pTestObj || !bHitTest || pTestObj != this ||
+ pTestObj->GetOutlinerParaObject() != GetOutlinerParaObject() )
+ {
+ if( bHitTest )
+ rOutliner.SetTextObj( this );
+
+ rOutliner.SetUpdateLayout(true);
+ rOutliner.SetText(*pPara);
+ }
+ }
+ else
+ {
+ rOutliner.SetTextObj( nullptr );
+ }
+
+ rOutliner.SetUpdateLayout(true);
+ rOutliner.SetControlWord(nStat0);
+
+ SdrText* pText = getActiveText();
+ if( pText )
+ pText->CheckPortionInfo( rOutliner );
+
+ Point aTextPos(aAnkRect.TopLeft());
+ Size aTextSiz(rOutliner.GetPaperSize()); // GetPaperSize() has a little added tolerance, no?
+
+ // For draw objects containing text correct horizontal/vertical alignment if text is bigger
+ // than the object itself. Without that correction, the text would always be
+ // formatted to the left edge (or top edge when vertical) of the draw object.
+
+ if( !IsTextFrame() )
+ {
+ if(aAnkRect.GetWidth() < aTextSiz.Width() && !IsVerticalWriting())
+ {
+ // Horizontal case here. Correct only if eHAdj == SDRTEXTHORZADJUST_BLOCK,
+ // else the alignment is wanted.
+ if(SDRTEXTHORZADJUST_BLOCK == eHAdj)
+ {
+ SvxAdjust eAdjust = GetObjectItemSet().Get(EE_PARA_JUST).GetAdjust();
+ switch (eAdjust)
+ {
+ case SvxAdjust::Left: eHAdj = SDRTEXTHORZADJUST_LEFT; break;
+ case SvxAdjust::Right: eHAdj = SDRTEXTHORZADJUST_RIGHT; break;
+ case SvxAdjust::Center: eHAdj = SDRTEXTHORZADJUST_CENTER; break;
+ default: break;
+ }
+ }
+ }
+
+ if(aAnkRect.GetHeight() < aTextSiz.Height() && IsVerticalWriting())
+ {
+ // Vertical case here. Correct only if eHAdj == SDRTEXTVERTADJUST_BLOCK,
+ // else the alignment is wanted.
+ if(SDRTEXTVERTADJUST_BLOCK == eVAdj)
+ {
+ eVAdj = SDRTEXTVERTADJUST_CENTER;
+ }
+ }
+ }
+
+ if (eHAdj==SDRTEXTHORZADJUST_CENTER || eHAdj==SDRTEXTHORZADJUST_RIGHT)
+ {
+ tools::Long nFreeWdt=aAnkRect.GetWidth()-aTextSiz.Width();
+ if (eHAdj==SDRTEXTHORZADJUST_CENTER)
+ aTextPos.AdjustX(nFreeWdt/2 );
+ if (eHAdj==SDRTEXTHORZADJUST_RIGHT)
+ aTextPos.AdjustX(nFreeWdt );
+ }
+ if (eVAdj==SDRTEXTVERTADJUST_CENTER || eVAdj==SDRTEXTVERTADJUST_BOTTOM)
+ {
+ tools::Long nFreeHgt=aAnkRect.GetHeight()-aTextSiz.Height();
+ if (eVAdj==SDRTEXTVERTADJUST_CENTER)
+ aTextPos.AdjustY(nFreeHgt/2 );
+ if (eVAdj==SDRTEXTVERTADJUST_BOTTOM)
+ aTextPos.AdjustY(nFreeHgt );
+ }
+ if (maGeo.nRotationAngle != 0_deg100)
+ RotatePoint(aTextPos,aAnkRect.TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
+
+ if (pAnchorRect)
+ *pAnchorRect=aAnkRect;
+
+ // using rTextRect together with ContourFrame doesn't always work correctly
+ rTextRect=tools::Rectangle(aTextPos,aTextSiz);
+}
+
+void SdrObjCustomShape::NbcSetOutlinerParaObject(std::optional<OutlinerParaObject> pTextObject)
+{
+ SdrTextObj::NbcSetOutlinerParaObject( std::move(pTextObject) );
+ SetBoundRectDirty();
+ SetBoundAndSnapRectsDirty(true);
+ InvalidateRenderGeometry();
+}
+
+SdrObjCustomShape* SdrObjCustomShape::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new SdrObjCustomShape(rTargetModel, *this);
+}
+
+OUString SdrObjCustomShape::TakeObjNameSingul() const
+{
+ OUString sName(SvxResId(STR_ObjNameSingulCUSTOMSHAPE));
+ OUString aNm(GetName());
+ if (!aNm.isEmpty())
+ sName += " '" + aNm + "'";
+ return sName;
+}
+
+OUString SdrObjCustomShape::TakeObjNamePlural() const
+{
+ return SvxResId(STR_ObjNamePluralCUSTOMSHAPE);
+}
+
+basegfx::B2DPolyPolygon SdrObjCustomShape::TakeXorPoly() const
+{
+ return GetLineGeometry( false );
+}
+
+basegfx::B2DPolyPolygon SdrObjCustomShape::TakeContour() const
+{
+ const SdrObject* pSdrObject = GetSdrObjectFromCustomShape();
+ if ( pSdrObject )
+ return pSdrObject->TakeContour();
+ return basegfx::B2DPolyPolygon();
+}
+
+SdrObjectUniquePtr SdrObjCustomShape::DoConvertToPolyObj(bool bBezier, bool bAddText) const
+{
+ // #i37011#
+ SdrObjectUniquePtr pRetval;
+ SdrObject* pRenderedCustomShape = nullptr;
+
+ if ( !mXRenderedCustomShape.is() )
+ {
+ // force CustomShape
+ GetSdrObjectFromCustomShape();
+ }
+
+ if ( mXRenderedCustomShape.is() )
+ {
+ pRenderedCustomShape = SdrObject::getSdrObjectFromXShape(mXRenderedCustomShape);
+ }
+
+ if ( pRenderedCustomShape )
+ {
+ // Clone to same SdrModel
+ SdrObject* pCandidate(pRenderedCustomShape->CloneSdrObject(pRenderedCustomShape->getSdrModelFromSdrObject()));
+ DBG_ASSERT(pCandidate, "SdrObjCustomShape::DoConvertToPolyObj: Could not clone SdrObject (!)");
+ pRetval = pCandidate->DoConvertToPolyObj(bBezier, bAddText);
+ SdrObject::Free( pCandidate );
+
+ if(pRetval)
+ {
+ const bool bShadow(GetMergedItem(SDRATTR_SHADOW).GetValue());
+ if(bShadow)
+ {
+ pRetval->SetMergedItem(makeSdrShadowItem(true));
+ }
+ }
+
+ if(bAddText && HasText() && !IsTextPath())
+ {
+ pRetval = ImpConvertAddText(std::move(pRetval), bBezier);
+ }
+ }
+
+ return pRetval;
+}
+
+void SdrObjCustomShape::InternalSetStyleSheet( SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr, bool bBroadcast )
+{
+ // #i40944#
+ InvalidateRenderGeometry();
+ SdrObject::InternalSetStyleSheet( pNewStyleSheet, bDontRemoveHardAttr, bBroadcast );
+}
+
+void SdrObjCustomShape::handlePageChange(SdrPage* pOldPage, SdrPage* pNewPage)
+{
+ // call parent
+ SdrTextObj::handlePageChange(pOldPage, pNewPage);
+
+ if(nullptr != pNewPage)
+ {
+ // invalidating rectangles by SetRectsDirty is not sufficient,
+ // AdjustTextFrameWidthAndHeight() also has to be made, both
+ // actions are done by NbcSetSnapRect
+ tools::Rectangle aTmp( maRect ); //creating temporary rectangle #i61108#
+ NbcSetSnapRect( aTmp );
+ }
+}
+
+std::unique_ptr<SdrObjGeoData> SdrObjCustomShape::NewGeoData() const
+{
+ return std::make_unique<SdrAShapeObjGeoData>();
+}
+
+void SdrObjCustomShape::SaveGeoData(SdrObjGeoData& rGeo) const
+{
+ SdrTextObj::SaveGeoData( rGeo );
+ SdrAShapeObjGeoData& rAGeo=static_cast<SdrAShapeObjGeoData&>(rGeo);
+ rAGeo.fObjectRotation = fObjectRotation;
+ rAGeo.bMirroredX = IsMirroredX();
+ rAGeo.bMirroredY = IsMirroredY();
+
+ const Any* pAny = GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ).GetPropertyValueByName( "AdjustmentValues" );
+ if ( pAny )
+ *pAny >>= rAGeo.aAdjustmentSeq;
+}
+
+void SdrObjCustomShape::RestoreGeoData(const SdrObjGeoData& rGeo)
+{
+ SdrTextObj::RestoreGeoData( rGeo );
+ const SdrAShapeObjGeoData& rAGeo=static_cast<const SdrAShapeObjGeoData&>(rGeo);
+ fObjectRotation = rAGeo.fObjectRotation;
+ SetMirroredX( rAGeo.bMirroredX );
+ SetMirroredY( rAGeo.bMirroredY );
+
+ SdrCustomShapeGeometryItem rGeometryItem = GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY );
+ PropertyValue aPropVal;
+ aPropVal.Name = "AdjustmentValues";
+ aPropVal.Value <<= rAGeo.aAdjustmentSeq;
+ rGeometryItem.SetPropertyValue( aPropVal );
+ SetMergedItem( rGeometryItem );
+
+ InvalidateRenderGeometry();
+}
+
+void SdrObjCustomShape::AdjustToMaxRect(const tools::Rectangle& rMaxRect, bool bShrinkOnly /* = false */)
+{
+ SAL_INFO_IF(bShrinkOnly, "svx", "Case bShrinkOnly == true is not implemented yet.");
+
+ if (rMaxRect.IsEmpty() || rMaxRect == GetSnapRect())
+ return;
+
+ // Get a matrix, that would produce the existing shape, when applied to a unit square
+ basegfx::B2DPolyPolygon aPolyPolygon; //not used, but formal needed
+ basegfx::B2DHomMatrix aMatrix;
+ TRGetBaseGeometry(aMatrix, aPolyPolygon);
+ // Using TRSetBaseGeometry(aMatrix, aPolyPolygon) would regenerate the current shape. But
+ // applying aMatrix to a unit square will not generate the current shape. Scaling,
+ // rotation and translation are correct, but shear angle has wrong sign. So break up
+ // matrix and create a mathematically correct new one.
+ basegfx::B2DTuple aScale;
+ basegfx::B2DTuple aTranslate;
+ double fRotate, fShearX;
+ aMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
+ basegfx::B2DHomMatrix aMathMatrix;
+ aMathMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ aScale,
+ basegfx::fTools::equalZero(fShearX) ? 0.0 : -fShearX,
+ basegfx::fTools::equalZero(fRotate) ? 0.0 : fRotate,
+ aTranslate);
+
+ // Calculate scaling factors from size of the transformed unit polygon as ersatz for the not
+ // usable current snap rectangle.
+ basegfx::B2DPolygon aB2DPolygon(basegfx::utils::createUnitPolygon());
+ aB2DPolygon.transform(aMathMatrix);
+ basegfx::B2DRange aB2DRange(aB2DPolygon.getB2DRange());
+ double fPolygonWidth = aB2DRange.getWidth();
+ if (fPolygonWidth == 0)
+ fPolygonWidth = 1;
+ double fPolygonHeight = aB2DRange.getHeight();
+ if (fPolygonHeight == 0)
+ fPolygonHeight = 1;
+ const double aFactorX = static_cast<double>(rMaxRect.GetWidth()) / fPolygonWidth;
+ const double aFactorY = static_cast<double>(rMaxRect.GetHeight()) / fPolygonHeight;
+
+ // Generate matrix, that would produce the desired rMaxRect when applied to unit square
+ aMathMatrix.scale(aFactorX, aFactorY);
+ aB2DPolygon = basegfx::utils::createUnitPolygon();
+ aB2DPolygon.transform(aMathMatrix);
+ aB2DRange = aB2DPolygon.getB2DRange();
+ const double fPolygonLeft = aB2DRange.getMinX();
+ const double fPolygonTop = aB2DRange.getMinY();
+ aMathMatrix.translate(rMaxRect.Left() - fPolygonLeft, rMaxRect.Top() - fPolygonTop);
+
+ // Create a Matrix from aMathMatrix, which is usable with TRSetBaseGeometry
+ aMathMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
+ aMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ aScale,
+ basegfx::fTools::equalZero(fShearX) ? 0.0 : -fShearX,
+ basegfx::fTools::equalZero(fRotate) ? 0.0 : fRotate,
+ aTranslate);
+
+ // Now use TRSetBaseGeometry to actually perform scale, shear, rotate and translate
+ // on the shape. That considers gluepoints, interaction handles and text area, and includes
+ // setting rectangles dirty and broadcast.
+ TRSetBaseGeometry(aMatrix, aPolyPolygon);
+}
+
+void SdrObjCustomShape::TRSetBaseGeometry(const basegfx::B2DHomMatrix& rMatrix, const basegfx::B2DPolyPolygon& /*rPolyPolygon*/)
+{
+ // The shape might have already flipping in its enhanced geometry. LibreOffice applies
+ // such after all transformations. We remove it, but remember it to apply them later.
+ bool bIsMirroredX = IsMirroredX();
+ bool bIsMirroredY = IsMirroredY();
+ if (bIsMirroredX || bIsMirroredY)
+ {
+ Point aCurrentCenter = GetSnapRect().Center();
+ if (bIsMirroredX) // mirror on the y-axis
+ {
+ Mirror(aCurrentCenter, Point(aCurrentCenter.X(), aCurrentCenter.Y() + 1000));
+ }
+ if (bIsMirroredY) // mirror on the x-axis
+ {
+ Mirror(aCurrentCenter, Point(aCurrentCenter.X() + 1000, aCurrentCenter.Y()));
+ }
+ }
+
+ // break up matrix
+ basegfx::B2DTuple aScale;
+ basegfx::B2DTuple aTranslate;
+ double fRotate, fShearX;
+ rMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // reset object shear and rotations
+ fObjectRotation = 0.0;
+ maGeo.nRotationAngle = 0_deg100;
+ maGeo.RecalcSinCos();
+ maGeo.nShearAngle = 0_deg100;
+ maGeo.RecalcTan();
+
+ // if anchor is used, make position relative to it
+ if(getSdrModelFromSdrObject().IsWriter())
+ {
+ if(GetAnchorPos().X() || GetAnchorPos().Y())
+ {
+ aTranslate += basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
+ }
+ }
+
+ // scale
+ Size aSize(FRound(fabs(aScale.getX())), FRound(fabs(aScale.getY())));
+ // fdo#47434 We need a valid rectangle here
+ if( !aSize.Height() ) aSize.setHeight( 1 );
+ if( !aSize.Width() ) aSize.setWidth( 1 );
+ tools::Rectangle aBaseRect(Point(), aSize);
+ SetLogicRect(aBaseRect);
+
+ // Apply flipping from Matrix, which is a transformation relative to origin
+ if (basegfx::fTools::less(aScale.getX(), 0.0))
+ Mirror(Point(0, 0), Point(0, 1000)); // mirror on the y-axis
+ if (basegfx::fTools::less(aScale.getY(), 0.0))
+ Mirror(Point(0, 0), Point(1000, 0)); // mirror on the x-axis
+
+ // shear?
+ if(!basegfx::fTools::equalZero(fShearX))
+ {
+ GeoStat aGeoStat;
+ // #i123181# The fix for #121932# here was wrong, the trunk version does not correct the
+ // mirrored shear values, neither at the object level, nor on the API or XML level. Taking
+ // back the mirroring of the shear angle
+ aGeoStat.nShearAngle = Degree100(FRound(basegfx::rad2deg<100>(atan(fShearX))));
+ aGeoStat.RecalcTan();
+ Shear(Point(), aGeoStat.nShearAngle, aGeoStat.mfTanShearAngle, false);
+ }
+
+ // rotation?
+ if(!basegfx::fTools::equalZero(fRotate))
+ {
+ GeoStat aGeoStat;
+
+ // #i78696#
+ // fRotate is mathematically correct, but aGeoStat.nRotationAngle is
+ // mirrored -> mirror value here
+ aGeoStat.nRotationAngle = NormAngle36000(Degree100(FRound(-basegfx::rad2deg<100>(fRotate))));
+ aGeoStat.RecalcSinCos();
+ Rotate(Point(), aGeoStat.nRotationAngle, aGeoStat.mfSinRotationAngle, aGeoStat.mfCosRotationAngle);
+ }
+
+ // translate?
+ if(!aTranslate.equalZero())
+ {
+ Move(Size(FRound(aTranslate.getX()), FRound(aTranslate.getY())));
+ }
+
+ // Apply flipping from enhanced geometry at center of the shape.
+ if (!(bIsMirroredX || bIsMirroredY))
+ return;
+
+ // create mathematically matrix for the applied transformations
+ // aScale was in most cases built from a rectangle including edge
+ // and is therefore mathematically too large by 1
+ if (aScale.getX() > 2.0 && aScale.getY() > 2.0)
+ aScale -= basegfx::B2DTuple(1.0, 1.0);
+ basegfx::B2DHomMatrix aMathMat = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ aScale, -fShearX, basegfx::fTools::equalZero(fRotate) ? 0.0 : fRotate,
+ aTranslate);
+ // Use matrix to get current center
+ basegfx::B2DPoint aCenter(0.5,0.5);
+ aCenter = aMathMat * aCenter;
+ double fCenterX = aCenter.getX();
+ double fCenterY = aCenter.getY();
+ if (bIsMirroredX) // vertical axis
+ Mirror(Point(FRound(fCenterX),FRound(fCenterY)),
+ Point(FRound(fCenterX), FRound(fCenterY + 1000.0)));
+ if (bIsMirroredY) // horizontal axis
+ Mirror(Point(FRound(fCenterX),FRound(fCenterY)),
+ Point(FRound(fCenterX + 1000.0), FRound(fCenterY)));
+}
+
+// taking fObjectRotation instead of aGeo.nAngle
+bool SdrObjCustomShape::TRGetBaseGeometry(basegfx::B2DHomMatrix& rMatrix, basegfx::B2DPolyPolygon& /*rPolyPolygon*/) const
+{
+ // get turn and shear
+ double fRotate = basegfx::deg2rad(fObjectRotation);
+ double fShearX = toRadians(maGeo.nShearAngle);
+
+ // get aRect, this is the unrotated snaprect
+ tools::Rectangle aRectangle(maRect);
+
+ bool bMirroredX = IsMirroredX();
+ bool bMirroredY = IsMirroredY();
+ if ( bMirroredX || bMirroredY )
+ { // we have to retrieve the unmirrored rect
+
+ GeoStat aNewGeo(maGeo);
+
+ if ( bMirroredX )
+ {
+ fShearX = -fShearX;
+ tools::Polygon aPol = Rect2Poly(maRect, aNewGeo);
+ tools::Rectangle aBoundRect( aPol.GetBoundRect() );
+
+ Point aRef1( ( aBoundRect.Left() + aBoundRect.Right() ) >> 1, aBoundRect.Top() );
+ Point aRef2( aRef1.X(), aRef1.Y() + 1000 );
+ sal_uInt16 i;
+ sal_uInt16 nPointCount=aPol.GetSize();
+ for (i=0; i<nPointCount; i++)
+ {
+ MirrorPoint(aPol[i],aRef1,aRef2);
+ }
+ // mirror polygon and move it a bit
+ tools::Polygon aPol0(aPol);
+ aPol[0]=aPol0[1];
+ aPol[1]=aPol0[0];
+ aPol[2]=aPol0[3];
+ aPol[3]=aPol0[2];
+ aPol[4]=aPol0[1];
+ Poly2Rect(aPol,aRectangle,aNewGeo);
+ }
+ if ( bMirroredY )
+ {
+ fShearX = -fShearX;
+ tools::Polygon aPol( Rect2Poly( aRectangle, aNewGeo ) );
+ tools::Rectangle aBoundRect( aPol.GetBoundRect() );
+
+ Point aRef1( aBoundRect.Left(), ( aBoundRect.Top() + aBoundRect.Bottom() ) >> 1 );
+ Point aRef2( aRef1.X() + 1000, aRef1.Y() );
+ sal_uInt16 i;
+ sal_uInt16 nPointCount=aPol.GetSize();
+ for (i=0; i<nPointCount; i++)
+ {
+ MirrorPoint(aPol[i],aRef1,aRef2);
+ }
+ // mirror polygon and move it a bit
+ tools::Polygon aPol0(aPol);
+ aPol[0]=aPol0[1]; // This was WRONG for vertical (!)
+ aPol[1]=aPol0[0]; // #i121932# Despite my own comment above
+ aPol[2]=aPol0[3]; // it was *not* wrong even when the reordering
+ aPol[3]=aPol0[2]; // *seems* to be specific for X-Mirrorings. Oh
+ aPol[4]=aPol0[1]; // will I be happy when this old stuff is |gone| with aw080 (!)
+ Poly2Rect(aPol,aRectangle,aNewGeo);
+ }
+ }
+
+ // fill other values
+ basegfx::B2DTuple aScale(aRectangle.GetWidth(), aRectangle.GetHeight());
+ basegfx::B2DTuple aTranslate(aRectangle.Left(), aRectangle.Top());
+
+ // position may be relative to anchorpos, convert
+ if(getSdrModelFromSdrObject().IsWriter())
+ {
+ if(GetAnchorPos().X() || GetAnchorPos().Y())
+ {
+ aTranslate -= basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
+ }
+ }
+
+ // build matrix
+ rMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ aScale,
+ basegfx::fTools::equalZero(fShearX) ? 0.0 : tan(fShearX),
+ basegfx::fTools::equalZero(fRotate) ? 0.0 : -fRotate,
+ aTranslate);
+
+ return false;
+}
+
+std::unique_ptr<sdr::contact::ViewContact> SdrObjCustomShape::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::ViewContactOfSdrObjCustomShape>(*this);
+}
+
+// #i33136#
+bool SdrObjCustomShape::doConstructOrthogonal(std::u16string_view rName)
+{
+ bool bRetval(false);
+
+ if(o3tl::equalsIgnoreAsciiCase(rName, u"quadrat"))
+ {
+ bRetval = true;
+ }
+ else if(o3tl::equalsIgnoreAsciiCase(rName, u"round-quadrat"))
+ {
+ bRetval = true;
+ }
+ else if(o3tl::equalsIgnoreAsciiCase(rName, u"circle"))
+ {
+ bRetval = true;
+ }
+ else if(o3tl::equalsIgnoreAsciiCase(rName, u"circle-pie"))
+ {
+ bRetval = true;
+ }
+ else if(o3tl::equalsIgnoreAsciiCase(rName, u"ring"))
+ {
+ bRetval = true;
+ }
+
+ return bRetval;
+}
+
+// #i37011# centralize throw-away of render geometry
+void SdrObjCustomShape::InvalidateRenderGeometry()
+{
+ mXRenderedCustomShape = nullptr;
+ SdrObject::Free( mpLastShadowGeometry );
+ mpLastShadowGeometry = nullptr;
+}
+
+void SdrObjCustomShape::setUnoShape(const uno::Reference<drawing::XShape>& rxUnoShape)
+{
+ SdrTextObj::setUnoShape(rxUnoShape);
+
+ // The shape engine is created with _current_ shape. This means we
+ // _must_ reset it when the shape changes.
+ mxCustomShapeEngine.set(nullptr);
+}
+
+OUString SdrObjCustomShape::GetCustomShapeName() const
+{
+ OUString sShapeName;
+ OUString aEngine( GetMergedItem( SDRATTR_CUSTOMSHAPE_ENGINE ).GetValue() );
+ if ( aEngine.isEmpty()
+ || aEngine == "com.sun.star.drawing.EnhancedCustomShapeEngine" )
+ {
+ OUString sShapeType;
+ const SdrCustomShapeGeometryItem& rGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
+ const Any* pAny = rGeometryItem.GetPropertyValueByName( "Type" );
+ if ( pAny && ( *pAny >>= sShapeType ) )
+ sShapeName = EnhancedCustomShapeTypeNames::GetAccName( sShapeType );
+ }
+ return sShapeName;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdoattr.cxx b/svx/source/svdraw/svdoattr.cxx
new file mode 100644
index 000000000..2c1b208d3
--- /dev/null
+++ b/svx/source/svdraw/svdoattr.cxx
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/svdoattr.hxx>
+#include <svx/svdmodel.hxx>
+#include <svl/hint.hxx>
+#include <svl/itemset.hxx>
+#include <svx/xdef.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xlnwtit.hxx>
+#include <sdr/properties/attributeproperties.hxx>
+
+using namespace com::sun::star;
+
+std::unique_ptr<sdr::properties::BaseProperties> SdrAttrObj::CreateObjectSpecificProperties()
+{
+ return std::make_unique<sdr::properties::AttributeProperties>(*this);
+}
+
+SdrAttrObj::SdrAttrObj(SdrModel& rSdrModel)
+ : SdrObject(rSdrModel)
+{
+}
+
+SdrAttrObj::SdrAttrObj(SdrModel& rSdrModel, SdrAttrObj const& rSource)
+ : SdrObject(rSdrModel, rSource)
+{
+}
+
+SdrAttrObj::~SdrAttrObj() {}
+
+const tools::Rectangle& SdrAttrObj::GetSnapRect() const
+{
+ if (m_bSnapRectDirty)
+ {
+ const_cast<SdrAttrObj*>(this)->RecalcSnapRect();
+ const_cast<SdrAttrObj*>(this)->m_bSnapRectDirty = false;
+ }
+
+ return maSnapRect;
+}
+
+// syntactical sugar for ItemSet accesses
+void SdrAttrObj::Notify(SfxBroadcaster& /*rBC*/, const SfxHint& rHint)
+{
+ bool bDataChg(SfxHintId::DataChanged == rHint.GetId());
+
+ if (bDataChg)
+ {
+ tools::Rectangle aBoundRect = GetLastBoundRect();
+ SetBoundRectDirty();
+ SetBoundAndSnapRectsDirty(/*bNotMyself*/ true);
+
+ // This may have led to object change
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::ChangeAttr, aBoundRect);
+ }
+}
+
+sal_Int32 SdrAttrObj::ImpGetLineWdt() const
+{
+ sal_Int32 nRetval(0);
+
+ if (drawing::LineStyle_NONE != GetObjectItem(XATTR_LINESTYLE).GetValue())
+ {
+ nRetval = GetObjectItem(XATTR_LINEWIDTH).GetValue();
+ }
+
+ return nRetval;
+}
+
+bool SdrAttrObj::HasFill() const
+{
+ return m_bClosedObj
+ && GetProperties().GetObjectItemSet().Get(XATTR_FILLSTYLE).GetValue()
+ != drawing::FillStyle_NONE;
+}
+
+bool SdrAttrObj::HasLine() const
+{
+ return GetProperties().GetObjectItemSet().Get(XATTR_LINESTYLE).GetValue()
+ != drawing::LineStyle_NONE;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdobj.cxx b/svx/source/svdraw/svdobj.cxx
new file mode 100644
index 000000000..4f13059a7
--- /dev/null
+++ b/svx/source/svdraw/svdobj.cxx
@@ -0,0 +1,3375 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/svdobj.hxx>
+#include <config_features.h>
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/text/RelOrientation.hpp>
+#include <com/sun/star/frame/XTerminateListener.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygoncutter.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <drawinglayer/processor2d/contourextractor2d.hxx>
+#include <drawinglayer/processor2d/linegeometryextractor2d.hxx>
+#include <comphelper/processfactory.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/outlobj.hxx>
+#include <o3tl/deleter.hxx>
+#include <math.h>
+#include <svl/grabbagitem.hxx>
+#include <tools/bigint.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/helpers.hxx>
+#include <unotools/configmgr.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <vector>
+
+#include <svx/shapepropertynotifier.hxx>
+#include <svx/svdotable.hxx>
+
+#include <svx/sdr/contact/displayinfo.hxx>
+#include <sdr/contact/objectcontactofobjlistpainter.hxx>
+#include <svx/sdr/contact/viewcontactofsdrobj.hxx>
+#include <sdr/properties/emptyproperties.hxx>
+#include <svx/sdrhittesthelper.hxx>
+#include <svx/sdrobjectuser.hxx>
+#include <svx/sdrobjectfilter.hxx>
+#include <svx/svddrag.hxx>
+#include <svx/svdetc.hxx>
+#include <svx/svdhdl.hxx>
+#include <svx/svditer.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdoashp.hxx>
+#include <svx/svdocapt.hxx>
+#include <svx/svdocirc.hxx>
+#include <svx/svdoedge.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svdomeas.hxx>
+#include <svx/svdomedia.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdopage.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/svdorect.hxx>
+#include <svx/svdotext.hxx>
+#include <svx/svdouno.hxx>
+#include <svx/svdovirt.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdpool.hxx>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include <svx/svdtrans.hxx>
+#include <svx/svdundo.hxx>
+#include <svx/svdview.hxx>
+#include <sxlayitm.hxx>
+#include <sxlogitm.hxx>
+#include <sxmovitm.hxx>
+#include <sxoneitm.hxx>
+#include <sxopitm.hxx>
+#include <sxreoitm.hxx>
+#include <sxrooitm.hxx>
+#include <sxsaitm.hxx>
+#include <sxsoitm.hxx>
+#include <sxtraitm.hxx>
+#include <svx/unopage.hxx>
+#include <svx/unoshape.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflclit.hxx>
+#include <svx/xfltrit.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/xlnclit.hxx>
+#include <svx/xlnedwit.hxx>
+#include <svx/xlnstwit.hxx>
+#include <svx/xlntrit.hxx>
+#include <svx/xlnwtit.hxx>
+#include <svx/svdglue.hxx>
+#include <svx/svdsob.hxx>
+#include <svdobjplusdata.hxx>
+#include <svdobjuserdatalist.hxx>
+
+#include <unordered_set>
+
+#include <optional>
+#include <libxml/xmlwriter.h>
+#include <memory>
+
+#include <svx/scene3d.hxx>
+#include <rtl/character.hxx>
+#include <tools/UnitConversion.hxx>
+#include <o3tl/string_view.hxx>
+
+using namespace ::com::sun::star;
+
+
+SdrObjUserCall::~SdrObjUserCall()
+{
+}
+
+void SdrObjUserCall::Changed(const SdrObject& /*rObj*/, SdrUserCallType /*eType*/, const tools::Rectangle& /*rOldBoundRect*/)
+{
+}
+
+sal_Int32 SdrObjUserCall::GetPDFAnchorStructureElementId(SdrObject const&, OutputDevice const&)
+{
+ return -1;
+}
+
+SdrObjMacroHitRec::SdrObjMacroHitRec() :
+ pVisiLayer(nullptr),
+ pPageView(nullptr),
+ nTol(0) {}
+
+
+SdrObjUserData::SdrObjUserData(SdrInventor nInv, sal_uInt16 nId) :
+ nInventor(nInv),
+ nIdentifier(nId) {}
+
+SdrObjUserData::SdrObjUserData(const SdrObjUserData& rData) :
+ nInventor(rData.nInventor),
+ nIdentifier(rData.nIdentifier) {}
+
+SdrObjUserData::~SdrObjUserData() {}
+
+SdrObjGeoData::SdrObjGeoData():
+ bMovProt(false),
+ bSizProt(false),
+ bNoPrint(false),
+ bClosedObj(false),
+ mbVisible(true),
+ mnLayerID(0)
+{
+}
+
+SdrObjGeoData::~SdrObjGeoData()
+{
+}
+
+SdrObjTransformInfoRec::SdrObjTransformInfoRec() :
+ bMoveAllowed(true),
+ bResizeFreeAllowed(true),
+ bResizePropAllowed(true),
+ bRotateFreeAllowed(true),
+ bRotate90Allowed(true),
+ bMirrorFreeAllowed(true),
+ bMirror45Allowed(true),
+ bMirror90Allowed(true),
+ bTransparenceAllowed(true),
+ bShearAllowed(true),
+ bEdgeRadiusAllowed(true),
+ bNoOrthoDesired(true),
+ bNoContortion(true),
+ bCanConvToPath(true),
+ bCanConvToPoly(true),
+ bCanConvToContour(false),
+ bCanConvToPathLineToArea(true),
+ bCanConvToPolyLineToArea(true) {}
+
+struct SdrObject::Impl
+{
+ sdr::ObjectUserVector maObjectUsers;
+ std::optional<double> mnRelativeWidth;
+ std::optional<double> mnRelativeHeight;
+ sal_Int16 meRelativeWidthRelation;
+ sal_Int16 meRelativeHeightRelation;
+
+ Impl() :
+ meRelativeWidthRelation(text::RelOrientation::PAGE_FRAME),
+ meRelativeHeightRelation(text::RelOrientation::PAGE_FRAME) {}
+};
+
+const std::shared_ptr< svx::diagram::IDiagramHelper >& SdrObject::getDiagramHelper() const
+{
+ static std::shared_ptr< svx::diagram::IDiagramHelper > aEmpty;
+ return aEmpty;
+}
+
+// BaseProperties section
+
+std::unique_ptr<sdr::properties::BaseProperties> SdrObject::CreateObjectSpecificProperties()
+{
+ return std::make_unique<sdr::properties::EmptyProperties>(*this);
+}
+
+sdr::properties::BaseProperties& SdrObject::GetProperties() const
+{
+ if(!mpProperties)
+ {
+ // CAUTION(!) Do *not* call this during SdrObject construction,
+ // that will lead to wrong type-casts (dependent on constructor-level)
+ // and thus eventually create the wrong sdr::properties (!). Is there
+ // a way to check if on the stack is a SdrObject-constructor (?)
+ const_cast< SdrObject* >(this)->mpProperties =
+ const_cast< SdrObject* >(this)->CreateObjectSpecificProperties();
+ }
+
+ return *mpProperties;
+}
+
+
+// ObjectUser section
+
+void SdrObject::AddObjectUser(sdr::ObjectUser& rNewUser)
+{
+ mpImpl->maObjectUsers.push_back(&rNewUser);
+}
+
+void SdrObject::RemoveObjectUser(sdr::ObjectUser& rOldUser)
+{
+ const sdr::ObjectUserVector::iterator aFindResult =
+ std::find(mpImpl->maObjectUsers.begin(), mpImpl->maObjectUsers.end(), &rOldUser);
+ if (aFindResult != mpImpl->maObjectUsers.end())
+ {
+ mpImpl->maObjectUsers.erase(aFindResult);
+ }
+}
+
+
+// DrawContact section
+
+std::unique_ptr<sdr::contact::ViewContact> SdrObject::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::ViewContactOfSdrObj>(*this);
+}
+
+sdr::contact::ViewContact& SdrObject::GetViewContact() const
+{
+ if(!mpViewContact)
+ {
+ const_cast< SdrObject* >(this)->mpViewContact =
+ const_cast< SdrObject* >(this)->CreateObjectSpecificViewContact();
+ }
+
+ return *mpViewContact;
+}
+
+// DrawContact support: Methods for handling Object changes
+void SdrObject::ActionChanged() const
+{
+ // Do necessary ViewContact actions
+ GetViewContact().ActionChanged();
+}
+
+SdrPage* SdrObject::getSdrPageFromSdrObject() const
+{
+ if (SdrObjList* pParentList = getParentSdrObjListFromSdrObject())
+ {
+ return pParentList->getSdrPageFromSdrObjList();
+ }
+
+ return nullptr;
+}
+
+SdrModel& SdrObject::getSdrModelFromSdrObject() const
+{
+ return mrSdrModelFromSdrObject;
+}
+
+void SdrObject::setParentOfSdrObject(SdrObjList* pNewObjList)
+{
+ if(getParentSdrObjListFromSdrObject() == pNewObjList)
+ return;
+
+ // remember current page
+ SdrPage* pOldPage(getSdrPageFromSdrObject());
+
+ // set new parent
+ mpParentOfSdrObject = pNewObjList;
+
+ // get new page
+ SdrPage* pNewPage(getSdrPageFromSdrObject());
+
+ // broadcast page change over objects if needed
+ if(pOldPage != pNewPage)
+ {
+ handlePageChange(pOldPage, pNewPage);
+ }
+}
+
+SdrObjList* SdrObject::getParentSdrObjListFromSdrObject() const
+{
+ return mpParentOfSdrObject;
+}
+
+SdrObjList* SdrObject::getChildrenOfSdrObject() const
+{
+ // default has no children
+ return nullptr;
+}
+
+void SdrObject::SetBoundRectDirty()
+{
+ m_aOutRect = tools::Rectangle();
+}
+
+#ifdef DBG_UTIL
+// SdrObjectLifetimeWatchDog:
+void impAddIncarnatedSdrObjectToSdrModel(const SdrObject& rSdrObject, SdrModel& rSdrModel)
+{
+ rSdrModel.maAllIncarnatedObjects.insert(&rSdrObject);
+}
+void impRemoveIncarnatedSdrObjectToSdrModel(const SdrObject& rSdrObject, SdrModel& rSdrModel)
+{
+ if(!rSdrModel.maAllIncarnatedObjects.erase(&rSdrObject))
+ {
+ SAL_WARN("svx","SdrObject::~SdrObject: Destructed incarnation of SdrObject not member of this SdrModel (!)");
+ }
+}
+#endif
+
+SdrObject::SdrObject(SdrModel& rSdrModel)
+: mpFillGeometryDefiningShape(nullptr)
+ ,mrSdrModelFromSdrObject(rSdrModel)
+ ,m_pUserCall(nullptr)
+ ,mpImpl(new Impl)
+ ,mpParentOfSdrObject(nullptr)
+ ,m_nOrdNum(0)
+ ,mnNavigationPosition(SAL_MAX_UINT32)
+ ,mnLayerID(0)
+ ,mpSvxShape( nullptr )
+ ,mbDoNotInsertIntoPageAutomatically(false)
+{
+ m_bVirtObj =false;
+ m_bSnapRectDirty =true;
+ m_bMovProt =false;
+ m_bSizProt =false;
+ m_bNoPrint =false;
+ m_bEmptyPresObj =false;
+ m_bNotVisibleAsMaster=false;
+ m_bClosedObj =false;
+ mbVisible = true;
+
+ // #i25616#
+ mbLineIsOutsideGeometry = false;
+
+ // #i25616#
+ mbSupportTextIndentingOnLineWidthChange = false;
+
+ m_bIsEdge=false;
+ m_bIs3DObj=false;
+ m_bMarkProt=false;
+ m_bIsUnoObj=false;
+#ifdef DBG_UTIL
+ // SdrObjectLifetimeWatchDog:
+ impAddIncarnatedSdrObjectToSdrModel(*this, getSdrModelFromSdrObject());
+#endif
+}
+
+SdrObject::SdrObject(SdrModel& rSdrModel, SdrObject const & rSource)
+: mpFillGeometryDefiningShape(nullptr)
+ ,mrSdrModelFromSdrObject(rSdrModel)
+ ,m_pUserCall(nullptr)
+ ,mpImpl(new Impl)
+ ,mpParentOfSdrObject(nullptr)
+ ,m_nOrdNum(0)
+ ,mnNavigationPosition(SAL_MAX_UINT32)
+ ,mnLayerID(0)
+ ,mpSvxShape( nullptr )
+ ,mbDoNotInsertIntoPageAutomatically(false)
+{
+ m_bVirtObj =false;
+ m_bSnapRectDirty =true;
+ m_bMovProt =false;
+ m_bSizProt =false;
+ m_bNoPrint =false;
+ m_bEmptyPresObj =false;
+ m_bNotVisibleAsMaster=false;
+ m_bClosedObj =false;
+ mbVisible = true;
+
+ // #i25616#
+ mbLineIsOutsideGeometry = false;
+
+ // #i25616#
+ mbSupportTextIndentingOnLineWidthChange = false;
+
+ m_bIsEdge=false;
+ m_bIs3DObj=false;
+ m_bMarkProt=false;
+ m_bIsUnoObj=false;
+#ifdef DBG_UTIL
+ // SdrObjectLifetimeWatchDog:
+ impAddIncarnatedSdrObjectToSdrModel(*this, getSdrModelFromSdrObject());
+#endif
+
+ mpProperties.reset();
+ mpViewContact.reset();
+
+ // The CloneSdrObject() method uses the local copy constructor from the individual
+ // sdr::properties::BaseProperties class. Since the target class maybe for another
+ // draw object, an SdrObject needs to be provided, as in the normal constructor.
+ mpProperties = rSource.GetProperties().Clone(*this);
+
+ m_aOutRect=rSource.m_aOutRect;
+ mnLayerID = rSource.mnLayerID;
+ m_aAnchor =rSource.m_aAnchor;
+ m_bVirtObj=rSource.m_bVirtObj;
+ m_bSizProt=rSource.m_bSizProt;
+ m_bMovProt=rSource.m_bMovProt;
+ m_bNoPrint=rSource.m_bNoPrint;
+ mbVisible=rSource.mbVisible;
+ m_bMarkProt=rSource.m_bMarkProt;
+ m_bEmptyPresObj =rSource.m_bEmptyPresObj;
+ m_bNotVisibleAsMaster=rSource.m_bNotVisibleAsMaster;
+ m_bSnapRectDirty=true;
+ m_pPlusData.reset();
+ if (rSource.m_pPlusData!=nullptr) {
+ m_pPlusData.reset(rSource.m_pPlusData->Clone(this));
+ }
+ if (m_pPlusData!=nullptr && m_pPlusData->pBroadcast!=nullptr) {
+ m_pPlusData->pBroadcast.reset(); // broadcaster isn't copied
+ }
+
+ m_pGrabBagItem.reset();
+ if (rSource.m_pGrabBagItem!=nullptr)
+ m_pGrabBagItem.reset(rSource.m_pGrabBagItem->Clone());
+}
+
+SdrObject::~SdrObject()
+{
+ // Tell all the registered ObjectUsers that the page is in destruction.
+ // And clear the vector. This means that user do not need to call RemoveObjectUser()
+ // when they get called from ObjectInDestruction().
+ sdr::ObjectUserVector aList;
+ aList.swap(mpImpl->maObjectUsers);
+ for(sdr::ObjectUser* pObjectUser : aList)
+ {
+ DBG_ASSERT(pObjectUser, "SdrObject::~SdrObject: corrupt ObjectUser list (!)");
+ pObjectUser->ObjectInDestruction(*this);
+ }
+
+ // UserCall
+ SendUserCall(SdrUserCallType::Delete, GetLastBoundRect());
+ o3tl::reset_preserve_ptr_during(m_pPlusData);
+
+ m_pGrabBagItem.reset();
+ mpProperties.reset();
+ mpViewContact.reset();
+
+#ifdef DBG_UTIL
+ // SdrObjectLifetimeWatchDog:
+ impRemoveIncarnatedSdrObjectToSdrModel(*this, getSdrModelFromSdrObject());
+#endif
+}
+
+void SdrObject::Free( SdrObject*& _rpObject )
+{
+ SdrObject* pObject = _rpObject; _rpObject = nullptr;
+
+ if(nullptr == pObject)
+ {
+ // nothing to do
+ return;
+ }
+
+ SvxShape* pShape(pObject->getSvxShape());
+
+ if(pShape)
+ {
+ if(pShape->HasSdrObjectOwnership())
+ {
+ // only the SvxShape is allowed to delete me, and will reset
+ // the ownership before doing so
+ return;
+ }
+ else
+ {
+ // not only delete pObject, but also need to dispose uno shape
+ try
+ {
+ pShape->InvalidateSdrObject();
+ uno::Reference< lang::XComponent > xShapeComp( pObject->getWeakUnoShape().get(), uno::UNO_QUERY_THROW );
+ xShapeComp->dispose();
+ }
+ catch( const uno::Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ }
+
+ delete pObject;
+}
+
+void SdrObject::SetBoundAndSnapRectsDirty(bool bNotMyself, bool bRecursive)
+{
+ if (!bNotMyself)
+ {
+ SetBoundRectDirty();
+ m_bSnapRectDirty=true;
+ }
+
+ if (bRecursive && nullptr != getParentSdrObjListFromSdrObject())
+ {
+ getParentSdrObjListFromSdrObject()->SetSdrObjListRectsDirty();
+ }
+}
+
+void SdrObject::handlePageChange(SdrPage* pOldPage, SdrPage* pNewPage)
+{
+ // The creation of the UNO shape in SdrObject::getUnoShape is influenced
+ // by pPage, so when the page changes we need to discard the cached UNO
+ // shape so that a new one will be created.
+ // If the page is changing to another page with the same model, we
+ // assume they create compatible UNO shape objects so we shouldn't have
+ // to invalidate.
+ // TTTT: This causes quite some problems in SvxDrawPage::add when used
+ // e.g. from Writer - the SdrObject may be cloned to target model, and
+ // the xShape was added to it by purpose (see there). Thus it will be
+ // good to think about if this is really needed - it *seems* to be intended
+ // for a xShape being a on-demand-creatable resource - with the argument that
+ // the SdrPage/UnoPage used influences the SvxShape creation. This uses
+ // resources and would be nice to get rid of anyways.
+ if(nullptr == pOldPage || nullptr == pNewPage)
+ {
+ SvxShape* const pShape(getSvxShape());
+
+ if (pShape && !pShape->HasSdrObjectOwnership())
+ {
+ setUnoShape(nullptr);
+ }
+ }
+}
+
+
+// global static ItemPool for not-yet-inserted items
+static rtl::Reference<SdrItemPool> mpGlobalItemPool;
+
+/** If we let the libc runtime clean us up, we trigger a crash */
+namespace
+{
+class TerminateListener : public ::cppu::WeakImplHelper< css::frame::XTerminateListener >
+{
+ void SAL_CALL queryTermination( const lang::EventObject& ) override
+ {}
+ void SAL_CALL notifyTermination( const lang::EventObject& ) override
+ {
+ mpGlobalItemPool.clear();
+ }
+ virtual void SAL_CALL disposing( const ::css::lang::EventObject& ) override
+ {}
+};
+};
+
+// init global static itempool
+SdrItemPool& SdrObject::GetGlobalDrawObjectItemPool()
+{
+ if(!mpGlobalItemPool)
+ {
+ mpGlobalItemPool = new SdrItemPool();
+ rtl::Reference<SfxItemPool> pGlobalOutlPool = EditEngine::CreatePool();
+ mpGlobalItemPool->SetSecondaryPool(pGlobalOutlPool.get());
+ mpGlobalItemPool->SetDefaultMetric(SdrEngineDefaults::GetMapUnit());
+ mpGlobalItemPool->FreezeIdRanges();
+ if (utl::ConfigManager::IsFuzzing())
+ mpGlobalItemPool->acquire();
+ else
+ {
+ uno::Reference< frame::XDesktop2 > xDesktop = frame::Desktop::create(comphelper::getProcessComponentContext());
+ uno::Reference< frame::XTerminateListener > xListener( new TerminateListener );
+ xDesktop->addTerminateListener( xListener );
+ }
+ }
+
+ return *mpGlobalItemPool;
+}
+
+void SdrObject::SetRelativeWidth( double nValue )
+{
+ mpImpl->mnRelativeWidth = nValue;
+}
+
+void SdrObject::SetRelativeWidthRelation( sal_Int16 eValue )
+{
+ mpImpl->meRelativeWidthRelation = eValue;
+}
+
+void SdrObject::SetRelativeHeight( double nValue )
+{
+ mpImpl->mnRelativeHeight = nValue;
+}
+
+void SdrObject::SetRelativeHeightRelation( sal_Int16 eValue )
+{
+ mpImpl->meRelativeHeightRelation = eValue;
+}
+
+const double* SdrObject::GetRelativeWidth( ) const
+{
+ if (!mpImpl->mnRelativeWidth)
+ return nullptr;
+
+ return &*mpImpl->mnRelativeWidth;
+}
+
+sal_Int16 SdrObject::GetRelativeWidthRelation() const
+{
+ return mpImpl->meRelativeWidthRelation;
+}
+
+const double* SdrObject::GetRelativeHeight( ) const
+{
+ if (!mpImpl->mnRelativeHeight)
+ return nullptr;
+
+ return &*mpImpl->mnRelativeHeight;
+}
+
+sal_Int16 SdrObject::GetRelativeHeightRelation() const
+{
+ return mpImpl->meRelativeHeightRelation;
+}
+
+SfxItemPool& SdrObject::GetObjectItemPool() const
+{
+ return getSdrModelFromSdrObject().GetItemPool();
+}
+
+SdrInventor SdrObject::GetObjInventor() const
+{
+ return SdrInventor::Default;
+}
+
+SdrObjKind SdrObject::GetObjIdentifier() const
+{
+ return SdrObjKind::NONE;
+}
+
+void SdrObject::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
+{
+ rInfo.bRotateFreeAllowed=false;
+ rInfo.bMirrorFreeAllowed=false;
+ rInfo.bTransparenceAllowed = false;
+ rInfo.bShearAllowed =false;
+ rInfo.bEdgeRadiusAllowed=false;
+ rInfo.bCanConvToPath =false;
+ rInfo.bCanConvToPoly =false;
+ rInfo.bCanConvToContour = false;
+ rInfo.bCanConvToPathLineToArea=false;
+ rInfo.bCanConvToPolyLineToArea=false;
+}
+
+SdrLayerID SdrObject::GetLayer() const
+{
+ return mnLayerID;
+}
+
+void SdrObject::getMergedHierarchySdrLayerIDSet(SdrLayerIDSet& rSet) const
+{
+ rSet.Set(GetLayer());
+ SdrObjList* pOL=GetSubList();
+ if (pOL!=nullptr) {
+ const size_t nObjCount = pOL->GetObjCount();
+ for (size_t nObjNum = 0; nObjNum<nObjCount; ++nObjNum) {
+ pOL->GetObj(nObjNum)->getMergedHierarchySdrLayerIDSet(rSet);
+ }
+ }
+}
+
+void SdrObject::NbcSetLayer(SdrLayerID nLayer)
+{
+ mnLayerID = nLayer;
+}
+
+void SdrObject::SetLayer(SdrLayerID nLayer)
+{
+ NbcSetLayer(nLayer);
+ SetChanged();
+ BroadcastObjectChange();
+}
+
+void SdrObject::AddListener(SfxListener& rListener)
+{
+ ImpForcePlusData();
+ if (m_pPlusData->pBroadcast==nullptr) m_pPlusData->pBroadcast.reset(new SfxBroadcaster);
+
+ // SdrEdgeObj may be connected to same SdrObject on both ends so allow it
+ // to listen twice
+ SdrEdgeObj const*const pEdge(dynamic_cast<SdrEdgeObj const*>(&rListener));
+ rListener.StartListening(*m_pPlusData->pBroadcast, pEdge ? DuplicateHandling::Allow : DuplicateHandling::Unexpected);
+}
+
+void SdrObject::RemoveListener(SfxListener& rListener)
+{
+ if (m_pPlusData!=nullptr && m_pPlusData->pBroadcast!=nullptr) {
+ rListener.EndListening(*m_pPlusData->pBroadcast);
+ if (!m_pPlusData->pBroadcast->HasListeners()) {
+ m_pPlusData->pBroadcast.reset();
+ }
+ }
+}
+
+const SfxBroadcaster* SdrObject::GetBroadcaster() const
+{
+ return m_pPlusData!=nullptr ? m_pPlusData->pBroadcast.get() : nullptr;
+}
+
+void SdrObject::AddReference(SdrVirtObj& rVrtObj)
+{
+ AddListener(rVrtObj);
+}
+
+void SdrObject::DelReference(SdrVirtObj& rVrtObj)
+{
+ RemoveListener(rVrtObj);
+}
+
+bool SdrObject::IsGroupObject() const
+{
+ return GetSubList()!=nullptr;
+}
+
+SdrObjList* SdrObject::GetSubList() const
+{
+ return nullptr;
+}
+
+SdrObject* SdrObject::getParentSdrObjectFromSdrObject() const
+{
+ SdrObjList* pParent(getParentSdrObjListFromSdrObject());
+
+ if(nullptr == pParent)
+ {
+ return nullptr;
+ }
+
+ return pParent->getSdrObjectFromSdrObjList();
+}
+
+void SdrObject::SetName(const OUString& rStr, const bool bSetChanged)
+{
+ if (!rStr.isEmpty() && !m_pPlusData)
+ {
+ ImpForcePlusData();
+ }
+
+ if(!(m_pPlusData && m_pPlusData->aObjName != rStr))
+ return;
+
+ // Undo/Redo for setting object's name (#i73249#)
+ bool bUndo( false );
+ if ( getSdrModelFromSdrObject().IsUndoEnabled() )
+ {
+ bUndo = true;
+ std::unique_ptr<SdrUndoAction> pUndoAction =
+ SdrUndoFactory::CreateUndoObjectStrAttr(
+ *this,
+ SdrUndoObjStrAttr::ObjStrAttrType::Name,
+ GetName(),
+ rStr );
+ getSdrModelFromSdrObject().BegUndo( pUndoAction->GetComment() );
+ getSdrModelFromSdrObject().AddUndo( std::move(pUndoAction) );
+ }
+ m_pPlusData->aObjName = rStr;
+ // Undo/Redo for setting object's name (#i73249#)
+ if ( bUndo )
+ {
+ getSdrModelFromSdrObject().EndUndo();
+ }
+ if (bSetChanged)
+ {
+ SetChanged();
+ BroadcastObjectChange();
+ }
+}
+
+const OUString & SdrObject::GetName() const
+{
+ static const OUString EMPTY = u"";
+
+ if(m_pPlusData)
+ {
+ return m_pPlusData->aObjName;
+ }
+
+ return EMPTY;
+}
+
+void SdrObject::SetTitle(const OUString& rStr)
+{
+ if (!rStr.isEmpty() && !m_pPlusData)
+ {
+ ImpForcePlusData();
+ }
+
+ if(!(m_pPlusData && m_pPlusData->aObjTitle != rStr))
+ return;
+
+ // Undo/Redo for setting object's title (#i73249#)
+ bool bUndo( false );
+ if ( getSdrModelFromSdrObject().IsUndoEnabled() )
+ {
+ bUndo = true;
+ std::unique_ptr<SdrUndoAction> pUndoAction =
+ SdrUndoFactory::CreateUndoObjectStrAttr(
+ *this,
+ SdrUndoObjStrAttr::ObjStrAttrType::Title,
+ GetTitle(),
+ rStr );
+ getSdrModelFromSdrObject().BegUndo( pUndoAction->GetComment() );
+ getSdrModelFromSdrObject().AddUndo( std::move(pUndoAction) );
+ }
+ m_pPlusData->aObjTitle = rStr;
+ // Undo/Redo for setting object's title (#i73249#)
+ if ( bUndo )
+ {
+ getSdrModelFromSdrObject().EndUndo();
+ }
+ SetChanged();
+ BroadcastObjectChange();
+}
+
+OUString SdrObject::GetTitle() const
+{
+ if(m_pPlusData)
+ {
+ return m_pPlusData->aObjTitle;
+ }
+
+ return OUString();
+}
+
+void SdrObject::SetDescription(const OUString& rStr)
+{
+ if (!rStr.isEmpty() && !m_pPlusData)
+ {
+ ImpForcePlusData();
+ }
+
+ if(!(m_pPlusData && m_pPlusData->aObjDescription != rStr))
+ return;
+
+ // Undo/Redo for setting object's description (#i73249#)
+ bool bUndo( false );
+ if ( getSdrModelFromSdrObject().IsUndoEnabled() )
+ {
+ bUndo = true;
+ std::unique_ptr<SdrUndoAction> pUndoAction =
+ SdrUndoFactory::CreateUndoObjectStrAttr(
+ *this,
+ SdrUndoObjStrAttr::ObjStrAttrType::Description,
+ GetDescription(),
+ rStr );
+ getSdrModelFromSdrObject().BegUndo( pUndoAction->GetComment() );
+ getSdrModelFromSdrObject().AddUndo( std::move(pUndoAction) );
+ }
+ m_pPlusData->aObjDescription = rStr;
+ // Undo/Redo for setting object's description (#i73249#)
+ if ( bUndo )
+ {
+ getSdrModelFromSdrObject().EndUndo();
+ }
+ SetChanged();
+ BroadcastObjectChange();
+}
+
+OUString SdrObject::GetDescription() const
+{
+ if(m_pPlusData)
+ {
+ return m_pPlusData->aObjDescription;
+ }
+
+ return OUString();
+}
+
+sal_uInt32 SdrObject::GetOrdNum() const
+{
+ if (SdrObjList* pParentList = getParentSdrObjListFromSdrObject())
+ {
+ if (pParentList->IsObjOrdNumsDirty())
+ {
+ pParentList->RecalcObjOrdNums();
+ }
+ } else const_cast<SdrObject*>(this)->m_nOrdNum=0;
+ return m_nOrdNum;
+}
+
+void SdrObject::SetOrdNum(sal_uInt32 nNum)
+{
+ m_nOrdNum = nNum;
+}
+
+void SdrObject::GetGrabBagItem(css::uno::Any& rVal) const
+{
+ if (m_pGrabBagItem != nullptr)
+ m_pGrabBagItem->QueryValue(rVal);
+ else
+ rVal <<= uno::Sequence<beans::PropertyValue>();
+}
+
+void SdrObject::SetGrabBagItem(const css::uno::Any& rVal)
+{
+ if (m_pGrabBagItem == nullptr)
+ m_pGrabBagItem.reset(new SfxGrabBagItem);
+
+ m_pGrabBagItem->PutValue(rVal, 0);
+
+ SetChanged();
+ BroadcastObjectChange();
+}
+
+sal_uInt32 SdrObject::GetNavigationPosition() const
+{
+ if (nullptr != getParentSdrObjListFromSdrObject() && getParentSdrObjListFromSdrObject()->RecalcNavigationPositions())
+ {
+ return mnNavigationPosition;
+ }
+ else
+ return GetOrdNum();
+}
+
+
+void SdrObject::SetNavigationPosition (const sal_uInt32 nNewPosition)
+{
+ mnNavigationPosition = nNewPosition;
+}
+
+
+// To make clearer that this method may trigger RecalcBoundRect and thus may be
+// expensive and sometimes problematic (inside a bigger object change you will get
+// non-useful BoundRects sometimes) I rename that method from GetBoundRect() to
+// GetCurrentBoundRect().
+const tools::Rectangle& SdrObject::GetCurrentBoundRect() const
+{
+ if(m_aOutRect.IsEmpty())
+ {
+ const_cast< SdrObject* >(this)->RecalcBoundRect();
+ }
+
+ return m_aOutRect;
+}
+
+// To have a possibility to get the last calculated BoundRect e.g for producing
+// the first rectangle for repaints (old and new need to be used) without forcing
+// a RecalcBoundRect (which may be problematical and expensive sometimes) I add here
+// a new method for accessing the last BoundRect.
+const tools::Rectangle& SdrObject::GetLastBoundRect() const
+{
+ return m_aOutRect;
+}
+
+void SdrObject::RecalcBoundRect()
+{
+ // #i101680# suppress BoundRect calculations on import(s)
+ if ((getSdrModelFromSdrObject().isLocked()) || utl::ConfigManager::IsFuzzing())
+ return;
+
+ // central new method which will calculate the BoundRect using primitive geometry
+ if(!m_aOutRect.IsEmpty())
+ return;
+
+ // Use view-independent data - we do not want any connections
+ // to e.g. GridOffset in SdrObject-level
+ drawinglayer::primitive2d::Primitive2DContainer xPrimitives;
+ GetViewContact().getViewIndependentPrimitive2DContainer(xPrimitives);
+
+ if(xPrimitives.empty())
+ return;
+
+ // use neutral ViewInformation and get the range of the primitives
+ const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
+ const basegfx::B2DRange aRange(xPrimitives.getB2DRange(aViewInformation2D));
+
+ if(!aRange.isEmpty())
+ {
+ m_aOutRect = tools::Rectangle(
+ static_cast<tools::Long>(floor(aRange.getMinX())),
+ static_cast<tools::Long>(floor(aRange.getMinY())),
+ static_cast<tools::Long>(ceil(aRange.getMaxX())),
+ static_cast<tools::Long>(ceil(aRange.getMaxY())));
+ return;
+ }
+}
+
+void SdrObject::BroadcastObjectChange() const
+{
+ if ((getSdrModelFromSdrObject().isLocked()) || utl::ConfigManager::IsFuzzing())
+ return;
+
+ bool bPlusDataBroadcast(m_pPlusData && m_pPlusData->pBroadcast);
+ bool bObjectChange(IsInserted());
+
+ if(!(bPlusDataBroadcast || bObjectChange))
+ return;
+
+ SdrHint aHint(SdrHintKind::ObjectChange, *this);
+
+ if(bPlusDataBroadcast)
+ {
+ m_pPlusData->pBroadcast->Broadcast(aHint);
+ }
+
+ if(bObjectChange)
+ {
+ getSdrModelFromSdrObject().Broadcast(aHint);
+ }
+}
+
+void SdrObject::SetChanged()
+{
+ // For testing purposes, use the new ViewContact for change
+ // notification now.
+ ActionChanged();
+
+ // TTTT Need to check meaning/usage of IsInserted in one
+ // of the next changes. It should not mean to have a SdrModel
+ // set (this is guaranteed now), but should be connected to
+ // being added to a SdrPage (?)
+ // TTTT tdf#120066 Indeed - This triggers e.g. by CustomShape
+ // geometry-presenting SdrObjects that are in a SdrObjGroup,
+ // but the SdrObjGroup is *by purpose* not inserted.
+ // Need to check deeper and maybe identify all ::IsInserted()
+ // calls by rename and let the compiler work...
+ if(nullptr != getSdrPageFromSdrObject())
+ {
+ getSdrModelFromSdrObject().SetChanged();
+ }
+}
+
+// tooling for painting a single object to an OutputDevice.
+void SdrObject::SingleObjectPainter(OutputDevice& rOut) const
+{
+ sdr::contact::SdrObjectVector aObjectVector;
+ aObjectVector.push_back(const_cast< SdrObject* >(this));
+
+ sdr::contact::ObjectContactOfObjListPainter aPainter(rOut, std::move(aObjectVector), getSdrPageFromSdrObject());
+ sdr::contact::DisplayInfo aDisplayInfo;
+
+ aPainter.ProcessDisplay(aDisplayInfo);
+}
+
+bool SdrObject::LineGeometryUsageIsNecessary() const
+{
+ drawing::LineStyle eXLS = GetMergedItem(XATTR_LINESTYLE).GetValue();
+ return (eXLS != drawing::LineStyle_NONE);
+}
+
+bool SdrObject::HasLimitedRotation() const
+{
+ // RotGrfFlyFrame: Default is false, support full rotation
+ return false;
+}
+
+SdrObject* SdrObject::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new SdrObject(rTargetModel, *this);
+}
+
+OUString SdrObject::TakeObjNameSingul() const
+{
+ OUString sName(SvxResId(STR_ObjNameSingulNONE));
+
+ OUString aName(GetName());
+ if (!aName.isEmpty())
+ sName += " '" + aName + "'";
+ return sName;
+}
+
+OUString SdrObject::TakeObjNamePlural() const
+{
+ return SvxResId(STR_ObjNamePluralNONE);
+}
+
+OUString SdrObject::ImpGetDescriptionStr(TranslateId pStrCacheID) const
+{
+ OUString aStr = SvxResId(pStrCacheID);
+ sal_Int32 nPos = aStr.indexOf("%1");
+ if (nPos >= 0)
+ {
+ // Replace '%1' with the object name.
+ OUString aObjName(TakeObjNameSingul());
+ aStr = aStr.replaceAt(nPos, 2, aObjName);
+ }
+
+ nPos = aStr.indexOf("%2");
+ if (nPos >= 0)
+ // Replace '%2' with the passed value.
+ aStr = aStr.replaceAt(nPos, 2, u"0");
+ return aStr;
+}
+
+void SdrObject::ImpForcePlusData()
+{
+ if (!m_pPlusData)
+ m_pPlusData.reset( new SdrObjPlusData );
+}
+
+OUString SdrObject::GetMetrStr(tools::Long nVal) const
+{
+ return getSdrModelFromSdrObject().GetMetricString(nVal);
+}
+
+basegfx::B2DPolyPolygon SdrObject::TakeXorPoly() const
+{
+ basegfx::B2DPolyPolygon aRetval;
+ const tools::Rectangle aR(GetCurrentBoundRect());
+ aRetval.append(basegfx::utils::createPolygonFromRect(vcl::unotools::b2DRectangleFromRectangle(aR)));
+
+ return aRetval;
+}
+
+basegfx::B2DPolyPolygon SdrObject::TakeContour() const
+{
+ basegfx::B2DPolyPolygon aRetval;
+
+ // create cloned object without text, but with drawing::LineStyle_SOLID,
+ // COL_BLACK as line color and drawing::FillStyle_NONE
+ SdrObject* pClone(CloneSdrObject(getSdrModelFromSdrObject()));
+
+ if(pClone)
+ {
+ const SdrTextObj* pTextObj = dynamic_cast< const SdrTextObj* >(this);
+
+ if(pTextObj)
+ {
+ // no text and no text animation
+ pClone->SetMergedItem(SdrTextAniKindItem(SdrTextAniKind::NONE));
+ pClone->SetOutlinerParaObject(std::nullopt);
+ }
+
+ const SdrEdgeObj* pEdgeObj = dynamic_cast< const SdrEdgeObj* >(this);
+
+ if(pEdgeObj)
+ {
+ // create connections if connector, will be cleaned up when
+ // deleting the connector again
+ SdrObject* pLeft = pEdgeObj->GetConnectedNode(true);
+ SdrObject* pRight = pEdgeObj->GetConnectedNode(false);
+
+ if(pLeft)
+ {
+ pClone->ConnectToNode(true, pLeft);
+ }
+
+ if(pRight)
+ {
+ pClone->ConnectToNode(false, pRight);
+ }
+ }
+
+ SfxItemSet aNewSet(GetObjectItemPool());
+
+ // #i101980# ignore LineWidth; that's what the old implementation
+ // did. With line width, the result may be huge due to fat/thick
+ // line decompositions
+ aNewSet.Put(XLineWidthItem(0));
+
+ // solid black lines and no fill
+ aNewSet.Put(XLineStyleItem(drawing::LineStyle_SOLID));
+ aNewSet.Put(XLineColorItem(OUString(), COL_BLACK));
+ aNewSet.Put(XFillStyleItem(drawing::FillStyle_NONE));
+ pClone->SetMergedItemSet(aNewSet);
+
+ // get sequence from clone
+ const sdr::contact::ViewContact& rVC(pClone->GetViewContact());
+ drawinglayer::primitive2d::Primitive2DContainer xSequence;
+ rVC.getViewIndependentPrimitive2DContainer(xSequence);
+
+ if(!xSequence.empty())
+ {
+ // use neutral ViewInformation
+ const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
+
+ // create extractor, process and get result (with hairlines as opened polygons)
+ drawinglayer::processor2d::ContourExtractor2D aExtractor(aViewInformation2D, false);
+ aExtractor.process(xSequence);
+ const basegfx::B2DPolyPolygonVector& rResult(aExtractor.getExtractedContour());
+ const sal_uInt32 nSize(rResult.size());
+
+ // when count is one, it is implied that the object has only its normal
+ // contour anyways and TakeContour() is to return an empty PolyPolygon
+ // (see old implementation for historical reasons)
+ if(nSize > 1)
+ {
+ // the topology for contour is correctly a vector of PolyPolygons; for
+ // historical reasons cut it back to a single tools::PolyPolygon here
+ for(sal_uInt32 a(0); a < nSize; a++)
+ {
+ aRetval.append(rResult[a]);
+ }
+ }
+ }
+
+ // Always use SdrObject::Free to delete SdrObjects (!)
+ SdrObject::Free(pClone);
+ }
+
+ return aRetval;
+}
+
+sal_uInt32 SdrObject::GetHdlCount() const
+{
+ return 8;
+}
+
+void SdrObject::AddToHdlList(SdrHdlList& rHdlList) const
+{
+ const tools::Rectangle& rR=GetSnapRect();
+ for (sal_uInt32 nHdlNum=0; nHdlNum<8; ++nHdlNum)
+ {
+ std::unique_ptr<SdrHdl> pH;
+ switch (nHdlNum) {
+ case 0: pH.reset(new SdrHdl(rR.TopLeft(), SdrHdlKind::UpperLeft)); break;
+ case 1: pH.reset(new SdrHdl(rR.TopCenter(), SdrHdlKind::Upper)); break;
+ case 2: pH.reset(new SdrHdl(rR.TopRight(), SdrHdlKind::UpperRight)); break;
+ case 3: pH.reset(new SdrHdl(rR.LeftCenter(), SdrHdlKind::Left )); break;
+ case 4: pH.reset(new SdrHdl(rR.RightCenter(), SdrHdlKind::Right)); break;
+ case 5: pH.reset(new SdrHdl(rR.BottomLeft(), SdrHdlKind::LowerLeft)); break;
+ case 6: pH.reset(new SdrHdl(rR.BottomCenter(),SdrHdlKind::Lower)); break;
+ case 7: pH.reset(new SdrHdl(rR.BottomRight(), SdrHdlKind::LowerRight)); break;
+ }
+ rHdlList.AddHdl(std::move(pH));
+ }
+}
+
+void SdrObject::AddToPlusHdlList(SdrHdlList&, SdrHdl&) const
+{
+}
+
+void SdrObject::addCropHandles(SdrHdlList& /*rTarget*/) const
+{
+ // Default implementation, does nothing. Overloaded in
+ // SdrGrafObj and SwVirtFlyDrawObj
+}
+
+tools::Rectangle SdrObject::ImpDragCalcRect(const SdrDragStat& rDrag) const
+{
+ tools::Rectangle aTmpRect(GetSnapRect());
+ tools::Rectangle aRect(aTmpRect);
+ const SdrHdl* pHdl=rDrag.GetHdl();
+ SdrHdlKind eHdl=pHdl==nullptr ? SdrHdlKind::Move : pHdl->GetKind();
+ bool bEcke=(eHdl==SdrHdlKind::UpperLeft || eHdl==SdrHdlKind::UpperRight || eHdl==SdrHdlKind::LowerLeft || eHdl==SdrHdlKind::LowerRight);
+ bool bOrtho=rDrag.GetView()!=nullptr && rDrag.GetView()->IsOrtho();
+ bool bBigOrtho=bEcke && bOrtho && rDrag.GetView()->IsBigOrtho();
+ Point aPos(rDrag.GetNow());
+ bool bLft=(eHdl==SdrHdlKind::UpperLeft || eHdl==SdrHdlKind::Left || eHdl==SdrHdlKind::LowerLeft);
+ bool bRgt=(eHdl==SdrHdlKind::UpperRight || eHdl==SdrHdlKind::Right || eHdl==SdrHdlKind::LowerRight);
+ bool bTop=(eHdl==SdrHdlKind::UpperRight || eHdl==SdrHdlKind::Upper || eHdl==SdrHdlKind::UpperLeft);
+ bool bBtm=(eHdl==SdrHdlKind::LowerRight || eHdl==SdrHdlKind::Lower || eHdl==SdrHdlKind::LowerLeft);
+ if (bLft) aTmpRect.SetLeft(aPos.X() );
+ if (bRgt) aTmpRect.SetRight(aPos.X() );
+ if (bTop) aTmpRect.SetTop(aPos.Y() );
+ if (bBtm) aTmpRect.SetBottom(aPos.Y() );
+ if (bOrtho) { // Ortho
+ tools::Long nWdt0=aRect.Right() -aRect.Left();
+ tools::Long nHgt0=aRect.Bottom()-aRect.Top();
+ tools::Long nXMul=aTmpRect.Right() -aTmpRect.Left();
+ tools::Long nYMul=aTmpRect.Bottom()-aTmpRect.Top();
+ tools::Long nXDiv=nWdt0;
+ tools::Long nYDiv=nHgt0;
+ bool bXNeg=(nXMul<0)!=(nXDiv<0);
+ bool bYNeg=(nYMul<0)!=(nYDiv<0);
+ nXMul=std::abs(nXMul);
+ nYMul=std::abs(nYMul);
+ nXDiv=std::abs(nXDiv);
+ nYDiv=std::abs(nYDiv);
+ Fraction aXFact(nXMul,nXDiv); // fractions for canceling
+ Fraction aYFact(nYMul,nYDiv); // and for comparing
+ nXMul=aXFact.GetNumerator();
+ nYMul=aYFact.GetNumerator();
+ nXDiv=aXFact.GetDenominator();
+ nYDiv=aYFact.GetDenominator();
+ if (bEcke) { // corner point handles
+ bool bUseX=(aXFact<aYFact) != bBigOrtho;
+ if (bUseX) {
+ tools::Long nNeed=tools::Long(BigInt(nHgt0)*BigInt(nXMul)/BigInt(nXDiv));
+ if (bYNeg) nNeed=-nNeed;
+ if (bTop) aTmpRect.SetTop(aTmpRect.Bottom()-nNeed );
+ if (bBtm) aTmpRect.SetBottom(aTmpRect.Top()+nNeed );
+ } else {
+ tools::Long nNeed=tools::Long(BigInt(nWdt0)*BigInt(nYMul)/BigInt(nYDiv));
+ if (bXNeg) nNeed=-nNeed;
+ if (bLft) aTmpRect.SetLeft(aTmpRect.Right()-nNeed );
+ if (bRgt) aTmpRect.SetRight(aTmpRect.Left()+nNeed );
+ }
+ } else { // apex handles
+ if ((bLft || bRgt) && nXDiv!=0) {
+ tools::Long nHgt0b=aRect.Bottom()-aRect.Top();
+ tools::Long nNeed=tools::Long(BigInt(nHgt0b)*BigInt(nXMul)/BigInt(nXDiv));
+ aTmpRect.AdjustTop( -((nNeed-nHgt0b)/2) );
+ aTmpRect.SetBottom(aTmpRect.Top()+nNeed );
+ }
+ if ((bTop || bBtm) && nYDiv!=0) {
+ tools::Long nWdt0b=aRect.Right()-aRect.Left();
+ tools::Long nNeed=tools::Long(BigInt(nWdt0b)*BigInt(nYMul)/BigInt(nYDiv));
+ aTmpRect.AdjustLeft( -((nNeed-nWdt0b)/2) );
+ aTmpRect.SetRight(aTmpRect.Left()+nNeed );
+ }
+ }
+ }
+ aTmpRect.Justify();
+ return aTmpRect;
+}
+
+
+bool SdrObject::hasSpecialDrag() const
+{
+ return false;
+}
+
+bool SdrObject::supportsFullDrag() const
+{
+ return true;
+}
+
+SdrObjectUniquePtr SdrObject::getFullDragClone() const
+{
+ // default uses simple clone
+ return SdrObjectUniquePtr(CloneSdrObject(getSdrModelFromSdrObject()));
+}
+
+bool SdrObject::beginSpecialDrag(SdrDragStat& rDrag) const
+{
+ const SdrHdl* pHdl = rDrag.GetHdl();
+
+ SdrHdlKind eHdl = (pHdl == nullptr) ? SdrHdlKind::Move : pHdl->GetKind();
+
+ return eHdl==SdrHdlKind::UpperLeft || eHdl==SdrHdlKind::Upper || eHdl==SdrHdlKind::UpperRight ||
+ eHdl==SdrHdlKind::Left || eHdl==SdrHdlKind::Right || eHdl==SdrHdlKind::LowerLeft ||
+ eHdl==SdrHdlKind::Lower || eHdl==SdrHdlKind::LowerRight;
+}
+
+bool SdrObject::applySpecialDrag(SdrDragStat& rDrag)
+{
+ tools::Rectangle aNewRect(ImpDragCalcRect(rDrag));
+
+ if(aNewRect != GetSnapRect())
+ {
+ NbcSetSnapRect(aNewRect);
+ }
+
+ return true;
+}
+
+OUString SdrObject::getSpecialDragComment(const SdrDragStat& /*rDrag*/) const
+{
+ return OUString();
+}
+
+basegfx::B2DPolyPolygon SdrObject::getSpecialDragPoly(const SdrDragStat& /*rDrag*/) const
+{
+ // default has nothing to add
+ return basegfx::B2DPolyPolygon();
+}
+
+
+// Create
+bool SdrObject::BegCreate(SdrDragStat& rStat)
+{
+ rStat.SetOrtho4Possible();
+ tools::Rectangle aRect1(rStat.GetStart(), rStat.GetNow());
+ aRect1.Justify();
+ rStat.SetActionRect(aRect1);
+ m_aOutRect = aRect1;
+ return true;
+}
+
+bool SdrObject::MovCreate(SdrDragStat& rStat)
+{
+ rStat.TakeCreateRect(m_aOutRect);
+ rStat.SetActionRect(m_aOutRect);
+ m_aOutRect.Justify();
+
+ return true;
+}
+
+bool SdrObject::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd)
+{
+ rStat.TakeCreateRect(m_aOutRect);
+ m_aOutRect.Justify();
+
+ return (eCmd==SdrCreateCmd::ForceEnd || rStat.GetPointCount()>=2);
+}
+
+void SdrObject::BrkCreate(SdrDragStat& /*rStat*/)
+{
+}
+
+bool SdrObject::BckCreate(SdrDragStat& /*rStat*/)
+{
+ return false;
+}
+
+basegfx::B2DPolyPolygon SdrObject::TakeCreatePoly(const SdrDragStat& rDrag) const
+{
+ tools::Rectangle aRect1;
+ rDrag.TakeCreateRect(aRect1);
+ aRect1.Justify();
+
+ basegfx::B2DPolyPolygon aRetval;
+ aRetval.append(basegfx::utils::createPolygonFromRect(vcl::unotools::b2DRectangleFromRectangle(aRect1)));
+ return aRetval;
+}
+
+PointerStyle SdrObject::GetCreatePointer() const
+{
+ return PointerStyle::Cross;
+}
+
+// transformations
+void SdrObject::NbcMove(const Size& rSiz)
+{
+ m_aOutRect.Move(rSiz);
+ SetBoundAndSnapRectsDirty();
+}
+
+void SdrObject::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
+{
+ bool bXMirr=(xFact.GetNumerator()<0) != (xFact.GetDenominator()<0);
+ bool bYMirr=(yFact.GetNumerator()<0) != (yFact.GetDenominator()<0);
+ if (bXMirr || bYMirr) {
+ Point aRef1(GetSnapRect().Center());
+ if (bXMirr) {
+ Point aRef2(aRef1);
+ aRef2.AdjustY( 1 );
+ NbcMirrorGluePoints(aRef1,aRef2);
+ }
+ if (bYMirr) {
+ Point aRef2(aRef1);
+ aRef2.AdjustX( 1 );
+ NbcMirrorGluePoints(aRef1,aRef2);
+ }
+ }
+ ResizeRect(m_aOutRect,rRef,xFact,yFact);
+ SetBoundAndSnapRectsDirty();
+}
+
+void SdrObject::NbcRotate(const Point& rRef, Degree100 nAngle)
+{
+ if (nAngle)
+ {
+ double a = toRadians(nAngle);
+ NbcRotate( rRef, nAngle, sin( a ), cos( a ) );
+ }
+}
+
+void SdrObject::NbcRotate(const Point& rRef, Degree100 nAngle, double sn, double cs)
+{
+ SetGlueReallyAbsolute(true);
+ m_aOutRect.Move(-rRef.X(),-rRef.Y());
+ tools::Rectangle R(m_aOutRect);
+ if (sn==1.0 && cs==0.0) { // 90deg
+ m_aOutRect.SetLeft(-R.Bottom() );
+ m_aOutRect.SetRight(-R.Top() );
+ m_aOutRect.SetTop(R.Left() );
+ m_aOutRect.SetBottom(R.Right() );
+ } else if (sn==0.0 && cs==-1.0) { // 180deg
+ m_aOutRect.SetLeft(-R.Right() );
+ m_aOutRect.SetRight(-R.Left() );
+ m_aOutRect.SetTop(-R.Bottom() );
+ m_aOutRect.SetBottom(-R.Top() );
+ } else if (sn==-1.0 && cs==0.0) { // 270deg
+ m_aOutRect.SetLeft(R.Top() );
+ m_aOutRect.SetRight(R.Bottom() );
+ m_aOutRect.SetTop(-R.Right() );
+ m_aOutRect.SetBottom(-R.Left() );
+ }
+ m_aOutRect.Move(rRef.X(),rRef.Y());
+ m_aOutRect.Justify(); // just in case
+ SetBoundAndSnapRectsDirty();
+ NbcRotateGluePoints(rRef,nAngle,sn,cs);
+ SetGlueReallyAbsolute(false);
+}
+
+void SdrObject::NbcMirror(const Point& rRef1, const Point& rRef2)
+{
+ SetGlueReallyAbsolute(true);
+ m_aOutRect.Move(-rRef1.X(),-rRef1.Y());
+ tools::Rectangle R(m_aOutRect);
+ tools::Long dx=rRef2.X()-rRef1.X();
+ tools::Long dy=rRef2.Y()-rRef1.Y();
+ if (dx==0) { // vertical axis
+ m_aOutRect.SetLeft(-R.Right() );
+ m_aOutRect.SetRight(-R.Left() );
+ } else if (dy==0) { // horizontal axis
+ m_aOutRect.SetTop(-R.Bottom() );
+ m_aOutRect.SetBottom(-R.Top() );
+ } else if (dx==dy) { // 45deg axis
+ m_aOutRect.SetLeft(R.Top() );
+ m_aOutRect.SetRight(R.Bottom() );
+ m_aOutRect.SetTop(R.Left() );
+ m_aOutRect.SetBottom(R.Right() );
+ } else if (dx==-dy) { // 45deg axis
+ m_aOutRect.SetLeft(-R.Bottom() );
+ m_aOutRect.SetRight(-R.Top() );
+ m_aOutRect.SetTop(-R.Right() );
+ m_aOutRect.SetBottom(-R.Left() );
+ }
+ m_aOutRect.Move(rRef1.X(),rRef1.Y());
+ m_aOutRect.Justify(); // just in case
+ SetBoundAndSnapRectsDirty();
+ NbcMirrorGluePoints(rRef1,rRef2);
+ SetGlueReallyAbsolute(false);
+}
+
+void SdrObject::NbcShear(const Point& rRef, Degree100 /*nAngle*/, double tn, bool bVShear)
+{
+ SetGlueReallyAbsolute(true);
+ NbcShearGluePoints(rRef,tn,bVShear);
+ SetGlueReallyAbsolute(false);
+}
+
+void SdrObject::Move(const Size& rSiz)
+{
+ if (rSiz.Width()!=0 || rSiz.Height()!=0) {
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ NbcMove(rSiz);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::MoveOnly,aBoundRect0);
+ }
+}
+
+void SdrObject::NbcCrop(const basegfx::B2DPoint& /*aRef*/, double /*fxFact*/, double /*fyFact*/)
+{
+ // Default: does nothing. Real behaviour in SwVirtFlyDrawObj and SdrDragCrop::EndSdrDrag.
+ // Where SwVirtFlyDrawObj is the only real user of it to do something local
+}
+
+void SdrObject::Resize(const Point& rRef, const Fraction& xFact, const Fraction& yFact, bool bUnsetRelative)
+{
+ if (xFact.GetNumerator() == xFact.GetDenominator() && yFact.GetNumerator() == yFact.GetDenominator())
+ return;
+
+ if (bUnsetRelative)
+ {
+ mpImpl->mnRelativeWidth.reset();
+ mpImpl->meRelativeWidthRelation = text::RelOrientation::PAGE_FRAME;
+ mpImpl->meRelativeHeightRelation = text::RelOrientation::PAGE_FRAME;
+ mpImpl->mnRelativeHeight.reset();
+ }
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ NbcResize(rRef,xFact,yFact);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+}
+
+void SdrObject::Crop(const basegfx::B2DPoint& rRef, double fxFact, double fyFact)
+{
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ NbcCrop(rRef, fxFact, fyFact);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+}
+
+void SdrObject::Rotate(const Point& rRef, Degree100 nAngle, double sn, double cs)
+{
+ if (nAngle) {
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ NbcRotate(rRef,nAngle,sn,cs);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+ }
+}
+
+void SdrObject::Mirror(const Point& rRef1, const Point& rRef2)
+{
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ NbcMirror(rRef1,rRef2);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+}
+
+void SdrObject::Shear(const Point& rRef, Degree100 nAngle, double tn, bool bVShear)
+{
+ if (nAngle) {
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ NbcShear(rRef,nAngle,tn,bVShear);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+ }
+}
+
+void SdrObject::NbcSetRelativePos(const Point& rPnt)
+{
+ Point aRelPos0(GetSnapRect().TopLeft()-m_aAnchor);
+ Size aSiz(rPnt.X()-aRelPos0.X(),rPnt.Y()-aRelPos0.Y());
+ NbcMove(aSiz); // This also calls SetRectsDirty()
+}
+
+void SdrObject::SetRelativePos(const Point& rPnt)
+{
+ if (rPnt!=GetRelativePos()) {
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ NbcSetRelativePos(rPnt);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::MoveOnly,aBoundRect0);
+ }
+}
+
+Point SdrObject::GetRelativePos() const
+{
+ return GetSnapRect().TopLeft()-m_aAnchor;
+}
+
+void SdrObject::ImpSetAnchorPos(const Point& rPnt)
+{
+ m_aAnchor = rPnt;
+}
+
+void SdrObject::NbcSetAnchorPos(const Point& rPnt)
+{
+ Size aSiz(rPnt.X()-m_aAnchor.X(),rPnt.Y()-m_aAnchor.Y());
+ m_aAnchor=rPnt;
+ NbcMove(aSiz); // This also calls SetRectsDirty()
+}
+
+void SdrObject::SetAnchorPos(const Point& rPnt)
+{
+ if (rPnt!=m_aAnchor) {
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ NbcSetAnchorPos(rPnt);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::MoveOnly,aBoundRect0);
+ }
+}
+
+const Point& SdrObject::GetAnchorPos() const
+{
+ return m_aAnchor;
+}
+
+void SdrObject::RecalcSnapRect()
+{
+}
+
+const tools::Rectangle& SdrObject::GetSnapRect() const
+{
+ return m_aOutRect;
+}
+
+void SdrObject::NbcSetSnapRect(const tools::Rectangle& rRect)
+{
+ m_aOutRect=rRect;
+}
+
+const tools::Rectangle& SdrObject::GetLogicRect() const
+{
+ return GetSnapRect();
+}
+
+void SdrObject::NbcSetLogicRect(const tools::Rectangle& rRect)
+{
+ NbcSetSnapRect(rRect);
+}
+
+void SdrObject::AdjustToMaxRect( const tools::Rectangle& rMaxRect, bool /* bShrinkOnly = false */ )
+{
+ SetLogicRect( rMaxRect );
+}
+
+void SdrObject::SetSnapRect(const tools::Rectangle& rRect)
+{
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ NbcSetSnapRect(rRect);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+}
+
+void SdrObject::SetLogicRect(const tools::Rectangle& rRect)
+{
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ NbcSetLogicRect(rRect);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+}
+
+Degree100 SdrObject::GetRotateAngle() const
+{
+ return 0_deg100;
+}
+
+Degree100 SdrObject::GetShearAngle(bool /*bVertical*/) const
+{
+ return 0_deg100;
+}
+
+sal_uInt32 SdrObject::GetSnapPointCount() const
+{
+ return GetPointCount();
+}
+
+Point SdrObject::GetSnapPoint(sal_uInt32 i) const
+{
+ return GetPoint(i);
+}
+
+bool SdrObject::IsPolyObj() const
+{
+ return false;
+}
+
+sal_uInt32 SdrObject::GetPointCount() const
+{
+ return 0;
+}
+
+Point SdrObject::GetPoint(sal_uInt32 /*i*/) const
+{
+ return Point();
+}
+
+void SdrObject::SetPoint(const Point& rPnt, sal_uInt32 i)
+{
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ NbcSetPoint(rPnt, i);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+}
+
+void SdrObject::NbcSetPoint(const Point& /*rPnt*/, sal_uInt32 /*i*/)
+{
+}
+
+bool SdrObject::HasTextEdit() const
+{
+ return false;
+}
+
+bool SdrObject::Equals(const SdrObject& rOtherObj) const
+{
+ return (m_aAnchor.X() == rOtherObj.m_aAnchor.X() && m_aAnchor.Y() == rOtherObj.m_aAnchor.Y() &&
+ m_nOrdNum == rOtherObj.m_nOrdNum && mnNavigationPosition == rOtherObj.mnNavigationPosition &&
+ mbSupportTextIndentingOnLineWidthChange == rOtherObj.mbSupportTextIndentingOnLineWidthChange &&
+ mbLineIsOutsideGeometry == rOtherObj.mbLineIsOutsideGeometry && m_bMarkProt == rOtherObj.m_bMarkProt &&
+ m_bIs3DObj == rOtherObj.m_bIs3DObj && m_bIsEdge == rOtherObj.m_bIsEdge && m_bClosedObj == rOtherObj.m_bClosedObj &&
+ m_bNotVisibleAsMaster == rOtherObj.m_bNotVisibleAsMaster && m_bEmptyPresObj == rOtherObj.m_bEmptyPresObj &&
+ mbVisible == rOtherObj.mbVisible && m_bNoPrint == rOtherObj.m_bNoPrint && m_bSizProt == rOtherObj.m_bSizProt &&
+ m_bMovProt == rOtherObj.m_bMovProt && m_bVirtObj == rOtherObj.m_bVirtObj &&
+ mnLayerID == rOtherObj.mnLayerID && GetMergedItemSet().Equals(rOtherObj.GetMergedItemSet(), false) );
+}
+
+void SdrObject::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SdrObject"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("symbol"), "%s", BAD_CAST(typeid(*this).name()));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("name"), "%s", BAD_CAST(GetName().toUtf8().getStr()));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("title"), "%s", BAD_CAST(GetTitle().toUtf8().getStr()));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("description"), "%s", BAD_CAST(GetDescription().toUtf8().getStr()));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("nOrdNum"), "%" SAL_PRIuUINT32, GetOrdNumDirect());
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("aOutRect"), BAD_CAST(m_aOutRect.toString().getStr()));
+
+ if (m_pGrabBagItem)
+ {
+ m_pGrabBagItem->dumpAsXml(pWriter);
+ }
+
+ if (mpProperties)
+ {
+ mpProperties->dumpAsXml(pWriter);
+ }
+
+ if (const OutlinerParaObject* pOutliner = GetOutlinerParaObject())
+ pOutliner->dumpAsXml(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+void SdrObject::SetOutlinerParaObject(std::optional<OutlinerParaObject> pTextObject)
+{
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ NbcSetOutlinerParaObject(std::move(pTextObject));
+ SetChanged();
+ BroadcastObjectChange();
+ if (GetCurrentBoundRect()!=aBoundRect0) {
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+ }
+
+ if (!getSdrModelFromSdrObject().IsUndoEnabled())
+ return;
+
+ // Don't do this during import.
+ SdrObject* pTopGroupObj = nullptr;
+ if (getParentSdrObjectFromSdrObject())
+ {
+ pTopGroupObj = getParentSdrObjectFromSdrObject();
+ while (pTopGroupObj->getParentSdrObjectFromSdrObject())
+ {
+ pTopGroupObj = pTopGroupObj->getParentSdrObjectFromSdrObject();
+ }
+ }
+ if (pTopGroupObj)
+ {
+ // A shape was modified, which is in a group shape. Empty the group shape's grab-bag,
+ // which potentially contains the old text of the shapes in case of diagrams.
+ pTopGroupObj->SetGrabBagItem(uno::Any(uno::Sequence<beans::PropertyValue>()));
+ }
+}
+
+void SdrObject::NbcSetOutlinerParaObject(std::optional<OutlinerParaObject> /*pTextObject*/)
+{
+}
+
+OutlinerParaObject* SdrObject::GetOutlinerParaObject() const
+{
+ return nullptr;
+}
+
+void SdrObject::NbcReformatText()
+{
+}
+
+void SdrObject::BurnInStyleSheetAttributes()
+{
+ GetProperties().ForceStyleToHardAttributes();
+}
+
+bool SdrObject::HasMacro() const
+{
+ return false;
+}
+
+SdrObject* SdrObject::CheckMacroHit(const SdrObjMacroHitRec& rRec) const
+{
+ if(rRec.pPageView)
+ {
+ return SdrObjectPrimitiveHit(*this, rRec.aPos, rRec.nTol, *rRec.pPageView, rRec.pVisiLayer, false);
+ }
+
+ return nullptr;
+}
+
+PointerStyle SdrObject::GetMacroPointer(const SdrObjMacroHitRec&) const
+{
+ return PointerStyle::RefHand;
+}
+
+void SdrObject::PaintMacro(OutputDevice& rOut, const tools::Rectangle& , const SdrObjMacroHitRec& ) const
+{
+ const RasterOp eRop(rOut.GetRasterOp());
+ const basegfx::B2DPolyPolygon aPolyPolygon(TakeXorPoly());
+
+ rOut.SetLineColor(COL_BLACK);
+ rOut.SetFillColor();
+ rOut.SetRasterOp(RasterOp::Invert);
+
+ for(auto const& rPolygon : aPolyPolygon)
+ {
+ rOut.DrawPolyLine(rPolygon);
+ }
+
+ rOut.SetRasterOp(eRop);
+}
+
+bool SdrObject::DoMacro(const SdrObjMacroHitRec&)
+{
+ return false;
+}
+
+bool SdrObject::IsMacroHit(const SdrObjMacroHitRec& rRec) const
+{
+ return CheckMacroHit(rRec) != nullptr;
+}
+
+
+std::unique_ptr<SdrObjGeoData> SdrObject::NewGeoData() const
+{
+ return std::make_unique<SdrObjGeoData>();
+}
+
+void SdrObject::SaveGeoData(SdrObjGeoData& rGeo) const
+{
+ rGeo.aBoundRect =GetCurrentBoundRect();
+ rGeo.aAnchor =m_aAnchor ;
+ rGeo.bMovProt =m_bMovProt ;
+ rGeo.bSizProt =m_bSizProt ;
+ rGeo.bNoPrint =m_bNoPrint ;
+ rGeo.mbVisible =mbVisible ;
+ rGeo.bClosedObj =m_bClosedObj ;
+ rGeo.mnLayerID = mnLayerID;
+
+ // user-defined gluepoints
+ if (m_pPlusData!=nullptr && m_pPlusData->pGluePoints!=nullptr) {
+ if (rGeo.pGPL!=nullptr) {
+ *rGeo.pGPL=*m_pPlusData->pGluePoints;
+ } else {
+ rGeo.pGPL.reset( new SdrGluePointList(*m_pPlusData->pGluePoints) );
+ }
+ } else {
+ rGeo.pGPL.reset();
+ }
+}
+
+void SdrObject::RestoreGeoData(const SdrObjGeoData& rGeo)
+{
+ SetBoundAndSnapRectsDirty();
+ m_aOutRect =rGeo.aBoundRect ;
+ m_aAnchor =rGeo.aAnchor ;
+ m_bMovProt =rGeo.bMovProt ;
+ m_bSizProt =rGeo.bSizProt ;
+ m_bNoPrint =rGeo.bNoPrint ;
+ mbVisible =rGeo.mbVisible ;
+ m_bClosedObj =rGeo.bClosedObj ;
+ mnLayerID = rGeo.mnLayerID;
+
+ // user-defined gluepoints
+ if (rGeo.pGPL!=nullptr) {
+ ImpForcePlusData();
+ if (m_pPlusData->pGluePoints!=nullptr) {
+ *m_pPlusData->pGluePoints=*rGeo.pGPL;
+ } else {
+ m_pPlusData->pGluePoints.reset(new SdrGluePointList(*rGeo.pGPL));
+ }
+ } else {
+ if (m_pPlusData!=nullptr && m_pPlusData->pGluePoints!=nullptr) {
+ m_pPlusData->pGluePoints.reset();
+ }
+ }
+}
+
+std::unique_ptr<SdrObjGeoData> SdrObject::GetGeoData() const
+{
+ std::unique_ptr<SdrObjGeoData> pGeo = NewGeoData();
+ SaveGeoData(*pGeo);
+ return pGeo;
+}
+
+void SdrObject::SetGeoData(const SdrObjGeoData& rGeo)
+{
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ RestoreGeoData(rGeo);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+}
+
+
+// ItemSet access
+
+const SfxItemSet& SdrObject::GetObjectItemSet() const
+{
+ return GetProperties().GetObjectItemSet();
+}
+
+const SfxItemSet& SdrObject::GetMergedItemSet() const
+{
+ return GetProperties().GetMergedItemSet();
+}
+
+void SdrObject::SetObjectItem(const SfxPoolItem& rItem)
+{
+ GetProperties().SetObjectItem(rItem);
+}
+
+void SdrObject::SetMergedItem(const SfxPoolItem& rItem)
+{
+ GetProperties().SetMergedItem(rItem);
+}
+
+void SdrObject::ClearMergedItem(const sal_uInt16 nWhich)
+{
+ GetProperties().ClearMergedItem(nWhich);
+}
+
+void SdrObject::SetObjectItemSet(const SfxItemSet& rSet)
+{
+ GetProperties().SetObjectItemSet(rSet);
+}
+
+void SdrObject::SetMergedItemSet(const SfxItemSet& rSet, bool bClearAllItems)
+{
+ GetProperties().SetMergedItemSet(rSet, bClearAllItems);
+}
+
+const SfxPoolItem& SdrObject::GetObjectItem(const sal_uInt16 nWhich) const
+{
+ return GetObjectItemSet().Get(nWhich);
+}
+
+const SfxPoolItem& SdrObject::GetMergedItem(const sal_uInt16 nWhich) const
+{
+ return GetMergedItemSet().Get(nWhich);
+}
+
+void SdrObject::SetMergedItemSetAndBroadcast(const SfxItemSet& rSet, bool bClearAllItems)
+{
+ GetProperties().SetMergedItemSetAndBroadcast(rSet, bClearAllItems);
+}
+
+void SdrObject::ApplyNotPersistAttr(const SfxItemSet& rAttr)
+{
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ NbcApplyNotPersistAttr(rAttr);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+}
+
+void SdrObject::NbcApplyNotPersistAttr(const SfxItemSet& rAttr)
+{
+ const tools::Rectangle& rSnap=GetSnapRect();
+ const tools::Rectangle& rLogic=GetLogicRect();
+ Point aRef1(rSnap.Center());
+
+ if (const SdrTransformRef1XItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_TRANSFORMREF1X))
+ {
+ aRef1.setX(pPoolItem->GetValue() );
+ }
+ if (const SdrTransformRef1YItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_TRANSFORMREF1Y))
+ {
+ aRef1.setY(pPoolItem->GetValue() );
+ }
+
+ tools::Rectangle aNewSnap(rSnap);
+ if (const SdrMoveXItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_MOVEX))
+ {
+ tools::Long n = pPoolItem->GetValue();
+ aNewSnap.Move(n,0);
+ }
+ if (const SdrMoveYItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_MOVEY))
+ {
+ tools::Long n = pPoolItem->GetValue();
+ aNewSnap.Move(0,n);
+ }
+ if (const SdrOnePositionXItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_ONEPOSITIONX))
+ {
+ tools::Long n = pPoolItem->GetValue();
+ aNewSnap.Move(n-aNewSnap.Left(),0);
+ }
+ if (const SdrOnePositionYItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_ONEPOSITIONY))
+ {
+ tools::Long n = pPoolItem->GetValue();
+ aNewSnap.Move(0,n-aNewSnap.Top());
+ }
+ if (const SdrOneSizeWidthItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_ONESIZEWIDTH))
+ {
+ tools::Long n = pPoolItem->GetValue();
+ aNewSnap.SetRight(aNewSnap.Left()+n );
+ }
+ if (const SdrOneSizeHeightItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_ONESIZEHEIGHT))
+ {
+ tools::Long n = pPoolItem->GetValue();
+ aNewSnap.SetBottom(aNewSnap.Top()+n );
+ }
+ if (aNewSnap!=rSnap) {
+ if (aNewSnap.GetSize()==rSnap.GetSize()) {
+ NbcMove(Size(aNewSnap.Left()-rSnap.Left(),aNewSnap.Top()-rSnap.Top()));
+ } else {
+ NbcSetSnapRect(aNewSnap);
+ }
+ }
+
+ if (const SdrShearAngleItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_SHEARANGLE))
+ {
+ Degree100 n = pPoolItem->GetValue();
+ n-=GetShearAngle();
+ if (n) {
+ double nTan = tan(toRadians(n));
+ NbcShear(aRef1,n,nTan,false);
+ }
+ }
+ if (const SdrAngleItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_ROTATEANGLE))
+ {
+ Degree100 n = pPoolItem->GetValue();
+ n-=GetRotateAngle();
+ if (n) {
+ NbcRotate(aRef1,n);
+ }
+ }
+ if (const SdrRotateOneItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_ROTATEONE))
+ {
+ Degree100 n = pPoolItem->GetValue();
+ NbcRotate(aRef1,n);
+ }
+ if (const SdrHorzShearOneItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_HORZSHEARONE))
+ {
+ Degree100 n = pPoolItem->GetValue();
+ double nTan = tan(toRadians(n));
+ NbcShear(aRef1,n,nTan,false);
+ }
+ if (const SdrVertShearOneItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_VERTSHEARONE))
+ {
+ Degree100 n = pPoolItem->GetValue();
+ double nTan = tan(toRadians(n));
+ NbcShear(aRef1,n,nTan,true);
+ }
+
+ if (const SdrYesNoItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_OBJMOVEPROTECT))
+ {
+ bool b = pPoolItem->GetValue();
+ SetMoveProtect(b);
+ }
+ if (const SdrYesNoItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_OBJSIZEPROTECT))
+ {
+ bool b = pPoolItem->GetValue();
+ SetResizeProtect(b);
+ }
+
+ /* move protect always sets size protect */
+ if( IsMoveProtect() )
+ SetResizeProtect( true );
+
+ if (const SdrObjPrintableItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_OBJPRINTABLE))
+ {
+ bool b = pPoolItem->GetValue();
+ SetPrintable(b);
+ }
+
+ if (const SdrObjVisibleItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_OBJVISIBLE))
+ {
+ bool b = pPoolItem->GetValue();
+ SetVisible(b);
+ }
+
+ SdrLayerID nLayer=SDRLAYER_NOTFOUND;
+ if (const SdrLayerIdItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_LAYERID))
+ {
+ nLayer = pPoolItem->GetValue();
+ }
+ if (const SdrLayerNameItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_LAYERNAME))
+ {
+ OUString aLayerName = pPoolItem->GetValue();
+ const SdrLayerAdmin& rLayAd(nullptr != getSdrPageFromSdrObject()
+ ? getSdrPageFromSdrObject()->GetLayerAdmin()
+ : getSdrModelFromSdrObject().GetLayerAdmin());
+ const SdrLayer* pLayer = rLayAd.GetLayer(aLayerName);
+
+ if(nullptr != pLayer)
+ {
+ nLayer=pLayer->GetID();
+ }
+ }
+ if (nLayer!=SDRLAYER_NOTFOUND) {
+ NbcSetLayer(nLayer);
+ }
+
+ if (const SfxStringItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_OBJECTNAME))
+ {
+ OUString aName = pPoolItem->GetValue();
+ SetName(aName);
+ }
+ tools::Rectangle aNewLogic(rLogic);
+ if (const SdrLogicSizeWidthItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_LOGICSIZEWIDTH))
+ {
+ tools::Long n = pPoolItem->GetValue();
+ aNewLogic.SetRight(aNewLogic.Left()+n );
+ }
+ if (const SdrLogicSizeHeightItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_LOGICSIZEHEIGHT))
+ {
+ tools::Long n = pPoolItem->GetValue();
+ aNewLogic.SetBottom(aNewLogic.Top()+n );
+ }
+ if (aNewLogic!=rLogic) {
+ NbcSetLogicRect(aNewLogic);
+ }
+ Fraction aResizeX(1,1);
+ Fraction aResizeY(1,1);
+ if (const SdrResizeXOneItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_RESIZEXONE))
+ {
+ aResizeX *= pPoolItem->GetValue();
+ }
+ if (const SdrResizeYOneItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_RESIZEYONE))
+ {
+ aResizeY *= pPoolItem->GetValue();
+ }
+ if (aResizeX!=Fraction(1,1) || aResizeY!=Fraction(1,1)) {
+ NbcResize(aRef1,aResizeX,aResizeY);
+ }
+}
+
+void SdrObject::TakeNotPersistAttr(SfxItemSet& rAttr) const
+{
+ const tools::Rectangle& rSnap=GetSnapRect();
+ const tools::Rectangle& rLogic=GetLogicRect();
+ rAttr.Put(SdrYesNoItem(SDRATTR_OBJMOVEPROTECT, IsMoveProtect()));
+ rAttr.Put(SdrYesNoItem(SDRATTR_OBJSIZEPROTECT, IsResizeProtect()));
+ rAttr.Put(SdrObjPrintableItem(IsPrintable()));
+ rAttr.Put(SdrObjVisibleItem(IsVisible()));
+ rAttr.Put(SdrAngleItem(SDRATTR_ROTATEANGLE, GetRotateAngle()));
+ rAttr.Put(SdrShearAngleItem(GetShearAngle()));
+ rAttr.Put(SdrOneSizeWidthItem(rSnap.GetWidth()-1));
+ rAttr.Put(SdrOneSizeHeightItem(rSnap.GetHeight()-1));
+ rAttr.Put(SdrOnePositionXItem(rSnap.Left()));
+ rAttr.Put(SdrOnePositionYItem(rSnap.Top()));
+ if (rLogic.GetWidth()!=rSnap.GetWidth()) {
+ rAttr.Put(SdrLogicSizeWidthItem(rLogic.GetWidth()-1));
+ }
+ if (rLogic.GetHeight()!=rSnap.GetHeight()) {
+ rAttr.Put(SdrLogicSizeHeightItem(rLogic.GetHeight()-1));
+ }
+ OUString aName(GetName());
+
+ if (!aName.isEmpty())
+ {
+ rAttr.Put(SfxStringItem(SDRATTR_OBJECTNAME, aName));
+ }
+
+ rAttr.Put(SdrLayerIdItem(GetLayer()));
+ const SdrLayerAdmin& rLayAd(nullptr != getSdrPageFromSdrObject()
+ ? getSdrPageFromSdrObject()->GetLayerAdmin()
+ : getSdrModelFromSdrObject().GetLayerAdmin());
+ const SdrLayer* pLayer = rLayAd.GetLayerPerID(GetLayer());
+ if(nullptr != pLayer)
+ {
+ rAttr.Put(SdrLayerNameItem(pLayer->GetName()));
+ }
+ Point aRef1(rSnap.Center());
+ Point aRef2(aRef1); aRef2.AdjustY( 1 );
+ rAttr.Put(SdrTransformRef1XItem(aRef1.X()));
+ rAttr.Put(SdrTransformRef1YItem(aRef1.Y()));
+ rAttr.Put(SdrTransformRef2XItem(aRef2.X()));
+ rAttr.Put(SdrTransformRef2YItem(aRef2.Y()));
+}
+
+SfxStyleSheet* SdrObject::GetStyleSheet() const
+{
+ return GetProperties().GetStyleSheet();
+}
+
+void SdrObject::SetStyleSheet(SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr)
+{
+ tools::Rectangle aBoundRect0;
+
+ if(m_pUserCall)
+ aBoundRect0 = GetLastBoundRect();
+
+ InternalSetStyleSheet(pNewStyleSheet, bDontRemoveHardAttr, true);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::ChangeAttr, aBoundRect0);
+}
+
+void SdrObject::NbcSetStyleSheet(SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr)
+{
+ InternalSetStyleSheet(pNewStyleSheet, bDontRemoveHardAttr, false);
+}
+
+void SdrObject::InternalSetStyleSheet(SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr, bool bBroadcast)
+{
+ GetProperties().SetStyleSheet(pNewStyleSheet, bDontRemoveHardAttr, bBroadcast);
+}
+
+// Broadcasting while setting attributes is managed by the AttrObj.
+
+
+SdrGluePoint SdrObject::GetVertexGluePoint(sal_uInt16 nPosNum) const
+{
+ // #i41936# Use SnapRect for default GluePoints
+ const tools::Rectangle aR(GetSnapRect());
+ Point aPt;
+
+ switch(nPosNum)
+ {
+ case 0 : aPt = aR.TopCenter(); break;
+ case 1 : aPt = aR.RightCenter(); break;
+ case 2 : aPt = aR.BottomCenter(); break;
+ case 3 : aPt = aR.LeftCenter(); break;
+ }
+
+ aPt -= aR.Center();
+ SdrGluePoint aGP(aPt);
+ aGP.SetPercent(false);
+
+ return aGP;
+}
+
+SdrGluePoint SdrObject::GetCornerGluePoint(sal_uInt16 nPosNum) const
+{
+ tools::Rectangle aR(GetCurrentBoundRect());
+ Point aPt;
+ switch (nPosNum) {
+ case 0 : aPt=aR.TopLeft(); break;
+ case 1 : aPt=aR.TopRight(); break;
+ case 2 : aPt=aR.BottomRight(); break;
+ case 3 : aPt=aR.BottomLeft(); break;
+ }
+ aPt-=GetSnapRect().Center();
+ SdrGluePoint aGP(aPt);
+ aGP.SetPercent(false);
+ return aGP;
+}
+
+const SdrGluePointList* SdrObject::GetGluePointList() const
+{
+ if (m_pPlusData!=nullptr) return m_pPlusData->pGluePoints.get();
+ return nullptr;
+}
+
+
+SdrGluePointList* SdrObject::ForceGluePointList()
+{
+ ImpForcePlusData();
+ if (m_pPlusData->pGluePoints==nullptr) {
+ m_pPlusData->pGluePoints.reset(new SdrGluePointList);
+ }
+ return m_pPlusData->pGluePoints.get();
+}
+
+void SdrObject::SetGlueReallyAbsolute(bool bOn)
+{
+ // First a const call to see whether there are any gluepoints.
+ // Force const call!
+ if (GetGluePointList()!=nullptr) {
+ SdrGluePointList* pGPL=ForceGluePointList();
+ pGPL->SetReallyAbsolute(bOn,*this);
+ }
+}
+
+void SdrObject::NbcRotateGluePoints(const Point& rRef, Degree100 nAngle, double sn, double cs)
+{
+ // First a const call to see whether there are any gluepoints.
+ // Force const call!
+ if (GetGluePointList()!=nullptr) {
+ SdrGluePointList* pGPL=ForceGluePointList();
+ pGPL->Rotate(rRef,nAngle,sn,cs,this);
+ }
+}
+
+void SdrObject::NbcMirrorGluePoints(const Point& rRef1, const Point& rRef2)
+{
+ // First a const call to see whether there are any gluepoints.
+ // Force const call!
+ if (GetGluePointList()!=nullptr) {
+ SdrGluePointList* pGPL=ForceGluePointList();
+ pGPL->Mirror(rRef1,rRef2,this);
+ }
+}
+
+void SdrObject::NbcShearGluePoints(const Point& rRef, double tn, bool bVShear)
+{
+ // First a const call to see whether there are any gluepoints.
+ // Force const call!
+ if (GetGluePointList()!=nullptr) {
+ SdrGluePointList* pGPL=ForceGluePointList();
+ pGPL->Shear(rRef,tn,bVShear,this);
+ }
+}
+
+void SdrObject::ConnectToNode(bool /*bTail1*/, SdrObject* /*pObj*/)
+{
+}
+
+void SdrObject::DisconnectFromNode(bool /*bTail1*/)
+{
+}
+
+SdrObject* SdrObject::GetConnectedNode(bool /*bTail1*/) const
+{
+ return nullptr;
+}
+
+
+static void extractLineContourFromPrimitive2DSequence(
+ const drawinglayer::primitive2d::Primitive2DContainer& rxSequence,
+ basegfx::B2DPolygonVector& rExtractedHairlines,
+ basegfx::B2DPolyPolygonVector& rExtractedLineFills)
+{
+ rExtractedHairlines.clear();
+ rExtractedLineFills.clear();
+
+ if(rxSequence.empty())
+ return;
+
+ // use neutral ViewInformation
+ const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
+
+ // create extractor, process and get result
+ drawinglayer::processor2d::LineGeometryExtractor2D aExtractor(aViewInformation2D);
+ aExtractor.process(rxSequence);
+
+ // copy line results
+ rExtractedHairlines = aExtractor.getExtractedHairlines();
+
+ // copy fill rsults
+ rExtractedLineFills = aExtractor.getExtractedLineFills();
+}
+
+
+SdrObject* SdrObject::ImpConvertToContourObj(bool bForceLineDash)
+{
+ SdrObject* pRetval(nullptr);
+
+ if(LineGeometryUsageIsNecessary())
+ {
+ basegfx::B2DPolyPolygon aMergedLineFillPolyPolygon;
+ basegfx::B2DPolyPolygon aMergedHairlinePolyPolygon;
+ drawinglayer::primitive2d::Primitive2DContainer xSequence;
+ GetViewContact().getViewIndependentPrimitive2DContainer(xSequence);
+
+ if(!xSequence.empty())
+ {
+ basegfx::B2DPolygonVector aExtractedHairlines;
+ basegfx::B2DPolyPolygonVector aExtractedLineFills;
+
+ extractLineContourFromPrimitive2DSequence(xSequence, aExtractedHairlines, aExtractedLineFills);
+
+ // for SdrObject creation, just copy all to a single Hairline-PolyPolygon
+ for(const basegfx::B2DPolygon & rExtractedHairline : aExtractedHairlines)
+ {
+ aMergedHairlinePolyPolygon.append(rExtractedHairline);
+ }
+
+ // check for fill rsults
+ if (!aExtractedLineFills.empty() && !utl::ConfigManager::IsFuzzing())
+ {
+ // merge to a single tools::PolyPolygon (OR)
+ aMergedLineFillPolyPolygon = basegfx::utils::mergeToSinglePolyPolygon(std::move(aExtractedLineFills));
+ }
+ }
+
+ if(aMergedLineFillPolyPolygon.count() || (bForceLineDash && aMergedHairlinePolyPolygon.count()))
+ {
+ SfxItemSet aSet(GetMergedItemSet());
+ drawing::FillStyle eOldFillStyle = aSet.Get(XATTR_FILLSTYLE).GetValue();
+ SdrPathObj* aLinePolygonPart = nullptr;
+ SdrPathObj* aLineHairlinePart = nullptr;
+ bool bBuildGroup(false);
+
+ if(aMergedLineFillPolyPolygon.count())
+ {
+ // create SdrObject for filled line geometry
+ aLinePolygonPart = new SdrPathObj(
+ getSdrModelFromSdrObject(),
+ SdrObjKind::PathFill,
+ aMergedLineFillPolyPolygon);
+
+ // correct item properties
+ aSet.Put(XLineWidthItem(0));
+ aSet.Put(XLineStyleItem(drawing::LineStyle_NONE));
+ Color aColorLine = aSet.Get(XATTR_LINECOLOR).GetColorValue();
+ sal_uInt16 nTransLine = aSet.Get(XATTR_LINETRANSPARENCE).GetValue();
+ aSet.Put(XFillColorItem(OUString(), aColorLine));
+ aSet.Put(XFillStyleItem(drawing::FillStyle_SOLID));
+ aSet.Put(XFillTransparenceItem(nTransLine));
+
+ aLinePolygonPart->SetMergedItemSet(aSet);
+ }
+
+ if(aMergedHairlinePolyPolygon.count())
+ {
+ // create SdrObject for hairline geometry
+ // OBJ_PATHLINE is necessary here, not OBJ_PATHFILL. This is intended
+ // to get a non-filled object. If the poly is closed, the PathObj takes care for
+ // the correct closed state.
+ aLineHairlinePart = new SdrPathObj(
+ getSdrModelFromSdrObject(),
+ SdrObjKind::PathLine,
+ aMergedHairlinePolyPolygon);
+
+ aSet.Put(XLineWidthItem(0));
+ aSet.Put(XFillStyleItem(drawing::FillStyle_NONE));
+ aSet.Put(XLineStyleItem(drawing::LineStyle_SOLID));
+
+ // it is also necessary to switch off line start and ends here
+ aSet.Put(XLineStartWidthItem(0));
+ aSet.Put(XLineEndWidthItem(0));
+
+ aLineHairlinePart->SetMergedItemSet(aSet);
+
+ if(aLinePolygonPart)
+ {
+ bBuildGroup = true;
+ }
+ }
+
+ // check if original geometry should be added (e.g. filled and closed)
+ bool bAddOriginalGeometry(false);
+ SdrPathObj* pPath = dynamic_cast<SdrPathObj*>(this);
+
+ if(pPath && pPath->IsClosed())
+ {
+ if(eOldFillStyle != drawing::FillStyle_NONE)
+ {
+ bAddOriginalGeometry = true;
+ }
+ }
+
+ // do we need a group?
+ if(bBuildGroup || bAddOriginalGeometry)
+ {
+ SdrObject* pGroup = new SdrObjGroup(getSdrModelFromSdrObject());
+
+ if(bAddOriginalGeometry)
+ {
+ // Add a clone of the original geometry.
+ aSet.ClearItem();
+ aSet.Put(GetMergedItemSet());
+ aSet.Put(XLineStyleItem(drawing::LineStyle_NONE));
+ aSet.Put(XLineWidthItem(0));
+
+ SdrObject* pClone(CloneSdrObject(getSdrModelFromSdrObject()));
+ pClone->SetMergedItemSet(aSet);
+
+ pGroup->GetSubList()->NbcInsertObject(pClone);
+ }
+
+ if(aLinePolygonPart)
+ {
+ pGroup->GetSubList()->NbcInsertObject(aLinePolygonPart);
+ }
+
+ if(aLineHairlinePart)
+ {
+ pGroup->GetSubList()->NbcInsertObject(aLineHairlinePart);
+ }
+
+ pRetval = pGroup;
+ }
+ else
+ {
+ if(aLinePolygonPart)
+ {
+ pRetval = aLinePolygonPart;
+ }
+ else if(aLineHairlinePart)
+ {
+ pRetval = aLineHairlinePart;
+ }
+ }
+ }
+ }
+
+ if(nullptr == pRetval)
+ {
+ // due to current method usage, create and return a clone when nothing has changed
+ SdrObject* pClone(CloneSdrObject(getSdrModelFromSdrObject()));
+ pRetval = pClone;
+ }
+
+ return pRetval;
+}
+
+
+void SdrObject::SetMarkProtect(bool bProt)
+{
+ m_bMarkProt = bProt;
+}
+
+
+void SdrObject::SetEmptyPresObj(bool bEpt)
+{
+ m_bEmptyPresObj = bEpt;
+}
+
+
+void SdrObject::SetNotVisibleAsMaster(bool bFlg)
+{
+ m_bNotVisibleAsMaster=bFlg;
+}
+
+
+// convert this path object to contour object, even when it is a group
+SdrObject* SdrObject::ConvertToContourObj(SdrObject* pRet, bool bForceLineDash) const
+{
+ if(dynamic_cast<const SdrObjGroup*>( pRet) != nullptr)
+ {
+ SdrObjList* pObjList2 = pRet->GetSubList();
+ SdrObject* pGroup = new SdrObjGroup(getSdrModelFromSdrObject());
+
+ for(size_t a=0; a<pObjList2->GetObjCount(); ++a)
+ {
+ SdrObject* pIterObj = pObjList2->GetObj(a);
+ pGroup->GetSubList()->NbcInsertObject(ConvertToContourObj(pIterObj, bForceLineDash));
+ }
+
+ pRet = pGroup;
+ }
+ else
+ {
+ if (SdrPathObj *pPathObj = dynamic_cast<SdrPathObj*>(pRet))
+ {
+ // bezier geometry got created, even for straight edges since the given
+ // object is a result of DoConvertToPolyObj. For conversion to contour
+ // this is not really needed and can be reduced again AFAP
+ pPathObj->SetPathPoly(basegfx::utils::simplifyCurveSegments(pPathObj->GetPathPoly()));
+ }
+
+ pRet = pRet->ImpConvertToContourObj(bForceLineDash);
+ }
+
+ // #i73441# preserve LayerID
+ if(pRet && pRet->GetLayer() != GetLayer())
+ {
+ pRet->SetLayer(GetLayer());
+ }
+
+ return pRet;
+}
+
+
+SdrObjectUniquePtr SdrObject::ConvertToPolyObj(bool bBezier, bool bLineToArea) const
+{
+ SdrObjectUniquePtr pRet = DoConvertToPolyObj(bBezier, true);
+
+ if(pRet && bLineToArea)
+ {
+ SdrObject* pNewRet = ConvertToContourObj(pRet.get());
+ pRet.reset(pNewRet);
+ }
+
+ // #i73441# preserve LayerID
+ if(pRet && pRet->GetLayer() != GetLayer())
+ {
+ pRet->SetLayer(GetLayer());
+ }
+
+ return pRet;
+}
+
+
+SdrObjectUniquePtr SdrObject::DoConvertToPolyObj(bool /*bBezier*/, bool /*bAddText*/) const
+{
+ return nullptr;
+}
+
+
+void SdrObject::InsertedStateChange()
+{
+ const bool bIsInserted(nullptr != getParentSdrObjListFromSdrObject());
+ const tools::Rectangle aBoundRect0(GetLastBoundRect());
+
+ if(bIsInserted)
+ {
+ SendUserCall(SdrUserCallType::Inserted, aBoundRect0);
+ }
+ else
+ {
+ SendUserCall(SdrUserCallType::Removed, aBoundRect0);
+ }
+
+ if(nullptr != m_pPlusData && nullptr != m_pPlusData->pBroadcast)
+ {
+ SdrHint aHint(bIsInserted ? SdrHintKind::ObjectInserted : SdrHintKind::ObjectRemoved, *this);
+ m_pPlusData->pBroadcast->Broadcast(aHint);
+ }
+}
+
+void SdrObject::SetMoveProtect(bool bProt)
+{
+ if(IsMoveProtect() != bProt)
+ {
+ // #i77187# secured and simplified
+ m_bMovProt = bProt;
+ SetChanged();
+ BroadcastObjectChange();
+ }
+}
+
+void SdrObject::SetResizeProtect(bool bProt)
+{
+ if(IsResizeProtect() != bProt)
+ {
+ // #i77187# secured and simplified
+ m_bSizProt = bProt;
+ SetChanged();
+ BroadcastObjectChange();
+ }
+}
+
+void SdrObject::SetPrintable(bool bPrn)
+{
+ if( bPrn == m_bNoPrint )
+ {
+ m_bNoPrint=!bPrn;
+ SetChanged();
+ if (IsInserted())
+ {
+ SdrHint aHint(SdrHintKind::ObjectChange, *this);
+ getSdrModelFromSdrObject().Broadcast(aHint);
+ }
+ }
+}
+
+void SdrObject::SetVisible(bool bVisible)
+{
+ if( bVisible != mbVisible )
+ {
+ mbVisible = bVisible;
+ SetChanged();
+ if (IsInserted())
+ {
+ SdrHint aHint(SdrHintKind::ObjectChange, *this);
+ getSdrModelFromSdrObject().Broadcast(aHint);
+ }
+ }
+}
+
+
+sal_uInt16 SdrObject::GetUserDataCount() const
+{
+ if (m_pPlusData==nullptr || m_pPlusData->pUserDataList==nullptr) return 0;
+ return m_pPlusData->pUserDataList->GetUserDataCount();
+}
+
+SdrObjUserData* SdrObject::GetUserData(sal_uInt16 nNum) const
+{
+ if (m_pPlusData==nullptr || m_pPlusData->pUserDataList==nullptr) return nullptr;
+ return &m_pPlusData->pUserDataList->GetUserData(nNum);
+}
+
+void SdrObject::AppendUserData(std::unique_ptr<SdrObjUserData> pData)
+{
+ if (!pData)
+ {
+ OSL_FAIL("SdrObject::AppendUserData(): pData is NULL pointer.");
+ return;
+ }
+
+ ImpForcePlusData();
+ if (!m_pPlusData->pUserDataList)
+ m_pPlusData->pUserDataList.reset( new SdrObjUserDataList );
+
+ m_pPlusData->pUserDataList->AppendUserData(std::move(pData));
+}
+
+void SdrObject::DeleteUserData(sal_uInt16 nNum)
+{
+ sal_uInt16 nCount=GetUserDataCount();
+ if (nNum<nCount) {
+ m_pPlusData->pUserDataList->DeleteUserData(nNum);
+ if (nCount==1) {
+ m_pPlusData->pUserDataList.reset();
+ }
+ } else {
+ OSL_FAIL("SdrObject::DeleteUserData(): Invalid Index.");
+ }
+}
+
+void SdrObject::SetUserCall(SdrObjUserCall* pUser)
+{
+ m_pUserCall = pUser;
+}
+
+
+void SdrObject::SendUserCall(SdrUserCallType eUserCall, const tools::Rectangle& rBoundRect) const
+{
+ SdrObject* pGroup(getParentSdrObjectFromSdrObject());
+
+ if ( m_pUserCall )
+ {
+ m_pUserCall->Changed( *this, eUserCall, rBoundRect );
+ }
+
+ if(nullptr != pGroup && pGroup->GetUserCall())
+ {
+ // broadcast to group
+ SdrUserCallType eChildUserType = SdrUserCallType::ChildChangeAttr;
+
+ switch( eUserCall )
+ {
+ case SdrUserCallType::MoveOnly:
+ eChildUserType = SdrUserCallType::ChildMoveOnly;
+ break;
+
+ case SdrUserCallType::Resize:
+ eChildUserType = SdrUserCallType::ChildResize;
+ break;
+
+ case SdrUserCallType::ChangeAttr:
+ eChildUserType = SdrUserCallType::ChildChangeAttr;
+ break;
+
+ case SdrUserCallType::Delete:
+ eChildUserType = SdrUserCallType::ChildDelete;
+ break;
+
+ case SdrUserCallType::Inserted:
+ eChildUserType = SdrUserCallType::ChildInserted;
+ break;
+
+ case SdrUserCallType::Removed:
+ eChildUserType = SdrUserCallType::ChildRemoved;
+ break;
+
+ default: break;
+ }
+
+ pGroup->GetUserCall()->Changed( *this, eChildUserType, rBoundRect );
+ }
+
+ // notify our UNO shape listeners
+ switch ( eUserCall )
+ {
+ case SdrUserCallType::Resize:
+ notifyShapePropertyChange( svx::ShapePropertyProviderId::Size );
+ [[fallthrough]]; // RESIZE might also imply a change of the position
+ case SdrUserCallType::MoveOnly:
+ notifyShapePropertyChange( svx::ShapePropertyProviderId::Position );
+ break;
+ default:
+ // not interested in
+ break;
+ }
+}
+
+void SdrObject::setUnoShape( const uno::Reference< drawing::XShape >& _rxUnoShape )
+{
+ const uno::Reference< uno::XInterface>& xOldUnoShape( maWeakUnoShape );
+ // the UNO shape would be gutted by the following code; return early
+ if ( _rxUnoShape == xOldUnoShape )
+ {
+ if ( !xOldUnoShape.is() )
+ {
+ // make sure there is no stale impl. pointer if the UNO
+ // shape was destroyed meanwhile (remember we only hold weak
+ // reference to it!)
+ mpSvxShape = nullptr;
+ }
+ return;
+ }
+
+ bool bTransferOwnership( false );
+ if ( xOldUnoShape.is() )
+ {
+ bTransferOwnership = mpSvxShape->HasSdrObjectOwnership();
+ // Remove yourself from the current UNO shape. Its destructor
+ // will reset our UNO shape otherwise.
+ mpSvxShape->InvalidateSdrObject();
+ }
+
+ maWeakUnoShape = _rxUnoShape;
+ mpSvxShape = comphelper::getFromUnoTunnel<SvxShape>( _rxUnoShape );
+
+ // I think this may never happen... But I am not sure enough .-)
+ if ( bTransferOwnership )
+ {
+ if (mpSvxShape)
+ mpSvxShape->TakeSdrObjectOwnership();
+ SAL_WARN( "svx.uno", "a UNO shape took over an SdrObject previously owned by another UNO shape!");
+ }
+}
+
+/** only for internal use! */
+SvxShape* SdrObject::getSvxShape()
+{
+ DBG_TESTSOLARMUTEX();
+ // retrieving the impl pointer and subsequently using it is not thread-safe, of course, so it needs to be
+ // guarded by the SolarMutex
+
+ uno::Reference< uno::XInterface > xShape( maWeakUnoShape );
+#if OSL_DEBUG_LEVEL > 0
+ OSL_ENSURE( !( !xShape.is() && mpSvxShape ),
+ "SdrObject::getSvxShape: still having IMPL-Pointer to dead object!" );
+#endif
+ //#113608#, make sure mpSvxShape is always synchronized with maWeakUnoShape
+ if ( mpSvxShape && !xShape.is() )
+ mpSvxShape = nullptr;
+
+ return mpSvxShape;
+}
+
+css::uno::Reference< css::drawing::XShape > SdrObject::getUnoShape()
+{
+ // try weak reference first
+ uno::Reference< css::drawing::XShape > xShape( getWeakUnoShape() );
+ if( xShape )
+ return xShape;
+
+ OSL_ENSURE( mpSvxShape == nullptr, "SdrObject::getUnoShape: XShape already dead, but still an IMPL pointer!" );
+
+ // try to access SdrPage from this SdrObject. This will only exist if the SdrObject is
+ // inserted in a SdrObjList (page/group/3dScene)
+ SdrPage* pPageCandidate(getSdrPageFromSdrObject());
+
+ // tdf#12152, tdf#120728
+ //
+ // With the paradigm change to only get a SdrPage for a SdrObject when the SdrObject
+ // is *inserted*, the functionality for creating 1:1 associated UNO API implementation
+ // SvxShapes was partially broken: The used ::CreateShape relies on the SvxPage being
+ // derived and the CreateShape method overloaded, implementing additional SdrInventor
+ // types as needed.
+ //
+ // The fallback to use SvxDrawPage::CreateShapeByTypeAndInventor is a trap: It's only
+ // a static fallback that handles the SdrInventor types SdrInventor::E3d and
+ // SdrInventor::Default. Due to that, e.g. the ReportDesigner broke in various conditions.
+ //
+ // That again has to do with the ReportDesigner being implemented using the UNO API
+ // aspects of SdrObjects early during their construction, not just after these are
+ // inserted to a SdrPage - but that is not illegal or wrong, the SdrObject exists already.
+ //
+ // As a current solution, use the (now always available) SdrModel and any of the
+ // existing SdrPages. The only important thing is to get a SdrPage where ::CreateShape is
+ // overloaded and implemented as needed.
+ //
+ // Note for the future:
+ // In a more ideal world there would be only one factory method for creating SdrObjects (not
+ // ::CreateShape and ::CreateShapeByTypeAndInventor). This also would not be placed at
+ // SdrPage/SvxPage at all, but at the Model where it belongs - where else would you expect
+ // objects for the current Model to be constructed? To have this at the Page only would make
+ // sense if different shapes would need to be constructed for different Pages in the same Model
+ // - this is never the case.
+ // At that Model extended functionality for that factory (or overloads and implementations)
+ // should be placed. But to be realistic, migrating the factories to Model now is too much
+ // work - maybe over time when melting SdrObject/SvxObject one day...
+ //
+ // More Note (added by noel grandin)
+ // Except that sd/ is being naughty and doing all kinds of magic during CreateShape that
+ // requires knowing which page the object is being created for. Fixing that would require
+ // moving a bunch of nasty logic from object creation time, to the point in time when
+ // it is actually added to a page.
+ if(nullptr == pPageCandidate)
+ {
+ // If not inserted, alternatively access a SdrPage using the SdrModel. There is
+ // no reason not to create and return a UNO API XShape when the SdrObject is not
+ // inserted - it may be in construction. Main paradigm is that it exists.
+ if(0 != getSdrModelFromSdrObject().GetPageCount())
+ {
+ // Take 1st SdrPage. That may be e.g. a special page (in SD), but the
+ // to-be-used method ::CreateShape will be correctly overloaded in
+ // all cases
+ pPageCandidate = getSdrModelFromSdrObject().GetPage(0);
+ }
+ }
+
+ if(nullptr != pPageCandidate)
+ {
+ uno::Reference< uno::XInterface > xPage(pPageCandidate->getUnoPage());
+ if( xPage.is() )
+ {
+ SvxDrawPage* pDrawPage = comphelper::getFromUnoTunnel<SvxDrawPage>(xPage);
+ if( pDrawPage )
+ {
+ // create one
+ xShape = pDrawPage->CreateShape( this );
+ setUnoShape( xShape );
+ }
+ }
+ }
+ else
+ {
+ // Fallback to static base functionality. CAUTION: This will only support
+ // the most basic stuff like SdrInventor::E3d and SdrInventor::Default. All
+ // the other SdrInventor enum entries are from overloads and are *not accessible*
+ // using this fallback (!) - what a bad trap
+ rtl::Reference<SvxShape> xNewShape = SvxDrawPage::CreateShapeByTypeAndInventor( GetObjIdentifier(), GetObjInventor(), this );
+ mpSvxShape = xNewShape.get();
+ maWeakUnoShape = xShape = mpSvxShape;
+ }
+
+ return xShape;
+}
+
+svx::PropertyChangeNotifier& SdrObject::getShapePropertyChangeNotifier()
+{
+ DBG_TESTSOLARMUTEX();
+
+ SvxShape* pSvxShape = getSvxShape();
+ ENSURE_OR_THROW( pSvxShape, "no SvxShape, yet!" );
+ return pSvxShape->getShapePropertyChangeNotifier();
+}
+
+void SdrObject::notifyShapePropertyChange( const svx::ShapePropertyProviderId _eProperty ) const
+{
+ DBG_TESTSOLARMUTEX();
+
+ SvxShape* pSvxShape = const_cast< SdrObject* >( this )->getSvxShape();
+ if ( pSvxShape )
+ return pSvxShape->getShapePropertyChangeNotifier().notifyPropertyChange( _eProperty );
+}
+
+
+// transformation interface for StarOfficeAPI. This implements support for
+// homogeneous 3x3 matrices containing the transformation of the SdrObject. At the
+// moment it contains a shearX, rotation and translation, but for setting all linear
+// transforms like Scale, ShearX, ShearY, Rotate and Translate are supported.
+
+
+// gets base transformation and rectangle of object. If it's an SdrPathObj it fills the PolyPolygon
+// with the base geometry and returns TRUE. Otherwise it returns FALSE.
+bool SdrObject::TRGetBaseGeometry(basegfx::B2DHomMatrix& rMatrix, basegfx::B2DPolyPolygon& /*rPolyPolygon*/) const
+{
+ // any kind of SdrObject, just use SnapRect
+ tools::Rectangle aRectangle(GetSnapRect());
+
+ // convert to transformation values
+ basegfx::B2DTuple aScale(aRectangle.GetWidth(), aRectangle.GetHeight());
+ basegfx::B2DTuple aTranslate(aRectangle.Left(), aRectangle.Top());
+
+ // position maybe relative to anchorpos, convert
+ if(getSdrModelFromSdrObject().IsWriter())
+ {
+ if(GetAnchorPos().X() || GetAnchorPos().Y())
+ {
+ aTranslate -= basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
+ }
+ }
+
+ // build matrix
+ rMatrix = basegfx::utils::createScaleTranslateB2DHomMatrix(aScale, aTranslate);
+
+ return false;
+}
+
+// sets the base geometry of the object using infos contained in the homogeneous 3x3 matrix.
+// If it's an SdrPathObj it will use the provided geometry information. The Polygon has
+// to use (0,0) as upper left and will be scaled to the given size in the matrix.
+void SdrObject::TRSetBaseGeometry(const basegfx::B2DHomMatrix& rMatrix, const basegfx::B2DPolyPolygon& /*rPolyPolygon*/)
+{
+ // break up matrix
+ basegfx::B2DTuple aScale;
+ basegfx::B2DTuple aTranslate;
+ double fRotate, fShearX;
+ rMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // #i75086# Old DrawingLayer (GeoStat and geometry) does not support holding negative scalings
+ // in X and Y which equal a 180 degree rotation. Recognize it and react accordingly
+ if(basegfx::fTools::less(aScale.getX(), 0.0) && basegfx::fTools::less(aScale.getY(), 0.0))
+ {
+ aScale.setX(fabs(aScale.getX()));
+ aScale.setY(fabs(aScale.getY()));
+ }
+
+ // if anchor is used, make position relative to it
+ if(getSdrModelFromSdrObject().IsWriter())
+ {
+ if(GetAnchorPos().X() || GetAnchorPos().Y())
+ {
+ aTranslate += basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
+ }
+ }
+
+ // build BaseRect
+ Point aPoint(FRound(aTranslate.getX()), FRound(aTranslate.getY()));
+ tools::Rectangle aBaseRect(aPoint, Size(FRound(aScale.getX()), FRound(aScale.getY())));
+
+ // set BaseRect
+ SetSnapRect(aBaseRect);
+}
+
+// Give info if object is in destruction
+bool SdrObject::IsInDestruction() const
+{
+ return getSdrModelFromSdrObject().IsInDestruction();
+}
+
+// return if fill is != drawing::FillStyle_NONE
+bool SdrObject::HasFillStyle() const
+{
+ return GetObjectItem(XATTR_FILLSTYLE).GetValue() != drawing::FillStyle_NONE;
+}
+
+bool SdrObject::HasLineStyle() const
+{
+ return GetObjectItem(XATTR_LINESTYLE).GetValue() != drawing::LineStyle_NONE;
+}
+
+
+// #i52224#
+// on import of OLE object from MS documents the BLIP size might be retrieved,
+// the following four methods are used to control it;
+// usually this data makes no sense after the import is finished, since the object
+// might be resized
+
+
+void SdrObject::SetBLIPSizeRectangle( const tools::Rectangle& aRect )
+{
+ maBLIPSizeRectangle = aRect;
+}
+
+void SdrObject::SetContextWritingMode( const sal_Int16 /*_nContextWritingMode*/ )
+{
+ // this base class does not support different writing modes, so ignore the call
+}
+
+void SdrObject::SetDoNotInsertIntoPageAutomatically(const bool bSet)
+{
+ mbDoNotInsertIntoPageAutomatically = bSet;
+}
+
+
+// #i121917#
+bool SdrObject::HasText() const
+{
+ return false;
+}
+
+bool SdrObject::IsTextBox() const
+{
+ return false;
+}
+
+void SdrObject::MakeNameUnique()
+{
+ if (GetName().isEmpty())
+ {
+ if (const E3dScene* pE3dObj = dynamic_cast<const E3dScene*>(this))
+ {
+ SdrObjList* pObjList = pE3dObj->GetSubList();
+ if (pObjList)
+ {
+ SdrObject* pObj0 = pObjList->GetObj(0);
+ if (pObj0)
+ SetName(pObj0->TakeObjNameSingul());
+ }
+ }
+ else
+ SetName(TakeObjNameSingul());
+ }
+
+ std::unordered_set<OUString> aNameSet;
+ MakeNameUnique(aNameSet);
+}
+
+void SdrObject::MakeNameUnique(std::unordered_set<OUString>& rNameSet)
+{
+ if (GetName().isEmpty())
+ return;
+
+ if (rNameSet.empty())
+ {
+ SdrPage* pPage;
+ SdrObject* pObj;
+ for (sal_uInt16 nPage(0); nPage < mrSdrModelFromSdrObject.GetPageCount(); ++nPage)
+ {
+ pPage = mrSdrModelFromSdrObject.GetPage(nPage);
+ SdrObjListIter aIter(pPage, SdrIterMode::DeepWithGroups);
+ while (aIter.IsMore())
+ {
+ pObj = aIter.Next();
+ if (pObj != this)
+ rNameSet.insert(pObj->GetName());
+ }
+ }
+ }
+
+ OUString sName(GetName().trim());
+ OUString sRootName(sName);
+
+ if (!sName.isEmpty() && rtl::isAsciiDigit(sName[sName.getLength() - 1]))
+ {
+ sal_Int32 nPos(sName.getLength() - 1);
+ while (nPos > 0 && rtl::isAsciiDigit(sName[--nPos]));
+ sRootName = o3tl::trim(sName.subView(0, nPos + 1));
+ }
+ else
+ sName += " 1";
+
+ for (sal_uInt32 n = 1; rNameSet.find(sName) != rNameSet.end(); n++)
+ sName = sRootName + " " + OUString::number(n);
+ rNameSet.insert(sName);
+
+ SetName(sName);
+}
+
+void SdrObject::ForceMetricToItemPoolMetric(basegfx::B2DPolyPolygon& rPolyPolygon) const noexcept
+{
+ MapUnit eMapUnit(getSdrModelFromSdrObject().GetItemPool().GetMetric(0));
+ if(eMapUnit == MapUnit::Map100thMM)
+ return;
+
+ if (const auto eTo = MapToO3tlLength(eMapUnit); eTo != o3tl::Length::invalid)
+ {
+ const double fConvert(o3tl::convert(1.0, o3tl::Length::mm100, eTo));
+ rPolyPolygon.transform(basegfx::utils::createScaleB2DHomMatrix(fConvert, fConvert));
+ }
+ else
+ {
+ OSL_FAIL("Missing unit translation to PoolMetric!");
+ }
+}
+
+SdrObject* SdrObjFactory::CreateObjectFromFactory(SdrModel& rSdrModel, SdrInventor nInventor, SdrObjKind nObjIdentifier)
+{
+ SdrObjCreatorParams aParams { nInventor, nObjIdentifier, rSdrModel };
+ for (const auto & i : ImpGetUserMakeObjHdl()) {
+ SdrObject* pObj = i.Call(aParams);
+ if (pObj) {
+ return pObj;
+ }
+ }
+ return nullptr;
+}
+
+SdrObject* SdrObjFactory::MakeNewObject(
+ SdrModel& rSdrModel,
+ SdrInventor nInventor,
+ SdrObjKind nIdentifier,
+ const tools::Rectangle* pSnapRect)
+{
+ SdrObject* pObj(nullptr);
+ bool bSetSnapRect(nullptr != pSnapRect);
+
+ if (nInventor == SdrInventor::Default)
+ {
+ switch (nIdentifier)
+ {
+ case SdrObjKind::Measure:
+ {
+ if(nullptr != pSnapRect)
+ {
+ pObj = new SdrMeasureObj(
+ rSdrModel,
+ pSnapRect->TopLeft(),
+ pSnapRect->BottomRight());
+ }
+ else
+ {
+ pObj = new SdrMeasureObj(rSdrModel);
+ }
+ }
+ break;
+ case SdrObjKind::Line:
+ {
+ if(nullptr != pSnapRect)
+ {
+ basegfx::B2DPolygon aPoly;
+
+ aPoly.append(
+ basegfx::B2DPoint(
+ pSnapRect->Left(),
+ pSnapRect->Top()));
+ aPoly.append(
+ basegfx::B2DPoint(
+ pSnapRect->Right(),
+ pSnapRect->Bottom()));
+ pObj = new SdrPathObj(
+ rSdrModel,
+ SdrObjKind::Line,
+ basegfx::B2DPolyPolygon(aPoly));
+ }
+ else
+ {
+ pObj = new SdrPathObj(
+ rSdrModel,
+ SdrObjKind::Line);
+ }
+ }
+ break;
+ case SdrObjKind::Text:
+ case SdrObjKind::TitleText:
+ case SdrObjKind::OutlineText:
+ {
+ if(nullptr != pSnapRect)
+ {
+ pObj = new SdrRectObj(
+ rSdrModel,
+ nIdentifier,
+ *pSnapRect);
+ bSetSnapRect = false;
+ }
+ else
+ {
+ pObj = new SdrRectObj(
+ rSdrModel,
+ nIdentifier);
+ }
+ }
+ break;
+ case SdrObjKind::CircleOrEllipse:
+ case SdrObjKind::CircleSection:
+ case SdrObjKind::CircleArc:
+ case SdrObjKind::CircleCut:
+ {
+ SdrCircKind eCircKind = ToSdrCircKind(nIdentifier);
+ if(nullptr != pSnapRect)
+ {
+ pObj = new SdrCircObj(rSdrModel, eCircKind, *pSnapRect);
+ bSetSnapRect = false;
+ }
+ else
+ {
+ pObj = new SdrCircObj(rSdrModel, eCircKind);
+ }
+ }
+ break;
+ case SdrObjKind::NONE : pObj=new SdrObject(rSdrModel); break;
+ case SdrObjKind::Group : pObj=new SdrObjGroup(rSdrModel); break;
+ case SdrObjKind::Polygon : pObj=new SdrPathObj(rSdrModel, SdrObjKind::Polygon ); break;
+ case SdrObjKind::PolyLine : pObj=new SdrPathObj(rSdrModel, SdrObjKind::PolyLine ); break;
+ case SdrObjKind::PathLine : pObj=new SdrPathObj(rSdrModel, SdrObjKind::PathLine ); break;
+ case SdrObjKind::PathFill : pObj=new SdrPathObj(rSdrModel, SdrObjKind::PathFill ); break;
+ case SdrObjKind::FreehandLine : pObj=new SdrPathObj(rSdrModel, SdrObjKind::FreehandLine ); break;
+ case SdrObjKind::FreehandFill : pObj=new SdrPathObj(rSdrModel, SdrObjKind::FreehandFill ); break;
+ case SdrObjKind::PathPoly : pObj=new SdrPathObj(rSdrModel, SdrObjKind::Polygon ); break;
+ case SdrObjKind::PathPolyLine : pObj=new SdrPathObj(rSdrModel, SdrObjKind::PolyLine ); break;
+ case SdrObjKind::Edge : pObj=new SdrEdgeObj(rSdrModel); break;
+ case SdrObjKind::Rectangle : pObj=new SdrRectObj(rSdrModel); break;
+ case SdrObjKind::Graphic : pObj=new SdrGrafObj(rSdrModel); break;
+ case SdrObjKind::OLE2 : pObj=new SdrOle2Obj(rSdrModel); break;
+ case SdrObjKind::OLEPluginFrame : pObj=new SdrOle2Obj(rSdrModel, true); break;
+ case SdrObjKind::Caption : pObj=new SdrCaptionObj(rSdrModel); break;
+ case SdrObjKind::Page : pObj=new SdrPageObj(rSdrModel); break;
+ case SdrObjKind::UNO : pObj=new SdrUnoObj(rSdrModel, OUString()); break;
+ case SdrObjKind::CustomShape: pObj=new SdrObjCustomShape(rSdrModel); break;
+#if HAVE_FEATURE_AVMEDIA
+ case SdrObjKind::Media : pObj=new SdrMediaObj(rSdrModel); break;
+#endif
+ case SdrObjKind::Table : pObj=new sdr::table::SdrTableObj(rSdrModel); break;
+ default: break;
+ }
+ }
+
+ if (!pObj)
+ {
+ pObj = CreateObjectFromFactory(rSdrModel, nInventor, nIdentifier);
+ }
+
+ if (!pObj)
+ {
+ // Well, if no one wants it...
+ return nullptr;
+ }
+
+ if(bSetSnapRect && nullptr != pSnapRect)
+ {
+ pObj->SetSnapRect(*pSnapRect);
+ }
+
+ return pObj;
+}
+
+void SdrObjFactory::InsertMakeObjectHdl(Link<SdrObjCreatorParams, SdrObject*> const & rLink)
+{
+ std::vector<Link<SdrObjCreatorParams, SdrObject*>>& rLL=ImpGetUserMakeObjHdl();
+ auto it = std::find(rLL.begin(), rLL.end(), rLink);
+ if (it != rLL.end()) {
+ OSL_FAIL("SdrObjFactory::InsertMakeObjectHdl(): Link already in place.");
+ } else {
+ rLL.push_back(rLink);
+ }
+}
+
+void SdrObjFactory::RemoveMakeObjectHdl(Link<SdrObjCreatorParams, SdrObject*> const & rLink)
+{
+ std::vector<Link<SdrObjCreatorParams, SdrObject*>>& rLL=ImpGetUserMakeObjHdl();
+ auto it = std::find(rLL.begin(), rLL.end(), rLink);
+ if (it != rLL.end())
+ rLL.erase(it);
+}
+
+namespace svx
+{
+ ISdrObjectFilter::~ISdrObjectFilter()
+ {
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdobjplusdata.cxx b/svx/source/svdraw/svdobjplusdata.cxx
new file mode 100644
index 000000000..af27c5629
--- /dev/null
+++ b/svx/source/svdraw/svdobjplusdata.cxx
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+* This file is part of the LibreOffice project.
+*
+* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/.
+*/
+
+#include <svdobjplusdata.hxx>
+#include <svdobjuserdatalist.hxx>
+#include <o3tl/deleter.hxx>
+#include <svx/svdglue.hxx>
+#include <svl/SfxBroadcaster.hxx>
+#include <osl/diagnose.h>
+
+SdrObjPlusData::SdrObjPlusData()
+{
+}
+
+SdrObjPlusData::~SdrObjPlusData()
+{
+ o3tl::reset_preserve_ptr_during(pBroadcast);
+ pUserDataList.reset();
+ pGluePoints.reset();
+}
+
+SdrObjPlusData* SdrObjPlusData::Clone(SdrObject* pObj1) const
+{
+ SdrObjPlusData* pNewPlusData=new SdrObjPlusData;
+ if (pUserDataList!=nullptr) {
+ sal_uInt16 nCount=pUserDataList->GetUserDataCount();
+ if (nCount!=0) {
+ pNewPlusData->pUserDataList.reset(new SdrObjUserDataList);
+ for (sal_uInt16 i=0; i<nCount; i++) {
+ std::unique_ptr<SdrObjUserData> pNewUserData=pUserDataList->GetUserData(i).Clone(pObj1);
+ if (pNewUserData!=nullptr) {
+ pNewPlusData->pUserDataList->AppendUserData(std::move(pNewUserData));
+ } else {
+ OSL_FAIL("SdrObjPlusData::Clone(): UserData.Clone() returns NULL.");
+ }
+ }
+ }
+ }
+ if (pGluePoints!=nullptr) pNewPlusData->pGluePoints.reset(new SdrGluePointList(*pGluePoints));
+ // MtfAnimator isn't copied either
+
+ // #i68101#
+ // copy object name, title and description
+ pNewPlusData->aObjName = aObjName;
+ pNewPlusData->aObjTitle = aObjTitle;
+ pNewPlusData->aObjDescription = aObjDescription;
+
+ return pNewPlusData;
+}
+
+void SdrObjPlusData::SetGluePoints(const SdrGluePointList& rPts)
+{
+ *pGluePoints = rPts;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdobjuserdatalist.cxx b/svx/source/svdraw/svdobjuserdatalist.cxx
new file mode 100644
index 000000000..cae5e8db6
--- /dev/null
+++ b/svx/source/svdraw/svdobjuserdatalist.cxx
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+* This file is part of the LibreOffice project.
+*
+* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/.
+*/
+
+#include <memory>
+#include <svdobjuserdatalist.hxx>
+
+SdrObjUserDataList::SdrObjUserDataList() {}
+SdrObjUserDataList::~SdrObjUserDataList() {}
+
+size_t SdrObjUserDataList::GetUserDataCount() const
+{
+ return maList.size();
+}
+
+SdrObjUserData& SdrObjUserDataList::GetUserData(size_t nNum)
+{
+ return *maList.at(nNum);
+}
+
+void SdrObjUserDataList::AppendUserData(std::unique_ptr<SdrObjUserData> pData)
+{
+ maList.push_back(std::move(pData));
+}
+
+void SdrObjUserDataList::DeleteUserData(size_t nNum)
+{
+ maList.erase(maList.begin()+nNum);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdocapt.cxx b/svx/source/svdraw/svdocapt.cxx
new file mode 100644
index 000000000..4734ea222
--- /dev/null
+++ b/svx/source/svdraw/svdocapt.cxx
@@ -0,0 +1,756 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <cassert>
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/tuple/b2dtuple.hxx>
+#include <tools/bigint.hxx>
+#include <tools/helpers.hxx>
+
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+
+#include <sdr/contact/viewcontactofsdrcaptionobj.hxx>
+#include <sdr/properties/captionproperties.hxx>
+#include <svx/sdrhittesthelper.hxx>
+#include <svx/sdooitm.hxx>
+#include <svx/svddrag.hxx>
+#include <svx/svdhdl.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdocapt.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdtrans.hxx>
+#include <svx/svdview.hxx>
+#include <svx/sxcecitm.hxx>
+#include <svx/sxcgitm.hxx>
+#include <svx/sxcllitm.hxx>
+#include <svx/sxctitm.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/ptrstyle.hxx>
+
+namespace {
+
+enum EscDir {LKS,RTS,OBN,UNT};
+
+}
+
+class ImpCaptParams
+{
+public:
+ SdrCaptionType eType;
+ tools::Long nGap;
+ tools::Long nEscRel;
+ tools::Long nEscAbs;
+ tools::Long nLineLen;
+ SdrCaptionEscDir eEscDir;
+ bool bFitLineLen;
+ bool bEscRel;
+ bool bFixedAngle;
+
+public:
+ ImpCaptParams()
+ : eType(SdrCaptionType::Type3),
+ nGap(0), nEscRel(5000), nEscAbs(0),
+ nLineLen(0), eEscDir(SdrCaptionEscDir::Horizontal),
+ bFitLineLen(true), bEscRel(true), bFixedAngle(false)
+ {
+ }
+ void CalcEscPos(const Point& rTail, const tools::Rectangle& rRect, Point& rPt, EscDir& rDir) const;
+};
+
+void ImpCaptParams::CalcEscPos(const Point& rTailPt, const tools::Rectangle& rRect, Point& rPt, EscDir& rDir) const
+{
+ Point aTl(rTailPt); // copy locally for performance reasons
+ tools::Long nX,nY;
+ if (bEscRel) {
+ nX=rRect.Right()-rRect.Left();
+ nX=BigMulDiv(nX,nEscRel,10000);
+ nY=rRect.Bottom()-rRect.Top();
+ nY=BigMulDiv(nY,nEscRel,10000);
+ } else {
+ nX=nEscAbs;
+ nY=nEscAbs;
+ }
+ nX+=rRect.Left();
+ nY+=rRect.Top();
+ Point aBestPt;
+ EscDir eBestDir=LKS;
+ bool bTryH=eEscDir==SdrCaptionEscDir::BestFit;
+ if (!bTryH) {
+ if (eType!=SdrCaptionType::Type1) {
+ bTryH=eEscDir==SdrCaptionEscDir::Horizontal;
+ } else {
+ bTryH=eEscDir==SdrCaptionEscDir::Vertical;
+ }
+ }
+ bool bTryV=eEscDir==SdrCaptionEscDir::BestFit;
+ if (!bTryV) {
+ if (eType!=SdrCaptionType::Type1) {
+ bTryV=eEscDir==SdrCaptionEscDir::Vertical;
+ } else {
+ bTryV=eEscDir==SdrCaptionEscDir::Horizontal;
+ }
+ }
+
+ if (bTryH) {
+ Point aLft(rRect.Left()-nGap,nY);
+ Point aRgt(rRect.Right()+nGap,nY);
+ bool bLft=(aTl.X()-aLft.X()<aRgt.X()-aTl.X());
+ if (bLft) {
+ eBestDir=LKS;
+ aBestPt=aLft;
+ } else {
+ eBestDir=RTS;
+ aBestPt=aRgt;
+ }
+ }
+ if (bTryV) {
+ Point aTop(nX,rRect.Top()-nGap);
+ Point aBtm(nX,rRect.Bottom()+nGap);
+ bool bTop=(aTl.Y()-aTop.Y()<aBtm.Y()-aTl.Y());
+ Point aBest2;
+ EscDir eBest2;
+ if (bTop) {
+ eBest2=OBN;
+ aBest2=aTop;
+ } else {
+ eBest2=UNT;
+ aBest2=aBtm;
+ }
+ bool bTakeIt=eEscDir!=SdrCaptionEscDir::BestFit;
+ if (!bTakeIt) {
+ BigInt aHorX(aBestPt.X()-aTl.X()); aHorX*=aHorX;
+ BigInt aHorY(aBestPt.Y()-aTl.Y()); aHorY*=aHorY;
+ BigInt aVerX(aBest2.X()-aTl.X()); aVerX*=aVerX;
+ BigInt aVerY(aBest2.Y()-aTl.Y()); aVerY*=aVerY;
+ if (eType!=SdrCaptionType::Type1) {
+ bTakeIt=aVerX+aVerY<aHorX+aHorY;
+ } else {
+ bTakeIt=aVerX+aVerY>=aHorX+aHorY;
+ }
+ }
+ if (bTakeIt) {
+ aBestPt=aBest2;
+ eBestDir=eBest2;
+ }
+ }
+ rPt=aBestPt;
+ rDir=eBestDir;
+}
+
+
+// BaseProperties section
+
+std::unique_ptr<sdr::properties::BaseProperties> SdrCaptionObj::CreateObjectSpecificProperties()
+{
+ return std::make_unique<sdr::properties::CaptionProperties>(*this);
+}
+
+
+// DrawContact section
+
+std::unique_ptr<sdr::contact::ViewContact> SdrCaptionObj::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::ViewContactOfSdrCaptionObj>(*this);
+}
+
+
+SdrCaptionObj::SdrCaptionObj(SdrModel& rSdrModel)
+: SdrRectObj(rSdrModel, SdrObjKind::Text),
+ aTailPoly(3), // default size: 3 points = 2 lines
+ mbSpecialTextBoxShadow(false),
+ mbFixedTail(false),
+ mbSuppressGetBitmap(false)
+{
+}
+
+SdrCaptionObj::SdrCaptionObj(SdrModel& rSdrModel, SdrCaptionObj const & rSource)
+: SdrRectObj(rSdrModel, rSource),
+ mbSuppressGetBitmap(false)
+{
+ aTailPoly = rSource.aTailPoly;
+ mbSpecialTextBoxShadow = rSource.mbSpecialTextBoxShadow;
+ mbFixedTail = rSource.mbFixedTail;
+ maFixedTailPos = rSource.maFixedTailPos;
+}
+
+SdrCaptionObj::SdrCaptionObj(
+ SdrModel& rSdrModel,
+ const tools::Rectangle& rRect,
+ const Point& rTail)
+: SdrRectObj(rSdrModel, SdrObjKind::Text,rRect),
+ aTailPoly(3), // default size: 3 points = 2 lines
+ mbSpecialTextBoxShadow(false),
+ mbFixedTail(false),
+ mbSuppressGetBitmap(false)
+{
+ aTailPoly[0]=maFixedTailPos=rTail;
+}
+
+SdrCaptionObj::~SdrCaptionObj()
+{
+}
+
+void SdrCaptionObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
+{
+ rInfo.bRotateFreeAllowed=false;
+ rInfo.bRotate90Allowed =false;
+ rInfo.bMirrorFreeAllowed=false;
+ rInfo.bMirror45Allowed =false;
+ rInfo.bMirror90Allowed =false;
+ rInfo.bTransparenceAllowed = false;
+ rInfo.bShearAllowed =false;
+ rInfo.bEdgeRadiusAllowed=false;
+ rInfo.bCanConvToPath =true;
+ rInfo.bCanConvToPoly =true;
+ rInfo.bCanConvToPathLineToArea=false;
+ rInfo.bCanConvToPolyLineToArea=false;
+ rInfo.bCanConvToContour = (rInfo.bCanConvToPoly || LineGeometryUsageIsNecessary());
+}
+
+SdrObjKind SdrCaptionObj::GetObjIdentifier() const
+{
+ return SdrObjKind::Caption;
+}
+
+SdrCaptionObj* SdrCaptionObj::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new SdrCaptionObj(rTargetModel, *this);
+}
+
+OUString SdrCaptionObj::TakeObjNameSingul() const
+{
+ OUString sName(SvxResId(STR_ObjNameSingulCAPTION));
+
+ OUString aName(GetName());
+ if (!aName.isEmpty())
+ sName += " '" + aName + "'";
+
+ return sName;
+}
+
+OUString SdrCaptionObj::TakeObjNamePlural() const
+{
+ return SvxResId(STR_ObjNamePluralCAPTION);
+}
+
+basegfx::B2DPolyPolygon SdrCaptionObj::TakeXorPoly() const
+{
+ basegfx::B2DPolyPolygon aPolyPoly(SdrRectObj::TakeXorPoly());
+ aPolyPoly.append(aTailPoly.getB2DPolygon());
+
+ return aPolyPoly;
+}
+
+sal_uInt32 SdrCaptionObj::GetHdlCount() const
+{
+ sal_uInt32 nCount1(SdrRectObj::GetHdlCount());
+ // Currently only dragging the tail's end is implemented.
+ return nCount1 + 1;
+}
+
+void SdrCaptionObj::AddToHdlList(SdrHdlList& rHdlList) const
+{
+ SdrRectObj::AddToHdlList(rHdlList);
+ // Currently only dragging the tail's end is implemented.
+ std::unique_ptr<SdrHdl> pHdl(new SdrHdl(aTailPoly.GetPoint(0), SdrHdlKind::Poly));
+ pHdl->SetPolyNum(1);
+ pHdl->SetPointNum(0);
+ rHdlList.AddHdl(std::move(pHdl));
+}
+
+bool SdrCaptionObj::hasSpecialDrag() const
+{
+ return true;
+}
+
+bool SdrCaptionObj::beginSpecialDrag(SdrDragStat& rDrag) const
+{
+ const SdrHdl* pHdl = rDrag.GetHdl();
+ rDrag.SetEndDragChangesAttributes(true);
+ rDrag.SetEndDragChangesGeoAndAttributes(true);
+
+ if(pHdl && 0 == pHdl->GetPolyNum())
+ {
+ return SdrRectObj::beginSpecialDrag(rDrag);
+ }
+ else
+ {
+ rDrag.SetOrtho8Possible();
+
+ if(!pHdl)
+ {
+ if (m_bMovProt)
+ return false;
+
+ rDrag.SetNoSnap();
+ rDrag.SetActionRect(maRect);
+
+ Point aHit(rDrag.GetStart());
+
+ if(rDrag.GetPageView() && SdrObjectPrimitiveHit(*this, aHit, 0, *rDrag.GetPageView(), nullptr, false))
+ {
+ return true;
+ }
+ }
+ else
+ {
+ if((1 == pHdl->GetPolyNum()) && (0 == pHdl->GetPointNum()))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool SdrCaptionObj::applySpecialDrag(SdrDragStat& rDrag)
+{
+ const SdrHdl* pHdl = rDrag.GetHdl();
+
+ if(pHdl && 0 == pHdl->GetPolyNum())
+ {
+ const bool bRet(SdrRectObj::applySpecialDrag(rDrag));
+ ImpRecalcTail();
+ ActionChanged();
+
+ return bRet;
+ }
+ else
+ {
+ Point aDelt(rDrag.GetNow()-rDrag.GetStart());
+
+ if(!pHdl)
+ {
+ maRect.Move(aDelt.X(),aDelt.Y());
+ }
+ else
+ {
+ aTailPoly[0] += aDelt;
+ }
+
+ ImpRecalcTail();
+ ActionChanged();
+
+ return true;
+ }
+}
+
+OUString SdrCaptionObj::getSpecialDragComment(const SdrDragStat& rDrag) const
+{
+ const bool bCreateComment(rDrag.GetView() && this == rDrag.GetView()->GetCreateObj());
+
+ if(bCreateComment)
+ {
+ return OUString();
+ }
+ else
+ {
+ const SdrHdl* pHdl = rDrag.GetHdl();
+
+ if(pHdl && 0 == pHdl->GetPolyNum())
+ {
+ return SdrRectObj::getSpecialDragComment(rDrag);
+ }
+ else
+ {
+ if(!pHdl)
+ {
+ return ImpGetDescriptionStr(STR_DragCaptFram);
+ }
+ else
+ {
+ return ImpGetDescriptionStr(STR_DragCaptTail);
+ }
+ }
+ }
+}
+
+
+void SdrCaptionObj::ImpGetCaptParams(ImpCaptParams& rPara) const
+{
+ const SfxItemSet& rSet = GetObjectItemSet();
+ rPara.eType =rSet.Get(SDRATTR_CAPTIONTYPE ).GetValue();
+ rPara.bFixedAngle=rSet.Get(SDRATTR_CAPTIONFIXEDANGLE).GetValue();
+ rPara.nGap =static_cast<const SdrCaptionGapItem&> (rSet.Get(SDRATTR_CAPTIONGAP )).GetValue();
+ rPara.eEscDir =rSet.Get(SDRATTR_CAPTIONESCDIR ).GetValue();
+ rPara.bEscRel =rSet.Get(SDRATTR_CAPTIONESCISREL ).GetValue();
+ rPara.nEscRel =rSet.Get(SDRATTR_CAPTIONESCREL ).GetValue();
+ rPara.nEscAbs =rSet.Get(SDRATTR_CAPTIONESCABS ).GetValue();
+ rPara.nLineLen =rSet.Get(SDRATTR_CAPTIONLINELEN ).GetValue();
+ rPara.bFitLineLen=rSet.Get(SDRATTR_CAPTIONFITLINELEN).GetValue();
+}
+
+void SdrCaptionObj::ImpRecalcTail()
+{
+ ImpCaptParams aPara;
+ ImpGetCaptParams(aPara);
+ ImpCalcTail(aPara, aTailPoly, maRect);
+ SetBoundAndSnapRectsDirty();
+ SetXPolyDirty();
+}
+
+// #i35971#
+// SdrCaptionObj::ImpCalcTail1 does move the object(!). What a hack.
+// I really wonder why this had not triggered problems before. I am
+// sure there are some places where SetTailPos() is called at least
+// twice or SetSnapRect after it again just to work around this.
+// Changed this method to not do that.
+// Also found why this has been done: For interactive dragging of the
+// tail end pos for SdrCaptionType::Type1. This sure was the simplest method
+// to achieve this, at the cost of making a whole group of const methods
+// of this object implicitly change the object's position.
+void SdrCaptionObj::ImpCalcTail1(const ImpCaptParams& rPara, tools::Polygon& rPoly, tools::Rectangle const & rRect)
+{
+ tools::Polygon aPol(2);
+ Point aTl(rPoly[0]);
+
+ aPol[0] = aTl;
+ aPol[1] = aTl;
+
+ EscDir eEscDir;
+ Point aEscPos;
+
+ rPara.CalcEscPos(aTl, rRect, aEscPos, eEscDir);
+ aPol[1] = aEscPos;
+
+ if(eEscDir==LKS || eEscDir==RTS)
+ {
+ aPol[0].setX( aEscPos.X() );
+ }
+ else
+ {
+ aPol[0].setY( aEscPos.Y() );
+ }
+
+ rPoly = aPol;
+}
+
+void SdrCaptionObj::ImpCalcTail2(const ImpCaptParams& rPara, tools::Polygon& rPoly, tools::Rectangle const & rRect)
+{ // Gap/EscDir/EscPos/Angle
+ tools::Polygon aPol(2);
+ Point aTl(rPoly[0]);
+ aPol[0]=aTl;
+
+ EscDir eEscDir;
+ Point aEscPos;
+ rPara.CalcEscPos(aTl,rRect,aEscPos,eEscDir);
+ aPol[1]=aEscPos;
+
+ if (!rPara.bFixedAngle) {
+ // TODO: Implementation missing.
+ }
+ rPoly=aPol;
+}
+
+void SdrCaptionObj::ImpCalcTail3(const ImpCaptParams& rPara, tools::Polygon& rPoly, tools::Rectangle const & rRect)
+{ // Gap/EscDir/EscPos/Angle/LineLen
+ tools::Polygon aPol(3);
+ Point aTl(rPoly[0]);
+ aPol[0]=aTl;
+
+ EscDir eEscDir;
+ Point aEscPos;
+ rPara.CalcEscPos(aTl,rRect,aEscPos,eEscDir);
+ aPol[1]=aEscPos;
+ aPol[2]=aEscPos;
+
+ if (eEscDir==LKS || eEscDir==RTS) {
+ if (rPara.bFitLineLen) {
+ aPol[1].setX((aTl.X()+aEscPos.X())/2 );
+ } else {
+ if (eEscDir==LKS) aPol[1].AdjustX( -(rPara.nLineLen) );
+ else aPol[1].AdjustX(rPara.nLineLen );
+ }
+ } else {
+ if (rPara.bFitLineLen) {
+ aPol[1].setY((aTl.Y()+aEscPos.Y())/2 );
+ } else {
+ if (eEscDir==OBN) aPol[1].AdjustY( -(rPara.nLineLen) );
+ else aPol[1].AdjustY(rPara.nLineLen );
+ }
+ }
+ if (!rPara.bFixedAngle) {
+ // TODO: Implementation missing.
+ }
+ rPoly=aPol;
+}
+
+void SdrCaptionObj::ImpCalcTail(const ImpCaptParams& rPara, tools::Polygon& rPoly, tools::Rectangle const & rRect)
+{
+ switch (rPara.eType) {
+ case SdrCaptionType::Type1: ImpCalcTail1(rPara,rPoly,rRect); break;
+ case SdrCaptionType::Type2: ImpCalcTail2(rPara,rPoly,rRect); break;
+ case SdrCaptionType::Type3: ImpCalcTail3(rPara,rPoly,rRect); break;
+ case SdrCaptionType::Type4: ImpCalcTail3(rPara,rPoly,rRect); break;
+ }
+}
+
+bool SdrCaptionObj::BegCreate(SdrDragStat& rStat)
+{
+ if (maRect.IsEmpty()) return false; // Create currently only works with the given Rect
+
+ ImpCaptParams aPara;
+ ImpGetCaptParams(aPara);
+ maRect.SetPos(rStat.GetNow());
+ aTailPoly[0]=rStat.GetStart();
+ ImpCalcTail(aPara,aTailPoly,maRect);
+ rStat.SetActionRect(maRect);
+ return true;
+}
+
+bool SdrCaptionObj::MovCreate(SdrDragStat& rStat)
+{
+ ImpCaptParams aPara;
+ ImpGetCaptParams(aPara);
+ maRect.SetPos(rStat.GetNow());
+ ImpCalcTail(aPara,aTailPoly,maRect);
+ rStat.SetActionRect(maRect);
+ SetBoundRectDirty();
+ m_bSnapRectDirty=true;
+ return true;
+}
+
+bool SdrCaptionObj::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd)
+{
+ ImpCaptParams aPara;
+ ImpGetCaptParams(aPara);
+ maRect.SetPos(rStat.GetNow());
+ ImpCalcTail(aPara,aTailPoly,maRect);
+ SetBoundAndSnapRectsDirty();
+ return (eCmd==SdrCreateCmd::ForceEnd || rStat.GetPointCount()>=2);
+}
+
+bool SdrCaptionObj::BckCreate(SdrDragStat& /*rStat*/)
+{
+ return false;
+}
+
+void SdrCaptionObj::BrkCreate(SdrDragStat& /*rStat*/)
+{
+}
+
+basegfx::B2DPolyPolygon SdrCaptionObj::TakeCreatePoly(const SdrDragStat& /*rDrag*/) const
+{
+ basegfx::B2DPolyPolygon aRetval;
+ const basegfx::B2DRange aRange =vcl::unotools::b2DRectangleFromRectangle(maRect);
+ aRetval.append(basegfx::utils::createPolygonFromRect(aRange));
+ aRetval.append(aTailPoly.getB2DPolygon());
+ return aRetval;
+}
+
+PointerStyle SdrCaptionObj::GetCreatePointer() const
+{
+ return PointerStyle::DrawCaption;
+}
+
+void SdrCaptionObj::NbcMove(const Size& rSiz)
+{
+ SdrRectObj::NbcMove(rSiz);
+ MovePoly(aTailPoly,rSiz);
+ if(mbFixedTail)
+ SetTailPos(GetFixedTailPos());
+}
+
+void SdrCaptionObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
+{
+ SdrRectObj::NbcResize(rRef,xFact,yFact);
+ ResizePoly(aTailPoly,rRef,xFact,yFact);
+ ImpRecalcTail();
+ if(mbFixedTail)
+ SetTailPos(GetFixedTailPos());
+}
+
+void SdrCaptionObj::NbcSetRelativePos(const Point& rPnt)
+{
+ Point aRelPos0(aTailPoly.GetPoint(0)-m_aAnchor);
+ Size aSiz(rPnt.X()-aRelPos0.X(),rPnt.Y()-aRelPos0.Y());
+ NbcMove(aSiz); // This also calls SetRectsDirty()
+}
+
+Point SdrCaptionObj::GetRelativePos() const
+{
+ return aTailPoly.GetPoint(0)-m_aAnchor;
+}
+
+const tools::Rectangle& SdrCaptionObj::GetLogicRect() const
+{
+ return maRect;
+}
+
+void SdrCaptionObj::NbcSetLogicRect(const tools::Rectangle& rRect)
+{
+ SdrRectObj::NbcSetLogicRect(rRect);
+ ImpRecalcTail();
+}
+
+const Point& SdrCaptionObj::GetTailPos() const
+{
+ return aTailPoly[0];
+}
+
+void SdrCaptionObj::SetTailPos(const Point& rPos)
+{
+ if (aTailPoly.GetSize()==0 || aTailPoly[0]!=rPos) {
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ NbcSetTailPos(rPos);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+ }
+}
+
+void SdrCaptionObj::NbcSetTailPos(const Point& rPos)
+{
+ aTailPoly[0]=rPos;
+ ImpRecalcTail();
+}
+
+sal_uInt32 SdrCaptionObj::GetSnapPointCount() const
+{
+ // TODO: Implementation missing.
+ return 0;
+}
+
+Point SdrCaptionObj::GetSnapPoint(sal_uInt32 /*i*/) const
+{
+ // TODO: Implementation missing.
+ return Point(0,0);
+}
+
+void SdrCaptionObj::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
+{
+ SdrRectObj::Notify(rBC,rHint);
+ ImpRecalcTail();
+}
+
+std::unique_ptr<SdrObjGeoData> SdrCaptionObj::NewGeoData() const
+{
+ return std::make_unique<SdrCaptObjGeoData>();
+}
+
+void SdrCaptionObj::SaveGeoData(SdrObjGeoData& rGeo) const
+{
+ SdrRectObj::SaveGeoData(rGeo);
+ SdrCaptObjGeoData& rCGeo=static_cast<SdrCaptObjGeoData&>(rGeo);
+ rCGeo.aTailPoly=aTailPoly;
+}
+
+void SdrCaptionObj::RestoreGeoData(const SdrObjGeoData& rGeo)
+{
+ SdrRectObj::RestoreGeoData(rGeo);
+ const SdrCaptObjGeoData& rCGeo=static_cast<const SdrCaptObjGeoData&>(rGeo);
+ aTailPoly=rCGeo.aTailPoly;
+}
+
+SdrObjectUniquePtr SdrCaptionObj::DoConvertToPolyObj(bool bBezier, bool bAddText) const
+{
+ SdrObjectUniquePtr pRect = SdrRectObj::DoConvertToPolyObj(bBezier, bAddText);
+ SdrObjectUniquePtr pTail = ImpConvertMakeObj(basegfx::B2DPolyPolygon(aTailPoly.getB2DPolygon()), false, bBezier);
+ SdrObjectUniquePtr pRet;
+ if (pTail && !pRect)
+ pRet = std::move(pTail);
+ else if (pRect && !pTail)
+ pRet = std::move(pRect);
+ else if (pTail && pRect)
+ {
+ if (pTail->GetSubList())
+ {
+ pTail->GetSubList()->NbcInsertObject(pRect.release());
+ pRet = std::move(pTail);
+ }
+ else if (pRect->GetSubList())
+ {
+ pRect->GetSubList()->NbcInsertObject(pTail.release(),0);
+ pRet = std::move(pRect);
+ }
+ else
+ {
+ SdrObjGroup* pGrp = new SdrObjGroup(getSdrModelFromSdrObject());
+ pGrp->GetSubList()->NbcInsertObject(pRect.release());
+ pGrp->GetSubList()->NbcInsertObject(pTail.release(),0);
+ pRet.reset(pGrp);
+ }
+ }
+ return pRet;
+}
+
+namespace {
+
+void handleNegativeScale(basegfx::B2DTuple & scale, double * rotate) {
+ assert(rotate != nullptr);
+
+ // #i75086# Old DrawingLayer (GeoStat and geometry) does not support holding negative scalings
+ // in X and Y which equal a 180 degree rotation. Recognize it and react accordingly
+ if(basegfx::fTools::less(scale.getX(), 0.0) && basegfx::fTools::less(scale.getY(), 0.0))
+ {
+ scale.setX(fabs(scale.getX()));
+ scale.setY(fabs(scale.getY()));
+ *rotate = fmod(*rotate + M_PI, 2 * M_PI);
+ }
+}
+
+}
+
+// #i32599#
+// Add own implementation for TRSetBaseGeometry to handle TailPos over changes.
+void SdrCaptionObj::TRSetBaseGeometry(const basegfx::B2DHomMatrix& rMatrix, const basegfx::B2DPolyPolygon& /*rPolyPolygon*/)
+{
+ // break up matrix
+ basegfx::B2DTuple aScale;
+ basegfx::B2DTuple aTranslate;
+ double fRotate, fShearX;
+ rMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
+
+ handleNegativeScale(aScale, &fRotate);
+
+ // if anchor is used, make position relative to it
+ if(getSdrModelFromSdrObject().IsWriter())
+ {
+ if(GetAnchorPos().X() || GetAnchorPos().Y())
+ {
+ aTranslate += basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
+ }
+ }
+
+ // build BaseRect
+ Point aPoint(FRound(aTranslate.getX()), FRound(aTranslate.getY()));
+ tools::Rectangle aBaseRect(aPoint, Size(FRound(aScale.getX()), FRound(aScale.getY())));
+
+ // set BaseRect, but rescue TailPos over this call
+ const Point aTailPoint = GetTailPos();
+ SetSnapRect(aBaseRect);
+ SetTailPos(aTailPoint);
+ ImpRecalcTail();
+}
+
+// geometry access
+basegfx::B2DPolygon SdrCaptionObj::getTailPolygon() const
+{
+ return aTailPoly.getB2DPolygon();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdocirc.cxx b/svx/source/svdraw/svdocirc.cxx
new file mode 100644
index 000000000..0204b2c2e
--- /dev/null
+++ b/svx/source/svdraw/svdocirc.cxx
@@ -0,0 +1,1154 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <math.h>
+#include <rtl/ustrbuf.hxx>
+
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+
+#include <sdr/contact/viewcontactofsdrcircobj.hxx>
+#include <sdr/properties/circleproperties.hxx>
+#include <svx/svddrag.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdocirc.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/svdtrans.hxx>
+#include <svx/svdview.hxx>
+#include <svx/sxciaitm.hxx>
+#include <sxcikitm.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/xlnedit.hxx>
+#include <svx/xlnedwit.hxx>
+#include <svx/xlnstit.hxx>
+#include <svx/xlnstwit.hxx>
+#include <svx/xlnwtit.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/ptrstyle.hxx>
+
+using namespace com::sun::star;
+
+static Point GetAnglePnt(const tools::Rectangle& rR, Degree100 nAngle)
+{
+ Point aCenter(rR.Center());
+ tools::Long nWdt=rR.Right()-rR.Left();
+ tools::Long nHgt=rR.Bottom()-rR.Top();
+ tools::Long nMaxRad=(std::max(nWdt,nHgt)+1) /2;
+ double a = toRadians(nAngle);
+ Point aRetval(FRound(cos(a)*nMaxRad),-FRound(sin(a)*nMaxRad));
+ if (nWdt==0) aRetval.setX(0 );
+ if (nHgt==0) aRetval.setY(0 );
+ if (nWdt!=nHgt) {
+ if (nWdt>nHgt) {
+ if (nWdt!=0) {
+ // stop possible overruns for very large objects
+ if (std::abs(nHgt)>32767 || std::abs(aRetval.Y())>32767) {
+ aRetval.setY(BigMulDiv(aRetval.Y(),nHgt,nWdt) );
+ } else {
+ aRetval.setY(aRetval.Y()*nHgt/nWdt );
+ }
+ }
+ } else {
+ if (nHgt!=0) {
+ // stop possible overruns for very large objects
+ if (std::abs(nWdt)>32767 || std::abs(aRetval.X())>32767) {
+ aRetval.setX(BigMulDiv(aRetval.X(),nWdt,nHgt) );
+ } else {
+ aRetval.setX(aRetval.X()*nWdt/nHgt );
+ }
+ }
+ }
+ }
+ aRetval+=aCenter;
+ return aRetval;
+}
+
+
+// BaseProperties section
+
+std::unique_ptr<sdr::properties::BaseProperties> SdrCircObj::CreateObjectSpecificProperties()
+{
+ return std::make_unique<sdr::properties::CircleProperties>(*this);
+}
+
+
+// DrawContact section
+
+std::unique_ptr<sdr::contact::ViewContact> SdrCircObj::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::ViewContactOfSdrCircObj>(*this);
+}
+
+SdrCircKind ToSdrCircKind(SdrObjKind eKind)
+{
+ switch (eKind)
+ {
+ case SdrObjKind::CircleOrEllipse: return SdrCircKind::Full;
+ case SdrObjKind::CircleSection: return SdrCircKind::Section;
+ case SdrObjKind::CircleArc: return SdrCircKind::Arc;
+ case SdrObjKind::CircleCut: return SdrCircKind::Cut;
+ default: assert(false);
+ }
+ return SdrCircKind::Full;
+}
+
+SdrCircObj::SdrCircObj(
+ SdrModel& rSdrModel,
+ SdrCircKind eNewKind)
+: SdrRectObj(rSdrModel)
+{
+ nStartAngle=0_deg100;
+ nEndAngle=36000_deg100;
+ meCircleKind=eNewKind;
+ m_bClosedObj=eNewKind!=SdrCircKind::Arc;
+}
+
+SdrCircObj::SdrCircObj(SdrModel& rSdrModel, SdrCircObj const & rSource)
+: SdrRectObj(rSdrModel, rSource)
+{
+ meCircleKind = rSource.meCircleKind;
+ nStartAngle = rSource.nStartAngle;
+ nEndAngle = rSource.nEndAngle;
+ m_bClosedObj = rSource.m_bClosedObj;
+}
+
+SdrCircObj::SdrCircObj(
+ SdrModel& rSdrModel,
+ SdrCircKind eNewKind,
+ const tools::Rectangle& rRect)
+: SdrRectObj(rSdrModel, rRect)
+{
+ nStartAngle=0_deg100;
+ nEndAngle=36000_deg100;
+ meCircleKind=eNewKind;
+ m_bClosedObj=eNewKind!=SdrCircKind::Arc;
+}
+
+SdrCircObj::SdrCircObj(
+ SdrModel& rSdrModel,
+ SdrCircKind eNewKind,
+ const tools::Rectangle& rRect,
+ Degree100 nNewStartAngle,
+ Degree100 nNewEndAngle)
+: SdrRectObj(rSdrModel, rRect)
+{
+ Degree100 nAngleDif=nNewEndAngle-nNewStartAngle;
+ nStartAngle=NormAngle36000(nNewStartAngle);
+ nEndAngle=NormAngle36000(nNewEndAngle);
+ if (nAngleDif==36000_deg100) nEndAngle+=nAngleDif; // full circle
+ meCircleKind=eNewKind;
+ m_bClosedObj=eNewKind!=SdrCircKind::Arc;
+}
+
+SdrCircObj::~SdrCircObj()
+{
+}
+
+void SdrCircObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
+{
+ bool bCanConv=!HasText() || ImpCanConvTextToCurve();
+ rInfo.bEdgeRadiusAllowed = false;
+ rInfo.bCanConvToPath=bCanConv;
+ rInfo.bCanConvToPoly=bCanConv;
+ rInfo.bCanConvToContour = !IsFontwork() && (rInfo.bCanConvToPoly || LineGeometryUsageIsNecessary());
+}
+
+SdrObjKind SdrCircObj::GetObjIdentifier() const
+{
+ switch (meCircleKind)
+ {
+ case SdrCircKind::Full: return SdrObjKind::CircleOrEllipse;
+ case SdrCircKind::Section: return SdrObjKind::CircleSection;
+ case SdrCircKind::Cut: return SdrObjKind::CircleCut;
+ case SdrCircKind::Arc: return SdrObjKind::CircleArc;
+ default: assert(false);
+ }
+ return SdrObjKind::CircleOrEllipse;
+}
+
+bool SdrCircObj::PaintNeedsXPolyCirc() const
+{
+ // XPoly is necessary for all rotated ellipse objects, circle and
+ // ellipse segments.
+ // If not WIN, then (for now) also for circle/ellipse segments and circle/
+ // ellipse arcs (for precision)
+ bool bNeed = maGeo.nRotationAngle || maGeo.nShearAngle || meCircleKind == SdrCircKind::Cut;
+ // If not WIN, then for everything except full circle (for now!)
+ if (meCircleKind!=SdrCircKind::Full) bNeed = true;
+
+ const SfxItemSet& rSet = GetObjectItemSet();
+ if(!bNeed)
+ {
+ // XPoly is necessary for everything that isn't LineSolid or LineNone
+ drawing::LineStyle eLine = rSet.Get(XATTR_LINESTYLE).GetValue();
+ bNeed = eLine != drawing::LineStyle_NONE && eLine != drawing::LineStyle_SOLID;
+
+ // XPoly is necessary for thick lines
+ if(!bNeed && eLine != drawing::LineStyle_NONE)
+ bNeed = rSet.Get(XATTR_LINEWIDTH).GetValue() != 0;
+
+ // XPoly is necessary for circle arcs with line ends
+ if(!bNeed && meCircleKind == SdrCircKind::Arc)
+ {
+ // start of the line is here if StartPolygon, StartWidth!=0
+ bNeed=rSet.Get(XATTR_LINESTART).GetLineStartValue().count() != 0 &&
+ rSet.Get(XATTR_LINESTARTWIDTH).GetValue() != 0;
+
+ if(!bNeed)
+ {
+ // end of the line is here if EndPolygon, EndWidth!=0
+ bNeed = rSet.Get(XATTR_LINEEND).GetLineEndValue().count() != 0 &&
+ rSet.Get(XATTR_LINEENDWIDTH).GetValue() != 0;
+ }
+ }
+ }
+
+ // XPoly is necessary if Fill !=None and !=Solid
+ if(!bNeed && meCircleKind != SdrCircKind::Arc)
+ {
+ drawing::FillStyle eFill=rSet.Get(XATTR_FILLSTYLE).GetValue();
+ bNeed = eFill != drawing::FillStyle_NONE && eFill != drawing::FillStyle_SOLID;
+ }
+
+ if(!bNeed && meCircleKind != SdrCircKind::Full && nStartAngle == nEndAngle)
+ bNeed = true; // otherwise we're drawing a full circle
+
+ return bNeed;
+}
+
+basegfx::B2DPolygon SdrCircObj::ImpCalcXPolyCirc(const SdrCircKind eCircleKind, const tools::Rectangle& rRect1, Degree100 nStart, Degree100 nEnd) const
+{
+ const basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(rRect1);
+ basegfx::B2DPolygon aCircPolygon;
+
+ if(SdrCircKind::Full == eCircleKind)
+ {
+ // create full circle. Do not use createPolygonFromEllipse; it's necessary
+ // to get the start point to the bottom of the circle to keep compatible to
+ // old geometry creation
+ aCircPolygon = basegfx::utils::createPolygonFromUnitCircle(1);
+
+ // needs own scaling and translation from unit circle to target size (same as
+ // would be in createPolygonFromEllipse)
+ const basegfx::B2DPoint aCenter(aRange.getCenter());
+ const basegfx::B2DHomMatrix aMatrix(basegfx::utils::createScaleTranslateB2DHomMatrix(
+ aRange.getWidth() / 2.0, aRange.getHeight() / 2.0,
+ aCenter.getX(), aCenter.getY()));
+ aCircPolygon.transform(aMatrix);
+ }
+ else
+ {
+ // mirror start, end for geometry creation since model coordinate system is mirrored in Y
+ const double fStart(toRadians((36000_deg100 - nEnd) % 36000_deg100));
+ const double fEnd(toRadians((36000_deg100 - nStart) % 36000_deg100));
+
+ // create circle segment. This is not closed by default
+ aCircPolygon = basegfx::utils::createPolygonFromEllipseSegment(
+ aRange.getCenter(), aRange.getWidth() / 2.0, aRange.getHeight() / 2.0,
+ fStart, fEnd);
+
+ // check closing states
+ const bool bCloseSegment(SdrCircKind::Arc != eCircleKind);
+ const bool bCloseUsingCenter(SdrCircKind::Section == eCircleKind);
+
+ if(bCloseSegment)
+ {
+ if(bCloseUsingCenter)
+ {
+ // add center point at start (for historical reasons)
+ basegfx::B2DPolygon aSector;
+ aSector.append(aRange.getCenter());
+ aSector.append(aCircPolygon);
+ aCircPolygon = aSector;
+ }
+
+ // close
+ aCircPolygon.setClosed(true);
+ }
+ }
+
+ // #i76950#
+ if (maGeo.nShearAngle || maGeo.nRotationAngle)
+ {
+ // translate top left to (0,0)
+ const basegfx::B2DPoint aTopLeft(aRange.getMinimum());
+ basegfx::B2DHomMatrix aMatrix(basegfx::utils::createTranslateB2DHomMatrix(
+ -aTopLeft.getX(), -aTopLeft.getY()));
+
+ // shear, rotate and back to top left (if needed)
+ aMatrix = basegfx::utils::createShearXRotateTranslateB2DHomMatrix(
+ -maGeo.mfTanShearAngle,
+ maGeo.nRotationAngle ? toRadians(36000_deg100 - maGeo.nRotationAngle) : 0.0,
+ aTopLeft) * aMatrix;
+
+ // apply transformation
+ aCircPolygon.transform(aMatrix);
+ }
+
+ return aCircPolygon;
+}
+
+void SdrCircObj::RecalcXPoly()
+{
+ basegfx::B2DPolygon aPolyCirc(ImpCalcXPolyCirc(meCircleKind, maRect, nStartAngle, nEndAngle));
+ mpXPoly = XPolygon(aPolyCirc);
+}
+
+OUString SdrCircObj::TakeObjNameSingul() const
+{
+ TranslateId pID=STR_ObjNameSingulCIRC;
+ if (maRect.GetWidth() == maRect.GetHeight() && maGeo.nShearAngle==0_deg100)
+ {
+ switch (meCircleKind) {
+ case SdrCircKind::Full: pID=STR_ObjNameSingulCIRC; break;
+ case SdrCircKind::Section: pID=STR_ObjNameSingulSECT; break;
+ case SdrCircKind::Arc: pID=STR_ObjNameSingulCARC; break;
+ case SdrCircKind::Cut: pID=STR_ObjNameSingulCCUT; break;
+ default: break;
+ }
+ } else {
+ switch (meCircleKind) {
+ case SdrCircKind::Full: pID=STR_ObjNameSingulCIRCE; break;
+ case SdrCircKind::Section: pID=STR_ObjNameSingulSECTE; break;
+ case SdrCircKind::Arc: pID=STR_ObjNameSingulCARCE; break;
+ case SdrCircKind::Cut: pID=STR_ObjNameSingulCCUTE; break;
+ default: break;
+ }
+ }
+ OUString sName(SvxResId(pID));
+
+ OUString aName(GetName());
+ if (!aName.isEmpty())
+ sName += " '" + aName + "'";
+ return sName;
+}
+
+OUString SdrCircObj::TakeObjNamePlural() const
+{
+ TranslateId pID=STR_ObjNamePluralCIRC;
+ if (maRect.GetWidth() == maRect.GetHeight() && maGeo.nShearAngle==0_deg100)
+ {
+ switch (meCircleKind) {
+ case SdrCircKind::Full: pID=STR_ObjNamePluralCIRC; break;
+ case SdrCircKind::Section: pID=STR_ObjNamePluralSECT; break;
+ case SdrCircKind::Arc: pID=STR_ObjNamePluralCARC; break;
+ case SdrCircKind::Cut: pID=STR_ObjNamePluralCCUT; break;
+ default: break;
+ }
+ } else {
+ switch (meCircleKind) {
+ case SdrCircKind::Full: pID=STR_ObjNamePluralCIRCE; break;
+ case SdrCircKind::Section: pID=STR_ObjNamePluralSECTE; break;
+ case SdrCircKind::Arc: pID=STR_ObjNamePluralCARCE; break;
+ case SdrCircKind::Cut: pID=STR_ObjNamePluralCCUTE; break;
+ default: break;
+ }
+ }
+ return SvxResId(pID);
+}
+
+SdrCircObj* SdrCircObj::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new SdrCircObj(rTargetModel, *this);
+}
+
+basegfx::B2DPolyPolygon SdrCircObj::TakeXorPoly() const
+{
+ const basegfx::B2DPolygon aCircPolygon(ImpCalcXPolyCirc(meCircleKind, maRect, nStartAngle, nEndAngle));
+ return basegfx::B2DPolyPolygon(aCircPolygon);
+}
+
+namespace {
+
+struct ImpCircUser : public SdrDragStatUserData
+{
+ tools::Rectangle aR;
+ Point aCenter;
+ Point aP1;
+ tools::Long nHgt;
+ tools::Long nWdt;
+ Degree100 nStart;
+ Degree100 nEnd;
+
+public:
+ ImpCircUser()
+ : nHgt(0),
+ nWdt(0),
+ nStart(0),
+ nEnd(0)
+ {}
+ void SetCreateParams(SdrDragStat const & rStat);
+};
+
+}
+
+sal_uInt32 SdrCircObj::GetHdlCount() const
+{
+ if(SdrCircKind::Full != meCircleKind)
+ {
+ return 10;
+ }
+ else
+ {
+ return 8;
+ }
+}
+
+void SdrCircObj::AddToHdlList(SdrHdlList& rHdlList) const
+{
+ for (sal_uInt32 nHdlNum=(SdrCircKind::Full==meCircleKind)?2:0; nHdlNum<=9; ++nHdlNum)
+ {
+ Point aPnt;
+ SdrHdlKind eLocalKind(SdrHdlKind::Move);
+ sal_uInt32 nPNum(0);
+
+ switch (nHdlNum)
+ {
+ case 0:
+ aPnt = GetAnglePnt(maRect,nStartAngle);
+ eLocalKind = SdrHdlKind::Circle;
+ nPNum = 1;
+ break;
+ case 1:
+ aPnt = GetAnglePnt(maRect,nEndAngle);
+ eLocalKind = SdrHdlKind::Circle;
+ nPNum = 2;
+ break;
+ case 2:
+ aPnt = maRect.TopLeft();
+ eLocalKind = SdrHdlKind::UpperLeft;
+ break;
+ case 3:
+ aPnt = maRect.TopCenter();
+ eLocalKind = SdrHdlKind::Upper;
+ break;
+ case 4:
+ aPnt = maRect.TopRight();
+ eLocalKind = SdrHdlKind::UpperRight;
+ break;
+ case 5:
+ aPnt = maRect.LeftCenter();
+ eLocalKind = SdrHdlKind::Left;
+ break;
+ case 6:
+ aPnt = maRect.RightCenter();
+ eLocalKind = SdrHdlKind::Right;
+ break;
+ case 7:
+ aPnt = maRect.BottomLeft();
+ eLocalKind = SdrHdlKind::LowerLeft;
+ break;
+ case 8:
+ aPnt = maRect.BottomCenter();
+ eLocalKind = SdrHdlKind::Lower;
+ break;
+ case 9:
+ aPnt = maRect.BottomRight();
+ eLocalKind = SdrHdlKind::LowerRight;
+ break;
+ }
+
+ if (maGeo.nShearAngle)
+ {
+ ShearPoint(aPnt, maRect.TopLeft(), maGeo.mfTanShearAngle);
+ }
+
+ if (maGeo.nRotationAngle)
+ {
+ RotatePoint(aPnt, maRect.TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
+ }
+
+ std::unique_ptr<SdrHdl> pH(new SdrHdl(aPnt,eLocalKind));
+ pH->SetPointNum(nPNum);
+ pH->SetObj(const_cast<SdrCircObj*>(this));
+ pH->SetRotationAngle(maGeo.nRotationAngle);
+ rHdlList.AddHdl(std::move(pH));
+ }
+}
+
+
+bool SdrCircObj::hasSpecialDrag() const
+{
+ return true;
+}
+
+bool SdrCircObj::beginSpecialDrag(SdrDragStat& rDrag) const
+{
+ const bool bAngle(rDrag.GetHdl() && SdrHdlKind::Circle == rDrag.GetHdl()->GetKind());
+
+ if(bAngle)
+ {
+ if(1 == rDrag.GetHdl()->GetPointNum() || 2 == rDrag.GetHdl()->GetPointNum())
+ {
+ rDrag.SetNoSnap();
+ }
+
+ return true;
+ }
+
+ return SdrTextObj::beginSpecialDrag(rDrag);
+}
+
+bool SdrCircObj::applySpecialDrag(SdrDragStat& rDrag)
+{
+ const bool bAngle(rDrag.GetHdl() && SdrHdlKind::Circle == rDrag.GetHdl()->GetKind());
+
+ if(bAngle)
+ {
+ Point aPt(rDrag.GetNow());
+
+ if (maGeo.nRotationAngle)
+ RotatePoint(aPt,maRect.TopLeft(), -maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
+
+ if (maGeo.nShearAngle)
+ ShearPoint(aPt,maRect.TopLeft(), -maGeo.mfTanShearAngle);
+
+ aPt -= maRect.Center();
+
+ tools::Long nWdt = maRect.Right() - maRect.Left();
+ tools::Long nHgt = maRect.Bottom() - maRect.Top();
+
+ if(nWdt>=nHgt)
+ {
+ aPt.setY(BigMulDiv(aPt.Y(),nWdt,nHgt) );
+ }
+ else
+ {
+ aPt.setX(BigMulDiv(aPt.X(),nHgt,nWdt) );
+ }
+
+ Degree100 nAngle=NormAngle36000(GetAngle(aPt));
+
+ if (rDrag.GetView() && rDrag.GetView()->IsAngleSnapEnabled())
+ {
+ Degree100 nSA=rDrag.GetView()->GetSnapAngle();
+
+ if (nSA)
+ {
+ nAngle+=nSA/2_deg100;
+ nAngle/=nSA;
+ nAngle*=nSA;
+ nAngle=NormAngle36000(nAngle);
+ }
+ }
+
+ if(1 == rDrag.GetHdl()->GetPointNum())
+ {
+ nStartAngle = nAngle;
+ }
+ else if(2 == rDrag.GetHdl()->GetPointNum())
+ {
+ nEndAngle = nAngle;
+ }
+
+ SetBoundAndSnapRectsDirty();
+ SetXPolyDirty();
+ ImpSetCircInfoToAttr();
+ SetChanged();
+
+ return true;
+ }
+ else
+ {
+ return SdrTextObj::applySpecialDrag(rDrag);
+ }
+}
+
+OUString SdrCircObj::getSpecialDragComment(const SdrDragStat& rDrag) const
+{
+ const bool bCreateComment(rDrag.GetView() && this == rDrag.GetView()->GetCreateObj());
+
+ if(bCreateComment)
+ {
+ OUStringBuffer aBuf(ImpGetDescriptionStr(STR_ViewCreateObj));
+ const sal_uInt32 nPointCount(rDrag.GetPointCount());
+
+ if(SdrCircKind::Full != meCircleKind && nPointCount > 2)
+ {
+ const ImpCircUser* pU = static_cast<const ImpCircUser*>(rDrag.GetUser());
+ Degree100 nAngle;
+
+ aBuf.append(" (");
+
+ if(3 == nPointCount)
+ {
+ nAngle = pU->nStart;
+ }
+ else
+ {
+ nAngle = pU->nEnd;
+ }
+
+ aBuf.append(SdrModel::GetAngleString(nAngle));
+ aBuf.append(')');
+ }
+
+ return aBuf.makeStringAndClear();
+ }
+ else
+ {
+ const bool bAngle(rDrag.GetHdl() && SdrHdlKind::Circle == rDrag.GetHdl()->GetKind());
+
+ if(bAngle)
+ {
+ const Degree100 nAngle(1 == rDrag.GetHdl()->GetPointNum() ? nStartAngle : nEndAngle);
+
+ return ImpGetDescriptionStr(STR_DragCircAngle) +
+ " (" +
+ SdrModel::GetAngleString(nAngle) +
+ ")";
+ }
+ else
+ {
+ return SdrTextObj::getSpecialDragComment(rDrag);
+ }
+ }
+}
+
+
+void ImpCircUser::SetCreateParams(SdrDragStat const & rStat)
+{
+ rStat.TakeCreateRect(aR);
+ aR.Justify();
+ aCenter=aR.Center();
+ nWdt=aR.Right()-aR.Left();
+ nHgt=aR.Bottom()-aR.Top();
+ nStart=0_deg100;
+ nEnd=36000_deg100;
+ if (rStat.GetPointCount()>2) {
+ Point aP(rStat.GetPoint(2)-aCenter);
+ if (nWdt==0) aP.setX(0 );
+ if (nHgt==0) aP.setY(0 );
+ if (nWdt>=nHgt) {
+ if (nHgt!=0) aP.setY(aP.Y()*nWdt/nHgt );
+ } else {
+ if (nWdt!=0) aP.setX(aP.X()*nHgt/nWdt );
+ }
+ nStart=NormAngle36000(GetAngle(aP));
+ if (rStat.GetView()!=nullptr && rStat.GetView()->IsAngleSnapEnabled()) {
+ Degree100 nSA=rStat.GetView()->GetSnapAngle();
+ if (nSA) { // angle snapping
+ nStart+=nSA/2_deg100;
+ nStart/=nSA;
+ nStart*=nSA;
+ nStart=NormAngle36000(nStart);
+ }
+ }
+ aP1 = GetAnglePnt(aR,nStart);
+ nEnd=nStart;
+ } else aP1=aCenter;
+ if (rStat.GetPointCount()<=3)
+ return;
+
+ Point aP(rStat.GetPoint(3)-aCenter);
+ if (nWdt>=nHgt) {
+ aP.setY(BigMulDiv(aP.Y(),nWdt,nHgt) );
+ } else {
+ aP.setX(BigMulDiv(aP.X(),nHgt,nWdt) );
+ }
+ nEnd=NormAngle36000(GetAngle(aP));
+ if (rStat.GetView()!=nullptr && rStat.GetView()->IsAngleSnapEnabled()) {
+ Degree100 nSA=rStat.GetView()->GetSnapAngle();
+ if (nSA) { // angle snapping
+ nEnd+=nSA/2_deg100;
+ nEnd/=nSA;
+ nEnd*=nSA;
+ nEnd=NormAngle36000(nEnd);
+ }
+ }
+}
+
+void SdrCircObj::ImpSetCreateParams(SdrDragStat& rStat)
+{
+ ImpCircUser* pU=static_cast<ImpCircUser*>(rStat.GetUser());
+ if (pU==nullptr) {
+ pU=new ImpCircUser;
+ rStat.SetUser(std::unique_ptr<ImpCircUser>(pU));
+ }
+ pU->SetCreateParams(rStat);
+}
+
+bool SdrCircObj::BegCreate(SdrDragStat& rStat)
+{
+ rStat.SetOrtho4Possible();
+ tools::Rectangle aRect1(rStat.GetStart(), rStat.GetNow());
+ aRect1.Justify();
+ rStat.SetActionRect(aRect1);
+ maRect = aRect1;
+ ImpSetCreateParams(rStat);
+ return true;
+}
+
+bool SdrCircObj::MovCreate(SdrDragStat& rStat)
+{
+ ImpSetCreateParams(rStat);
+ ImpCircUser* pU=static_cast<ImpCircUser*>(rStat.GetUser());
+ rStat.SetActionRect(pU->aR);
+ maRect = pU->aR; // for ObjName
+ ImpJustifyRect(maRect);
+ nStartAngle=pU->nStart;
+ nEndAngle=pU->nEnd;
+ SetBoundRectDirty();
+ m_bSnapRectDirty=true;
+ SetXPolyDirty();
+
+ // #i103058# push current angle settings to ItemSet to
+ // allow FullDrag visualisation
+ if(rStat.GetPointCount() >= 4)
+ {
+ ImpSetCircInfoToAttr();
+ }
+
+ return true;
+}
+
+bool SdrCircObj::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd)
+{
+ ImpSetCreateParams(rStat);
+ ImpCircUser* pU=static_cast<ImpCircUser*>(rStat.GetUser());
+ bool bRet = false;
+ if (eCmd==SdrCreateCmd::ForceEnd && rStat.GetPointCount()<4) meCircleKind=SdrCircKind::Full;
+ if (meCircleKind==SdrCircKind::Full) {
+ bRet=rStat.GetPointCount()>=2;
+ if (bRet) {
+ maRect = pU->aR;
+ ImpJustifyRect(maRect);
+ }
+ } else {
+ rStat.SetNoSnap(rStat.GetPointCount()>=2);
+ rStat.SetOrtho4Possible(rStat.GetPointCount()<2);
+ bRet=rStat.GetPointCount()>=4;
+ if (bRet) {
+ maRect = pU->aR;
+ ImpJustifyRect(maRect);
+ nStartAngle=pU->nStart;
+ nEndAngle=pU->nEnd;
+ }
+ }
+ m_bClosedObj=meCircleKind!=SdrCircKind::Arc;
+ SetBoundAndSnapRectsDirty();
+ SetXPolyDirty();
+ ImpSetCircInfoToAttr();
+ if (bRet)
+ rStat.SetUser(nullptr);
+ return bRet;
+}
+
+void SdrCircObj::BrkCreate(SdrDragStat& rStat)
+{
+ rStat.SetUser(nullptr);
+}
+
+bool SdrCircObj::BckCreate(SdrDragStat& rStat)
+{
+ rStat.SetNoSnap(rStat.GetPointCount()>=3);
+ rStat.SetOrtho4Possible(rStat.GetPointCount()<3);
+ return meCircleKind!=SdrCircKind::Full;
+}
+
+basegfx::B2DPolyPolygon SdrCircObj::TakeCreatePoly(const SdrDragStat& rDrag) const
+{
+ const ImpCircUser* pU = static_cast<const ImpCircUser*>(rDrag.GetUser());
+
+ if(rDrag.GetPointCount() < 4)
+ {
+ // force to OBJ_CIRC to get full visualisation
+ basegfx::B2DPolyPolygon aRetval(ImpCalcXPolyCirc(SdrCircKind::Full, pU->aR, pU->nStart, pU->nEnd));
+
+ if(3 == rDrag.GetPointCount())
+ {
+ // add edge to first point on ellipse
+ basegfx::B2DPolygon aNew;
+
+ aNew.append(basegfx::B2DPoint(pU->aCenter.X(), pU->aCenter.Y()));
+ aNew.append(basegfx::B2DPoint(pU->aP1.X(), pU->aP1.Y()));
+ aRetval.append(aNew);
+ }
+
+ return aRetval;
+ }
+ else
+ {
+ return basegfx::B2DPolyPolygon(ImpCalcXPolyCirc(meCircleKind, pU->aR, pU->nStart, pU->nEnd));
+ }
+}
+
+PointerStyle SdrCircObj::GetCreatePointer() const
+{
+ switch (meCircleKind) {
+ case SdrCircKind::Full: return PointerStyle::DrawEllipse;
+ case SdrCircKind::Section: return PointerStyle::DrawPie;
+ case SdrCircKind::Arc: return PointerStyle::DrawArc;
+ case SdrCircKind::Cut: return PointerStyle::DrawCircleCut;
+ default: break;
+ } // switch
+ return PointerStyle::Cross;
+}
+
+void SdrCircObj::NbcMove(const Size& aSiz)
+{
+ maRect.Move(aSiz);
+ m_aOutRect.Move(aSiz);
+ maSnapRect.Move(aSiz);
+ SetXPolyDirty();
+ SetBoundAndSnapRectsDirty(true);
+}
+
+void SdrCircObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
+{
+ Degree100 nAngle0 = maGeo.nRotationAngle;
+ bool bNoShearRota = (maGeo.nRotationAngle == 0_deg100 && maGeo.nShearAngle == 0_deg100);
+ SdrTextObj::NbcResize(rRef,xFact,yFact);
+ bNoShearRota |= (maGeo.nRotationAngle == 0_deg100 && maGeo.nShearAngle == 0_deg100);
+ if (meCircleKind!=SdrCircKind::Full) {
+ bool bXMirr=(xFact.GetNumerator()<0) != (xFact.GetDenominator()<0);
+ bool bYMirr=(yFact.GetNumerator()<0) != (yFact.GetDenominator()<0);
+ if (bXMirr || bYMirr) {
+ // At bXMirr!=bYMirr we should actually swap both line ends.
+ // That, however, is pretty bad (because of forced "hard" formatting).
+ // Alternatively, we could implement a bMirrored flag (maybe even
+ // a more general one, e. g. for mirrored text, ...).
+ Degree100 nS0=nStartAngle;
+ Degree100 nE0=nEndAngle;
+ if (bNoShearRota) {
+ // the RectObj already mirrors at VMirror because of a 180deg rotation
+ if (! (bXMirr && bYMirr)) {
+ Degree100 nTmp=nS0;
+ nS0=18000_deg100-nE0;
+ nE0=18000_deg100-nTmp;
+ }
+ } else { // mirror contorted ellipses
+ if (bXMirr!=bYMirr) {
+ nS0+=nAngle0;
+ nE0+=nAngle0;
+ if (bXMirr) {
+ Degree100 nTmp=nS0;
+ nS0=18000_deg100-nE0;
+ nE0=18000_deg100-nTmp;
+ }
+ if (bYMirr) {
+ Degree100 nTmp=nS0;
+ nS0=-nE0;
+ nE0=-nTmp;
+ }
+ nS0 -= maGeo.nRotationAngle;
+ nE0 -= maGeo.nRotationAngle;
+ }
+ }
+ Degree100 nAngleDif=nE0-nS0;
+ nStartAngle=NormAngle36000(nS0);
+ nEndAngle =NormAngle36000(nE0);
+ if (nAngleDif==36000_deg100) nEndAngle+=nAngleDif; // full circle
+ }
+ }
+ SetXPolyDirty();
+ ImpSetCircInfoToAttr();
+}
+
+void SdrCircObj::NbcShear(const Point& rRef, Degree100 nAngle, double tn, bool bVShear)
+{
+ SdrTextObj::NbcShear(rRef,nAngle,tn,bVShear);
+ SetXPolyDirty();
+ ImpSetCircInfoToAttr();
+}
+
+void SdrCircObj::NbcMirror(const Point& rRef1, const Point& rRef2)
+{
+ bool bFreeMirr=meCircleKind!=SdrCircKind::Full;
+ Point aTmpPt1;
+ Point aTmpPt2;
+ if (bFreeMirr) { // some preparations for using an arbitrary axis of reflection
+ Point aCenter(maRect.Center());
+ tools::Long nWdt=maRect.GetWidth()-1;
+ tools::Long nHgt=maRect.GetHeight()-1;
+ tools::Long nMaxRad=(std::max(nWdt,nHgt)+1) /2;
+ // starting point
+ double a = toRadians(nStartAngle);
+ aTmpPt1=Point(FRound(cos(a)*nMaxRad),-FRound(sin(a)*nMaxRad));
+ if (nWdt==0) aTmpPt1.setX(0 );
+ if (nHgt==0) aTmpPt1.setY(0 );
+ aTmpPt1+=aCenter;
+ // finishing point
+ a = toRadians(nEndAngle);
+ aTmpPt2=Point(FRound(cos(a)*nMaxRad),-FRound(sin(a)*nMaxRad));
+ if (nWdt==0) aTmpPt2.setX(0 );
+ if (nHgt==0) aTmpPt2.setY(0 );
+ aTmpPt2+=aCenter;
+ if (maGeo.nRotationAngle)
+ {
+ RotatePoint(aTmpPt1, maRect.TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
+ RotatePoint(aTmpPt2, maRect.TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
+ }
+ if (maGeo.nShearAngle)
+ {
+ ShearPoint(aTmpPt1, maRect.TopLeft(), maGeo.mfTanShearAngle);
+ ShearPoint(aTmpPt2, maRect.TopLeft(), maGeo.mfTanShearAngle);
+ }
+ }
+ SdrTextObj::NbcMirror(rRef1,rRef2);
+ if (meCircleKind!=SdrCircKind::Full) { // adapt starting and finishing angle
+ MirrorPoint(aTmpPt1,rRef1,rRef2);
+ MirrorPoint(aTmpPt2,rRef1,rRef2);
+ // unrotate:
+ if (maGeo.nRotationAngle)
+ {
+ RotatePoint(aTmpPt1, maRect.TopLeft(), -maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle); // -sin for reversion
+ RotatePoint(aTmpPt2, maRect.TopLeft(), -maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle); // -sin for reversion
+ }
+ // unshear:
+ if (maGeo.nShearAngle)
+ {
+ ShearPoint(aTmpPt1, maRect.TopLeft(), -maGeo.mfTanShearAngle); // -tan for reversion
+ ShearPoint(aTmpPt2, maRect.TopLeft(), -maGeo.mfTanShearAngle); // -tan for reversion
+ }
+ Point aCenter(maRect.Center());
+ aTmpPt1-=aCenter;
+ aTmpPt2-=aCenter;
+ // because it's mirrored, the angles are swapped, too
+ nStartAngle=GetAngle(aTmpPt2);
+ nEndAngle =GetAngle(aTmpPt1);
+ Degree100 nAngleDif=nEndAngle-nStartAngle;
+ nStartAngle=NormAngle36000(nStartAngle);
+ nEndAngle =NormAngle36000(nEndAngle);
+ if (nAngleDif==36000_deg100) nEndAngle+=nAngleDif; // full circle
+ }
+ SetXPolyDirty();
+ ImpSetCircInfoToAttr();
+}
+
+std::unique_ptr<SdrObjGeoData> SdrCircObj::NewGeoData() const
+{
+ return std::make_unique<SdrCircObjGeoData>();
+}
+
+void SdrCircObj::SaveGeoData(SdrObjGeoData& rGeo) const
+{
+ SdrRectObj::SaveGeoData(rGeo);
+ SdrCircObjGeoData& rCGeo=static_cast<SdrCircObjGeoData&>(rGeo);
+ rCGeo.nStartAngle=nStartAngle;
+ rCGeo.nEndAngle =nEndAngle;
+}
+
+void SdrCircObj::RestoreGeoData(const SdrObjGeoData& rGeo)
+{
+ SdrRectObj::RestoreGeoData(rGeo);
+ const SdrCircObjGeoData& rCGeo=static_cast<const SdrCircObjGeoData&>(rGeo);
+ nStartAngle=rCGeo.nStartAngle;
+ nEndAngle =rCGeo.nEndAngle;
+ SetXPolyDirty();
+ ImpSetCircInfoToAttr();
+}
+
+static void Union(tools::Rectangle& rR, const Point& rP)
+{
+ if (rP.X()<rR.Left ()) rR.SetLeft(rP.X() );
+ if (rP.X()>rR.Right ()) rR.SetRight(rP.X() );
+ if (rP.Y()<rR.Top ()) rR.SetTop(rP.Y() );
+ if (rP.Y()>rR.Bottom()) rR.SetBottom(rP.Y() );
+}
+
+void SdrCircObj::TakeUnrotatedSnapRect(tools::Rectangle& rRect) const
+{
+ rRect = maRect;
+ if (meCircleKind!=SdrCircKind::Full) {
+ const Point aPntStart(GetAnglePnt(maRect,nStartAngle));
+ const Point aPntEnd(GetAnglePnt(maRect,nEndAngle));
+ Degree100 a=nStartAngle;
+ Degree100 e=nEndAngle;
+ rRect.SetLeft(maRect.Right() );
+ rRect.SetRight(maRect.Left() );
+ rRect.SetTop(maRect.Bottom() );
+ rRect.SetBottom(maRect.Top() );
+ Union(rRect,aPntStart);
+ Union(rRect,aPntEnd);
+ if ((a<=18000_deg100 && e>=18000_deg100) || (a>e && (a<=18000_deg100 || e>=18000_deg100))) {
+ Union(rRect,maRect.LeftCenter());
+ }
+ if ((a<=27000_deg100 && e>=27000_deg100) || (a>e && (a<=27000_deg100 || e>=27000_deg100))) {
+ Union(rRect,maRect.BottomCenter());
+ }
+ if (a>e) {
+ Union(rRect,maRect.RightCenter());
+ }
+ if ((a<=9000_deg100 && e>=9000_deg100) || (a>e && (a<=9000_deg100 || e>=9000_deg100))) {
+ Union(rRect,maRect.TopCenter());
+ }
+ if (meCircleKind==SdrCircKind::Section) {
+ Union(rRect,maRect.Center());
+ }
+ if (maGeo.nRotationAngle)
+ {
+ Point aDst(rRect.TopLeft());
+ aDst-=maRect.TopLeft();
+ Point aDst0(aDst);
+ RotatePoint(aDst,Point(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
+ aDst-=aDst0;
+ rRect.Move(aDst.X(),aDst.Y());
+ }
+ }
+ if (maGeo.nShearAngle==0_deg100)
+ return;
+
+ tools::Long nDst = FRound((rRect.Bottom() - rRect.Top()) * maGeo.mfTanShearAngle);
+ if (maGeo.nShearAngle > 0_deg100)
+ {
+ Point aRef(rRect.TopLeft());
+ rRect.AdjustLeft( -nDst );
+ Point aTmpPt(rRect.TopLeft());
+ RotatePoint(aTmpPt, aRef, maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
+ aTmpPt-=rRect.TopLeft();
+ rRect.Move(aTmpPt.X(),aTmpPt.Y());
+ } else {
+ rRect.AdjustRight( -nDst );
+ }
+}
+
+void SdrCircObj::RecalcSnapRect()
+{
+ if (PaintNeedsXPolyCirc()) {
+ maSnapRect=GetXPoly().GetBoundRect();
+ } else {
+ TakeUnrotatedSnapRect(maSnapRect);
+ }
+}
+
+void SdrCircObj::NbcSetSnapRect(const tools::Rectangle& rRect)
+{
+ if (maGeo.nRotationAngle || maGeo.nShearAngle || meCircleKind != SdrCircKind::Full)
+ {
+ tools::Rectangle aSR0(GetSnapRect());
+ tools::Long nWdt0=aSR0.Right()-aSR0.Left();
+ tools::Long nHgt0=aSR0.Bottom()-aSR0.Top();
+ tools::Long nWdt1=rRect.Right()-rRect.Left();
+ tools::Long nHgt1=rRect.Bottom()-rRect.Top();
+ NbcResize(maSnapRect.TopLeft(),Fraction(nWdt1,nWdt0),Fraction(nHgt1,nHgt0));
+ NbcMove(Size(rRect.Left()-aSR0.Left(),rRect.Top()-aSR0.Top()));
+ } else {
+ maRect=rRect;
+ ImpJustifyRect(maRect);
+ }
+ SetBoundAndSnapRectsDirty();
+ SetXPolyDirty();
+ ImpSetCircInfoToAttr();
+}
+
+sal_uInt32 SdrCircObj::GetSnapPointCount() const
+{
+ if (meCircleKind==SdrCircKind::Full) {
+ return 1;
+ } else {
+ return 3;
+ }
+}
+
+Point SdrCircObj::GetSnapPoint(sal_uInt32 i) const
+{
+ switch (i) {
+ case 1 : return GetAnglePnt(maRect,nStartAngle);
+ case 2 : return GetAnglePnt(maRect,nEndAngle);
+ default: return maRect.Center();
+ }
+}
+
+void SdrCircObj::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
+{
+ SetXPolyDirty();
+ SdrRectObj::Notify(rBC,rHint);
+ ImpSetAttrToCircInfo();
+}
+
+
+void SdrCircObj::ImpSetAttrToCircInfo()
+{
+ const SfxItemSet& rSet = GetObjectItemSet();
+ SdrCircKind eNewKind = rSet.Get(SDRATTR_CIRCKIND).GetValue();
+
+ Degree100 nNewStart = rSet.Get(SDRATTR_CIRCSTARTANGLE).GetValue();
+ Degree100 nNewEnd = rSet.Get(SDRATTR_CIRCENDANGLE).GetValue();
+
+ bool bKindChg = meCircleKind != eNewKind;
+ bool bAngleChg = nNewStart != nStartAngle || nNewEnd != nEndAngle;
+
+ if(bKindChg || bAngleChg)
+ {
+ meCircleKind = eNewKind;
+ nStartAngle = nNewStart;
+ nEndAngle = nNewEnd;
+
+ if(bKindChg || (meCircleKind != SdrCircKind::Full && bAngleChg))
+ {
+ SetXPolyDirty();
+ SetBoundAndSnapRectsDirty();
+ }
+ }
+}
+
+void SdrCircObj::ImpSetCircInfoToAttr()
+{
+ const SfxItemSet& rSet = GetObjectItemSet();
+
+ SdrCircKind eOldKindA = rSet.Get(SDRATTR_CIRCKIND).GetValue();
+ Degree100 nOldStartAngle = rSet.Get(SDRATTR_CIRCSTARTANGLE).GetValue();
+ Degree100 nOldEndAngle = rSet.Get(SDRATTR_CIRCENDANGLE).GetValue();
+
+ if(meCircleKind == eOldKindA && nStartAngle == nOldStartAngle && nEndAngle == nOldEndAngle)
+ return;
+
+ // since SetItem() implicitly calls ImpSetAttrToCircInfo()
+ // setting the item directly is necessary here.
+ if(meCircleKind != eOldKindA)
+ {
+ GetProperties().SetObjectItemDirect(SdrCircKindItem(meCircleKind));
+ }
+
+ if(nStartAngle != nOldStartAngle)
+ {
+ GetProperties().SetObjectItemDirect(makeSdrCircStartAngleItem(nStartAngle));
+ }
+
+ if(nEndAngle != nOldEndAngle)
+ {
+ GetProperties().SetObjectItemDirect(makeSdrCircEndAngleItem(nEndAngle));
+ }
+
+ SetXPolyDirty();
+ ImpSetAttrToCircInfo();
+}
+
+SdrObjectUniquePtr SdrCircObj::DoConvertToPolyObj(bool bBezier, bool bAddText) const
+{
+ const bool bFill(meCircleKind != SdrCircKind::Arc);
+ const basegfx::B2DPolygon aCircPolygon(ImpCalcXPolyCirc(meCircleKind, maRect, nStartAngle, nEndAngle));
+ SdrObjectUniquePtr pRet = ImpConvertMakeObj(basegfx::B2DPolyPolygon(aCircPolygon), bFill, bBezier);
+
+ if(bAddText)
+ {
+ pRet = ImpConvertAddText(std::move(pRet), bBezier);
+ }
+
+ return pRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdoedge.cxx b/svx/source/svdraw/svdoedge.cxx
new file mode 100644
index 000000000..769a720c3
--- /dev/null
+++ b/svx/source/svdraw/svdoedge.cxx
@@ -0,0 +1,2647 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <osl/diagnose.h>
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <svl/hint.hxx>
+
+#include <sdr/contact/viewcontactofsdredgeobj.hxx>
+#include <sdr/properties/connectorproperties.hxx>
+#include <svx/sdrhittesthelper.hxx>
+#include <svx/svddrag.hxx>
+#include <svx/svddrgmt.hxx>
+#include <svx/svdhdl.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdoedge.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdtrans.hxx>
+#include <svx/svdview.hxx>
+#include <svx/sxekitm.hxx>
+#include <svx/sxelditm.hxx>
+#include <svx/sxenditm.hxx>
+#include <svx/xpoly.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <comphelper/lok.hxx>
+
+void SdrObjConnection::ResetVars()
+{
+ pObj=nullptr;
+ nConId=0;
+ bBestConn=true;
+ bBestVertex=true;
+ bAutoVertex=false;
+ bAutoCorner=false;
+}
+
+bool SdrObjConnection::TakeGluePoint(SdrGluePoint& rGP) const
+{
+ bool bRet = false;
+ if (pObj!=nullptr) { // one object has to be docked already!
+ if (bAutoVertex) {
+ rGP=pObj->GetVertexGluePoint(nConId);
+ bRet = true;
+ } else if (bAutoCorner) {
+ rGP=pObj->GetCornerGluePoint(nConId);
+ bRet = true;
+ } else {
+ const SdrGluePointList* pGPL=pObj->GetGluePointList();
+ if (pGPL!=nullptr) {
+ sal_uInt16 nNum=pGPL->FindGluePoint(nConId);
+ if (nNum!=SDRGLUEPOINT_NOTFOUND) {
+ rGP=(*pGPL)[nNum];
+ bRet = true;
+ }
+ }
+ }
+ }
+ if (bRet) {
+ Point aPt(rGP.GetAbsolutePos(*pObj));
+ aPt+=aObjOfs;
+ rGP.SetPos(aPt);
+ }
+ return bRet;
+}
+
+Point& SdrEdgeInfoRec::ImpGetLineOffsetPoint(SdrEdgeLineCode eLineCode)
+{
+ switch (eLineCode) {
+ case SdrEdgeLineCode::Obj1Line2 : return aObj1Line2;
+ case SdrEdgeLineCode::Obj1Line3 : return aObj1Line3;
+ case SdrEdgeLineCode::Obj2Line2 : return aObj2Line2;
+ case SdrEdgeLineCode::Obj2Line3 : return aObj2Line3;
+ case SdrEdgeLineCode::MiddleLine: return aMiddleLine;
+ } // switch
+ return aMiddleLine;
+}
+
+sal_uInt16 SdrEdgeInfoRec::ImpGetPolyIdx(SdrEdgeLineCode eLineCode, const XPolygon& rXP) const
+{
+ switch (eLineCode) {
+ case SdrEdgeLineCode::Obj1Line2 : return 1;
+ case SdrEdgeLineCode::Obj1Line3 : return 2;
+ case SdrEdgeLineCode::Obj2Line2 : return rXP.GetPointCount()-3;
+ case SdrEdgeLineCode::Obj2Line3 : return rXP.GetPointCount()-4;
+ case SdrEdgeLineCode::MiddleLine: return nMiddleLine;
+ } // switch
+ return 0;
+}
+
+bool SdrEdgeInfoRec::ImpIsHorzLine(SdrEdgeLineCode eLineCode, const XPolygon& rXP) const
+{
+ sal_uInt16 nIdx=ImpGetPolyIdx(eLineCode,rXP);
+ bool bHorz=nAngle1==0 || nAngle1==18000;
+ if (eLineCode==SdrEdgeLineCode::Obj2Line2 || eLineCode==SdrEdgeLineCode::Obj2Line3) {
+ nIdx=rXP.GetPointCount()-nIdx;
+ bHorz=nAngle2==0 || nAngle2==18000;
+ }
+ if ((nIdx & 1)==1) bHorz=!bHorz;
+ return bHorz;
+}
+
+void SdrEdgeInfoRec::ImpSetLineOffset(SdrEdgeLineCode eLineCode, const XPolygon& rXP, tools::Long nVal)
+{
+ Point& rPt=ImpGetLineOffsetPoint(eLineCode);
+ if (ImpIsHorzLine(eLineCode,rXP)) rPt.setY(nVal );
+ else rPt.setX(nVal );
+}
+
+tools::Long SdrEdgeInfoRec::ImpGetLineOffset(SdrEdgeLineCode eLineCode, const XPolygon& rXP) const
+{
+ const Point& rPt = const_cast<SdrEdgeInfoRec*>(this)->ImpGetLineOffsetPoint(eLineCode);
+ if (ImpIsHorzLine(eLineCode,rXP))
+ return rPt.Y();
+ else
+ return rPt.X();
+}
+
+
+// BaseProperties section
+
+std::unique_ptr<sdr::properties::BaseProperties> SdrEdgeObj::CreateObjectSpecificProperties()
+{
+ return std::make_unique<sdr::properties::ConnectorProperties>(*this);
+}
+
+
+// DrawContact section
+
+std::unique_ptr<sdr::contact::ViewContact> SdrEdgeObj::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::ViewContactOfSdrEdgeObj>(*this);
+}
+
+
+SdrEdgeObj::SdrEdgeObj(SdrModel& rSdrModel)
+: SdrTextObj(rSdrModel),
+ nNotifyingCount(0),
+ bEdgeTrackDirty(false),
+ bEdgeTrackUserDefined(false),
+ // Default is to allow default connects
+ mbSuppressDefaultConnect(false),
+ mbBoundRectCalculationRunning(false),
+ mbSuppressed(false)
+{
+ m_bClosedObj=false;
+ m_bIsEdge=true;
+ pEdgeTrack = XPolygon();
+}
+
+SdrEdgeObj::SdrEdgeObj(SdrModel& rSdrModel, SdrEdgeObj const & rSource)
+: SdrTextObj(rSdrModel, rSource),
+ nNotifyingCount(0),
+ bEdgeTrackDirty(false),
+ bEdgeTrackUserDefined(false),
+ // Default is to allow default connects
+ mbSuppressDefaultConnect(false),
+ mbBoundRectCalculationRunning(false),
+ mbSuppressed(false)
+{
+ m_bClosedObj = false;
+ m_bIsEdge = true;
+ pEdgeTrack = rSource.pEdgeTrack;
+ bEdgeTrackDirty=rSource.bEdgeTrackDirty;
+ aCon1 =rSource.aCon1;
+ aCon2 =rSource.aCon2;
+ aCon1.pObj=nullptr;
+ aCon2.pObj=nullptr;
+ aEdgeInfo=rSource.aEdgeInfo;
+}
+
+SdrEdgeObj::~SdrEdgeObj()
+{
+ SdrEdgeObj::DisconnectFromNode(true);
+ SdrEdgeObj::DisconnectFromNode(false);
+}
+
+void SdrEdgeObj::handlePageChange(SdrPage* pOldPage, SdrPage* pNewPage)
+{
+ // call parent
+ SdrTextObj::handlePageChange(pOldPage, pNewPage);
+
+ if(nullptr != GetConnection(true).GetObject() || nullptr != GetConnection(false).GetObject())
+ {
+ // check broadcasters; when we are not inserted we do not need broadcasters
+ // TTTT not yet added, but keep hint to do this here
+ // mpCon1->ownerPageChange();
+ // mpCon2->ownerPageChange();
+ }
+}
+
+void SdrEdgeObj::ImpSetAttrToEdgeInfo()
+{
+ const SfxItemSet& rSet = GetObjectItemSet();
+ SdrEdgeKind eKind = rSet.Get(SDRATTR_EDGEKIND).GetValue();
+ sal_Int32 nVal1 = rSet.Get(SDRATTR_EDGELINE1DELTA).GetValue();
+ sal_Int32 nVal2 = rSet.Get(SDRATTR_EDGELINE2DELTA).GetValue();
+ sal_Int32 nVal3 = rSet.Get(SDRATTR_EDGELINE3DELTA).GetValue();
+
+ if(eKind == SdrEdgeKind::OrthoLines || eKind == SdrEdgeKind::Bezier)
+ {
+ sal_Int32 nVals[3] = { nVal1, nVal2, nVal3 };
+ sal_uInt16 n = 0;
+
+ if(aEdgeInfo.nObj1Lines >= 2 && n < 3)
+ {
+ aEdgeInfo.ImpSetLineOffset(SdrEdgeLineCode::Obj1Line2, *pEdgeTrack, nVals[n]);
+ n++;
+ }
+
+ if(aEdgeInfo.nObj1Lines >= 3 && n < 3)
+ {
+ aEdgeInfo.ImpSetLineOffset(SdrEdgeLineCode::Obj1Line3, *pEdgeTrack, nVals[n]);
+ n++;
+ }
+
+ if(aEdgeInfo.nMiddleLine != 0xFFFF && n < 3)
+ {
+ aEdgeInfo.ImpSetLineOffset(SdrEdgeLineCode::MiddleLine, *pEdgeTrack, nVals[n]);
+ n++;
+ }
+
+ if(aEdgeInfo.nObj2Lines >= 3 && n < 3)
+ {
+ aEdgeInfo.ImpSetLineOffset(SdrEdgeLineCode::Obj2Line3, *pEdgeTrack, nVals[n]);
+ n++;
+ }
+
+ if(aEdgeInfo.nObj2Lines >= 2 && n < 3)
+ {
+ aEdgeInfo.ImpSetLineOffset(SdrEdgeLineCode::Obj2Line2, *pEdgeTrack, nVals[n]);
+ n++;
+ }
+ }
+ else if(eKind == SdrEdgeKind::ThreeLines)
+ {
+ bool bHor1 = aEdgeInfo.nAngle1 == 0 || aEdgeInfo.nAngle1 == 18000;
+ bool bHor2 = aEdgeInfo.nAngle2 == 0 || aEdgeInfo.nAngle2 == 18000;
+
+ if(bHor1)
+ {
+ aEdgeInfo.aObj1Line2.setX( nVal1 );
+ }
+ else
+ {
+ aEdgeInfo.aObj1Line2.setY( nVal1 );
+ }
+
+ if(bHor2)
+ {
+ aEdgeInfo.aObj2Line2.setX( nVal2 );
+ }
+ else
+ {
+ aEdgeInfo.aObj2Line2.setY( nVal2 );
+ }
+ }
+
+ ImpDirtyEdgeTrack();
+}
+
+void SdrEdgeObj::ImpSetEdgeInfoToAttr()
+{
+ const SfxItemSet& rSet = GetObjectItemSet();
+ SdrEdgeKind eKind = rSet.Get(SDRATTR_EDGEKIND).GetValue();
+ sal_Int32 nValCnt = rSet.Get(SDRATTR_EDGELINEDELTACOUNT).GetValue();
+ sal_Int32 nVal1 = rSet.Get(SDRATTR_EDGELINE1DELTA).GetValue();
+ sal_Int32 nVal2 = rSet.Get(SDRATTR_EDGELINE2DELTA).GetValue();
+ sal_Int32 nVal3 = rSet.Get(SDRATTR_EDGELINE3DELTA).GetValue();
+ sal_Int32 nVals[3] = { nVal1, nVal2, nVal3 };
+ sal_uInt16 n = 0;
+
+ if(eKind == SdrEdgeKind::OrthoLines || eKind == SdrEdgeKind::Bezier)
+ {
+ if(aEdgeInfo.nObj1Lines >= 2 && n < 3)
+ {
+ nVals[n] = aEdgeInfo.ImpGetLineOffset(SdrEdgeLineCode::Obj1Line2, *pEdgeTrack);
+ n++;
+ }
+
+ if(aEdgeInfo.nObj1Lines >= 3 && n < 3)
+ {
+ nVals[n] = aEdgeInfo.ImpGetLineOffset(SdrEdgeLineCode::Obj1Line3, *pEdgeTrack);
+ n++;
+ }
+
+ if(aEdgeInfo.nMiddleLine != 0xFFFF && n < 3)
+ {
+ nVals[n] = aEdgeInfo.ImpGetLineOffset(SdrEdgeLineCode::MiddleLine, *pEdgeTrack);
+ n++;
+ }
+
+ if(aEdgeInfo.nObj2Lines >= 3 && n < 3)
+ {
+ nVals[n] = aEdgeInfo.ImpGetLineOffset(SdrEdgeLineCode::Obj2Line3, *pEdgeTrack);
+ n++;
+ }
+
+ if(aEdgeInfo.nObj2Lines >= 2 && n < 3)
+ {
+ nVals[n] = aEdgeInfo.ImpGetLineOffset(SdrEdgeLineCode::Obj2Line2, *pEdgeTrack);
+ n++;
+ }
+ }
+ else if(eKind == SdrEdgeKind::ThreeLines)
+ {
+ bool bHor1 = aEdgeInfo.nAngle1 == 0 || aEdgeInfo.nAngle1 == 18000;
+ bool bHor2 = aEdgeInfo.nAngle2 == 0 || aEdgeInfo.nAngle2 == 18000;
+
+ n = 2;
+ nVals[0] = bHor1 ? aEdgeInfo.aObj1Line2.X() : aEdgeInfo.aObj1Line2.Y();
+ nVals[1] = bHor2 ? aEdgeInfo.aObj2Line2.X() : aEdgeInfo.aObj2Line2.Y();
+ }
+
+ if(!(n != nValCnt || nVals[0] != nVal1 || nVals[1] != nVal2 || nVals[2] != nVal3))
+ return;
+
+ // Here no more notifying is necessary, just local changes are OK.
+ if(n != nValCnt)
+ {
+ GetProperties().SetObjectItemDirect(SdrEdgeLineDeltaCountItem(n));
+ }
+
+ if(nVals[0] != nVal1)
+ {
+ GetProperties().SetObjectItemDirect(makeSdrEdgeLine1DeltaItem(nVals[0]));
+ }
+
+ if(nVals[1] != nVal2)
+ {
+ GetProperties().SetObjectItemDirect(makeSdrEdgeLine2DeltaItem(nVals[1]));
+ }
+
+ if(nVals[2] != nVal3)
+ {
+ GetProperties().SetObjectItemDirect(makeSdrEdgeLine3DeltaItem(nVals[2]));
+ }
+
+ if(n < 3)
+ {
+ GetProperties().ClearObjectItemDirect(SDRATTR_EDGELINE3DELTA);
+ }
+
+ if(n < 2)
+ {
+ GetProperties().ClearObjectItemDirect(SDRATTR_EDGELINE2DELTA);
+ }
+
+ if(n < 1)
+ {
+ GetProperties().ClearObjectItemDirect(SDRATTR_EDGELINE1DELTA);
+ }
+}
+
+void SdrEdgeObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
+{
+ // #i54102# allow rotation, mirror and shear
+ rInfo.bRotateFreeAllowed = true;
+ rInfo.bRotate90Allowed = true;
+ rInfo.bMirrorFreeAllowed = true;
+ rInfo.bMirror45Allowed = true;
+ rInfo.bMirror90Allowed = true;
+ rInfo.bTransparenceAllowed = false;
+ rInfo.bShearAllowed = true;
+ rInfo.bEdgeRadiusAllowed = false;
+ bool bCanConv=!HasText() || ImpCanConvTextToCurve();
+ rInfo.bCanConvToPath=bCanConv;
+ rInfo.bCanConvToPoly=bCanConv;
+ rInfo.bCanConvToContour = (rInfo.bCanConvToPoly || LineGeometryUsageIsNecessary());
+}
+
+SdrObjKind SdrEdgeObj::GetObjIdentifier() const
+{
+ return SdrObjKind::Edge;
+}
+
+const tools::Rectangle& SdrEdgeObj::GetCurrentBoundRect() const
+{
+ if(bEdgeTrackDirty)
+ {
+ const_cast<SdrEdgeObj*>(this)->ImpRecalcEdgeTrack();
+ }
+
+ return SdrTextObj::GetCurrentBoundRect();
+}
+
+const tools::Rectangle& SdrEdgeObj::GetSnapRect() const
+{
+ if(bEdgeTrackDirty)
+ {
+ const_cast<SdrEdgeObj*>(this)->ImpRecalcEdgeTrack();
+ }
+
+ return SdrTextObj::GetSnapRect();
+}
+
+void SdrEdgeObj::RecalcSnapRect()
+{
+ maSnapRect=pEdgeTrack->GetBoundRect();
+}
+
+void SdrEdgeObj::TakeUnrotatedSnapRect(tools::Rectangle& rRect) const
+{
+ rRect=GetSnapRect();
+}
+
+SdrGluePoint SdrEdgeObj::GetVertexGluePoint(sal_uInt16 nNum) const
+{
+ Point aPt;
+ sal_uInt16 nPointCount=pEdgeTrack->GetPointCount();
+ if (nPointCount>0)
+ {
+ Point aOfs = GetSnapRect().Center();
+ if (nNum==2 && GetConnectedNode(true)==nullptr) aPt=(*pEdgeTrack)[0];
+ else if (nNum==3 && GetConnectedNode(false)==nullptr) aPt=(*pEdgeTrack)[nPointCount-1];
+ else {
+ if ((nPointCount & 1) ==1) {
+ aPt=(*pEdgeTrack)[nPointCount/2];
+ } else {
+ Point aPt1((*pEdgeTrack)[nPointCount/2-1]);
+ Point aPt2((*pEdgeTrack)[nPointCount/2]);
+ aPt1+=aPt2;
+ aPt1.setX( aPt1.X() / 2 );
+ aPt1.setY( aPt1.Y() / 2 );
+ aPt=aPt1;
+ }
+ }
+ aPt-=aOfs;
+ }
+ SdrGluePoint aGP(aPt);
+ aGP.SetPercent(false);
+ return aGP;
+}
+
+SdrGluePoint SdrEdgeObj::GetCornerGluePoint(sal_uInt16 nNum) const
+{
+ return GetVertexGluePoint(nNum);
+}
+
+const SdrGluePointList* SdrEdgeObj::GetGluePointList() const
+{
+ return nullptr; // no user defined gluepoints for connectors
+}
+
+SdrGluePointList* SdrEdgeObj::ForceGluePointList()
+{
+ return nullptr; // no user defined gluepoints for connectors
+}
+
+void SdrEdgeObj::ConnectToNode(bool bTail1, SdrObject* pObj)
+{
+ SdrObjConnection& rCon=GetConnection(bTail1);
+ DisconnectFromNode(bTail1);
+ if (pObj!=nullptr) {
+ pObj->AddListener(*this);
+ rCon.pObj=pObj;
+
+ // #i120437# If connection is set, reset bEdgeTrackUserDefined
+ bEdgeTrackUserDefined = false;
+
+ ImpDirtyEdgeTrack();
+ }
+}
+
+void SdrEdgeObj::DisconnectFromNode(bool bTail1)
+{
+ SdrObjConnection& rCon=GetConnection(bTail1);
+ if (rCon.pObj!=nullptr) {
+ rCon.pObj->RemoveListener(*this);
+ rCon.pObj=nullptr;
+ }
+}
+
+SdrObject* SdrEdgeObj::GetConnectedNode(bool bTail1) const
+{
+ SdrObject* pObj(GetConnection(bTail1).pObj);
+
+ if(nullptr != pObj
+ && (pObj->getSdrPageFromSdrObject() != getSdrPageFromSdrObject() || !pObj->IsInserted()))
+ {
+ pObj = nullptr;
+ }
+
+ return pObj;
+}
+
+bool SdrEdgeObj::CheckNodeConnection(bool bTail1) const
+{
+ bool bRet = false;
+ const SdrObjConnection& rCon=GetConnection(bTail1);
+ sal_uInt16 nPointCount=pEdgeTrack->GetPointCount();
+
+ if(nullptr != rCon.pObj && rCon.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject() && 0 != nPointCount)
+ {
+ const SdrGluePointList* pGPL=rCon.pObj->GetGluePointList();
+ sal_uInt16 nGluePointCnt=pGPL==nullptr ? 0 : pGPL->GetCount();
+ sal_uInt16 nGesAnz=nGluePointCnt+8;
+ Point aTail(bTail1 ? (*pEdgeTrack)[0] : (*pEdgeTrack)[sal_uInt16(nPointCount-1)]);
+ for (sal_uInt16 i=0; i<nGesAnz && !bRet; i++) {
+ if (i<nGluePointCnt) { // UserDefined
+ bRet=aTail==(*pGPL)[i].GetAbsolutePos(*rCon.pObj);
+ } else if (i<nGluePointCnt+4) { // Vertex
+ SdrGluePoint aPt(rCon.pObj->GetVertexGluePoint(i-nGluePointCnt));
+ bRet=aTail==aPt.GetAbsolutePos(*rCon.pObj);
+ } else { // Corner
+ SdrGluePoint aPt(rCon.pObj->GetCornerGluePoint(i-nGluePointCnt-4));
+ bRet=aTail==aPt.GetAbsolutePos(*rCon.pObj);
+ }
+ }
+ }
+ return bRet;
+}
+
+void SdrEdgeObj::ImpSetTailPoint(bool bTail1, const Point& rPt)
+{
+ sal_uInt16 nPointCount=pEdgeTrack->GetPointCount();
+ if (nPointCount==0) {
+ (*pEdgeTrack)[0]=rPt;
+ (*pEdgeTrack)[1]=rPt;
+ } else if (nPointCount==1) {
+ if (!bTail1) (*pEdgeTrack)[1]=rPt;
+ else { (*pEdgeTrack)[1]=(*pEdgeTrack)[0]; (*pEdgeTrack)[0]=rPt; }
+ } else {
+ if (!bTail1) (*pEdgeTrack)[sal_uInt16(nPointCount-1)]=rPt;
+ else (*pEdgeTrack)[0]=rPt;
+ }
+ ImpRecalcEdgeTrack();
+ SetBoundAndSnapRectsDirty();
+}
+
+void SdrEdgeObj::ImpDirtyEdgeTrack()
+{
+ if ( !bEdgeTrackUserDefined || !getSdrModelFromSdrObject().isLocked() )
+ bEdgeTrackDirty = true;
+}
+
+void SdrEdgeObj::ImpUndirtyEdgeTrack()
+{
+ if (bEdgeTrackDirty && getSdrModelFromSdrObject().isLocked())
+ {
+ ImpRecalcEdgeTrack();
+ }
+}
+
+void SdrEdgeObj::ImpRecalcEdgeTrack()
+{
+ // #i120437# if bEdgeTrackUserDefined, do not recalculate
+ if(bEdgeTrackUserDefined)
+ {
+ return;
+ }
+
+ // #i120437# also not when model locked during import, but remember
+ if(getSdrModelFromSdrObject().isLocked())
+ {
+ mbSuppressed = true;
+ return;
+ }
+
+ // #i110649#
+ if(mbBoundRectCalculationRunning)
+ {
+ // This object is involved into another ImpRecalcEdgeTrack() call
+ // from another SdrEdgeObj. Do not calculate again to avoid loop.
+ // Also, do not change bEdgeTrackDirty so that it gets recalculated
+ // later at the first non-looping call.
+ }
+ else
+ {
+ // To not run in a depth loop, use a coloring algorithm on
+ // SdrEdgeObj BoundRect calculations
+ mbBoundRectCalculationRunning = true;
+
+ if(mbSuppressed)
+ {
+ // #i123048# If layouting was ever suppressed, it needs to be done once
+ // and the attr need to be set at EdgeInfo, else these attr *will be lost*
+ // in the following call to ImpSetEdgeInfoToAttr() since they were never
+ // set before (!)
+ *pEdgeTrack=ImpCalcEdgeTrack(*pEdgeTrack,aCon1,aCon2,&aEdgeInfo);
+ ImpSetAttrToEdgeInfo();
+ mbSuppressed = false;
+ }
+
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetCurrentBoundRect();
+ SetBoundAndSnapRectsDirty();
+ *pEdgeTrack=ImpCalcEdgeTrack(*pEdgeTrack,aCon1,aCon2,&aEdgeInfo);
+ ImpSetEdgeInfoToAttr(); // copy values from aEdgeInfo into the pool
+ bEdgeTrackDirty=false;
+
+ // Only redraw here, no object change
+ ActionChanged();
+
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+
+ mbBoundRectCalculationRunning = false;
+ }
+}
+
+SdrEscapeDirection SdrEdgeObj::ImpCalcEscAngle(SdrObject const * pObj, const Point& rPt)
+{
+ if (pObj==nullptr) return SdrEscapeDirection::ALL;
+ tools::Rectangle aR(pObj->GetSnapRect());
+ tools::Long dxl=rPt.X()-aR.Left();
+ tools::Long dyo=rPt.Y()-aR.Top();
+ tools::Long dxr=aR.Right()-rPt.X();
+ tools::Long dyu=aR.Bottom()-rPt.Y();
+ bool bxMitt=std::abs(dxl-dxr)<2;
+ bool byMitt=std::abs(dyo-dyu)<2;
+ tools::Long dx=std::min(dxl,dxr);
+ tools::Long dy=std::min(dyo,dyu);
+ bool bDiag=std::abs(dx-dy)<2;
+ if (bxMitt && byMitt) return SdrEscapeDirection::ALL; // in the center
+ if (bDiag) { // diagonally
+ SdrEscapeDirection nRet=SdrEscapeDirection::SMART;
+ if (byMitt) nRet|=SdrEscapeDirection::VERT;
+ if (bxMitt) nRet|=SdrEscapeDirection::HORZ;
+ if (dxl<dxr) { // left
+ if (dyo<dyu) nRet|=SdrEscapeDirection::LEFT | SdrEscapeDirection::TOP;
+ else nRet|=SdrEscapeDirection::LEFT | SdrEscapeDirection::BOTTOM;
+ } else { // right
+ if (dyo<dyu) nRet|=SdrEscapeDirection::RIGHT | SdrEscapeDirection::TOP;
+ else nRet|=SdrEscapeDirection::RIGHT | SdrEscapeDirection::BOTTOM;
+ }
+ return nRet;
+ }
+ if (dx<dy) { // horizontal
+ if (bxMitt) return SdrEscapeDirection::HORZ;
+ if (dxl<dxr) return SdrEscapeDirection::LEFT;
+ else return SdrEscapeDirection::RIGHT;
+ } else { // vertical
+ if (byMitt) return SdrEscapeDirection::VERT;
+ if (dyo<dyu) return SdrEscapeDirection::TOP;
+ else return SdrEscapeDirection::BOTTOM;
+ }
+}
+
+XPolygon SdrEdgeObj::ImpCalcObjToCenter(const Point& rStPt, tools::Long nEscAngle, const tools::Rectangle& rRect, const Point& rMeeting)
+{
+ XPolygon aXP;
+ aXP.Insert(XPOLY_APPEND,rStPt,PolyFlags::Normal);
+ bool bRts=nEscAngle==0;
+ bool bObn=nEscAngle==9000;
+ bool bLks=nEscAngle==18000;
+ bool bUnt=nEscAngle==27000;
+
+ Point aP1(rStPt); // mandatory difference first,...
+ if (bLks) aP1.setX(rRect.Left() );
+ if (bRts) aP1.setX(rRect.Right() );
+ if (bObn) aP1.setY(rRect.Top() );
+ if (bUnt) aP1.setY(rRect.Bottom() );
+
+ Point aP2(aP1); // ...now increase to Meeting height, if necessary
+ if (bLks && rMeeting.X()<=aP2.X()) aP2.setX(rMeeting.X() );
+ if (bRts && rMeeting.X()>=aP2.X()) aP2.setX(rMeeting.X() );
+ if (bObn && rMeeting.Y()<=aP2.Y()) aP2.setY(rMeeting.Y() );
+ if (bUnt && rMeeting.Y()>=aP2.Y()) aP2.setY(rMeeting.Y() );
+ aXP.Insert(XPOLY_APPEND,aP2,PolyFlags::Normal);
+
+ Point aP3(aP2);
+ if ((bLks && rMeeting.X()>aP2.X()) || (bRts && rMeeting.X()<aP2.X())) { // around
+ if (rMeeting.Y()<aP2.Y()) {
+ aP3.setY(rRect.Top() );
+ if (rMeeting.Y()<aP3.Y()) aP3.setY(rMeeting.Y() );
+ } else {
+ aP3.setY(rRect.Bottom() );
+ if (rMeeting.Y()>aP3.Y()) aP3.setY(rMeeting.Y() );
+ }
+ aXP.Insert(XPOLY_APPEND,aP3,PolyFlags::Normal);
+ if (aP3.Y()!=rMeeting.Y()) {
+ aP3.setX(rMeeting.X() );
+ aXP.Insert(XPOLY_APPEND,aP3,PolyFlags::Normal);
+ }
+ }
+ if ((bObn && rMeeting.Y()>aP2.Y()) || (bUnt && rMeeting.Y()<aP2.Y())) { // around
+ if (rMeeting.X()<aP2.X()) {
+ aP3.setX(rRect.Left() );
+ if (rMeeting.X()<aP3.X()) aP3.setX(rMeeting.X() );
+ } else {
+ aP3.setX(rRect.Right() );
+ if (rMeeting.X()>aP3.X()) aP3.setX(rMeeting.X() );
+ }
+ aXP.Insert(XPOLY_APPEND,aP3,PolyFlags::Normal);
+ if (aP3.X()!=rMeeting.X()) {
+ aP3.setY(rMeeting.Y() );
+ aXP.Insert(XPOLY_APPEND,aP3,PolyFlags::Normal);
+ }
+ }
+#ifdef DBG_UTIL
+ if (aXP.GetPointCount()>4) {
+ OSL_FAIL("SdrEdgeObj::ImpCalcObjToCenter(): Polygon has more than 4 points!");
+ }
+#endif
+ return aXP;
+}
+
+XPolygon SdrEdgeObj::ImpCalcEdgeTrack(const XPolygon& rTrack0, SdrObjConnection& rCon1, SdrObjConnection& rCon2, SdrEdgeInfoRec* pInfo) const
+{
+ Point aPt1,aPt2;
+ SdrGluePoint aGP1,aGP2;
+ SdrEscapeDirection nEsc1=SdrEscapeDirection::ALL,nEsc2=SdrEscapeDirection::ALL;
+ tools::Rectangle aBoundRect1;
+ tools::Rectangle aBoundRect2;
+ tools::Rectangle aBewareRect1;
+ tools::Rectangle aBewareRect2;
+ // first, get the old corner points
+ if (rTrack0.GetPointCount()!=0) {
+ aPt1=rTrack0[0];
+ sal_uInt16 nSiz=rTrack0.GetPointCount();
+ nSiz--;
+ aPt2=rTrack0[nSiz];
+ } else {
+ if (!m_aOutRect.IsEmpty()) {
+ aPt1=m_aOutRect.TopLeft();
+ aPt2=m_aOutRect.BottomRight();
+ }
+ }
+
+ // #i54102# To allow interactive preview, do also if not inserted
+ const bool bCon1(nullptr != rCon1.pObj && rCon1.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
+ const bool bCon2(nullptr != rCon2.pObj && rCon2.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
+ const SfxItemSet& rSet = GetObjectItemSet();
+
+ if (bCon1)
+ {
+ if (rCon1.pObj==static_cast<SdrObject const *>(this))
+ {
+ // check, just in case
+ aBoundRect1=m_aOutRect;
+ }
+ else
+ {
+ aBoundRect1 = rCon1.pObj->GetCurrentBoundRect();
+ }
+
+ aBoundRect1.Move(rCon1.aObjOfs.X(),rCon1.aObjOfs.Y());
+ aBewareRect1=aBoundRect1;
+ sal_Int32 nH = rSet.Get(SDRATTR_EDGENODE1HORZDIST).GetValue();
+ sal_Int32 nV = rSet.Get(SDRATTR_EDGENODE1VERTDIST).GetValue();
+ aBewareRect1.AdjustLeft( -nH );
+ aBewareRect1.AdjustRight(nH );
+ aBewareRect1.AdjustTop( -nV );
+ aBewareRect1.AdjustBottom(nV );
+ }
+ else
+ {
+ aBoundRect1=tools::Rectangle(aPt1,aPt1);
+ aBoundRect1.Move(rCon1.aObjOfs.X(),rCon1.aObjOfs.Y());
+ aBewareRect1=aBoundRect1;
+ }
+
+ if (bCon2)
+ {
+ if (rCon2.pObj==static_cast<SdrObject const *>(this))
+ { // check, just in case
+ aBoundRect2=m_aOutRect;
+ }
+ else
+ {
+ aBoundRect2 = rCon2.pObj->GetCurrentBoundRect();
+ }
+
+ aBoundRect2.Move(rCon2.aObjOfs.X(),rCon2.aObjOfs.Y());
+ aBewareRect2=aBoundRect2;
+ sal_Int32 nH = rSet.Get(SDRATTR_EDGENODE2HORZDIST).GetValue();
+ sal_Int32 nV = rSet.Get(SDRATTR_EDGENODE2VERTDIST).GetValue();
+ aBewareRect2.AdjustLeft( -nH );
+ aBewareRect2.AdjustRight(nH );
+ aBewareRect2.AdjustTop( -nV );
+ aBewareRect2.AdjustBottom(nV );
+ }
+ else
+ {
+ aBoundRect2=tools::Rectangle(aPt2,aPt2);
+ aBoundRect2.Move(rCon2.aObjOfs.X(),rCon2.aObjOfs.Y());
+ aBewareRect2=aBoundRect2;
+ }
+
+ XPolygon aBestXP;
+ sal_uIntPtr nBestQual=0xFFFFFFFF;
+ SdrEdgeInfoRec aBestInfo;
+ bool bAuto1=bCon1 && rCon1.bBestVertex;
+ bool bAuto2=bCon2 && rCon2.bBestVertex;
+ if (bAuto1) rCon1.bAutoVertex=true;
+ if (bAuto2) rCon2.bAutoVertex=true;
+ sal_uInt16 nBestAuto1=0;
+ sal_uInt16 nBestAuto2=0;
+ sal_uInt16 nCount1=bAuto1 ? 4 : 1;
+ sal_uInt16 nCount2=bAuto2 ? 4 : 1;
+
+ for (sal_uInt16 nNum1=0; nNum1<nCount1; nNum1++)
+ {
+ if (bAuto1) rCon1.nConId=nNum1;
+ if (bCon1 && rCon1.TakeGluePoint(aGP1))
+ {
+ aPt1=aGP1.GetPos();
+ nEsc1=aGP1.GetEscDir();
+ if (nEsc1==SdrEscapeDirection::SMART) nEsc1=ImpCalcEscAngle(rCon1.pObj,aPt1-rCon1.aObjOfs);
+ }
+ for (sal_uInt16 nNum2=0; nNum2<nCount2; nNum2++)
+ {
+ if (bAuto2) rCon2.nConId=nNum2;
+ if (bCon2 && rCon2.TakeGluePoint(aGP2))
+ {
+ aPt2=aGP2.GetPos();
+ nEsc2=aGP2.GetEscDir();
+ if (nEsc2==SdrEscapeDirection::SMART) nEsc2=ImpCalcEscAngle(rCon2.pObj,aPt2-rCon2.aObjOfs);
+ }
+ for (tools::Long nA1=0; nA1<36000; nA1+=9000)
+ {
+ SdrEscapeDirection nE1 = nA1==0 ? SdrEscapeDirection::RIGHT : nA1==9000 ? SdrEscapeDirection::TOP : nA1==18000 ? SdrEscapeDirection::LEFT : nA1==27000 ? SdrEscapeDirection::BOTTOM : SdrEscapeDirection::SMART;
+ for (tools::Long nA2=0; nA2<36000; nA2+=9000)
+ {
+ SdrEscapeDirection nE2 = nA2==0 ? SdrEscapeDirection::RIGHT : nA2==9000 ? SdrEscapeDirection::TOP : nA2==18000 ? SdrEscapeDirection::LEFT : nA2==27000 ? SdrEscapeDirection::BOTTOM : SdrEscapeDirection::SMART;
+ if ((nEsc1&nE1) && (nEsc2&nE2))
+ {
+ sal_uIntPtr nQual=0;
+ SdrEdgeInfoRec aInfo;
+ if (pInfo!=nullptr) aInfo=*pInfo;
+ XPolygon aXP(ImpCalcEdgeTrack(aPt1,nA1,aBoundRect1,aBewareRect1,aPt2,nA2,aBoundRect2,aBewareRect2,&nQual,&aInfo));
+ if (nQual<nBestQual)
+ {
+ aBestXP=std::move(aXP);
+ nBestQual=nQual;
+ aBestInfo=aInfo;
+ nBestAuto1=nNum1;
+ nBestAuto2=nNum2;
+ }
+ }
+ }
+ }
+ }
+ }
+ if (bAuto1) rCon1.nConId=nBestAuto1;
+ if (bAuto2) rCon2.nConId=nBestAuto2;
+ if (pInfo!=nullptr) *pInfo=aBestInfo;
+ return aBestXP;
+}
+
+XPolygon SdrEdgeObj::ImpCalcEdgeTrack(const Point& rPt1, tools::Long nAngle1, const tools::Rectangle& rBoundRect1, const tools::Rectangle& rBewareRect1,
+ const Point& rPt2, tools::Long nAngle2, const tools::Rectangle& rBoundRect2, const tools::Rectangle& rBewareRect2,
+ sal_uIntPtr* pnQuality, SdrEdgeInfoRec* pInfo) const
+{
+ SdrEdgeKind eKind=GetObjectItem(SDRATTR_EDGEKIND).GetValue();
+ bool bRts1=nAngle1==0;
+ bool bObn1=nAngle1==9000;
+ bool bLks1=nAngle1==18000;
+ bool bUnt1=nAngle1==27000;
+ bool bHor1=bLks1 || bRts1;
+ bool bVer1=bObn1 || bUnt1;
+ bool bRts2=nAngle2==0;
+ bool bObn2=nAngle2==9000;
+ bool bLks2=nAngle2==18000;
+ bool bUnt2=nAngle2==27000;
+ bool bHor2=bLks2 || bRts2;
+ bool bVer2=bObn2 || bUnt2;
+ bool bInfo=pInfo!=nullptr;
+ if (bInfo) {
+ pInfo->nAngle1=nAngle1;
+ pInfo->nAngle2=nAngle2;
+ pInfo->nObj1Lines=1;
+ pInfo->nObj2Lines=1;
+ pInfo->nMiddleLine=0xFFFF;
+ }
+ Point aPt1(rPt1);
+ Point aPt2(rPt2);
+ tools::Rectangle aBoundRect1 (rBoundRect1 );
+ tools::Rectangle aBoundRect2 (rBoundRect2 );
+ tools::Rectangle aBewareRect1(rBewareRect1);
+ tools::Rectangle aBewareRect2(rBewareRect2);
+ Point aMeeting((aPt1.X()+aPt2.X()+1)/2,(aPt1.Y()+aPt2.Y()+1)/2);
+ if (eKind==SdrEdgeKind::OneLine) {
+ XPolygon aXP(2);
+ aXP[0]=rPt1;
+ aXP[1]=rPt2;
+ if (pnQuality!=nullptr) {
+ *pnQuality=std::abs(rPt1.X()-rPt2.X())+std::abs(rPt1.Y()-rPt2.Y());
+ }
+ return aXP;
+ } else if (eKind==SdrEdgeKind::ThreeLines) {
+ XPolygon aXP(4);
+ aXP[0]=rPt1;
+ aXP[1]=rPt1;
+ aXP[2]=rPt2;
+ aXP[3]=rPt2;
+ if (bRts1) aXP[1].setX(aBewareRect1.Right() ); //+=500;
+ if (bObn1) aXP[1].setY(aBewareRect1.Top() ); //-=500;
+ if (bLks1) aXP[1].setX(aBewareRect1.Left() ); //-=500;
+ if (bUnt1) aXP[1].setY(aBewareRect1.Bottom() ); //+=500;
+ if (bRts2) aXP[2].setX(aBewareRect2.Right() ); //+=500;
+ if (bObn2) aXP[2].setY(aBewareRect2.Top() ); //-=500;
+ if (bLks2) aXP[2].setX(aBewareRect2.Left() ); //-=500;
+ if (bUnt2) aXP[2].setY(aBewareRect2.Bottom() ); //+=500;
+ if (pnQuality!=nullptr) {
+ tools::Long nQ=std::abs(aXP[1].X()-aXP[0].X())+std::abs(aXP[1].Y()-aXP[0].Y());
+ nQ+=std::abs(aXP[2].X()-aXP[1].X())+std::abs(aXP[2].Y()-aXP[1].Y());
+ nQ+=std::abs(aXP[3].X()-aXP[2].X())+std::abs(aXP[3].Y()-aXP[2].Y());
+ *pnQuality=nQ;
+ }
+ if (bInfo) {
+ pInfo->nObj1Lines=2;
+ pInfo->nObj2Lines=2;
+ if (bHor1) {
+ aXP[1].AdjustX(pInfo->aObj1Line2.X() );
+ } else {
+ aXP[1].AdjustY(pInfo->aObj1Line2.Y() );
+ }
+ if (bHor2) {
+ aXP[2].AdjustX(pInfo->aObj2Line2.X() );
+ } else {
+ aXP[2].AdjustY(pInfo->aObj2Line2.Y() );
+ }
+ }
+ return aXP;
+ }
+ sal_uInt16 nIntersections=0;
+ {
+ Point aC1(aBewareRect1.Center());
+ Point aC2(aBewareRect2.Center());
+ if (aBewareRect1.Left()<=aBewareRect2.Right() && aBewareRect1.Right()>=aBewareRect2.Left()) {
+ // overlapping on the x axis
+ tools::Long n1=std::max(aBewareRect1.Left(),aBewareRect2.Left());
+ tools::Long n2=std::min(aBewareRect1.Right(),aBewareRect2.Right());
+ aMeeting.setX((n1+n2+1)/2 );
+ } else {
+ // otherwise the center point of the empty space
+ if (aC1.X()<aC2.X()) {
+ aMeeting.setX((aBewareRect1.Right()+aBewareRect2.Left()+1)/2 );
+ } else {
+ aMeeting.setX((aBewareRect1.Left()+aBewareRect2.Right()+1)/2 );
+ }
+ }
+ if (aBewareRect1.Top()<=aBewareRect2.Bottom() && aBewareRect1.Bottom()>=aBewareRect2.Top()) {
+ // overlapping on the x axis
+ tools::Long n1=std::max(aBewareRect1.Top(),aBewareRect2.Top());
+ tools::Long n2=std::min(aBewareRect1.Bottom(),aBewareRect2.Bottom());
+ aMeeting.setY((n1+n2+1)/2 );
+ } else {
+ // otherwise the center point of the empty space
+ if (aC1.Y()<aC2.Y()) {
+ aMeeting.setY((aBewareRect1.Bottom()+aBewareRect2.Top()+1)/2 );
+ } else {
+ aMeeting.setY((aBewareRect1.Top()+aBewareRect2.Bottom()+1)/2 );
+ }
+ }
+ // Here, there are three cases:
+ // 1. both go into the same direction
+ // 2. both go into opposite directions
+ // 3. one is vertical, the other is horizontal
+ tools::Long nXMin=std::min(aBewareRect1.Left(),aBewareRect2.Left());
+ tools::Long nXMax=std::max(aBewareRect1.Right(),aBewareRect2.Right());
+ tools::Long nYMin=std::min(aBewareRect1.Top(),aBewareRect2.Top());
+ tools::Long nYMax=std::max(aBewareRect1.Bottom(),aBewareRect2.Bottom());
+ bool bBewareOverlap=aBewareRect1.Right()>aBewareRect2.Left() && aBewareRect1.Left()<aBewareRect2.Right() &&
+ aBewareRect1.Bottom()>aBewareRect2.Top() && aBewareRect1.Top()<aBewareRect2.Bottom();
+ unsigned nMainCase=3;
+ if (nAngle1==nAngle2) nMainCase=1;
+ else if ((bHor1 && bHor2) || (bVer1 && bVer2)) nMainCase=2;
+ if (nMainCase==1) { // case 1 (both go in one direction) is possible
+ if (bVer1) aMeeting.setX((aPt1.X()+aPt2.X()+1)/2 ); // Here, this is better than
+ if (bHor1) aMeeting.setY((aPt1.Y()+aPt2.Y()+1)/2 ); // using center point of empty space
+ // bX1Ok means that the vertical exiting Obj1 doesn't conflict with Obj2, ...
+ bool bX1Ok=aPt1.X()<=aBewareRect2.Left() || aPt1.X()>=aBewareRect2.Right();
+ bool bX2Ok=aPt2.X()<=aBewareRect1.Left() || aPt2.X()>=aBewareRect1.Right();
+ bool bY1Ok=aPt1.Y()<=aBewareRect2.Top() || aPt1.Y()>=aBewareRect2.Bottom();
+ bool bY2Ok=aPt2.Y()<=aBewareRect1.Top() || aPt2.Y()>=aBewareRect1.Bottom();
+ if (bLks1 && (bY1Ok || aBewareRect1.Left()<aBewareRect2.Right()) && (bY2Ok || aBewareRect2.Left()<aBewareRect1.Right())) {
+ aMeeting.setX(nXMin );
+ }
+ if (bRts1 && (bY1Ok || aBewareRect1.Right()>aBewareRect2.Left()) && (bY2Ok || aBewareRect2.Right()>aBewareRect1.Left())) {
+ aMeeting.setX(nXMax );
+ }
+ if (bObn1 && (bX1Ok || aBewareRect1.Top()<aBewareRect2.Bottom()) && (bX2Ok || aBewareRect2.Top()<aBewareRect1.Bottom())) {
+ aMeeting.setY(nYMin );
+ }
+ if (bUnt1 && (bX1Ok || aBewareRect1.Bottom()>aBewareRect2.Top()) && (bX2Ok || aBewareRect2.Bottom()>aBewareRect1.Top())) {
+ aMeeting.setY(nYMax );
+ }
+ } else if (nMainCase==2) {
+ // case 2:
+ if (bHor1) { // both horizontal
+ /* 9 sub-cases:
+ (legend: line exits to the left (-|), right (|-))
+
+ 2.1: Facing; overlap only on y axis
+ * * *
+ |--| *
+ * * *
+
+ 2.2, 2.3: Facing, offset vertically; no overlap on either
+ axis
+ |- * * * * *
+ * -| * * -| *
+ * * * , * * *
+
+ 2.4, 2.5: One below the other; overlap only on y axis
+ * |- * * * *
+ * -| * * -| *
+ * * * , * |- *
+
+ 2.6, 2.7: Not facing, offset vertically; no overlap on either
+ axis
+ * * |- * * *
+ * -| * * -| *
+ * * * , * * |-
+
+ 2.8: Not facing; overlap only on y axis
+ * * *
+ * -| |-
+ * * *
+
+ 2.9: The objects's BewareRects overlap on x and y axis
+
+ These cases, with some modifications are also valid for
+ horizontal line exits.
+ Cases 2.1 through 2.7 are covered well enough with the
+ default meetings. Only for cases 2.8 and 2.9 do we determine
+ special meeting points here.
+ */
+
+ // normalization; be aR1 the one exiting to the right,
+ // be aR2 the one exiting to the left
+ tools::Rectangle aBewR1(bRts1 ? aBewareRect1 : aBewareRect2);
+ tools::Rectangle aBewR2(bRts1 ? aBewareRect2 : aBewareRect1);
+ tools::Rectangle aBndR1(bRts1 ? aBoundRect1 : aBoundRect2);
+ tools::Rectangle aBndR2(bRts1 ? aBoundRect2 : aBoundRect1);
+ if (aBewR1.Bottom()>aBewR2.Top() && aBewR1.Top()<aBewR2.Bottom()) {
+ // overlap on y axis; cases 2.1, 2.8, 2.9
+ if (aBewR1.Right()>aBewR2.Left()) {
+ /* Cases 2.8, 2.9:
+ Case 2.8: always going around on the outside
+ (bDirect=false).
+
+ Case 2.9 could also be a direct connection (in the
+ case that the BewareRects overlap only slightly and
+ the BoundRects don't overlap at all and if the
+ line exits would otherwise violate the respective
+ other object's BewareRect).
+ */
+ bool bCase29Direct = false;
+ bool bCase29=aBewR1.Right()>aBewR2.Left();
+ if (aBndR1.Right()<=aBndR2.Left()) { // case 2.9 without BoundRect overlap
+ if ((aPt1.Y()>aBewareRect2.Top() && aPt1.Y()<aBewareRect2.Bottom()) ||
+ (aPt2.Y()>aBewareRect1.Top() && aPt2.Y()<aBewareRect1.Bottom())) {
+ bCase29Direct = true;
+ }
+ }
+ if (!bCase29Direct) {
+ bool bObenLang=std::abs(nYMin-aMeeting.Y())<=std::abs(nYMax-aMeeting.Y());
+ if (bObenLang) {
+ aMeeting.setY(nYMin );
+ } else {
+ aMeeting.setY(nYMax );
+ }
+ if (bCase29) {
+ // now make sure that the surrounded object
+ // isn't traversed
+ if ((aBewR1.Center().Y()<aBewR2.Center().Y()) != bObenLang) {
+ aMeeting.setX(aBewR2.Right() );
+ } else {
+ aMeeting.setX(aBewR1.Left() );
+ }
+ }
+ } else {
+ // We need a direct connection (3-line Z connection),
+ // because we have to violate the BewareRects.
+ // Use rule of three to scale down the BewareRects.
+ tools::Long nWant1=aBewR1.Right()-aBndR1.Right(); // distance at Obj1
+ tools::Long nWant2=aBndR2.Left()-aBewR2.Left(); // distance at Obj2
+ tools::Long nSpace=aBndR2.Left()-aBndR1.Right(); // available space
+ tools::Long nGet1=BigMulDiv(nWant1,nSpace,nWant1+nWant2);
+ tools::Long nGet2=nSpace-nGet1;
+ if (bRts1) { // revert normalization
+ aBewareRect1.AdjustRight(nGet1-nWant1 );
+ aBewareRect2.AdjustLeft( -(nGet2-nWant2) );
+ } else {
+ aBewareRect2.AdjustRight(nGet1-nWant1 );
+ aBewareRect1.AdjustLeft( -(nGet2-nWant2) );
+ }
+ nIntersections++; // lower quality
+ }
+ }
+ }
+ } else if (bVer1) { // both horizontal
+ tools::Rectangle aBewR1(bUnt1 ? aBewareRect1 : aBewareRect2);
+ tools::Rectangle aBewR2(bUnt1 ? aBewareRect2 : aBewareRect1);
+ tools::Rectangle aBndR1(bUnt1 ? aBoundRect1 : aBoundRect2);
+ tools::Rectangle aBndR2(bUnt1 ? aBoundRect2 : aBoundRect1);
+ if (aBewR1.Right()>aBewR2.Left() && aBewR1.Left()<aBewR2.Right()) {
+ // overlap on y axis; cases 2.1, 2.8, 2.9
+ if (aBewR1.Bottom()>aBewR2.Top()) {
+ /* Cases 2.8, 2.9
+ Case 2.8 always going around on the outside (bDirect=false).
+
+ Case 2.9 could also be a direct connection (in the
+ case that the BewareRects overlap only slightly and
+ the BoundRects don't overlap at all and if the
+ line exits would otherwise violate the respective
+ other object's BewareRect).
+ */
+ bool bCase29Direct = false;
+ bool bCase29=aBewR1.Bottom()>aBewR2.Top();
+ if (aBndR1.Bottom()<=aBndR2.Top()) { // case 2.9 without BoundRect overlap
+ if ((aPt1.X()>aBewareRect2.Left() && aPt1.X()<aBewareRect2.Right()) ||
+ (aPt2.X()>aBewareRect1.Left() && aPt2.X()<aBewareRect1.Right())) {
+ bCase29Direct = true;
+ }
+ }
+ if (!bCase29Direct) {
+ bool bLinksLang=std::abs(nXMin-aMeeting.X())<=std::abs(nXMax-aMeeting.X());
+ if (bLinksLang) {
+ aMeeting.setX(nXMin );
+ } else {
+ aMeeting.setX(nXMax );
+ }
+ if (bCase29) {
+ // now make sure that the surrounded object
+ // isn't traversed
+ if ((aBewR1.Center().X()<aBewR2.Center().X()) != bLinksLang) {
+ aMeeting.setY(aBewR2.Bottom() );
+ } else {
+ aMeeting.setY(aBewR1.Top() );
+ }
+ }
+ } else {
+ // We need a direct connection (3-line Z connection),
+ // because we have to violate the BewareRects.
+ // Use rule of three to scale down the BewareRects.
+ tools::Long nWant1=aBewR1.Bottom()-aBndR1.Bottom(); // difference at Obj1
+ tools::Long nWant2=aBndR2.Top()-aBewR2.Top(); // difference at Obj2
+ tools::Long nSpace=aBndR2.Top()-aBndR1.Bottom(); // available space
+ tools::Long nGet1=BigMulDiv(nWant1,nSpace,nWant1+nWant2);
+ tools::Long nGet2=nSpace-nGet1;
+ if (bUnt1) { // revert normalization
+ aBewareRect1.AdjustBottom(nGet1-nWant1 );
+ aBewareRect2.AdjustTop( -(nGet2-nWant2) );
+ } else {
+ aBewareRect2.AdjustBottom(nGet1-nWant1 );
+ aBewareRect1.AdjustTop( -(nGet2-nWant2) );
+ }
+ nIntersections++; // lower quality
+ }
+ }
+ }
+ }
+ } else if (nMainCase==3) { // case 3: one horizontal, the other vertical
+ /* legend:
+ The line exits to the:
+ -| left
+
+ |- right
+
+ _|_ top
+
+ T bottom
+
+ * . * . * -- no overlap, at most might touch
+ . . . . . -- overlap
+ * . |- . * -- same height
+ . . . . . -- overlap
+ * . * . * -- no overlap, at most might touch
+
+ Overall, there are 96 possible constellations, some of these can't even
+ be unambiguously assigned to a certain case/method of handling.
+
+
+ 3.1: All those constellations that are covered reasonably well
+ by the default MeetingPoint (20+12).
+
+ T T T . _|_ _|_ . T T T these 12 * . * T * * . * . * * T * . * * . * . *
+ . . . . _|_ _|_ . . . . constellations . . . . . . . . . T . . . . . T . . . .
+ * . |- . * * . -| . * are covered * . |- . _|_ * . |- . T _|_ . -| . * T . -| . *
+ . . . . T T . . . . only in . . . . _|_ . . . . . _|_ . . . . . . . . .
+ _|__|__|_ . T T . _|__|__|_ part: * . * _|_ * * . * . * * _|_ * . * * . * . *
+
+ The last 16 of these cases can be excluded, if the objects face each other openly.
+
+
+ 3.2: The objects face each other openly, thus a connection using only two lines is possible (4+20);
+ This case is priority #1.
+ * . * . T T . * . * these 20 * . * T * * T * . * * . * . * * . * . *
+ . . . . . . . . . . constellations . . . T T T T . . . . . . . . . . . . .
+ * . |- . * * . -| . * are covered * . |-_|__|_ _|__|_-| . * * . |- T T T T -| . *
+ . . . . . . . . . . only in . . . _|__|_ _|__|_ . . . . . . . . . . . . .
+ * . * . _|_ _|_ . * . * part: * . * _|_ * * _|_ * . * * . * . * * . * . *
+
+ 3.3: The line exits point away from the other object or miss its back (52+4).
+ _|__|__|__|_ * * _|__|__|__|_ * . . . * * . * . * these 4 * . * . * * . * . *
+ _|__|__|__|_ . . _|__|__|__|_ T T T . . . . T T T constellations . . . T . . T . . .
+ _|__|_ |- . * * . -| _|__|_ T T |- . * * . -| T T are covered * . |- . * * . -| . *
+ _|__|__|_ . . . . _|__|__|_ T T T T . . T T T T only in . . . _|_ . . _|_ . . .
+ * . * . * * . * . * T T T T * * T T T T part: * . * . * * . * . *
+ */
+
+ // case 3.2
+ tools::Rectangle aTmpR1(aBewareRect1);
+ tools::Rectangle aTmpR2(aBewareRect2);
+ if (bBewareOverlap) {
+ // overlapping BewareRects: use BoundRects for checking for case 3.2
+ aTmpR1=aBoundRect1;
+ aTmpR2=aBoundRect2;
+ }
+ if ((((bRts1 && aTmpR1.Right ()<=aPt2.X()) || (bLks1 && aTmpR1.Left()>=aPt2.X())) &&
+ ((bUnt2 && aTmpR2.Bottom()<=aPt1.Y()) || (bObn2 && aTmpR2.Top ()>=aPt1.Y()))) ||
+ (((bRts2 && aTmpR2.Right ()<=aPt1.X()) || (bLks2 && aTmpR2.Left()>=aPt1.X())) &&
+ ((bUnt1 && aTmpR1.Bottom()<=aPt2.Y()) || (bObn1 && aTmpR1.Top ()>=aPt2.Y())))) {
+ // case 3.2 applies: connector with only 2 lines
+ if (bHor1) {
+ aMeeting.setX(aPt2.X() );
+ aMeeting.setY(aPt1.Y() );
+ } else {
+ aMeeting.setX(aPt1.X() );
+ aMeeting.setY(aPt2.Y() );
+ }
+ // in the case of overlapping BewareRects:
+ aBewareRect1=aTmpR1;
+ aBewareRect2=aTmpR2;
+ } else if ((((bRts1 && aBewareRect1.Right ()>aBewareRect2.Left ()) ||
+ (bLks1 && aBewareRect1.Left ()<aBewareRect2.Right ())) &&
+ ((bUnt2 && aBewareRect2.Bottom()>aBewareRect1.Top ()) ||
+ (bObn2 && aBewareRect2.Top ()<aBewareRect1.Bottom()))) ||
+ (((bRts2 && aBewareRect2.Right ()>aBewareRect1.Left ()) ||
+ (bLks2 && aBewareRect2.Left ()<aBewareRect1.Right ())) &&
+ ((bUnt1 && aBewareRect1.Bottom()>aBewareRect2.Top ()) ||
+ (bObn1 && aBewareRect1.Top ()<aBewareRect2.Bottom())))) {
+ // case 3.3
+ if (bRts1 || bRts2) { aMeeting.setX(nXMax ); }
+ if (bLks1 || bLks2) { aMeeting.setX(nXMin ); }
+ if (bUnt1 || bUnt2) { aMeeting.setY(nYMax ); }
+ if (bObn1 || bObn2) { aMeeting.setY(nYMin ); }
+ }
+ }
+ }
+
+ XPolygon aXP1(ImpCalcObjToCenter(aPt1,nAngle1,aBewareRect1,aMeeting));
+ XPolygon aXP2(ImpCalcObjToCenter(aPt2,nAngle2,aBewareRect2,aMeeting));
+ sal_uInt16 nXP1Cnt=aXP1.GetPointCount();
+ sal_uInt16 nXP2Cnt=aXP2.GetPointCount();
+ if (bInfo) {
+ pInfo->nObj1Lines=nXP1Cnt; if (nXP1Cnt>1) pInfo->nObj1Lines--;
+ pInfo->nObj2Lines=nXP2Cnt; if (nXP2Cnt>1) pInfo->nObj2Lines--;
+ }
+ Point aEP1(aXP1[nXP1Cnt-1]);
+ Point aEP2(aXP2[nXP2Cnt-1]);
+ bool bInsMeetingPoint=aEP1.X()!=aEP2.X() && aEP1.Y()!=aEP2.Y();
+ bool bHorzE1=aEP1.Y()==aXP1[nXP1Cnt-2].Y(); // is last line of XP1 horizontal?
+ bool bHorzE2=aEP2.Y()==aXP2[nXP2Cnt-2].Y(); // is last line of XP2 horizontal?
+ if (aEP1==aEP2 && ((bHorzE1 && bHorzE2 && aEP1.Y()==aEP2.Y()) || (!bHorzE1 && !bHorzE2 && aEP1.X()==aEP2.X()))) {
+ // special casing 'I' connectors
+ nXP1Cnt--; aXP1.Remove(nXP1Cnt,1);
+ nXP2Cnt--; aXP2.Remove(nXP2Cnt,1);
+ }
+ if (bInsMeetingPoint) {
+ aXP1.Insert(XPOLY_APPEND,aMeeting,PolyFlags::Normal);
+ if (bInfo) {
+ // Inserting a MeetingPoint adds 2 new lines,
+ // either might become the center line.
+ if (pInfo->nObj1Lines==pInfo->nObj2Lines) {
+ pInfo->nObj1Lines++;
+ pInfo->nObj2Lines++;
+ } else {
+ if (pInfo->nObj1Lines>pInfo->nObj2Lines) {
+ pInfo->nObj2Lines++;
+ pInfo->nMiddleLine=nXP1Cnt-1;
+ } else {
+ pInfo->nObj1Lines++;
+ pInfo->nMiddleLine=nXP1Cnt;
+ }
+ }
+ }
+ } else if (bInfo && aEP1!=aEP2 && nXP1Cnt+nXP2Cnt>=4) {
+ // By connecting both ends, another line is added, this becomes the center line.
+ pInfo->nMiddleLine=nXP1Cnt-1;
+ }
+ sal_uInt16 nNum=aXP2.GetPointCount();
+ if (aXP1[nXP1Cnt-1]==aXP2[nXP2Cnt-1] && nXP1Cnt>1 && nXP2Cnt>1) nNum--;
+ while (nNum>0) {
+ nNum--;
+ aXP1.Insert(XPOLY_APPEND,aXP2[nNum],PolyFlags::Normal);
+ }
+ sal_uInt16 nPointCount=aXP1.GetPointCount();
+ char cForm;
+ if (bInfo || pnQuality!=nullptr) {
+ if (nPointCount==2) cForm='I';
+ else if (nPointCount==3) cForm='L';
+ else if (nPointCount==4) { // Z or U
+ if (nAngle1==nAngle2) cForm='U';
+ else cForm='Z';
+ } else if (nPointCount==6) { // S or C or ...
+ if (nAngle1!=nAngle2) {
+ // For type S, line 2 has the same direction as line 4.
+ // For type C, the opposite is true.
+ Point aP1(aXP1[1]);
+ Point aP2(aXP1[2]);
+ Point aP3(aXP1[3]);
+ Point aP4(aXP1[4]);
+ if (aP1.Y()==aP2.Y()) { // else both lines are horizontal
+ if ((aP1.X()<aP2.X())==(aP3.X()<aP4.X())) cForm='S';
+ else cForm='C';
+ } else { // else both lines are vertical
+ if ((aP1.Y()<aP2.Y())==(aP3.Y()<aP4.Y())) cForm='S';
+ else cForm='C';
+ }
+ } else cForm='4'; // else is case 3 with 5 lines
+ } else cForm='?';
+ // more shapes:
+ if (bInfo) {
+ if (cForm=='I' || cForm=='L' || cForm=='Z' || cForm=='U') {
+ pInfo->nObj1Lines=1;
+ pInfo->nObj2Lines=1;
+ if (cForm=='Z' || cForm=='U') {
+ pInfo->nMiddleLine=1;
+ } else {
+ pInfo->nMiddleLine=0xFFFF;
+ }
+ } else if (cForm=='S' || cForm=='C') {
+ pInfo->nObj1Lines=2;
+ pInfo->nObj2Lines=2;
+ pInfo->nMiddleLine=2;
+ }
+ }
+ }
+ else
+ {
+ cForm = 0;
+ }
+ if (pnQuality!=nullptr) {
+ sal_uIntPtr nQual=0;
+ sal_uIntPtr nQual0=nQual; // prevent overruns
+ bool bOverflow = false;
+ Point aPt0(aXP1[0]);
+ for (sal_uInt16 nPntNum=1; nPntNum<nPointCount; nPntNum++) {
+ Point aPt1b(aXP1[nPntNum]);
+ nQual+=std::abs(aPt1b.X()-aPt0.X())+std::abs(aPt1b.Y()-aPt0.Y());
+ if (nQual<nQual0) bOverflow = true;
+ nQual0=nQual;
+ aPt0=aPt1b;
+ }
+
+ sal_uInt16 nTmp=nPointCount;
+ if (cForm=='Z') {
+ nTmp=2; // Z shape with good quality (nTmp=2 instead of 4)
+ sal_uIntPtr n1=std::abs(aXP1[1].X()-aXP1[0].X())+std::abs(aXP1[1].Y()-aXP1[0].Y());
+ sal_uIntPtr n2=std::abs(aXP1[2].X()-aXP1[1].X())+std::abs(aXP1[2].Y()-aXP1[1].Y());
+ sal_uIntPtr n3=std::abs(aXP1[3].X()-aXP1[2].X())+std::abs(aXP1[3].Y()-aXP1[2].Y());
+ // try to make lines lengths similar
+ sal_uIntPtr nBesser=0;
+ n1+=n3;
+ n3=n2/4;
+ if (n1>=n2) nBesser=6;
+ else if (n1>=3*n3) nBesser=4;
+ else if (n1>=2*n3) nBesser=2;
+ if (aXP1[0].Y()!=aXP1[1].Y()) nBesser++; // vertical starting line gets a plus (for H/V-Prio)
+ if (nQual>nBesser) nQual-=nBesser; else nQual=0;
+ }
+ if (nTmp>=3) {
+ nQual0=nQual;
+ nQual+=static_cast<sal_uIntPtr>(nTmp)*0x01000000;
+ if (nQual<nQual0 || nTmp>15) bOverflow = true;
+ }
+ if (nPointCount>=2) { // check exit angle again
+ Point aP1(aXP1[1]); aP1-=aXP1[0];
+ Point aP2(aXP1[nPointCount-2]); aP2-=aXP1[nPointCount-1];
+ tools::Long nAng1=0; if (aP1.X()<0) nAng1=18000; if (aP1.Y()>0) nAng1=27000;
+ if (aP1.Y()<0) nAng1=9000;
+ if (aP1.X()!=0 && aP1.Y()!=0) nAng1=1; // slant?!
+ tools::Long nAng2=0; if (aP2.X()<0) nAng2=18000; if (aP2.Y()>0) nAng2=27000;
+ if (aP2.Y()<0) nAng2=9000;
+ if (aP2.X()!=0 && aP2.Y()!=0) nAng2=1; // slant?!
+ if (nAng1!=nAngle1) nIntersections++;
+ if (nAng2!=nAngle2) nIntersections++;
+ }
+
+ // For the quality check, use the original Rects and at the same time
+ // check whether one them was scaled down for the calculation of the
+ // Edges (e. g. case 2.9)
+ aBewareRect1=rBewareRect1;
+ aBewareRect2=rBewareRect2;
+
+ for (sal_uInt16 i=0; i<nPointCount; i++) {
+ Point aPt1b(aXP1[i]);
+ bool b1=aPt1b.X()>aBewareRect1.Left() && aPt1b.X()<aBewareRect1.Right() &&
+ aPt1b.Y()>aBewareRect1.Top() && aPt1b.Y()<aBewareRect1.Bottom();
+ bool b2=aPt1b.X()>aBewareRect2.Left() && aPt1b.X()<aBewareRect2.Right() &&
+ aPt1b.Y()>aBewareRect2.Top() && aPt1b.Y()<aBewareRect2.Bottom();
+ sal_uInt16 nInt0=nIntersections;
+ if (i==0 || i==nPointCount-1) {
+ if (b1 && b2) nIntersections++;
+ } else {
+ if (b1) nIntersections++;
+ if (b2) nIntersections++;
+ }
+ // check for overlaps
+ if (i>0 && nInt0==nIntersections) {
+ if (aPt0.Y()==aPt1b.Y()) { // horizontal line
+ if (aPt0.Y()>aBewareRect1.Top() && aPt0.Y()<aBewareRect1.Bottom() &&
+ ((aPt0.X()<=aBewareRect1.Left() && aPt1b.X()>=aBewareRect1.Right()) ||
+ (aPt1b.X()<=aBewareRect1.Left() && aPt0.X()>=aBewareRect1.Right()))) nIntersections++;
+ if (aPt0.Y()>aBewareRect2.Top() && aPt0.Y()<aBewareRect2.Bottom() &&
+ ((aPt0.X()<=aBewareRect2.Left() && aPt1b.X()>=aBewareRect2.Right()) ||
+ (aPt1b.X()<=aBewareRect2.Left() && aPt0.X()>=aBewareRect2.Right()))) nIntersections++;
+ } else { // vertical line
+ if (aPt0.X()>aBewareRect1.Left() && aPt0.X()<aBewareRect1.Right() &&
+ ((aPt0.Y()<=aBewareRect1.Top() && aPt1b.Y()>=aBewareRect1.Bottom()) ||
+ (aPt1b.Y()<=aBewareRect1.Top() && aPt0.Y()>=aBewareRect1.Bottom()))) nIntersections++;
+ if (aPt0.X()>aBewareRect2.Left() && aPt0.X()<aBewareRect2.Right() &&
+ ((aPt0.Y()<=aBewareRect2.Top() && aPt1b.Y()>=aBewareRect2.Bottom()) ||
+ (aPt1b.Y()<=aBewareRect2.Top() && aPt0.Y()>=aBewareRect2.Bottom()))) nIntersections++;
+ }
+ }
+ aPt0=aPt1b;
+ }
+ if (nPointCount<=1) nIntersections++;
+ nQual0=nQual;
+ nQual+=static_cast<sal_uIntPtr>(nIntersections)*0x10000000;
+ if (nQual<nQual0 || nIntersections>15) bOverflow = true;
+
+ if (bOverflow || nQual==0xFFFFFFFF) nQual=0xFFFFFFFE;
+ *pnQuality=nQual;
+ }
+ if (bInfo) { // now apply line offsets to aXP1
+ if (pInfo->nMiddleLine!=0xFFFF) {
+ sal_uInt16 nIdx=pInfo->ImpGetPolyIdx(SdrEdgeLineCode::MiddleLine,aXP1);
+ if (pInfo->ImpIsHorzLine(SdrEdgeLineCode::MiddleLine,aXP1)) {
+ aXP1[nIdx].AdjustY(pInfo->aMiddleLine.Y() );
+ aXP1[nIdx+1].AdjustY(pInfo->aMiddleLine.Y() );
+ } else {
+ aXP1[nIdx].AdjustX(pInfo->aMiddleLine.X() );
+ aXP1[nIdx+1].AdjustX(pInfo->aMiddleLine.X() );
+ }
+ }
+ if (pInfo->nObj1Lines>=2) {
+ sal_uInt16 nIdx=pInfo->ImpGetPolyIdx(SdrEdgeLineCode::Obj1Line2,aXP1);
+ if (pInfo->ImpIsHorzLine(SdrEdgeLineCode::Obj1Line2,aXP1)) {
+ aXP1[nIdx].AdjustY(pInfo->aObj1Line2.Y() );
+ aXP1[nIdx+1].AdjustY(pInfo->aObj1Line2.Y() );
+ } else {
+ aXP1[nIdx].AdjustX(pInfo->aObj1Line2.X() );
+ aXP1[nIdx+1].AdjustX(pInfo->aObj1Line2.X() );
+ }
+ }
+ if (pInfo->nObj1Lines>=3) {
+ sal_uInt16 nIdx=pInfo->ImpGetPolyIdx(SdrEdgeLineCode::Obj1Line3,aXP1);
+ if (pInfo->ImpIsHorzLine(SdrEdgeLineCode::Obj1Line3,aXP1)) {
+ aXP1[nIdx].AdjustY(pInfo->aObj1Line3.Y() );
+ aXP1[nIdx+1].AdjustY(pInfo->aObj1Line3.Y() );
+ } else {
+ aXP1[nIdx].AdjustX(pInfo->aObj1Line3.X() );
+ aXP1[nIdx+1].AdjustX(pInfo->aObj1Line3.X() );
+ }
+ }
+ if (pInfo->nObj2Lines>=2) {
+ sal_uInt16 nIdx=pInfo->ImpGetPolyIdx(SdrEdgeLineCode::Obj2Line2,aXP1);
+ if (pInfo->ImpIsHorzLine(SdrEdgeLineCode::Obj2Line2,aXP1)) {
+ aXP1[nIdx].AdjustY(pInfo->aObj2Line2.Y() );
+ aXP1[nIdx+1].AdjustY(pInfo->aObj2Line2.Y() );
+ } else {
+ aXP1[nIdx].AdjustX(pInfo->aObj2Line2.X() );
+ aXP1[nIdx+1].AdjustX(pInfo->aObj2Line2.X() );
+ }
+ }
+ if (pInfo->nObj2Lines>=3) {
+ sal_uInt16 nIdx=pInfo->ImpGetPolyIdx(SdrEdgeLineCode::Obj2Line3,aXP1);
+ if (pInfo->ImpIsHorzLine(SdrEdgeLineCode::Obj2Line3,aXP1)) {
+ aXP1[nIdx].AdjustY(pInfo->aObj2Line3.Y() );
+ aXP1[nIdx+1].AdjustY(pInfo->aObj2Line3.Y() );
+ } else {
+ aXP1[nIdx].AdjustX(pInfo->aObj2Line3.X() );
+ aXP1[nIdx+1].AdjustX(pInfo->aObj2Line3.X() );
+ }
+ }
+ }
+ // make the connector a bezier curve, if appropriate
+ if (eKind==SdrEdgeKind::Bezier && nPointCount>2) {
+ Point* pPt1=&aXP1[0];
+ Point* pPt2=&aXP1[1];
+ Point* pPt3=&aXP1[nPointCount-2];
+ Point* pPt4=&aXP1[nPointCount-1];
+ tools::Long dx1=pPt2->X()-pPt1->X();
+ tools::Long dy1=pPt2->Y()-pPt1->Y();
+ tools::Long dx2=pPt3->X()-pPt4->X();
+ tools::Long dy2=pPt3->Y()-pPt4->Y();
+ if (cForm=='L') { // nPointCount==3
+ aXP1.SetFlags(1,PolyFlags::Control);
+ Point aPt3(*pPt2);
+ aXP1.Insert(2,aPt3,PolyFlags::Control);
+ nPointCount=aXP1.GetPointCount();
+ pPt2=&aXP1[1];
+ pPt3=&aXP1[nPointCount-2];
+ pPt2->AdjustX( -(dx1/3) );
+ pPt2->AdjustY( -(dy1/3) );
+ pPt3->AdjustX( -(dx2/3) );
+ pPt3->AdjustY( -(dy2/3) );
+ } else if (nPointCount>=4 && nPointCount<=6) { // Z or U or ...
+ // To all others, the end points of the original lines become control
+ // points for now. Thus, we need to do some more work for nPointCount>4!
+ aXP1.SetFlags(1,PolyFlags::Control);
+ aXP1.SetFlags(nPointCount-2,PolyFlags::Control);
+ // distance x1.5
+ pPt2->AdjustX(dx1/2 );
+ pPt2->AdjustY(dy1/2 );
+ pPt3->AdjustX(dx2/2 );
+ pPt3->AdjustY(dy2/2 );
+ if (nPointCount==5) {
+ // add a control point before and after center
+ Point aCenter(aXP1[2]);
+ tools::Long dx1b=aCenter.X()-aXP1[1].X();
+ tools::Long dy1b=aCenter.Y()-aXP1[1].Y();
+ tools::Long dx2b=aCenter.X()-aXP1[3].X();
+ tools::Long dy2b=aCenter.Y()-aXP1[3].Y();
+ aXP1.Insert(2,aCenter,PolyFlags::Control);
+ aXP1.SetFlags(3,PolyFlags::Symmetric);
+ aXP1.Insert(4,aCenter,PolyFlags::Control);
+ aXP1[2].AdjustX( -(dx1b/2) );
+ aXP1[2].AdjustY( -(dy1b/2) );
+ aXP1[3].AdjustX( -((dx1b+dx2b)/4) );
+ aXP1[3].AdjustY( -((dy1b+dy2b)/4) );
+ aXP1[4].AdjustX( -(dx2b/2) );
+ aXP1[4].AdjustY( -(dy2b/2) );
+ }
+ if (nPointCount==6) {
+ Point aPt1b(aXP1[2]);
+ Point aPt2b(aXP1[3]);
+ aXP1.Insert(2,aPt1b,PolyFlags::Control);
+ aXP1.Insert(5,aPt2b,PolyFlags::Control);
+ tools::Long dx=aPt1b.X()-aPt2b.X();
+ tools::Long dy=aPt1b.Y()-aPt2b.Y();
+ aXP1[3].AdjustX( -(dx/2) );
+ aXP1[3].AdjustY( -(dy/2) );
+ aXP1.SetFlags(3,PolyFlags::Symmetric);
+ aXP1.Remove(4,1); // because it's identical with aXP1[3]
+ }
+ }
+ }
+ return aXP1;
+}
+
+/*
+There could be a maximum of 64 different developments with 5 lines, a
+maximum of 32 developments with 4 lines, a maximum of 16 developments with
+3 lines, a maximum of 8 developments with 2 lines.
+This gives us a total of 124 possibilities.
+Normalized for the 1st exit angle to the right, there remain 31 possibilities.
+Now, normalizing away the vertical mirroring, we get to a total of 16
+characteristic developments with 1 through 5 lines:
+
+1 line (type "I") --
+
+2 lines (type "L") __|
+
+3 lines (type "U") __ (type "Z") _
+ __| _|
+ _ _
+4 lines #1 _| #2 | | #3 |_ #4 | |
+ _| _| _| _|
+ Of these, #1 is implausible, #2 is a rotated version of #3. This leaves
+ #2 (from now on referred to as 4.1) and #4 (from now on referred to as 4.2).
+ _ _
+5 lines #1 _| #2 _| #3 ___ #4 _
+ _| _| _| _| _| |_
+ _ _ _
+ #5 |_ #6 |_ #7 _| | #8 ____
+ _| _| _| |_ _|
+ Of these, 5.1, 5.2, 5.4 and 5.5 are implausible, 5.7 is a reversed version
+ of 5.3. This leaves 5.3 (type "4"), 5.6 (type "S") and 5.8 (type "C").
+
+We now have discerned the 9 basic types to cover all 400 possible constellations
+of object positions and exit angles. 4 of the 9 types have got a center
+line (CL). The number of object margins per object varies between 0 and 3:
+
+ CL O1 O2 Note
+"I": n 0 0
+"L": n 0 0
+"U": n 0-1 0-1
+"Z": y 0 0
+4.2: y 0 1 = U+1, respectively 1+U
+4.4: n 0-2 0-2 = Z+1
+"4": y 0 2 = Z+2
+"S": y 1 1 = 1+Z+1
+"C": n 0-3 0-3 = 1+U+1
+*/
+
+void SdrEdgeObj::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
+{
+ const SfxHintId nId = rHint.GetId();
+ bool bDataChg=nId==SfxHintId::DataChanged;
+ bool bDying=nId==SfxHintId::Dying;
+ bool bObj1=aCon1.pObj!=nullptr && aCon1.pObj->GetBroadcaster()==&rBC;
+ bool bObj2=aCon2.pObj!=nullptr && aCon2.pObj->GetBroadcaster()==&rBC;
+ if (bDying && (bObj1 || bObj2)) {
+ // catch Dying, so AttrObj doesn't start broadcasting
+ // about an alleged change of template
+ if (bObj1) aCon1.pObj=nullptr;
+ if (bObj2) aCon2.pObj=nullptr;
+ return;
+ }
+ if ( bObj1 || bObj2 )
+ {
+ bEdgeTrackUserDefined = false;
+ }
+ SdrTextObj::Notify(rBC,rHint);
+ if (nNotifyingCount!=0)return;
+
+// a locking flag
+ nNotifyingCount++;
+ const SdrHint* pSdrHint = ( rHint.GetId() == SfxHintId::ThisIsAnSdrHint ? static_cast<const SdrHint*>(&rHint) : nullptr );
+
+ if (bDataChg) { // StyleSheet changed
+ ImpSetAttrToEdgeInfo(); // when changing templates, copy values from Pool to aEdgeInfo
+ }
+ if (bDataChg ||
+ (bObj1 && aCon1.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject()) ||
+ (bObj2 && aCon2.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject()) ||
+ (pSdrHint && pSdrHint->GetKind()==SdrHintKind::ObjectRemoved))
+ {
+ // broadcasting only, if on the same page
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetCurrentBoundRect();
+ ImpDirtyEdgeTrack();
+
+ // only redraw here, object hasn't actually changed
+ ActionChanged();
+
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+ }
+ nNotifyingCount--;
+}
+
+/** updates edges that are connected to the edges of this object
+ as if the connected objects sent a repaint broadcast
+*/
+void SdrEdgeObj::Reformat()
+{
+ if( nullptr != aCon1.pObj )
+ {
+ SfxHint aHint( SfxHintId::DataChanged );
+ Notify( *const_cast<SfxBroadcaster*>(aCon1.pObj->GetBroadcaster()), aHint );
+ }
+
+ if( nullptr != aCon2.pObj )
+ {
+ SfxHint aHint( SfxHintId::DataChanged );
+ Notify( *const_cast<SfxBroadcaster*>(aCon2.pObj->GetBroadcaster()), aHint );
+ }
+}
+
+SdrEdgeObj* SdrEdgeObj::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new SdrEdgeObj(rTargetModel, *this);
+}
+
+OUString SdrEdgeObj::TakeObjNameSingul() const
+{
+ OUString sName(SvxResId(STR_ObjNameSingulEDGE));
+
+ OUString aName(GetName());
+ if (!aName.isEmpty())
+ sName += " '" + aName + "'";
+ return sName;
+}
+
+OUString SdrEdgeObj::TakeObjNamePlural() const
+{
+ return SvxResId(STR_ObjNamePluralEDGE);
+}
+
+basegfx::B2DPolyPolygon SdrEdgeObj::TakeXorPoly() const
+{
+ basegfx::B2DPolyPolygon aPolyPolygon;
+
+ if (bEdgeTrackDirty)
+ {
+ const_cast<SdrEdgeObj*>(this)->ImpRecalcEdgeTrack();
+ }
+
+ if(pEdgeTrack)
+ {
+ aPolyPolygon.append(pEdgeTrack->getB2DPolygon());
+ }
+
+ return aPolyPolygon;
+}
+
+void SdrEdgeObj::SetEdgeTrackPath( const basegfx::B2DPolyPolygon& rPoly )
+{
+ if ( !rPoly.count() )
+ {
+ bEdgeTrackDirty = true;
+ bEdgeTrackUserDefined = false;
+ }
+ else
+ {
+ *pEdgeTrack = XPolygon( rPoly.getB2DPolygon( 0 ) );
+ bEdgeTrackDirty = false;
+ bEdgeTrackUserDefined = true;
+
+ // #i110629# also set aRect and maSnapeRect depending on pEdgeTrack
+ const tools::Rectangle aPolygonBounds(pEdgeTrack->GetBoundRect());
+ maRect = aPolygonBounds;
+ maSnapRect = aPolygonBounds;
+ }
+}
+
+basegfx::B2DPolyPolygon SdrEdgeObj::GetEdgeTrackPath() const
+{
+ basegfx::B2DPolyPolygon aPolyPolygon;
+
+ if (bEdgeTrackDirty)
+ const_cast<SdrEdgeObj*>(this)->ImpRecalcEdgeTrack();
+
+ aPolyPolygon.append( pEdgeTrack->getB2DPolygon() );
+
+ return aPolyPolygon;
+}
+
+sal_uInt32 SdrEdgeObj::GetHdlCount() const
+{
+ SdrEdgeKind eKind=GetObjectItem(SDRATTR_EDGEKIND).GetValue();
+ sal_uInt32 nHdlCnt(0);
+ sal_uInt32 nPointCount(pEdgeTrack->GetPointCount());
+
+ if(nPointCount)
+ {
+ nHdlCnt = 2;
+ if ((eKind==SdrEdgeKind::OrthoLines || eKind==SdrEdgeKind::Bezier) && nPointCount >= 4)
+ {
+ sal_uInt32 nO1(aEdgeInfo.nObj1Lines > 0 ? aEdgeInfo.nObj1Lines - 1 : 0);
+ sal_uInt32 nO2(aEdgeInfo.nObj2Lines > 0 ? aEdgeInfo.nObj2Lines - 1 : 0);
+ sal_uInt32 nM(aEdgeInfo.nMiddleLine != 0xFFFF ? 1 : 0);
+ nHdlCnt += nO1 + nO2 + nM;
+ }
+ else if (eKind==SdrEdgeKind::ThreeLines && nPointCount == 4)
+ {
+ if(GetConnectedNode(true))
+ nHdlCnt++;
+
+ if(GetConnectedNode(false))
+ nHdlCnt++;
+ }
+ }
+
+ return nHdlCnt;
+}
+
+void SdrEdgeObj::AddToHdlList(SdrHdlList& rHdlList) const
+{
+ sal_uInt32 nPointCount(pEdgeTrack->GetPointCount());
+ if (nPointCount==0)
+ return;
+
+ {
+ std::unique_ptr<SdrHdl> pHdl(new ImpEdgeHdl((*pEdgeTrack)[0],SdrHdlKind::Poly));
+ if (aCon1.pObj!=nullptr && aCon1.bBestVertex) pHdl->Set1PixMore();
+ pHdl->SetPointNum(0);
+ rHdlList.AddHdl(std::move(pHdl));
+ }
+ {
+ std::unique_ptr<SdrHdl> pHdl(new ImpEdgeHdl((*pEdgeTrack)[sal_uInt16(nPointCount-1)],SdrHdlKind::Poly));
+ if (aCon2.pObj!=nullptr && aCon2.bBestVertex) pHdl->Set1PixMore();
+ pHdl->SetPointNum(1);
+ rHdlList.AddHdl(std::move(pHdl));
+ }
+ {
+ SdrEdgeKind eKind=GetObjectItem(SDRATTR_EDGEKIND).GetValue();
+ if ((eKind==SdrEdgeKind::OrthoLines || eKind==SdrEdgeKind::Bezier) && nPointCount >= 4)
+ {
+ sal_uInt32 nO1(aEdgeInfo.nObj1Lines > 0 ? aEdgeInfo.nObj1Lines - 1 : 0);
+ sal_uInt32 nO2(aEdgeInfo.nObj2Lines > 0 ? aEdgeInfo.nObj2Lines - 1 : 0);
+ sal_uInt32 nM(aEdgeInfo.nMiddleLine != 0xFFFF ? 1 : 0);
+ for(sal_uInt32 i = 0; i < (nO1 + nO2 + nM); ++i)
+ {
+ sal_Int32 nPt(0);
+ sal_uInt32 nNum = i;
+ std::unique_ptr<ImpEdgeHdl> pHdl(new ImpEdgeHdl(Point(),SdrHdlKind::Poly));
+ if (nNum<nO1) {
+ nPt=nNum+1;
+ if (nNum==0) pHdl->SetLineCode(SdrEdgeLineCode::Obj1Line2);
+ if (nNum==1) pHdl->SetLineCode(SdrEdgeLineCode::Obj1Line3);
+ } else {
+ nNum=nNum-nO1;
+ if (nNum<nO2) {
+ nPt=nPointCount-3-nNum;
+ if (nNum==0) pHdl->SetLineCode(SdrEdgeLineCode::Obj2Line2);
+ if (nNum==1) pHdl->SetLineCode(SdrEdgeLineCode::Obj2Line3);
+ } else {
+ nNum=nNum-nO2;
+ if (nNum<nM) {
+ nPt=aEdgeInfo.nMiddleLine;
+ pHdl->SetLineCode(SdrEdgeLineCode::MiddleLine);
+ }
+ }
+ }
+ if (nPt>0) {
+ Point aPos((*pEdgeTrack)[static_cast<sal_uInt16>(nPt)]);
+ aPos+=(*pEdgeTrack)[static_cast<sal_uInt16>(nPt)+1];
+ aPos.setX( aPos.X() / 2 );
+ aPos.setY( aPos.Y() / 2 );
+ pHdl->SetPos(aPos);
+ pHdl->SetPointNum(i + 2);
+ rHdlList.AddHdl(std::move(pHdl));
+ }
+ }
+ }
+ else if (eKind==SdrEdgeKind::ThreeLines && nPointCount == 4)
+ {
+ if(GetConnectedNode(true))
+ {
+ Point aPos((*pEdgeTrack)[1]);
+ std::unique_ptr<ImpEdgeHdl> pHdl(new ImpEdgeHdl(aPos,SdrHdlKind::Poly));
+ pHdl->SetLineCode(SdrEdgeLineCode::Obj1Line2);
+ pHdl->SetPointNum(2);
+ rHdlList.AddHdl(std::move(pHdl));
+ }
+ if(GetConnectedNode(false))
+ {
+ Point aPos((*pEdgeTrack)[2]);
+ std::unique_ptr<ImpEdgeHdl> pHdl(new ImpEdgeHdl(aPos,SdrHdlKind::Poly));
+ pHdl->SetLineCode(SdrEdgeLineCode::Obj2Line2);
+ pHdl->SetPointNum(3);
+ rHdlList.AddHdl(std::move(pHdl));
+ }
+ }
+ }
+}
+
+bool SdrEdgeObj::hasSpecialDrag() const
+{
+ return true;
+}
+
+SdrObjectUniquePtr SdrEdgeObj::getFullDragClone() const
+{
+ // use Clone operator
+ SdrEdgeObj* pRetval(CloneSdrObject(getSdrModelFromSdrObject()));
+
+ // copy connections for clone, SdrEdgeObj::operator= does not do this
+ pRetval->ConnectToNode(true, GetConnectedNode(true));
+ pRetval->ConnectToNode(false, GetConnectedNode(false));
+
+ return SdrObjectUniquePtr(pRetval);
+}
+
+bool SdrEdgeObj::beginSpecialDrag(SdrDragStat& rDrag) const
+{
+ if(!rDrag.GetHdl())
+ return false;
+
+ rDrag.SetEndDragChangesAttributes(true);
+
+ if(rDrag.GetHdl()->GetPointNum() < 2)
+ {
+ rDrag.SetNoSnap();
+ }
+
+ return true;
+}
+
+bool SdrEdgeObj::applySpecialDrag(SdrDragStat& rDragStat)
+{
+ SdrEdgeObj* pOriginalEdge = dynamic_cast< SdrEdgeObj* >(rDragStat.GetHdl()->GetObj());
+ const bool bOriginalEdgeModified(pOriginalEdge == this);
+
+ if(!bOriginalEdgeModified && pOriginalEdge)
+ {
+ // copy connections when clone is modified. This is needed because
+ // as preparation to this modification the data from the original object
+ // was copied to the clone using the operator=. As can be seen there,
+ // that operator does not copy the connections (for good reason)
+ ConnectToNode(true, pOriginalEdge->GetConnection(true).GetObject());
+ ConnectToNode(false, pOriginalEdge->GetConnection(false).GetObject());
+ }
+
+ if(rDragStat.GetHdl()->GetPointNum() < 2)
+ {
+ // start or end point connector drag
+ const bool bDragA(0 == rDragStat.GetHdl()->GetPointNum());
+ const Point aPointNow(rDragStat.GetNow());
+
+ rDragStat.SetEndDragChangesGeoAndAttributes(true);
+
+ if(rDragStat.GetPageView())
+ {
+ SdrObjConnection* pDraggedOne(bDragA ? &aCon1 : &aCon2);
+
+ // clear connection
+ DisconnectFromNode(bDragA);
+
+ // look for new connection
+ ImpFindConnector(aPointNow, *rDragStat.GetPageView(), *pDraggedOne, pOriginalEdge, nullptr, &rDragStat);
+
+ if(pDraggedOne->pObj)
+ {
+ // if found, officially connect to it; ImpFindConnector only
+ // sets pObj hard
+ SdrObject* pNewConnection = pDraggedOne->pObj;
+ pDraggedOne->pObj = nullptr;
+ ConnectToNode(bDragA, pNewConnection);
+ }
+
+ if(rDragStat.GetView() && !bOriginalEdgeModified)
+ {
+ // show IA helper, but only do this during IA, so not when the original
+ // Edge gets modified in the last call
+ rDragStat.GetView()->SetConnectMarker(*pDraggedOne);
+ }
+ }
+
+ if(pEdgeTrack)
+ {
+ // change pEdgeTrack to modified position
+ if(bDragA)
+ {
+ (*pEdgeTrack)[0] = aPointNow;
+ }
+ else
+ {
+ (*pEdgeTrack)[sal_uInt16(pEdgeTrack->GetPointCount()-1)] = aPointNow;
+ }
+ }
+
+ // reset edge info's offsets, this is an end point drag
+ aEdgeInfo.aObj1Line2 = Point();
+ aEdgeInfo.aObj1Line3 = Point();
+ aEdgeInfo.aObj2Line2 = Point();
+ aEdgeInfo.aObj2Line3 = Point();
+ aEdgeInfo.aMiddleLine = Point();
+ }
+ else
+ {
+ // control point connector drag
+ const ImpEdgeHdl* pEdgeHdl = static_cast<const ImpEdgeHdl*>(rDragStat.GetHdl());
+ const SdrEdgeLineCode eLineCode = pEdgeHdl->GetLineCode();
+ const Point aDist(rDragStat.GetNow() - rDragStat.GetStart());
+ sal_Int32 nDist(pEdgeHdl->IsHorzDrag() ? aDist.X() : aDist.Y());
+
+ nDist += aEdgeInfo.ImpGetLineOffset(eLineCode, *pEdgeTrack);
+ aEdgeInfo.ImpSetLineOffset(eLineCode, *pEdgeTrack, nDist);
+ }
+
+ // force recalculation of EdgeTrack
+ *pEdgeTrack = ImpCalcEdgeTrack(*pEdgeTrack, aCon1, aCon2, &aEdgeInfo);
+ bEdgeTrackDirty=false;
+
+ // save EdgeInfos and mark object as user modified
+ ImpSetEdgeInfoToAttr();
+ bEdgeTrackUserDefined = false;
+
+ SetBoundAndSnapRectsDirty();
+
+ if(bOriginalEdgeModified && rDragStat.GetView())
+ {
+ // hide connect marker helper again when original gets changed.
+ // This happens at the end of the interaction
+ rDragStat.GetView()->HideConnectMarker();
+ }
+
+ return true;
+}
+
+OUString SdrEdgeObj::getSpecialDragComment(const SdrDragStat& rDrag) const
+{
+ const bool bCreateComment(rDrag.GetView() && this == rDrag.GetView()->GetCreateObj());
+
+ if(bCreateComment)
+ {
+ return OUString();
+ }
+ else
+ {
+ return ImpGetDescriptionStr(STR_DragEdgeTail);
+ }
+}
+
+
+basegfx::B2DPolygon SdrEdgeObj::ImplAddConnectorOverlay(const SdrDragMethod& rDragMethod, bool bTail1, bool bTail2, bool bDetail) const
+{
+ basegfx::B2DPolygon aResult;
+
+ if(bDetail)
+ {
+ SdrObjConnection aMyCon1(aCon1);
+ SdrObjConnection aMyCon2(aCon2);
+
+ if (bTail1)
+ {
+ const basegfx::B2DPoint aTemp(rDragMethod.getCurrentTransformation() * basegfx::B2DPoint(aMyCon1.aObjOfs.X(), aMyCon1.aObjOfs.Y()));
+ aMyCon1.aObjOfs.setX( basegfx::fround(aTemp.getX()) );
+ aMyCon1.aObjOfs.setY( basegfx::fround(aTemp.getY()) );
+ }
+
+ if (bTail2)
+ {
+ const basegfx::B2DPoint aTemp(rDragMethod.getCurrentTransformation() * basegfx::B2DPoint(aMyCon2.aObjOfs.X(), aMyCon2.aObjOfs.Y()));
+ aMyCon2.aObjOfs.setX( basegfx::fround(aTemp.getX()) );
+ aMyCon2.aObjOfs.setY( basegfx::fround(aTemp.getY()) );
+ }
+
+ SdrEdgeInfoRec aInfo(aEdgeInfo);
+ XPolygon aXP(ImpCalcEdgeTrack(*pEdgeTrack, aMyCon1, aMyCon2, &aInfo));
+
+ if(aXP.GetPointCount())
+ {
+ aResult = aXP.getB2DPolygon();
+ }
+ }
+ else
+ {
+ Point aPt1((*pEdgeTrack)[0]);
+ Point aPt2((*pEdgeTrack)[sal_uInt16(pEdgeTrack->GetPointCount() - 1)]);
+
+ if (aCon1.pObj && (aCon1.bBestConn || aCon1.bBestVertex))
+ aPt1 = aCon1.pObj->GetSnapRect().Center();
+
+ if (aCon2.pObj && (aCon2.bBestConn || aCon2.bBestVertex))
+ aPt2 = aCon2.pObj->GetSnapRect().Center();
+
+ if (bTail1)
+ {
+ const basegfx::B2DPoint aTemp(rDragMethod.getCurrentTransformation() * basegfx::B2DPoint(aPt1.X(), aPt1.Y()));
+ aPt1.setX( basegfx::fround(aTemp.getX()) );
+ aPt1.setY( basegfx::fround(aTemp.getY()) );
+ }
+
+ if (bTail2)
+ {
+ const basegfx::B2DPoint aTemp(rDragMethod.getCurrentTransformation() * basegfx::B2DPoint(aPt2.X(), aPt2.Y()));
+ aPt2.setX( basegfx::fround(aTemp.getX()) );
+ aPt2.setY( basegfx::fround(aTemp.getY()) );
+ }
+
+ aResult.append(basegfx::B2DPoint(aPt1.X(), aPt1.Y()));
+ aResult.append(basegfx::B2DPoint(aPt2.X(), aPt2.Y()));
+ }
+
+ return aResult;
+}
+
+bool SdrEdgeObj::BegCreate(SdrDragStat& rDragStat)
+{
+ rDragStat.SetNoSnap();
+ pEdgeTrack->SetPointCount(2);
+ (*pEdgeTrack)[0]=rDragStat.GetStart();
+ (*pEdgeTrack)[1]=rDragStat.GetNow();
+ if (rDragStat.GetPageView()!=nullptr) {
+ ImpFindConnector(rDragStat.GetStart(),*rDragStat.GetPageView(),aCon1,this);
+ ConnectToNode(true,aCon1.pObj);
+ }
+ *pEdgeTrack=ImpCalcEdgeTrack(*pEdgeTrack,aCon1,aCon2,&aEdgeInfo);
+ return true;
+}
+
+bool SdrEdgeObj::MovCreate(SdrDragStat& rDragStat)
+{
+ sal_uInt16 nMax=pEdgeTrack->GetPointCount();
+ (*pEdgeTrack)[nMax-1]=rDragStat.GetNow();
+ if (rDragStat.GetPageView()!=nullptr) {
+ ImpFindConnector(rDragStat.GetNow(),*rDragStat.GetPageView(),aCon2,this);
+ rDragStat.GetView()->SetConnectMarker(aCon2);
+ }
+ SetBoundRectDirty();
+ m_bSnapRectDirty=true;
+ ConnectToNode(false,aCon2.pObj);
+ *pEdgeTrack=ImpCalcEdgeTrack(*pEdgeTrack,aCon1,aCon2,&aEdgeInfo);
+ bEdgeTrackDirty=false;
+ return true;
+}
+
+bool SdrEdgeObj::EndCreate(SdrDragStat& rDragStat, SdrCreateCmd eCmd)
+{
+ bool bOk=(eCmd==SdrCreateCmd::ForceEnd || rDragStat.GetPointCount()>=2);
+ if (bOk) {
+ ConnectToNode(true,aCon1.pObj);
+ ConnectToNode(false,aCon2.pObj);
+ if (rDragStat.GetView()!=nullptr) {
+ rDragStat.GetView()->HideConnectMarker();
+ }
+ ImpSetEdgeInfoToAttr(); // copy values from aEdgeInfo into the pool
+ }
+ SetBoundAndSnapRectsDirty();
+ return bOk;
+}
+
+bool SdrEdgeObj::BckCreate(SdrDragStat& rDragStat)
+{
+ if (rDragStat.GetView()!=nullptr) {
+ rDragStat.GetView()->HideConnectMarker();
+ }
+ return false;
+}
+
+void SdrEdgeObj::BrkCreate(SdrDragStat& rDragStat)
+{
+ if (rDragStat.GetView()!=nullptr) {
+ rDragStat.GetView()->HideConnectMarker();
+ }
+}
+
+basegfx::B2DPolyPolygon SdrEdgeObj::TakeCreatePoly(const SdrDragStat& /*rStatDrag*/) const
+{
+ basegfx::B2DPolyPolygon aRetval;
+ aRetval.append(pEdgeTrack->getB2DPolygon());
+ return aRetval;
+}
+
+PointerStyle SdrEdgeObj::GetCreatePointer() const
+{
+ return PointerStyle::DrawConnect;
+}
+
+bool SdrEdgeObj::ImpFindConnector(const Point& rPt, const SdrPageView& rPV, SdrObjConnection& rCon, const SdrEdgeObj* pThis, OutputDevice* pOut, SdrDragStat* pDragStat)
+{
+ rCon.ResetVars();
+ if (pOut==nullptr) pOut=rPV.GetView().GetFirstOutputDevice();
+ if (pOut==nullptr) return false;
+ SdrObjList* pOL=rPV.GetObjList();
+ const SdrLayerIDSet& rVisLayer=rPV.GetVisibleLayers();
+ // sensitive area of connectors is twice as large as the one of the handles
+ sal_uInt16 nMarkHdSiz=rPV.GetView().GetMarkHdlSizePixel();
+ Size aHalfConSiz(nMarkHdSiz,nMarkHdSiz);
+ if (comphelper::LibreOfficeKit::isActive() && pOut->GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
+ aHalfConSiz=pOut->PixelToLogic(aHalfConSiz, MapMode(MapUnit::Map100thMM));
+ else
+ aHalfConSiz=pOut->PixelToLogic(aHalfConSiz);
+ tools::Rectangle aMouseRect(rPt,rPt);
+ aMouseRect.AdjustLeft( -(aHalfConSiz.Width()) );
+ aMouseRect.AdjustTop( -(aHalfConSiz.Height()) );
+ aMouseRect.AdjustRight(aHalfConSiz.Width() );
+ aMouseRect.AdjustBottom(aHalfConSiz.Height() );
+ sal_uInt16 nBoundHitTol=static_cast<sal_uInt16>(aHalfConSiz.Width())/2; if (nBoundHitTol==0) nBoundHitTol=1;
+ size_t no=pOL->GetObjCount();
+ bool bFnd = false;
+ SdrObjConnection aTestCon;
+ bool bTiledRendering = comphelper::LibreOfficeKit::isActive();
+ bool bHasRequestedOrdNum = false;
+ sal_Int32 requestedOrdNum = -1;
+
+ if (bTiledRendering && pDragStat)
+ {
+ auto& glueOptions = pDragStat->GetGlueOptions();
+ if (glueOptions.objectOrdNum != -1)
+ {
+ requestedOrdNum = glueOptions.objectOrdNum;
+ bHasRequestedOrdNum = true;
+ }
+ }
+
+ while (no>0 && !bFnd) {
+ // issue: group objects on different layers return LayerID=0!
+ no--;
+ SdrObject* pObj=pOL->GetObj(no);
+ if (bHasRequestedOrdNum)
+ {
+ if (pObj->GetOrdNumDirect() != static_cast<sal_uInt32>(requestedOrdNum))
+ continue;
+ }
+ if (rVisLayer.IsSet(pObj->GetLayer()) && pObj->IsVisible() && // only visible objects
+ (pThis==nullptr || pObj!=static_cast<SdrObject const *>(pThis))) // don't connect it to itself
+ {
+ tools::Rectangle aObjBound(pObj->GetCurrentBoundRect());
+ if (aObjBound.Overlaps(aMouseRect)) {
+ aTestCon.ResetVars();
+ bool bEdge=dynamic_cast<const SdrEdgeObj *>(pObj) != nullptr; // no BestCon for Edge
+ // User-defined connectors have absolute priority.
+ // After those come Vertex, Corner and center (Best), all prioritized equally.
+ // Finally, a HitTest for the object.
+ const SdrGluePointList* pGPL=pObj->GetGluePointList();
+ sal_uInt16 nGluePointCnt=pGPL==nullptr ? 0 : pGPL->GetCount();
+ sal_uInt16 nGesAnz=nGluePointCnt+9;
+ bool bUserFnd = false;
+ sal_uIntPtr nBestDist=0xFFFFFFFF;
+ for (sal_uInt16 i=0; i<nGesAnz; i++)
+ {
+ bool bUser=i<nGluePointCnt;
+ bool bVertex=i>=nGluePointCnt+0 && i<nGluePointCnt+4;
+ bool bCorner=i>=nGluePointCnt+4 && i<nGluePointCnt+8;
+ bool bCenter=i==nGluePointCnt+8;
+ bool bOk = false;
+ Point aConPos;
+ sal_uInt16 nConNum=i;
+ if (bUser) {
+ const SdrGluePoint& rGP=(*pGPL)[nConNum];
+ aConPos=rGP.GetAbsolutePos(*pObj);
+ nConNum=rGP.GetId();
+ bOk = true;
+ } else if (bVertex && !bUserFnd) {
+ nConNum=nConNum-nGluePointCnt;
+ SdrGluePoint aPt(pObj->GetVertexGluePoint(nConNum));
+ aConPos=aPt.GetAbsolutePos(*pObj);
+ bOk = true;
+ } else if (bCorner && !bUserFnd) {
+ nConNum-=nGluePointCnt+4;
+ i+=3;
+ }
+ else if (bCenter && !bUserFnd && !bEdge)
+ {
+ // Suppress default connect at object center
+ if(!pThis || !pThis->GetSuppressDefaultConnect())
+ {
+ // not the edges!
+ nConNum=0;
+ aConPos=aObjBound.Center();
+ bOk = true;
+ }
+ }
+ if (bOk && aMouseRect.Contains(aConPos)) {
+ if (bUser) bUserFnd = true;
+ bFnd = true;
+ sal_uIntPtr nDist=static_cast<sal_uIntPtr>(std::abs(aConPos.X()-rPt.X()))+static_cast<sal_uIntPtr>(std::abs(aConPos.Y()-rPt.Y()));
+ if (nDist<nBestDist) {
+ nBestDist=nDist;
+ aTestCon.pObj=pObj;
+ aTestCon.nConId=nConNum;
+ aTestCon.bAutoCorner=bCorner;
+ aTestCon.bAutoVertex=bVertex;
+ aTestCon.bBestConn=false; // bCenter;
+ aTestCon.bBestVertex=bCenter;
+ }
+ }
+ }
+ // if no connector is hit, try HitTest again, for BestConnector (=bCenter)
+ if(!bFnd &&
+ !bEdge &&
+ SdrObjectPrimitiveHit(*pObj, rPt, nBoundHitTol, rPV, &rVisLayer, false))
+ {
+ // Suppress default connect at object inside bound
+ if(!pThis || !pThis->GetSuppressDefaultConnect())
+ {
+ bFnd = true;
+ aTestCon.pObj=pObj;
+ aTestCon.bBestConn=true;
+ }
+ }
+ if (bFnd) {
+ aMouseRect.AdjustLeft( -nBoundHitTol );
+ aMouseRect.AdjustTop( -nBoundHitTol );
+ aMouseRect.AdjustRight(nBoundHitTol );
+ aMouseRect.AdjustBottom(nBoundHitTol );
+ }
+
+ }
+ }
+ }
+ rCon=aTestCon;
+ return bFnd;
+}
+
+void SdrEdgeObj::NbcSetSnapRect(const tools::Rectangle& rRect)
+{
+ const tools::Rectangle aOld(GetSnapRect());
+
+ if(aOld == rRect)
+ return;
+
+ if (maRect.IsEmpty() && 0 == pEdgeTrack->GetPointCount())
+ {
+ // #i110629# When initializing, do not scale on empty Rectangle; this
+ // will mirror the underlying text object (!)
+ maRect = rRect;
+ maSnapRect = rRect;
+ }
+ else
+ {
+ tools::Long nMulX = rRect.Right() - rRect.Left();
+ tools::Long nDivX = aOld.Right() - aOld.Left();
+ tools::Long nMulY = rRect.Bottom() - rRect.Top();
+ tools::Long nDivY = aOld.Bottom() - aOld.Top();
+ if ( nDivX == 0 ) { nMulX = 1; nDivX = 1; }
+ if ( nDivY == 0 ) { nMulY = 1; nDivY = 1; }
+ Fraction aX(nMulX, nDivX);
+ Fraction aY(nMulY, nDivY);
+ NbcResize(aOld.TopLeft(), aX, aY);
+ NbcMove(Size(rRect.Left() - aOld.Left(), rRect.Top() - aOld.Top()));
+ }
+}
+
+void SdrEdgeObj::NbcMove(const Size& rSiz)
+{
+ SdrTextObj::NbcMove(rSiz);
+ MoveXPoly(*pEdgeTrack,rSiz);
+}
+
+void SdrEdgeObj::NbcResize(const Point& rRefPnt, const Fraction& aXFact, const Fraction& aYFact)
+{
+ SdrTextObj::NbcResize(rRefPnt,aXFact,aXFact);
+ ResizeXPoly(*pEdgeTrack,rRefPnt,aXFact,aYFact);
+
+ // if resize is not from paste, forget user distances
+ if (!getSdrModelFromSdrObject().IsPasteResize())
+ {
+ aEdgeInfo.aObj1Line2 = Point();
+ aEdgeInfo.aObj1Line3 = Point();
+ aEdgeInfo.aObj2Line2 = Point();
+ aEdgeInfo.aObj2Line3 = Point();
+ aEdgeInfo.aMiddleLine = Point();
+ }
+}
+
+// #i54102# added rotation support
+void SdrEdgeObj::NbcRotate(const Point& rRef, Degree100 nAngle, double sn, double cs)
+{
+ if(bEdgeTrackUserDefined)
+ {
+ // #i120437# special handling when track is imported, apply
+ // transformation directly to imported track.
+ SdrTextObj::NbcRotate(rRef, nAngle, sn, cs);
+ RotateXPoly(*pEdgeTrack, rRef, sn, cs);
+ }
+ else
+ {
+ // handle start and end point if not connected
+ const bool bCon1(nullptr != aCon1.pObj && aCon1.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
+ const bool bCon2(nullptr != aCon2.pObj && aCon2.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
+
+ if(!bCon1 && pEdgeTrack)
+ {
+ RotatePoint((*pEdgeTrack)[0],rRef,sn,cs);
+ ImpDirtyEdgeTrack();
+ }
+
+ if(!bCon2 && pEdgeTrack)
+ {
+ sal_uInt16 nPointCount = pEdgeTrack->GetPointCount();
+ RotatePoint((*pEdgeTrack)[sal_uInt16(nPointCount-1)],rRef,sn,cs);
+ ImpDirtyEdgeTrack();
+ }
+ }
+}
+
+// #i54102# added mirror support
+void SdrEdgeObj::NbcMirror(const Point& rRef1, const Point& rRef2)
+{
+ if(bEdgeTrackUserDefined)
+ {
+ // #i120437# special handling when track is imported, apply
+ // transformation directly to imported track.
+ SdrTextObj::NbcMirror(rRef1, rRef2);
+ MirrorXPoly(*pEdgeTrack, rRef1, rRef2);
+ }
+ else
+ {
+ // handle start and end point if not connected
+ const bool bCon1(nullptr != aCon1.pObj && aCon1.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
+ const bool bCon2(nullptr != aCon2.pObj && aCon2.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
+
+ if(!bCon1 && pEdgeTrack)
+ {
+ MirrorPoint((*pEdgeTrack)[0],rRef1,rRef2);
+ ImpDirtyEdgeTrack();
+ }
+
+ if(!bCon2 && pEdgeTrack)
+ {
+ sal_uInt16 nPointCount = pEdgeTrack->GetPointCount();
+ MirrorPoint((*pEdgeTrack)[sal_uInt16(nPointCount-1)],rRef1,rRef2);
+ ImpDirtyEdgeTrack();
+ }
+ }
+}
+
+// #i54102# added shear support
+void SdrEdgeObj::NbcShear(const Point& rRef, Degree100 nAngle, double tn, bool bVShear)
+{
+ if(bEdgeTrackUserDefined)
+ {
+ // #i120437# special handling when track is imported, apply
+ // transformation directly to imported track.
+ SdrTextObj::NbcShear(rRef, nAngle, tn, bVShear);
+ ShearXPoly(*pEdgeTrack, rRef, tn, bVShear);
+ }
+ else
+ {
+ // handle start and end point if not connected
+ const bool bCon1(nullptr != aCon1.pObj && aCon1.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
+ const bool bCon2(nullptr != aCon2.pObj && aCon2.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
+
+ if(!bCon1 && pEdgeTrack)
+ {
+ ShearPoint((*pEdgeTrack)[0],rRef,tn,bVShear);
+ ImpDirtyEdgeTrack();
+ }
+
+ if(!bCon2 && pEdgeTrack)
+ {
+ sal_uInt16 nPointCount = pEdgeTrack->GetPointCount();
+ ShearPoint((*pEdgeTrack)[sal_uInt16(nPointCount-1)],rRef,tn,bVShear);
+ ImpDirtyEdgeTrack();
+ }
+ }
+}
+
+SdrObjectUniquePtr SdrEdgeObj::DoConvertToPolyObj(bool bBezier, bool bAddText) const
+{
+ basegfx::B2DPolyPolygon aPolyPolygon;
+ aPolyPolygon.append(pEdgeTrack->getB2DPolygon());
+ SdrObjectUniquePtr pRet = ImpConvertMakeObj(aPolyPolygon, false, bBezier);
+
+ if(bAddText)
+ {
+ pRet = ImpConvertAddText(std::move(pRet), bBezier);
+ }
+
+ return pRet;
+}
+
+sal_uInt32 SdrEdgeObj::GetSnapPointCount() const
+{
+ return 2;
+}
+
+Point SdrEdgeObj::GetSnapPoint(sal_uInt32 i) const
+{
+ const_cast<SdrEdgeObj*>(this)->ImpUndirtyEdgeTrack();
+ sal_uInt16 nCount=pEdgeTrack->GetPointCount();
+ if (i==0) return (*pEdgeTrack)[0];
+ else return (*pEdgeTrack)[nCount-1];
+}
+
+bool SdrEdgeObj::IsPolyObj() const
+{
+ return false;
+}
+
+sal_uInt32 SdrEdgeObj::GetPointCount() const
+{
+ return 0;
+}
+
+Point SdrEdgeObj::GetPoint(sal_uInt32 i) const
+{
+ const_cast<SdrEdgeObj*>(this)->ImpUndirtyEdgeTrack();
+ sal_uInt16 nCount=pEdgeTrack->GetPointCount();
+ if (0 == i)
+ return (*pEdgeTrack)[0];
+ else
+ return (*pEdgeTrack)[nCount-1];
+}
+
+void SdrEdgeObj::NbcSetPoint(const Point& rPnt, sal_uInt32 i)
+{
+ // TODO: Need an implementation to connect differently.
+ ImpUndirtyEdgeTrack();
+ sal_uInt16 nCount=pEdgeTrack->GetPointCount();
+ if (0 == i)
+ (*pEdgeTrack)[0]=rPnt;
+ if (1 == i)
+ (*pEdgeTrack)[nCount-1]=rPnt;
+ SetEdgeTrackDirty();
+ SetBoundAndSnapRectsDirty();
+}
+
+SdrEdgeObjGeoData::SdrEdgeObjGeoData()
+ : pEdgeTrack(std::in_place)
+ , bEdgeTrackDirty(false)
+ , bEdgeTrackUserDefined(false)
+{
+}
+
+SdrEdgeObjGeoData::~SdrEdgeObjGeoData()
+{
+}
+
+std::unique_ptr<SdrObjGeoData> SdrEdgeObj::NewGeoData() const
+{
+ return std::make_unique<SdrEdgeObjGeoData>();
+}
+
+void SdrEdgeObj::SaveGeoData(SdrObjGeoData& rGeo) const
+{
+ SdrTextObj::SaveGeoData(rGeo);
+ SdrEdgeObjGeoData& rEGeo=static_cast<SdrEdgeObjGeoData&>(rGeo);
+ rEGeo.aCon1 =aCon1;
+ rEGeo.aCon2 =aCon2;
+ *rEGeo.pEdgeTrack =*pEdgeTrack;
+ rEGeo.bEdgeTrackDirty=bEdgeTrackDirty;
+ rEGeo.bEdgeTrackUserDefined=bEdgeTrackUserDefined;
+ rEGeo.aEdgeInfo =aEdgeInfo;
+}
+
+void SdrEdgeObj::RestoreGeoData(const SdrObjGeoData& rGeo)
+{
+ SdrTextObj::RestoreGeoData(rGeo);
+ const SdrEdgeObjGeoData& rEGeo=static_cast<const SdrEdgeObjGeoData&>(rGeo);
+ if (aCon1.pObj!=rEGeo.aCon1.pObj) {
+ if (aCon1.pObj!=nullptr) aCon1.pObj->RemoveListener(*this);
+ aCon1=rEGeo.aCon1;
+ if (aCon1.pObj!=nullptr) aCon1.pObj->AddListener(*this);
+ }
+ else
+ aCon1=rEGeo.aCon1;
+
+ if (aCon2.pObj!=rEGeo.aCon2.pObj) {
+ if (aCon2.pObj!=nullptr) aCon2.pObj->RemoveListener(*this);
+ aCon2=rEGeo.aCon2;
+ if (aCon2.pObj!=nullptr) aCon2.pObj->AddListener(*this);
+ }
+ else
+ aCon2=rEGeo.aCon2;
+
+ *pEdgeTrack =*rEGeo.pEdgeTrack;
+ bEdgeTrackDirty=rEGeo.bEdgeTrackDirty;
+ bEdgeTrackUserDefined=rEGeo.bEdgeTrackUserDefined;
+ aEdgeInfo =rEGeo.aEdgeInfo;
+}
+
+Point SdrEdgeObj::GetTailPoint( bool bTail ) const
+{
+ if( pEdgeTrack && pEdgeTrack->GetPointCount()!=0)
+ {
+ const XPolygon& rTrack0 = *pEdgeTrack;
+ if(bTail)
+ {
+ return rTrack0[0];
+ }
+ else
+ {
+ const sal_uInt16 nSiz = rTrack0.GetPointCount() - 1;
+ return rTrack0[nSiz];
+ }
+ }
+ else
+ {
+ if(bTail)
+ return m_aOutRect.TopLeft();
+ else
+ return m_aOutRect.BottomRight();
+ }
+
+}
+
+void SdrEdgeObj::SetTailPoint( bool bTail, const Point& rPt )
+{
+ ImpSetTailPoint( bTail, rPt );
+ SetChanged();
+}
+
+/** this method is used by the api to set a gluepoint for a connection
+ nId == -1 : The best default point is automatically chosen
+ 0 <= nId <= 3 : One of the default points is chosen
+ nId >= 4 : A user defined gluepoint is chosen
+*/
+void SdrEdgeObj::setGluePointIndex( bool bTail, sal_Int32 nIndex /* = -1 */ )
+{
+ SdrObjConnection& rConn1 = GetConnection( bTail );
+
+ rConn1.SetAutoVertex( nIndex >= 0 && nIndex <= 3 );
+ rConn1.SetBestConnection( nIndex < 0 );
+ rConn1.SetBestVertex( nIndex < 0 );
+
+ if( nIndex > 3 )
+ {
+ nIndex -= 3; // the start api index is 0, whereas the implementation in svx starts from 1
+
+ // for user defined gluepoints we have
+ // to get the id for this index first
+ const SdrGluePointList* pList = rConn1.GetObject() ? rConn1.GetObject()->GetGluePointList() : nullptr;
+ if( pList == nullptr || SDRGLUEPOINT_NOTFOUND == pList->FindGluePoint(static_cast<sal_uInt16>(nIndex)) )
+ return;
+ }
+ else if( nIndex < 0 )
+ {
+ nIndex = 0;
+ }
+
+ rConn1.SetConnectorId( static_cast<sal_uInt16>(nIndex) );
+
+ SetChanged();
+ SetBoundAndSnapRectsDirty();
+ ImpRecalcEdgeTrack();
+}
+
+/** this method is used by the api to return a gluepoint id for a connection.
+ See setGluePointId for possible return values */
+sal_Int32 SdrEdgeObj::getGluePointIndex( bool bTail )
+{
+ SdrObjConnection& rConn1 = GetConnection( bTail );
+ sal_Int32 nId = -1;
+ if( !rConn1.IsBestConnection() )
+ {
+ nId = rConn1.GetConnectorId();
+ if( !rConn1.IsAutoVertex() )
+ nId += 3; // the start api index is 0, whereas the implementation in svx starts from 1
+ }
+ return nId;
+}
+
+// Implementation was missing; edge track needs to be invalidated additionally.
+void SdrEdgeObj::NbcSetAnchorPos(const Point& rPnt)
+{
+ // call parent functionality
+ SdrTextObj::NbcSetAnchorPos(rPnt);
+
+ // Additionally, invalidate edge track
+ ImpDirtyEdgeTrack();
+}
+
+bool SdrEdgeObj::TRGetBaseGeometry(basegfx::B2DHomMatrix& rMatrix, basegfx::B2DPolyPolygon& rPolyPolygon) const
+{
+ // use base method from SdrObject, it's not rotatable and
+ // a call to GetSnapRect() is used. That's what we need for Connector.
+ return SdrObject::TRGetBaseGeometry(rMatrix, rPolyPolygon);
+}
+
+void SdrEdgeObj::TRSetBaseGeometry(const basegfx::B2DHomMatrix& rMatrix, const basegfx::B2DPolyPolygon& rPolyPolygon)
+{
+ // where appropriate take care for existing connections. For now, just use the
+ // implementation from SdrObject.
+ SdrObject::TRSetBaseGeometry(rMatrix, rPolyPolygon);
+}
+
+// for geometry access
+::basegfx::B2DPolygon SdrEdgeObj::getEdgeTrack() const
+{
+ if(bEdgeTrackDirty)
+ {
+ const_cast< SdrEdgeObj* >(this)->ImpRecalcEdgeTrack();
+ }
+
+ if(pEdgeTrack)
+ {
+ return pEdgeTrack->getB2DPolygon();
+ }
+ else
+ {
+ return ::basegfx::B2DPolygon();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdograf.cxx b/svx/source/svdraw/svdograf.cxx
new file mode 100644
index 000000000..c369c5fcc
--- /dev/null
+++ b/svx/source/svdraw/svdograf.cxx
@@ -0,0 +1,1274 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <unotools/streamwrap.hxx>
+
+#include <sfx2/lnkbase.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <tools/helpers.hxx>
+#include <tools/stream.hxx>
+#include <sot/exchange.hxx>
+#include <sot/formats.hxx>
+#include <vcl/GraphicObject.hxx>
+#include <vcl/svapp.hxx>
+
+#include <sfx2/linkmgr.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <svx/svdhdl.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/xbtmpit.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflbmtit.hxx>
+#include "svdfmtf.hxx"
+#include <sdgcoitm.hxx>
+#include <svx/sdgcpitm.hxx>
+#include <svx/sdggaitm.hxx>
+#include <sdginitm.hxx>
+#include <svx/sdgluitm.hxx>
+#include <svx/sdgmoitm.hxx>
+#include <sdgtritm.hxx>
+#include <sdr/properties/graphicproperties.hxx>
+#include <sdr/contact/viewcontactofgraphic.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <drawinglayer/processor2d/objectinfoextractor2d.hxx>
+#include <drawinglayer/primitive2d/objectinfoprimitive2d.hxx>
+#include <memory>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::io;
+
+class SdrGraphicLink : public sfx2::SvBaseLink
+{
+ SdrGrafObj& rGrafObj;
+
+public:
+ explicit SdrGraphicLink(SdrGrafObj& rObj);
+
+ virtual void Closed() override;
+
+ virtual ::sfx2::SvBaseLink::UpdateResult DataChanged(
+ const OUString& rMimeType, const css::uno::Any & rValue ) override;
+
+ void Connect() { GetRealObject(); }
+};
+
+SdrGraphicLink::SdrGraphicLink(SdrGrafObj& rObj)
+: ::sfx2::SvBaseLink( ::SfxLinkUpdateMode::ONCALL, SotClipboardFormatId::SVXB )
+, rGrafObj( rObj )
+{
+ SetSynchron( false );
+}
+
+::sfx2::SvBaseLink::UpdateResult SdrGraphicLink::DataChanged(
+ const OUString& rMimeType, const css::uno::Any & rValue )
+{
+ SdrModel& rModel(rGrafObj.getSdrModelFromSdrObject());
+ sfx2::LinkManager* pLinkManager(rModel.GetLinkManager());
+
+ if( pLinkManager && rValue.hasValue() )
+ {
+ sfx2::LinkManager::GetDisplayNames( this, nullptr, &rGrafObj.aFileName, nullptr, &rGrafObj.aFilterName );
+
+ Graphic aGraphic;
+ if (pLinkManager->GetGraphicFromAny(rMimeType, rValue, aGraphic, nullptr))
+ {
+ rGrafObj.ImpSetLinkedGraphic(aGraphic);
+ }
+ else if( SotExchange::GetFormatIdFromMimeType( rMimeType ) != sfx2::LinkManager::RegisterStatusInfoId() )
+ {
+ // broadcasting, to update slide sorter
+ rGrafObj.BroadcastObjectChange();
+ }
+ }
+ return SUCCESS;
+}
+
+void SdrGraphicLink::Closed()
+{
+ // close connection; set pLink of the object to NULL, as link instance is just about getting destructed.
+ rGrafObj.ForceSwapIn();
+ rGrafObj.pGraphicLink=nullptr;
+ rGrafObj.ReleaseGraphicLink();
+ SvBaseLink::Closed();
+}
+
+std::unique_ptr<sdr::properties::BaseProperties> SdrGrafObj::CreateObjectSpecificProperties()
+{
+ return std::make_unique<sdr::properties::GraphicProperties>(*this);
+}
+
+
+// DrawContact section
+
+std::unique_ptr<sdr::contact::ViewContact> SdrGrafObj::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::ViewContactOfGraphic>(*this);
+}
+
+
+// check if SVG and if try to get ObjectInfoPrimitive2D and extract info
+
+void SdrGrafObj::onGraphicChanged()
+{
+ if (!mpGraphicObject || !mpGraphicObject->GetGraphic().isAvailable())
+ return;
+
+ auto const & rVectorGraphicDataPtr = mpGraphicObject->GetGraphic().getVectorGraphicData();
+
+ if (!rVectorGraphicDataPtr)
+ return;
+
+ // Skip for PDF as it is only a bitmap primitive in a sequence and
+ // doesn't contain metadata. However getting the primitive sequence
+ // will also trigger a premature rendering of the PDF.
+ if (rVectorGraphicDataPtr->getType() == VectorGraphicDataType::Pdf)
+ return;
+
+ const std::deque<css::uno::Reference<css::graphic::XPrimitive2D>>& rContainer(rVectorGraphicDataPtr->getPrimitive2DSequence());
+
+ if (rContainer.empty())
+ return;
+
+ drawinglayer::geometry::ViewInformation2D aViewInformation2D;
+ drawinglayer::processor2d::ObjectInfoPrimitiveExtractor2D aProcessor(aViewInformation2D);
+
+ aProcessor.process(rContainer);
+
+ const drawinglayer::primitive2d::ObjectInfoPrimitive2D* pResult = aProcessor.getResult();
+
+ if (!pResult)
+ return;
+
+ OUString aName = pResult->getName();
+ OUString aTitle = pResult->getTitle();
+ OUString aDesc = pResult->getDesc();
+
+ if(!aName.isEmpty())
+ {
+ SetName(aName);
+ }
+
+ if(!aTitle.isEmpty())
+ {
+ SetTitle(aTitle);
+ }
+
+ if(!aDesc.isEmpty())
+ {
+ SetDescription(aDesc);
+ }
+}
+
+SdrGrafObj::SdrGrafObj(SdrModel& rSdrModel)
+: SdrRectObj(rSdrModel)
+ ,mpGraphicObject(new GraphicObject)
+ ,pGraphicLink(nullptr)
+ ,bMirrored(false)
+ ,mbIsSignatureLine(false)
+ ,mbIsSignatureLineShowSignDate(true)
+ ,mbIsSignatureLineCanAddComment(false)
+ ,mbSignatureLineIsSigned(false)
+{
+ onGraphicChanged();
+
+ // #i118485# Shear allowed and possible now
+ mbNoShear = false;
+
+ mbGrafAnimationAllowed = true;
+
+ // #i25616#
+ mbLineIsOutsideGeometry = true;
+
+ // #i25616#
+ mbSupportTextIndentingOnLineWidthChange = false;
+}
+
+SdrGrafObj::SdrGrafObj(SdrModel& rSdrModel, SdrGrafObj const & rSource)
+: SdrRectObj(rSdrModel, rSource)
+ ,mpGraphicObject(new GraphicObject)
+ ,pGraphicLink(nullptr)
+{
+ onGraphicChanged();
+
+ // #i118485# Shear allowed and possible now
+ mbNoShear = false;
+
+ mbGrafAnimationAllowed = true;
+
+ // #i25616#
+ mbLineIsOutsideGeometry = true;
+
+ // #i25616#
+ mbSupportTextIndentingOnLineWidthChange = false;
+
+ aFileName = rSource.aFileName;
+ bMirrored = rSource.bMirrored;
+
+ mbIsSignatureLine = rSource.mbIsSignatureLine;
+ maSignatureLineId = rSource.maSignatureLineId;
+ maSignatureLineSuggestedSignerName = rSource.maSignatureLineSuggestedSignerName;
+ maSignatureLineSuggestedSignerTitle = rSource.maSignatureLineSuggestedSignerTitle;
+ maSignatureLineSuggestedSignerEmail = rSource.maSignatureLineSuggestedSignerEmail;
+ maSignatureLineSigningInstructions = rSource.maSignatureLineSigningInstructions;
+ mbIsSignatureLineShowSignDate = rSource.mbIsSignatureLineShowSignDate;
+ mbIsSignatureLineCanAddComment = rSource.mbIsSignatureLineCanAddComment;
+ mbSignatureLineIsSigned = false;
+ mpSignatureLineUnsignedGraphic = rSource.mpSignatureLineUnsignedGraphic;
+
+ if(rSource.mpBarCode)
+ {
+ mpBarCode = std::make_unique<css::drawing::BarCode>(*rSource.mpBarCode);
+ }
+ else
+ {
+ mpBarCode.reset();
+ }
+
+ if (mbIsSignatureLine && rSource.mpSignatureLineUnsignedGraphic)
+ mpGraphicObject->SetGraphic(rSource.mpSignatureLineUnsignedGraphic);
+ else
+ mpGraphicObject->SetGraphic( rSource.GetGraphic() );
+
+ if( rSource.IsLinkedGraphic() )
+ {
+ SetGraphicLink( aFileName );
+ }
+
+ ImpSetAttrToGrafInfo();
+}
+
+SdrGrafObj::SdrGrafObj(
+ SdrModel& rSdrModel,
+ const Graphic& rGraphic,
+ const tools::Rectangle& rRect)
+: SdrRectObj(rSdrModel, rRect)
+ ,mpGraphicObject(new GraphicObject(rGraphic))
+ ,pGraphicLink(nullptr)
+ ,bMirrored(false)
+ ,mbIsSignatureLine(false)
+ ,mbIsSignatureLineShowSignDate(true)
+ ,mbIsSignatureLineCanAddComment(false)
+ ,mbSignatureLineIsSigned(false)
+{
+ onGraphicChanged();
+
+ // #i118485# Shear allowed and possible now
+ mbNoShear = false;
+
+ mbGrafAnimationAllowed = true;
+
+ // #i25616#
+ mbLineIsOutsideGeometry = true;
+
+ // #i25616#
+ mbSupportTextIndentingOnLineWidthChange = false;
+}
+
+SdrGrafObj::SdrGrafObj(
+ SdrModel& rSdrModel,
+ const Graphic& rGraphic)
+: SdrRectObj(rSdrModel)
+ ,mpGraphicObject(new GraphicObject(rGraphic))
+ ,pGraphicLink(nullptr)
+ ,bMirrored(false)
+ ,mbIsSignatureLine(false)
+ ,mbIsSignatureLineShowSignDate(true)
+ ,mbIsSignatureLineCanAddComment(false)
+ ,mbSignatureLineIsSigned(false)
+{
+ onGraphicChanged();
+
+ // #i118485# Shear allowed and possible now
+ mbNoShear = false;
+
+ mbGrafAnimationAllowed = true;
+
+ // #i25616#
+ mbLineIsOutsideGeometry = true;
+
+ // #i25616#
+ mbSupportTextIndentingOnLineWidthChange = false;
+}
+
+SdrGrafObj::~SdrGrafObj()
+{
+ ImpDeregisterLink();
+}
+
+void SdrGrafObj::SetGraphicObject(const GraphicObject& rGraphicObject)
+{
+ mpGraphicObject.reset(new GraphicObject(rGraphicObject));
+ mpReplacementGraphicObject.reset();
+ mpGraphicObject->SetUserData();
+ SetChanged();
+ BroadcastObjectChange();
+ onGraphicChanged();
+}
+
+const GraphicObject& SdrGrafObj::GetGraphicObject(bool bForceSwapIn) const
+{
+ if (bForceSwapIn)
+ ForceSwapIn();
+ return *mpGraphicObject;
+}
+
+const GraphicObject* SdrGrafObj::GetReplacementGraphicObject() const
+{
+ if (!mpReplacementGraphicObject && mpGraphicObject)
+ {
+ auto const & rVectorGraphicDataPtr = mpGraphicObject->GetGraphic().getVectorGraphicData();
+
+ if (rVectorGraphicDataPtr)
+ {
+ const_cast< SdrGrafObj* >(this)->mpReplacementGraphicObject.reset(new GraphicObject(rVectorGraphicDataPtr->getReplacement()));
+ }
+ else if (mpGraphicObject->GetGraphic().GetType() == GraphicType::GdiMetafile)
+ {
+ // Replacement graphic for PDF and metafiles is just the bitmap.
+ const_cast<SdrGrafObj*>(this)->mpReplacementGraphicObject.reset(new GraphicObject(mpGraphicObject->GetGraphic().GetBitmapEx()));
+ }
+ }
+
+ return mpReplacementGraphicObject.get();
+}
+
+void SdrGrafObj::NbcSetGraphic(const Graphic& rGraphic)
+{
+ mpGraphicObject->SetGraphic(rGraphic);
+ mpReplacementGraphicObject.reset();
+ mpGraphicObject->SetUserData();
+ onGraphicChanged();
+}
+
+void SdrGrafObj::SetGraphic( const Graphic& rGraphic )
+{
+ if (!rGraphic.getOriginURL().isEmpty())
+ {
+ ImpDeregisterLink();
+ aFileName = rGraphic.getOriginURL();
+ aFilterName = "";
+ }
+ NbcSetGraphic(rGraphic);
+ if (!rGraphic.getOriginURL().isEmpty())
+ {
+ ImpRegisterLink();
+ mpGraphicObject->SetUserData();
+ }
+ SetChanged();
+ BroadcastObjectChange();
+ ForceSwapIn();
+}
+
+const Graphic& SdrGrafObj::GetGraphic() const
+{
+ return mpGraphicObject->GetGraphic();
+}
+
+Graphic SdrGrafObj::GetTransformedGraphic( SdrGrafObjTransformsAttrs nTransformFlags ) const
+{
+ // Refactored most of the code to GraphicObject, where
+ // everybody can use e.g. the cropping functionality
+ MapMode aDestMap(
+ getSdrModelFromSdrObject().GetScaleUnit(),
+ Point(),
+ getSdrModelFromSdrObject().GetScaleFraction(),
+ getSdrModelFromSdrObject().GetScaleFraction());
+ const Size aDestSize( GetLogicRect().GetSize() );
+ GraphicAttr aActAttr = GetGraphicAttr(nTransformFlags);
+
+ // Delegate to moved code in GraphicObject
+ return GetGraphicObject().GetTransformedGraphic( aDestSize, aDestMap, aActAttr );
+}
+
+GraphicType SdrGrafObj::GetGraphicType() const
+{
+ return mpGraphicObject->GetType();
+}
+
+GraphicAttr SdrGrafObj::GetGraphicAttr( SdrGrafObjTransformsAttrs nTransformFlags ) const
+{
+ GraphicAttr aActAttr;
+
+ GraphicType eType = GetGraphicType();
+ if( SdrGrafObjTransformsAttrs::NONE != nTransformFlags &&
+ GraphicType::NONE != eType )
+ {
+ const bool bMirror = bool( nTransformFlags & SdrGrafObjTransformsAttrs::MIRROR );
+ const bool bRotate = bool( nTransformFlags & SdrGrafObjTransformsAttrs::ROTATE ) &&
+ (maGeo.nRotationAngle && maGeo.nRotationAngle != 18000_deg100);
+
+ // Need cropping info earlier
+ const_cast<SdrGrafObj*>(this)->ImpSetAttrToGrafInfo();
+
+ // Actually transform the graphic only in this case.
+ // Cropping always happens, though.
+ aActAttr = aGrafInfo;
+
+ if( bMirror )
+ {
+ sal_uInt16 nMirrorCase = ( maGeo.nRotationAngle == 18000_deg100 ) ? ( bMirrored ? 3 : 4 ) : ( bMirrored ? 2 : 1 );
+ bool bHMirr = nMirrorCase == 2 || nMirrorCase == 4;
+ bool bVMirr = nMirrorCase == 3 || nMirrorCase == 4;
+
+ aActAttr.SetMirrorFlags( ( bHMirr ? BmpMirrorFlags::Horizontal : BmpMirrorFlags::NONE ) | ( bVMirr ? BmpMirrorFlags::Vertical : BmpMirrorFlags::NONE ) );
+ }
+
+ if( bRotate )
+ aActAttr.SetRotation( to<Degree10>(maGeo.nRotationAngle ) );
+ }
+
+ return aActAttr;
+}
+
+bool SdrGrafObj::IsAnimated() const
+{
+ return mpGraphicObject->IsAnimated();
+}
+
+bool SdrGrafObj::IsEPS() const
+{
+ return mpGraphicObject->IsEPS();
+}
+
+MapMode SdrGrafObj::GetGrafPrefMapMode() const
+{
+ return mpGraphicObject->GetPrefMapMode();
+}
+
+Size SdrGrafObj::GetGrafPrefSize() const
+{
+ return mpGraphicObject->GetPrefSize();
+}
+
+void SdrGrafObj::SetGrafStreamURL( const OUString& rGraphicStreamURL )
+{
+ if( rGraphicStreamURL.isEmpty() )
+ {
+ mpGraphicObject->SetUserData();
+ }
+ else if(getSdrModelFromSdrObject().IsSwapGraphics() )
+ {
+ mpGraphicObject->SetUserData( rGraphicStreamURL );
+ }
+}
+
+OUString const & SdrGrafObj::GetGrafStreamURL() const
+{
+ return mpGraphicObject->GetUserData();
+}
+
+Size SdrGrafObj::getOriginalSize() const
+{
+ Size aSize = GetGrafPrefSize();
+
+ if (aGrafInfo.IsCropped())
+ {
+ const tools::Long aCroppedTop(OutputDevice::LogicToLogic(aGrafInfo.GetTopCrop(), getSdrModelFromSdrObject().GetScaleUnit(), GetGrafPrefMapMode().GetMapUnit()));
+ const tools::Long aCroppedBottom(OutputDevice::LogicToLogic(aGrafInfo.GetBottomCrop(), getSdrModelFromSdrObject().GetScaleUnit(), GetGrafPrefMapMode().GetMapUnit()));
+ const tools::Long aCroppedLeft(OutputDevice::LogicToLogic(aGrafInfo.GetLeftCrop(), getSdrModelFromSdrObject().GetScaleUnit(), GetGrafPrefMapMode().GetMapUnit()));
+ const tools::Long aCroppedRight(OutputDevice::LogicToLogic(aGrafInfo.GetRightCrop(), getSdrModelFromSdrObject().GetScaleUnit(), GetGrafPrefMapMode().GetMapUnit()));
+ const tools::Long aCroppedWidth(aSize.getWidth() - aCroppedLeft + aCroppedRight);
+ const tools::Long aCroppedHeight(aSize.getHeight() - aCroppedTop + aCroppedBottom);
+
+ aSize = Size ( aCroppedWidth, aCroppedHeight);
+ }
+
+ if ( GetGrafPrefMapMode().GetMapUnit() == MapUnit::MapPixel )
+ aSize = Application::GetDefaultDevice()->PixelToLogic(aSize, MapMode(getSdrModelFromSdrObject().GetScaleUnit()));
+ else
+ aSize = OutputDevice::LogicToLogic(aSize, GetGrafPrefMapMode(), MapMode(getSdrModelFromSdrObject().GetScaleUnit()));
+
+ return aSize;
+}
+
+// TODO Remove
+void SdrGrafObj::ForceSwapIn() const
+{
+ if (pGraphicLink && (mpGraphicObject->GetType() == GraphicType::NONE ||
+ mpGraphicObject->GetType() == GraphicType::Default) )
+ {
+ pGraphicLink->Update();
+ }
+}
+
+void SdrGrafObj::ImpRegisterLink()
+{
+ sfx2::LinkManager* pLinkManager(getSdrModelFromSdrObject().GetLinkManager());
+
+ if( pLinkManager != nullptr && pGraphicLink == nullptr )
+ {
+ if (!aFileName.isEmpty())
+ {
+ pGraphicLink = new SdrGraphicLink( *this );
+ pLinkManager->InsertFileLink(
+ *pGraphicLink, sfx2::SvBaseLinkObjectType::ClientGraphic, aFileName, (aFilterName.isEmpty() ? nullptr : &aFilterName));
+ pGraphicLink->Connect();
+ }
+ }
+}
+
+void SdrGrafObj::ImpDeregisterLink()
+{
+ sfx2::LinkManager* pLinkManager(getSdrModelFromSdrObject().GetLinkManager());
+
+ if( pLinkManager != nullptr && pGraphicLink!=nullptr)
+ {
+ // When using Remove, the *pGraphicLink is implicitly deleted
+ pLinkManager->Remove( pGraphicLink );
+ pGraphicLink=nullptr;
+ }
+}
+
+void SdrGrafObj::SetGraphicLink(const OUString& rFileName)
+{
+ Graphic aGraphic;
+ aGraphic.setOriginURL(rFileName);
+ SetGraphic(aGraphic);
+}
+
+void SdrGrafObj::ReleaseGraphicLink()
+{
+ ImpDeregisterLink();
+ aFileName.clear();
+ aFilterName.clear();
+
+ auto aGraphic = mpGraphicObject->GetGraphic();
+ aGraphic.setOriginURL("");
+ SetGraphic(aGraphic);
+}
+
+bool SdrGrafObj::IsLinkedGraphic() const
+{
+ return !mpGraphicObject->GetGraphic().getOriginURL().isEmpty();
+}
+
+void SdrGrafObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
+{
+ bool bNoPresGrf = ( mpGraphicObject->GetType() != GraphicType::NONE ) && !m_bEmptyPresObj;
+
+ rInfo.bResizeFreeAllowed = maGeo.nRotationAngle.get() % 9000 == 0 ||
+ maGeo.nRotationAngle.get() % 18000 == 0 ||
+ maGeo.nRotationAngle.get() % 27000 == 0;
+
+ rInfo.bResizePropAllowed = true;
+ rInfo.bRotateFreeAllowed = bNoPresGrf;
+ rInfo.bRotate90Allowed = bNoPresGrf;
+ rInfo.bMirrorFreeAllowed = bNoPresGrf;
+ rInfo.bMirror45Allowed = bNoPresGrf;
+ rInfo.bMirror90Allowed = !m_bEmptyPresObj;
+ rInfo.bTransparenceAllowed = false;
+
+ // #i118485# Shear allowed and possible now
+ rInfo.bShearAllowed = true;
+
+ rInfo.bEdgeRadiusAllowed=false;
+ rInfo.bCanConvToPath = !IsEPS();
+ rInfo.bCanConvToPathLineToArea = false;
+ rInfo.bCanConvToPolyLineToArea = false;
+ rInfo.bCanConvToPoly = !IsEPS();
+ rInfo.bCanConvToContour = (rInfo.bCanConvToPoly || LineGeometryUsageIsNecessary());
+}
+
+SdrObjKind SdrGrafObj::GetObjIdentifier() const
+{
+ return SdrObjKind::Graphic;
+}
+
+void SdrGrafObj::ImpSetLinkedGraphic( const Graphic& rGraphic )
+{
+ const bool bIsChanged(getSdrModelFromSdrObject().IsChanged());
+ NbcSetGraphic( rGraphic );
+ ActionChanged();
+ BroadcastObjectChange();
+ getSdrModelFromSdrObject().SetChanged(bIsChanged);
+}
+
+OUString SdrGrafObj::TakeObjNameSingul() const
+{
+ if (!mpGraphicObject)
+ return OUString();
+
+ auto const & rVectorGraphicDataPtr = mpGraphicObject->GetGraphic().getVectorGraphicData();
+
+ OUStringBuffer sName;
+
+ if (rVectorGraphicDataPtr)
+ {
+ switch (rVectorGraphicDataPtr->getType())
+ {
+ case VectorGraphicDataType::Svg:
+ {
+ sName.append(SvxResId(STR_ObjNameSingulGRAFSVG));
+ break;
+ }
+ case VectorGraphicDataType::Wmf:
+ {
+ sName.append(SvxResId(STR_ObjNameSingulGRAFWMF));
+ break;
+ }
+ case VectorGraphicDataType::Emf:
+ {
+ sName.append(SvxResId(STR_ObjNameSingulGRAFEMF));
+ break;
+ }
+ case VectorGraphicDataType::Pdf:
+ {
+ sName.append(SvxResId(STR_ObjNameSingulGRAFPDF));
+ break;
+ }
+ } //no default, see tdf#137813
+ }
+ else
+ {
+ switch( mpGraphicObject->GetType() )
+ {
+ case GraphicType::Bitmap:
+ {
+ TranslateId pId = ( ( mpGraphicObject->IsTransparent() || GetObjectItem( SDRATTR_GRAFTRANSPARENCE ).GetValue() ) ?
+ ( IsLinkedGraphic() ? STR_ObjNameSingulGRAFBMPTRANSLNK : STR_ObjNameSingulGRAFBMPTRANS ) :
+ ( IsLinkedGraphic() ? STR_ObjNameSingulGRAFBMPLNK : STR_ObjNameSingulGRAFBMP ) );
+
+ sName.append(SvxResId(pId));
+ }
+ break;
+
+ case GraphicType::GdiMetafile:
+ sName.append(SvxResId(IsLinkedGraphic() ? STR_ObjNameSingulGRAFMTFLNK : STR_ObjNameSingulGRAFMTF));
+ break;
+
+ case GraphicType::NONE:
+ sName.append(SvxResId(IsLinkedGraphic() ? STR_ObjNameSingulGRAFNONELNK : STR_ObjNameSingulGRAFNONE));
+ break;
+
+ default:
+ sName.append(SvxResId(IsLinkedGraphic() ? STR_ObjNameSingulGRAFLNK : STR_ObjNameSingulGRAF));
+ break;
+ }
+ }
+
+ const OUString aName(GetName());
+
+ if (!aName.isEmpty())
+ {
+ sName.append(" '");
+ sName.append(aName);
+ sName.append('\'' );
+ }
+
+ return sName.makeStringAndClear();
+}
+
+OUString SdrGrafObj::TakeObjNamePlural() const
+{
+ if (!mpGraphicObject)
+ return OUString();
+
+ auto const & rVectorGraphicDataPtr = mpGraphicObject->GetGraphic().getVectorGraphicData();
+
+ OUStringBuffer sName;
+
+ if (rVectorGraphicDataPtr)
+ {
+ switch (rVectorGraphicDataPtr->getType())
+ {
+ case VectorGraphicDataType::Svg:
+ {
+ sName.append(SvxResId(STR_ObjNamePluralGRAFSVG));
+ break;
+ }
+ case VectorGraphicDataType::Wmf:
+ {
+ sName.append(SvxResId(STR_ObjNamePluralGRAFWMF));
+ break;
+ }
+ case VectorGraphicDataType::Emf:
+ {
+ sName.append(SvxResId(STR_ObjNamePluralGRAFEMF));
+ break;
+ }
+ case VectorGraphicDataType::Pdf:
+ {
+ sName.append(SvxResId(STR_ObjNamePluralGRAFPDF));
+ break;
+ }
+ } //no default, see tdf#137813
+ }
+ else
+ {
+ switch(mpGraphicObject->GetType())
+ {
+ case GraphicType::Bitmap:
+ {
+ TranslateId pId = ( ( mpGraphicObject->IsTransparent() || GetObjectItem( SDRATTR_GRAFTRANSPARENCE ).GetValue() ) ?
+ ( IsLinkedGraphic() ? STR_ObjNamePluralGRAFBMPTRANSLNK : STR_ObjNamePluralGRAFBMPTRANS ) :
+ ( IsLinkedGraphic() ? STR_ObjNamePluralGRAFBMPLNK : STR_ObjNamePluralGRAFBMP ) );
+
+ sName.append(SvxResId(pId));
+ }
+ break;
+
+ case GraphicType::GdiMetafile:
+ sName.append(SvxResId(IsLinkedGraphic() ? STR_ObjNamePluralGRAFMTFLNK : STR_ObjNamePluralGRAFMTF));
+ break;
+
+ case GraphicType::NONE:
+ sName.append(SvxResId(IsLinkedGraphic() ? STR_ObjNamePluralGRAFNONELNK : STR_ObjNamePluralGRAFNONE));
+ break;
+
+ default:
+ sName.append(SvxResId(IsLinkedGraphic() ? STR_ObjNamePluralGRAFLNK : STR_ObjNamePluralGRAF));
+ break;
+ }
+ }
+
+ const OUString aName(GetName());
+
+ if (!aName.isEmpty())
+ {
+ sName.append(" '");
+ sName.append(aName);
+ sName.append('\'');
+ }
+
+ return sName.makeStringAndClear();
+}
+
+SdrObjectUniquePtr SdrGrafObj::getFullDragClone() const
+{
+ // call parent
+ SdrObjectUniquePtr pRetval = SdrRectObj::getFullDragClone();
+
+ // #i103116# the full drag clone leads to problems
+ // with linked graphics, so reset the link in this
+ // temporary interaction object and load graphic
+ if(pRetval && IsLinkedGraphic())
+ {
+ static_cast< SdrGrafObj* >(pRetval.get())->ReleaseGraphicLink();
+ }
+
+ return pRetval;
+}
+
+SdrGrafObj* SdrGrafObj::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new SdrGrafObj(rTargetModel, *this);
+}
+
+sal_uInt32 SdrGrafObj::GetHdlCount() const
+{
+ return 8;
+}
+
+void SdrGrafObj::AddToHdlList(SdrHdlList& rHdlList) const
+{
+ SdrHdlList tempList(nullptr);
+ SdrRectObj::AddToHdlList( tempList );
+ tempList.RemoveHdl(0);
+ tempList.MoveTo(rHdlList);
+}
+
+void SdrGrafObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
+{
+ SdrRectObj::NbcResize( rRef, xFact, yFact );
+
+ bool bMirrX = xFact.GetNumerator() < 0;
+ bool bMirrY = yFact.GetNumerator() < 0;
+
+ if( bMirrX != bMirrY )
+ bMirrored = !bMirrored;
+}
+
+void SdrGrafObj::NbcMirror(const Point& rRef1, const Point& rRef2)
+{
+ SdrRectObj::NbcMirror(rRef1,rRef2);
+ bMirrored = !bMirrored;
+}
+
+std::unique_ptr<SdrObjGeoData> SdrGrafObj::NewGeoData() const
+{
+ return std::make_unique<SdrGrafObjGeoData>();
+}
+
+void SdrGrafObj::SaveGeoData(SdrObjGeoData& rGeo) const
+{
+ SdrRectObj::SaveGeoData(rGeo);
+ SdrGrafObjGeoData& rGGeo=static_cast<SdrGrafObjGeoData&>(rGeo);
+ rGGeo.bMirrored=bMirrored;
+}
+
+void SdrGrafObj::RestoreGeoData(const SdrObjGeoData& rGeo)
+{
+ SdrRectObj::RestoreGeoData(rGeo);
+ const SdrGrafObjGeoData& rGGeo=static_cast<const SdrGrafObjGeoData&>(rGeo);
+ bMirrored=rGGeo.bMirrored;
+}
+
+void SdrGrafObj::handlePageChange(SdrPage* pOldPage, SdrPage* pNewPage)
+{
+ const bool bRemove(pNewPage == nullptr && pOldPage != nullptr);
+ const bool bInsert(pNewPage != nullptr && pOldPage == nullptr);
+
+ if( bRemove )
+ {
+ // No SwapIn necessary here, because if something's not loaded, it can't be animated either.
+ if( mpGraphicObject->IsAnimated())
+ mpGraphicObject->StopAnimation();
+
+ if( pGraphicLink != nullptr )
+ ImpDeregisterLink();
+ }
+
+ // call parent
+ SdrRectObj::handlePageChange(pOldPage, pNewPage);
+
+ if (!aFileName.isEmpty() && bInsert)
+ {
+ ImpRegisterLink();
+ }
+}
+
+void SdrGrafObj::StartAnimation()
+{
+ SetGrafAnimationAllowed(true);
+}
+
+bool SdrGrafObj::HasGDIMetaFile() const
+{
+ return( mpGraphicObject->GetType() == GraphicType::GdiMetafile );
+}
+
+bool SdrGrafObj::isEmbeddedVectorGraphicData() const
+{
+ return GraphicType::Bitmap == GetGraphicType() && GetGraphic().getVectorGraphicData();
+}
+
+GDIMetaFile SdrGrafObj::getMetafileFromEmbeddedVectorGraphicData() const
+{
+ GDIMetaFile aRetval;
+
+ if(isEmbeddedVectorGraphicData())
+ {
+ ScopedVclPtrInstance< VirtualDevice > pOut;
+ const tools::Rectangle aBoundRect(GetCurrentBoundRect());
+ const MapMode aMap(
+ getSdrModelFromSdrObject().GetScaleUnit(),
+ Point(),
+ getSdrModelFromSdrObject().GetScaleFraction(),
+ getSdrModelFromSdrObject().GetScaleFraction());
+
+ pOut->EnableOutput(false);
+ pOut->SetMapMode(aMap);
+ aRetval.Record(pOut);
+ SingleObjectPainter(*pOut);
+ aRetval.Stop();
+ aRetval.WindStart();
+ aRetval.Move(-aBoundRect.Left(), -aBoundRect.Top());
+ aRetval.SetPrefMapMode(aMap);
+ aRetval.SetPrefSize(aBoundRect.GetSize());
+ }
+
+ return aRetval;
+}
+
+GDIMetaFile SdrGrafObj::GetMetaFile(GraphicType &rGraphicType) const
+{
+ if (isEmbeddedVectorGraphicData())
+ {
+ // Embedded Vector Graphic Data
+ // There is currently no helper to create SdrObjects from primitives (even if I'm thinking
+ // about writing one for some time). To get the roundtrip to SdrObjects it is necessary to
+ // use the old converter path over the MetaFile mechanism. Create Metafile from Svg
+ // primitives here pretty directly
+ rGraphicType = GraphicType::GdiMetafile;
+ return getMetafileFromEmbeddedVectorGraphicData();
+ }
+ else if (GraphicType::GdiMetafile == rGraphicType)
+ {
+ return GetTransformedGraphic(SdrGrafObjTransformsAttrs::MIRROR).GetGDIMetaFile();
+ }
+ return GDIMetaFile();
+}
+
+SdrObjectUniquePtr SdrGrafObj::DoConvertToPolyObj(bool bBezier, bool bAddText ) const
+{
+ SdrObject* pRetval = nullptr;
+ GraphicType aGraphicType(GetGraphicType());
+ GDIMetaFile aMtf(GetMetaFile(aGraphicType));
+ switch(aGraphicType)
+ {
+ case GraphicType::GdiMetafile:
+ {
+ // Sort into group and return ONLY those objects that can be created from the MetaFile.
+ ImpSdrGDIMetaFileImport aFilter(
+ getSdrModelFromSdrObject(),
+ GetLayer(),
+ maRect);
+ SdrObjGroup* pGrp = new SdrObjGroup(getSdrModelFromSdrObject());
+
+ if(aFilter.DoImport(aMtf, *pGrp->GetSubList(), 0))
+ {
+ {
+ // copy transformation
+ GeoStat aGeoStat(GetGeoStat());
+
+ if(aGeoStat.nShearAngle)
+ {
+ aGeoStat.RecalcTan();
+ pGrp->NbcShear(maRect.TopLeft(), aGeoStat.nShearAngle, aGeoStat.mfTanShearAngle, false);
+ }
+
+ if(aGeoStat.nRotationAngle)
+ {
+ aGeoStat.RecalcSinCos();
+ pGrp->NbcRotate(maRect.TopLeft(), aGeoStat.nRotationAngle, aGeoStat.mfSinRotationAngle, aGeoStat.mfCosRotationAngle);
+ }
+ }
+
+ pRetval = pGrp;
+ pGrp->NbcSetLayer(GetLayer());
+
+ if(bAddText)
+ {
+ pRetval = ImpConvertAddText(SdrObjectUniquePtr(pRetval), bBezier).release();
+ }
+
+ // convert all children
+ if( pRetval )
+ {
+ SdrObject* pHalfDone = pRetval;
+ pRetval = pRetval->DoConvertToPolyObj(bBezier, bAddText).release();
+ SdrObject::Free( pHalfDone ); // resulting object is newly created
+
+ if( pRetval )
+ {
+ // flatten subgroups. As we call
+ // DoConvertToPolyObj() on the resulting group
+ // objects, subgroups can exist (e.g. text is
+ // a group object for every line).
+ SdrObjList* pList = pRetval->GetSubList();
+ if( pList )
+ pList->FlattenGroups();
+ }
+ }
+ }
+ else
+ {
+ // always use SdrObject::Free(...) for SdrObjects (!)
+ SdrObject* pTemp(pGrp);
+ SdrObject::Free(pTemp);
+ }
+
+ // #i118485# convert line and fill
+ SdrObjectUniquePtr pLineFill = SdrRectObj::DoConvertToPolyObj(bBezier, false);
+
+ if(pLineFill)
+ {
+ if(pRetval)
+ {
+ pGrp = dynamic_cast< SdrObjGroup* >(pRetval);
+
+ if(!pGrp)
+ {
+ pGrp = new SdrObjGroup(getSdrModelFromSdrObject());
+ pGrp->NbcSetLayer(GetLayer());
+ pGrp->GetSubList()->NbcInsertObject(pRetval);
+ }
+
+ pGrp->GetSubList()->NbcInsertObject(pLineFill.release(), 0);
+ }
+ else
+ {
+ pRetval = pLineFill.release();
+ }
+ }
+
+ break;
+ }
+ case GraphicType::Bitmap:
+ {
+ // create basic object and add fill
+ pRetval = SdrRectObj::DoConvertToPolyObj(bBezier, bAddText).release();
+
+ // save bitmap as an attribute
+ if(pRetval)
+ {
+ // retrieve bitmap for the fill
+ SfxItemSet aSet(GetObjectItemSet());
+
+ aSet.Put(XFillStyleItem(drawing::FillStyle_BITMAP));
+ const BitmapEx aBitmapEx(GetTransformedGraphic().GetBitmapEx());
+ aSet.Put(XFillBitmapItem(OUString(), Graphic(aBitmapEx)));
+ aSet.Put(XFillBmpTileItem(false));
+
+ pRetval->SetMergedItemSet(aSet);
+ }
+ break;
+ }
+ case GraphicType::NONE:
+ case GraphicType::Default:
+ {
+ pRetval = SdrRectObj::DoConvertToPolyObj(bBezier, bAddText).release();
+ break;
+ }
+ }
+
+ return SdrObjectUniquePtr(pRetval);
+}
+
+void SdrGrafObj::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+{
+ SetXPolyDirty();
+ SdrRectObj::Notify( rBC, rHint );
+ ImpSetAttrToGrafInfo();
+}
+
+
+void SdrGrafObj::SetMirrored( bool _bMirrored )
+{
+ bMirrored = _bMirrored;
+}
+
+void SdrGrafObj::ImpSetAttrToGrafInfo()
+{
+ const SfxItemSet& rSet = GetObjectItemSet();
+ const sal_uInt16 nTrans = rSet.Get( SDRATTR_GRAFTRANSPARENCE ).GetValue();
+ const SdrGrafCropItem& rCrop = rSet.Get( SDRATTR_GRAFCROP );
+
+ aGrafInfo.SetLuminance( rSet.Get( SDRATTR_GRAFLUMINANCE ).GetValue() );
+ aGrafInfo.SetContrast( rSet.Get( SDRATTR_GRAFCONTRAST ).GetValue() );
+ aGrafInfo.SetChannelR( rSet.Get( SDRATTR_GRAFRED ).GetValue() );
+ aGrafInfo.SetChannelG( rSet.Get( SDRATTR_GRAFGREEN ).GetValue() );
+ aGrafInfo.SetChannelB( rSet.Get( SDRATTR_GRAFBLUE ).GetValue() );
+ aGrafInfo.SetGamma( rSet.Get( SDRATTR_GRAFGAMMA ).GetValue() * 0.01 );
+ aGrafInfo.SetAlpha( 255 - static_cast<sal_uInt8>(FRound( std::min( nTrans, sal_uInt16(100) ) * 2.55 )) );
+ aGrafInfo.SetInvert( rSet.Get( SDRATTR_GRAFINVERT ).GetValue() );
+ aGrafInfo.SetDrawMode( rSet.Get( SDRATTR_GRAFMODE ).GetValue() );
+ aGrafInfo.SetCrop( rCrop.GetLeft(), rCrop.GetTop(), rCrop.GetRight(), rCrop.GetBottom() );
+
+ SetXPolyDirty();
+ SetBoundAndSnapRectsDirty();
+}
+
+void SdrGrafObj::AdjustToMaxRect( const tools::Rectangle& rMaxRect, bool bShrinkOnly )
+{
+ Size aSize;
+ Size aMaxSize( rMaxRect.GetSize() );
+ if (mpGraphicObject->GetPrefMapMode().GetMapUnit() == MapUnit::MapPixel)
+ aSize = Application::GetDefaultDevice()->PixelToLogic(mpGraphicObject->GetPrefSize(), MapMode(MapUnit::Map100thMM));
+ else
+ aSize = OutputDevice::LogicToLogic( mpGraphicObject->GetPrefSize(),
+ mpGraphicObject->GetPrefMapMode(),
+ MapMode( MapUnit::Map100thMM ) );
+
+ if( aSize.IsEmpty() )
+ return;
+
+ Point aPos( rMaxRect.TopLeft() );
+
+ // if the graphic is too large, fit it to page
+ if ( (!bShrinkOnly ||
+ ( aSize.Height() > aMaxSize.Height() ) ||
+ ( aSize.Width() > aMaxSize.Width() ) )&&
+ aSize.Height() && aMaxSize.Height() )
+ {
+ float fGrfWH = static_cast<float>(aSize.Width()) /
+ static_cast<float>(aSize.Height());
+ float fWinWH = static_cast<float>(aMaxSize.Width()) /
+ static_cast<float>(aMaxSize.Height());
+
+ // Scale graphic to page size
+ if ( fGrfWH < fWinWH )
+ {
+ aSize.setWidth( static_cast<tools::Long>(aMaxSize.Height() * fGrfWH) );
+ aSize.setHeight( aMaxSize.Height() );
+ }
+ else if ( fGrfWH > 0.F )
+ {
+ aSize.setWidth( aMaxSize.Width() );
+ aSize.setHeight( static_cast<tools::Long>(aMaxSize.Width() / fGrfWH) );
+ }
+
+ aPos = rMaxRect.Center();
+ }
+
+ if( bShrinkOnly )
+ aPos = maRect.TopLeft();
+
+ aPos.AdjustX( -(aSize.Width() / 2) );
+ aPos.AdjustY( -(aSize.Height() / 2) );
+ SetLogicRect( tools::Rectangle( aPos, aSize ) );
+}
+
+void SdrGrafObj::SetGrafAnimationAllowed(bool bNew)
+{
+ if(mbGrafAnimationAllowed != bNew)
+ {
+ mbGrafAnimationAllowed = bNew;
+ ActionChanged();
+ }
+}
+
+Reference< XInputStream > SdrGrafObj::getInputStream() const
+{
+ Reference< XInputStream > xStream;
+
+ if (mpGraphicObject && GetGraphic().IsGfxLink())
+ {
+ Graphic aGraphic( GetGraphic() );
+ GfxLink aLink( aGraphic.GetGfxLink() );
+ sal_uInt32 nSize = aLink.GetDataSize();
+ const void* pSourceData = static_cast<const void*>(aLink.GetData());
+ if( nSize && pSourceData )
+ {
+ sal_uInt8 * pBuffer = new sal_uInt8[ nSize ];
+ memcpy( pBuffer, pSourceData, nSize );
+
+ SvMemoryStream* pStream = new SvMemoryStream( static_cast<void*>(pBuffer), static_cast<std::size_t>(nSize), StreamMode::READ );
+ pStream->ObjectOwnsMemory( true );
+ xStream.set( new utl::OInputStreamWrapper( pStream, true ) );
+ }
+ }
+
+ if (!xStream.is() && !aFileName.isEmpty())
+ {
+ SvFileStream* pStream = new SvFileStream( aFileName, StreamMode::READ );
+ xStream.set( new utl::OInputStreamWrapper( pStream ) );
+ }
+
+ return xStream;
+}
+
+// moved crop handle creation here; this is the object type using them
+void SdrGrafObj::addCropHandles(SdrHdlList& rTarget) const
+{
+ basegfx::B2DHomMatrix aMatrix;
+ basegfx::B2DPolyPolygon aPolyPolygon;
+
+ // get object transformation
+ TRGetBaseGeometry(aMatrix, aPolyPolygon);
+
+ // part of object transformation correction, but used later, so defined outside next scope
+ double fShearX(0.0), fRotate(0.0);
+
+ { // TTTT correct shear, it comes currently mirrored from TRGetBaseGeometry, can be removed with aw080
+ basegfx::B2DTuple aScale;
+ basegfx::B2DTuple aTranslate;
+
+ aMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
+
+ if(!basegfx::fTools::equalZero(fShearX))
+ {
+ // shearX is used, correct it
+ fShearX = -fShearX;
+ }
+
+ aMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ aScale,
+ fShearX,
+ fRotate,
+ aTranslate);
+ }
+
+ // get crop values
+ const SdrGrafCropItem& rCrop = GetMergedItem(SDRATTR_GRAFCROP);
+
+ if(rCrop.GetLeft() || rCrop.GetTop() || rCrop.GetRight() ||rCrop.GetBottom())
+ {
+ // decompose object transformation to have current translate and scale
+ basegfx::B2DVector aScale, aTranslate;
+ double fLclRotate, fLclShearX;
+
+ aMatrix.decompose(aScale, aTranslate, fLclRotate, fLclShearX);
+
+ if(!aScale.equalZero())
+ {
+ // get crop scale
+ const basegfx::B2DVector aCropScaleFactor(
+ GetGraphicObject().calculateCropScaling(
+ aScale.getX(),
+ aScale.getY(),
+ rCrop.GetLeft(),
+ rCrop.GetTop(),
+ rCrop.GetRight(),
+ rCrop.GetBottom()));
+
+ // apply crop scale
+ const double fCropLeft(rCrop.GetLeft() * aCropScaleFactor.getX());
+ const double fCropTop(rCrop.GetTop() * aCropScaleFactor.getY());
+ const double fCropRight(rCrop.GetRight() * aCropScaleFactor.getX());
+ const double fCropBottom(rCrop.GetBottom() * aCropScaleFactor.getY());
+ basegfx::B2DHomMatrix aMatrixForCropViewHdl(aMatrix);
+
+ if(IsMirrored())
+ {
+ // create corrected new matrix, TTTT can be removed with aw080
+ // the old mirror only can mirror horizontally; the vertical mirror
+ // is faked by using the horizontal and 180 degree rotation. Since
+ // the object can be rotated differently from 180 degree, this is
+ // not safe to detect. Just correct horizontal mirror (which is
+ // in IsMirrored()) and keep the rotation angle
+ // caution: Do not modify aMatrix, it is used below to calculate
+ // the exact handle positions
+ basegfx::B2DHomMatrix aPreMultiply;
+
+ // mirrored X, apply
+ aPreMultiply.translate(-0.5, 0.0);
+ aPreMultiply.scale(-1.0, 1.0);
+ aPreMultiply.translate(0.5, 0.0);
+
+ aMatrixForCropViewHdl = aMatrixForCropViewHdl * aPreMultiply;
+ }
+
+ rTarget.AddHdl(
+ std::make_unique<SdrCropViewHdl>(
+ aMatrixForCropViewHdl,
+ GetGraphicObject().GetGraphic(),
+ fCropLeft,
+ fCropTop,
+ fCropRight,
+ fCropBottom));
+ }
+ }
+
+ basegfx::B2DPoint aPos;
+
+ aPos = aMatrix * basegfx::B2DPoint(0.0, 0.0);
+ rTarget.AddHdl(std::make_unique<SdrCropHdl>(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::UpperLeft, fShearX, fRotate));
+ aPos = aMatrix * basegfx::B2DPoint(0.5, 0.0);
+ rTarget.AddHdl(std::make_unique<SdrCropHdl>(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::Upper, fShearX, fRotate));
+ aPos = aMatrix * basegfx::B2DPoint(1.0, 0.0);
+ rTarget.AddHdl(std::make_unique<SdrCropHdl>(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::UpperRight, fShearX, fRotate));
+ aPos = aMatrix * basegfx::B2DPoint(0.0, 0.5);
+ rTarget.AddHdl(std::make_unique<SdrCropHdl>(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::Left , fShearX, fRotate));
+ aPos = aMatrix * basegfx::B2DPoint(1.0, 0.5);
+ rTarget.AddHdl(std::make_unique<SdrCropHdl>(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::Right, fShearX, fRotate));
+ aPos = aMatrix * basegfx::B2DPoint(0.0, 1.0);
+ rTarget.AddHdl(std::make_unique<SdrCropHdl>(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::LowerLeft, fShearX, fRotate));
+ aPos = aMatrix * basegfx::B2DPoint(0.5, 1.0);
+ rTarget.AddHdl(std::make_unique<SdrCropHdl>(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::Lower, fShearX, fRotate));
+ aPos = aMatrix * basegfx::B2DPoint(1.0, 1.0);
+ rTarget.AddHdl(std::make_unique<SdrCropHdl>(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::LowerRight, fShearX, fRotate));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdogrp.cxx b/svx/source/svdraw/svdogrp.cxx
new file mode 100644
index 000000000..7fc8e0b12
--- /dev/null
+++ b/svx/source/svdraw/svdogrp.cxx
@@ -0,0 +1,835 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/svdogrp.hxx>
+
+#include <svx/svdmodel.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdtrans.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+
+#include <sdr/properties/groupproperties.hxx>
+#include <sdr/contact/viewcontactofgroup.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <libxml/xmlwriter.h>
+#include <vcl/canvastools.hxx>
+#include <svx/diagram/IDiagramHelper.hxx>
+
+const std::shared_ptr< svx::diagram::IDiagramHelper >& SdrObjGroup::getDiagramHelper() const
+{
+ return mp_DiagramHelper;
+}
+
+// BaseProperties section
+std::unique_ptr<sdr::properties::BaseProperties> SdrObjGroup::CreateObjectSpecificProperties()
+{
+ return std::make_unique<sdr::properties::GroupProperties>(*this);
+}
+
+// DrawContact section
+std::unique_ptr<sdr::contact::ViewContact> SdrObjGroup::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::ViewContactOfGroup>(*this);
+}
+
+SdrObjGroup::SdrObjGroup(SdrModel& rSdrModel)
+: SdrObject(rSdrModel)
+, SdrObjList()
+, maRefPoint(0, 0)
+, mp_DiagramHelper()
+{
+ m_bClosedObj=false;
+}
+
+SdrObjGroup::SdrObjGroup(SdrModel& rSdrModel, SdrObjGroup const & rSource)
+: SdrObject(rSdrModel, rSource)
+, SdrObjList()
+, maRefPoint(0, 0)
+, mp_DiagramHelper()
+{
+ m_bClosedObj=false;
+
+ // copy child SdrObjects
+ if(nullptr != rSource.GetSubList())
+ {
+ // #i36404# Copy SubList, init model and page first
+ const SdrObjList& rSourceSubList(*rSource.GetSubList());
+
+ CopyObjects(rSourceSubList);
+
+ // tdf#116979: needed here, we need bSnapRectDirty to be true
+ // which it is after using SdrObject::operator= (see above),
+ // but set to false again using CopyObjects
+ SetBoundAndSnapRectsDirty();
+ }
+
+ // copy local parameters
+ maRefPoint = rSource.maRefPoint;
+}
+
+void SdrObjGroup::AddToHdlList(SdrHdlList& rHdlList) const
+{
+ // only for diagram, so do nothing for just groups
+ if(!isDiagram())
+ return;
+
+ svx::diagram::IDiagramHelper::AddAdditionalVisualization(*this, rHdlList);
+}
+
+SdrObjGroup::~SdrObjGroup()
+{
+}
+
+SdrPage* SdrObjGroup::getSdrPageFromSdrObjList() const
+{
+ return getSdrPageFromSdrObject();
+}
+
+SdrObject* SdrObjGroup::getSdrObjectFromSdrObjList() const
+{
+ return const_cast< SdrObjGroup* >(this);
+}
+
+SdrObjList* SdrObjGroup::getChildrenOfSdrObject() const
+{
+ return const_cast< SdrObjGroup* >(this);
+}
+
+void SdrObjGroup::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
+{
+ rInfo.bNoContortion=false;
+ const size_t nObjCount(GetObjCount());
+ for (size_t i=0; i<nObjCount; ++i) {
+ SdrObject* pObj(GetObj(i));
+ SdrObjTransformInfoRec aInfo;
+ pObj->TakeObjInfo(aInfo);
+ if (!aInfo.bMoveAllowed ) rInfo.bMoveAllowed =false;
+ if (!aInfo.bResizeFreeAllowed ) rInfo.bResizeFreeAllowed =false;
+ if (!aInfo.bResizePropAllowed ) rInfo.bResizePropAllowed =false;
+ if (!aInfo.bRotateFreeAllowed ) rInfo.bRotateFreeAllowed =false;
+ if (!aInfo.bRotate90Allowed ) rInfo.bRotate90Allowed =false;
+ if (!aInfo.bMirrorFreeAllowed ) rInfo.bMirrorFreeAllowed =false;
+ if (!aInfo.bMirror45Allowed ) rInfo.bMirror45Allowed =false;
+ if (!aInfo.bMirror90Allowed ) rInfo.bMirror90Allowed =false;
+ if (!aInfo.bShearAllowed ) rInfo.bShearAllowed =false;
+ if (!aInfo.bEdgeRadiusAllowed ) rInfo.bEdgeRadiusAllowed =false;
+ if (!aInfo.bNoOrthoDesired ) rInfo.bNoOrthoDesired =false;
+ if (aInfo.bNoContortion ) rInfo.bNoContortion =true;
+ if (!aInfo.bCanConvToPath ) rInfo.bCanConvToPath =false;
+
+ if(!aInfo.bCanConvToContour)
+ rInfo.bCanConvToContour = false;
+
+ if (!aInfo.bCanConvToPoly ) rInfo.bCanConvToPoly =false;
+ if (!aInfo.bCanConvToPathLineToArea) rInfo.bCanConvToPathLineToArea=false;
+ if (!aInfo.bCanConvToPolyLineToArea) rInfo.bCanConvToPolyLineToArea=false;
+ }
+ if (nObjCount==0) {
+ rInfo.bRotateFreeAllowed=false;
+ rInfo.bRotate90Allowed =false;
+ rInfo.bMirrorFreeAllowed=false;
+ rInfo.bMirror45Allowed =false;
+ rInfo.bMirror90Allowed =false;
+ rInfo.bTransparenceAllowed = false;
+ rInfo.bShearAllowed =false;
+ rInfo.bEdgeRadiusAllowed=false;
+ rInfo.bNoContortion =true;
+ }
+ if(nObjCount != 1)
+ {
+ // only allowed if single object selected
+ rInfo.bTransparenceAllowed = false;
+ }
+}
+
+void SdrObjGroup::SetBoundRectDirty()
+{
+ // avoid resetting aOutRect which in case of this object is model data,
+ // not re-creatable view data
+}
+
+SdrObjKind SdrObjGroup::GetObjIdentifier() const
+{
+ return SdrObjKind::Group;
+}
+
+SdrLayerID SdrObjGroup::GetLayer() const
+{
+ bool b1st = true;
+ SdrLayerID nLay = SdrObject::GetLayer();
+ const size_t nObjCount(GetObjCount());
+ for (size_t i=0; i<nObjCount; ++i) {
+ SdrLayerID nLay1(GetObj(i)->GetLayer());
+ if (b1st) { nLay=nLay1; b1st = false; }
+ else if (nLay1!=nLay) return SdrLayerID(0);
+ }
+ return nLay;
+}
+
+void SdrObjGroup::NbcSetLayer(SdrLayerID nLayer)
+{
+ SdrObject::NbcSetLayer(nLayer);
+ const size_t nObjCount(GetObjCount());
+ for (size_t i=0; i<nObjCount; ++i) {
+ GetObj(i)->NbcSetLayer(nLayer);
+ }
+}
+
+void SdrObjGroup::handlePageChange(SdrPage* pOldPage, SdrPage* pNewPage)
+{
+ // call parent
+ SdrObject::handlePageChange(pOldPage, pNewPage);
+
+ for(size_t i(0); i < GetObjCount(); i++)
+ {
+ GetObj(i)->handlePageChange(pOldPage, pNewPage);
+ }
+}
+
+SdrObjList* SdrObjGroup::GetSubList() const
+{
+ return const_cast< SdrObjGroup* >(this);
+}
+
+static bool containsOOXData(const css::uno::Any& rVal)
+{
+ const css::uno::Sequence<css::beans::PropertyValue>& propList(rVal.get< css::uno::Sequence<css::beans::PropertyValue> >());
+ for (const auto& rProp : std::as_const(propList))
+ {
+ if(rProp.Name.startsWith("OOX"))
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void SdrObjGroup::SetGrabBagItem(const css::uno::Any& rVal)
+{
+ // detect if the intention is to disable Diagram functionality
+ if(isDiagram() && !containsOOXData(rVal))
+ {
+ css::uno::Any aOld;
+ GetGrabBagItem(aOld);
+
+ if(containsOOXData(aOld))
+ {
+ mp_DiagramHelper.reset();
+ }
+ }
+
+ // call parent
+ SdrObject::SetGrabBagItem(rVal);
+}
+
+const tools::Rectangle& SdrObjGroup::GetCurrentBoundRect() const
+{
+ // <aOutRect> has to contain the bounding rectangle
+ if(0 != GetObjCount())
+ {
+ m_aOutRect = GetAllObjBoundRect();
+ }
+
+ return m_aOutRect;
+}
+
+const tools::Rectangle& SdrObjGroup::GetSnapRect() const
+{
+ // <aOutRect> has to contain the bounding rectangle
+ if(0 != GetObjCount())
+ {
+ return GetAllObjSnapRect();
+ }
+ else
+ {
+ return m_aOutRect;
+ }
+}
+
+SdrObjGroup* SdrObjGroup::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new SdrObjGroup(rTargetModel, *this);
+}
+
+OUString SdrObjGroup::TakeObjNameSingul() const
+{
+ OUString sName;
+
+ if(0 == GetObjCount())
+ {
+ sName = SvxResId(STR_ObjNameSingulGRUPEMPTY);
+ }
+ else
+ {
+ if(isDiagram())
+ sName = SvxResId(STR_ObjNameSingulDIAGRAM);
+ else
+ sName = SvxResId(STR_ObjNameSingulGRUP);
+ }
+
+ const OUString aName(GetName());
+
+ if (!aName.isEmpty())
+ sName += " '" + aName + "'";
+
+ return sName;
+}
+
+
+OUString SdrObjGroup::TakeObjNamePlural() const
+{
+ if(0 == GetObjCount())
+ return SvxResId(STR_ObjNamePluralGRUPEMPTY);
+ if(isDiagram())
+ return SvxResId(RID_GALLERYSTR_THEME_DIAGRAMS);
+ return SvxResId(STR_ObjNamePluralGRUP);
+}
+
+
+void SdrObjGroup::RecalcSnapRect()
+{
+ // TODO: unnecessary, because we use the Rects from the SubList
+}
+
+basegfx::B2DPolyPolygon SdrObjGroup::TakeXorPoly() const
+{
+ basegfx::B2DPolyPolygon aRetval;
+ const size_t nObjCount(GetObjCount());
+
+ for(size_t a = 0; a < nObjCount; ++a)
+ {
+ SdrObject* pObj(GetObj(a));
+ aRetval.append(pObj->TakeXorPoly());
+ }
+
+ if(!aRetval.count())
+ {
+ const basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(m_aOutRect);
+ aRetval.append(basegfx::utils::createPolygonFromRect(aRange));
+ }
+
+ return aRetval;
+}
+
+bool SdrObjGroup::beginSpecialDrag(SdrDragStat& /*rDrag*/) const
+{
+ return false;
+}
+
+
+bool SdrObjGroup::BegCreate(SdrDragStat& /*rStat*/)
+{
+ return false;
+}
+
+
+Degree100 SdrObjGroup::GetRotateAngle() const
+{
+ Degree100 nRetval(0);
+
+ if(0 != GetObjCount())
+ {
+ SdrObject* pObj(GetObj(0));
+
+ nRetval = pObj->GetRotateAngle();
+ }
+
+ return nRetval;
+}
+
+
+Degree100 SdrObjGroup::GetShearAngle(bool /*bVertical*/) const
+{
+ Degree100 nRetval(0);
+
+ if(0 != GetObjCount())
+ {
+ SdrObject* pObj(GetObj(0));
+
+ nRetval = pObj->GetShearAngle();
+ }
+
+ return nRetval;
+}
+
+
+void SdrObjGroup::NbcSetSnapRect(const tools::Rectangle& rRect)
+{
+ tools::Rectangle aOld(GetSnapRect());
+ tools::Long nMulX=rRect.Right()-rRect.Left();
+ tools::Long nDivX=aOld.Right()-aOld.Left();
+ tools::Long nMulY=rRect.Bottom()-rRect.Top();
+ tools::Long nDivY=aOld.Bottom()-aOld.Top();
+ if (nDivX==0) { nMulX=1; nDivX=1; }
+ if (nDivY==0) { nMulY=1; nDivY=1; }
+ if (nMulX!=nDivX || nMulY!=nDivY) {
+ Fraction aX(nMulX,nDivX);
+ Fraction aY(nMulY,nDivY);
+ NbcResize(aOld.TopLeft(),aX,aY);
+ }
+ if (rRect.Left()!=aOld.Left() || rRect.Top()!=aOld.Top()) {
+ NbcMove(Size(rRect.Left()-aOld.Left(),rRect.Top()-aOld.Top()));
+ }
+}
+
+
+void SdrObjGroup::NbcSetLogicRect(const tools::Rectangle& rRect)
+{
+ NbcSetSnapRect(rRect);
+}
+
+
+void SdrObjGroup::NbcMove(const Size& rSiz)
+{
+ maRefPoint.Move(rSiz);
+ const size_t nObjCount(GetObjCount());
+
+ if(0 != nObjCount)
+ {
+ for (size_t i=0; i<nObjCount; ++i)
+ {
+ SdrObject* pObj(GetObj(i));
+ pObj->NbcMove(rSiz);
+ }
+ }
+ else
+ {
+ m_aOutRect.Move(rSiz);
+ SetBoundAndSnapRectsDirty();
+ }
+}
+
+
+void SdrObjGroup::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
+{
+ bool bXMirr=(xFact.GetNumerator()<0) != (xFact.GetDenominator()<0);
+ bool bYMirr=(yFact.GetNumerator()<0) != (yFact.GetDenominator()<0);
+ if (bXMirr || bYMirr) {
+ Point aRef1(GetSnapRect().Center());
+ if (bXMirr) {
+ Point aRef2(aRef1);
+ aRef2.AdjustY( 1 );
+ NbcMirrorGluePoints(aRef1,aRef2);
+ }
+ if (bYMirr) {
+ Point aRef2(aRef1);
+ aRef2.AdjustX( 1 );
+ NbcMirrorGluePoints(aRef1,aRef2);
+ }
+ }
+
+ ResizePoint(maRefPoint, rRef, xFact, yFact);
+
+ const size_t nObjCount(GetObjCount());
+ if(0 != nObjCount)
+ {
+ for (size_t i=0; i<nObjCount; ++i)
+ {
+ SdrObject* pObj(GetObj(i));
+ pObj->NbcResize(rRef,xFact,yFact);
+ }
+ }
+ else
+ {
+ ResizeRect(m_aOutRect,rRef,xFact,yFact);
+ SetBoundAndSnapRectsDirty();
+ }
+}
+
+
+void SdrObjGroup::NbcRotate(const Point& rRef, Degree100 nAngle, double sn, double cs)
+{
+ SetGlueReallyAbsolute(true);
+ RotatePoint(maRefPoint, rRef, sn, cs);
+ const size_t nObjCount(GetObjCount());
+
+ for (size_t i=0; i<nObjCount; ++i)
+ {
+ SdrObject* pObj(GetObj(i));
+ pObj->NbcRotate(rRef,nAngle,sn,cs);
+ }
+
+ NbcRotateGluePoints(rRef,nAngle,sn,cs);
+ SetGlueReallyAbsolute(false);
+}
+
+
+void SdrObjGroup::NbcMirror(const Point& rRef1, const Point& rRef2)
+{
+ SetGlueReallyAbsolute(true);
+ MirrorPoint(maRefPoint, rRef1, rRef2); // implementation missing in SvdEtc!
+ const size_t nObjCount(GetObjCount());
+
+ for (size_t i=0; i<nObjCount; ++i)
+ {
+ SdrObject* pObj(GetObj(i));
+ pObj->NbcMirror(rRef1,rRef2);
+ }
+
+ NbcMirrorGluePoints(rRef1,rRef2);
+ SetGlueReallyAbsolute(false);
+}
+
+
+void SdrObjGroup::NbcShear(const Point& rRef, Degree100 nAngle, double tn, bool bVShear)
+{
+ SetGlueReallyAbsolute(true);
+ ShearPoint(maRefPoint, rRef, tn);
+ const size_t nObjCount(GetObjCount());
+
+ for (size_t i=0; i<nObjCount; ++i)
+ {
+ SdrObject* pObj(GetObj(i));
+ pObj->NbcShear(rRef,nAngle,tn,bVShear);
+ }
+
+ NbcShearGluePoints(rRef,tn,bVShear);
+ SetGlueReallyAbsolute(false);
+}
+
+
+void SdrObjGroup::NbcSetAnchorPos(const Point& rPnt)
+{
+ m_aAnchor=rPnt;
+ Size aSiz(rPnt.X()-m_aAnchor.X(),rPnt.Y()-m_aAnchor.Y());
+ maRefPoint.Move(aSiz);
+ const size_t nObjCount(GetObjCount());
+
+ for (size_t i=0; i<nObjCount; ++i)
+ {
+ SdrObject* pObj(GetObj(i));
+ pObj->NbcSetAnchorPos(rPnt);
+ }
+}
+
+
+void SdrObjGroup::SetSnapRect(const tools::Rectangle& rRect)
+{
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ tools::Rectangle aOld(GetSnapRect());
+ if (aOld.IsEmpty())
+ {
+ Fraction aX(1,1);
+ Fraction aY(1,1);
+ Resize(aOld.TopLeft(),aX,aY);
+ }
+ else
+ {
+ tools::Long nMulX=rRect.Right()-rRect.Left();
+ tools::Long nDivX=aOld.Right()-aOld.Left();
+ tools::Long nMulY=rRect.Bottom()-rRect.Top();
+ tools::Long nDivY=aOld.Bottom()-aOld.Top();
+ if (nDivX==0) { nMulX=1; nDivX=1; }
+ if (nDivY==0) { nMulY=1; nDivY=1; }
+ if (nMulX!=nDivX || nMulY!=nDivY) {
+ Fraction aX(nMulX,nDivX);
+ Fraction aY(nMulY,nDivY);
+ Resize(aOld.TopLeft(),aX,aY);
+ }
+ }
+ if (rRect.Left()!=aOld.Left() || rRect.Top()!=aOld.Top()) {
+ Move(Size(rRect.Left()-aOld.Left(),rRect.Top()-aOld.Top()));
+ }
+
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+}
+
+
+void SdrObjGroup::SetLogicRect(const tools::Rectangle& rRect)
+{
+ SetSnapRect(rRect);
+}
+
+
+void SdrObjGroup::Move(const Size& rSiz)
+{
+ if (rSiz.Width()==0 && rSiz.Height()==0)
+ return;
+
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ maRefPoint.Move(rSiz);
+ const size_t nObjCount(GetObjCount());
+
+ if(0 != nObjCount)
+ {
+ // first move the connectors, then everything else
+ for (size_t i=0; i<nObjCount; ++i)
+ {
+ SdrObject* pObj(GetObj(i));
+ if (pObj->IsEdgeObj())
+ pObj->Move(rSiz);
+ }
+
+ for (size_t i=0; i<nObjCount; ++i)
+ {
+ SdrObject* pObj(GetObj(i));
+ if (!pObj->IsEdgeObj())
+ pObj->Move(rSiz);
+ }
+ }
+ else
+ {
+ m_aOutRect.Move(rSiz);
+ SetBoundAndSnapRectsDirty();
+ }
+
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::MoveOnly,aBoundRect0);
+}
+
+
+void SdrObjGroup::Resize(const Point& rRef, const Fraction& xFact, const Fraction& yFact, bool bUnsetRelative)
+{
+ if (xFact.GetNumerator()==xFact.GetDenominator() && yFact.GetNumerator()==yFact.GetDenominator())
+ return;
+
+ bool bXMirr=(xFact.GetNumerator()<0) != (xFact.GetDenominator()<0);
+ bool bYMirr=(yFact.GetNumerator()<0) != (yFact.GetDenominator()<0);
+ if (bXMirr || bYMirr) {
+ Point aRef1(GetSnapRect().Center());
+ if (bXMirr) {
+ Point aRef2(aRef1);
+ aRef2.AdjustY( 1 );
+ NbcMirrorGluePoints(aRef1,aRef2);
+ }
+ if (bYMirr) {
+ Point aRef2(aRef1);
+ aRef2.AdjustX( 1 );
+ NbcMirrorGluePoints(aRef1,aRef2);
+ }
+ }
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ ResizePoint(maRefPoint, rRef, xFact, yFact);
+ const size_t nObjCount(GetObjCount());
+
+ if(0 != nObjCount)
+ {
+ // move the connectors first, everything else afterwards
+ for (size_t i=0; i<nObjCount; ++i)
+ {
+ SdrObject* pObj(GetObj(i));
+ if (pObj->IsEdgeObj())
+ pObj->Resize(rRef,xFact,yFact,bUnsetRelative);
+ }
+
+ for (size_t i=0; i<nObjCount; ++i)
+ {
+ SdrObject* pObj(GetObj(i));
+ if (!pObj->IsEdgeObj())
+ pObj->Resize(rRef,xFact,yFact,bUnsetRelative);
+ }
+ }
+ else
+ {
+ ResizeRect(m_aOutRect,rRef,xFact,yFact);
+ SetBoundAndSnapRectsDirty();
+ }
+
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+}
+
+
+void SdrObjGroup::Rotate(const Point& rRef, Degree100 nAngle, double sn, double cs)
+{
+ if (nAngle==0_deg100)
+ return;
+
+ SetGlueReallyAbsolute(true);
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ RotatePoint(maRefPoint, rRef, sn, cs);
+ // move the connectors first, everything else afterwards
+ const size_t nObjCount(GetObjCount());
+
+ for (size_t i=0; i<nObjCount; ++i)
+ {
+ SdrObject* pObj(GetObj(i));
+ if (pObj->IsEdgeObj())
+ pObj->Rotate(rRef,nAngle,sn,cs);
+ }
+
+ for (size_t i=0; i<nObjCount; ++i)
+ {
+ SdrObject* pObj(GetObj(i));
+ if (!pObj->IsEdgeObj())
+ pObj->Rotate(rRef,nAngle,sn,cs);
+ }
+
+ NbcRotateGluePoints(rRef,nAngle,sn,cs);
+ SetGlueReallyAbsolute(false);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+}
+
+
+void SdrObjGroup::Mirror(const Point& rRef1, const Point& rRef2)
+{
+ SetGlueReallyAbsolute(true);
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ MirrorPoint(maRefPoint, rRef1, rRef2); // implementation missing in SvdEtc!
+ // move the connectors first, everything else afterwards
+ const size_t nObjCount(GetObjCount());
+
+ for (size_t i=0; i<nObjCount; ++i)
+ {
+ SdrObject* pObj(GetObj(i));
+ if (pObj->IsEdgeObj())
+ pObj->Mirror(rRef1,rRef2);
+ }
+
+ for (size_t i=0; i<nObjCount; ++i)
+ {
+ SdrObject* pObj(GetObj(i));
+ if (!pObj->IsEdgeObj())
+ pObj->Mirror(rRef1,rRef2);
+ }
+
+ NbcMirrorGluePoints(rRef1,rRef2);
+ SetGlueReallyAbsolute(false);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+}
+
+
+void SdrObjGroup::Shear(const Point& rRef, Degree100 nAngle, double tn, bool bVShear)
+{
+ if (nAngle==0_deg100)
+ return;
+
+ SetGlueReallyAbsolute(true);
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ ShearPoint(maRefPoint, rRef, tn);
+ // move the connectors first, everything else afterwards
+ const size_t nObjCount(GetObjCount());
+
+ for (size_t i=0; i<nObjCount; ++i)
+ {
+ SdrObject* pObj(GetObj(i));
+ if (pObj->IsEdgeObj())
+ pObj->Shear(rRef,nAngle,tn,bVShear);
+ }
+
+ for (size_t i=0; i<nObjCount; ++i)
+ {
+ SdrObject* pObj(GetObj(i));
+ if (!pObj->IsEdgeObj())
+ pObj->Shear(rRef,nAngle,tn,bVShear);
+ }
+
+ NbcShearGluePoints(rRef,tn,bVShear);
+ SetGlueReallyAbsolute(false);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+
+}
+
+
+void SdrObjGroup::SetAnchorPos(const Point& rPnt)
+{
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ bool bChg=m_aAnchor!=rPnt;
+ m_aAnchor=rPnt;
+ Size aSiz(rPnt.X()-m_aAnchor.X(),rPnt.Y()-m_aAnchor.Y());
+ maRefPoint.Move(aSiz);
+ // move the connectors first, everything else afterwards
+ const size_t nObjCount(GetObjCount());
+
+ for (size_t i=0; i<nObjCount; ++i)
+ {
+ SdrObject* pObj(GetObj(i));
+ if (pObj->IsEdgeObj())
+ pObj->SetAnchorPos(rPnt);
+ }
+
+ for (size_t i=0; i<nObjCount; ++i)
+ {
+ SdrObject* pObj(GetObj(i));
+ if (!pObj->IsEdgeObj())
+ pObj->SetAnchorPos(rPnt);
+ }
+
+ if (bChg)
+ {
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::MoveOnly,aBoundRect0);
+ }
+}
+
+
+void SdrObjGroup::NbcSetRelativePos(const Point& rPnt)
+{
+ Point aRelPos0(GetSnapRect().TopLeft()-m_aAnchor);
+ Size aSiz(rPnt.X()-aRelPos0.X(),rPnt.Y()-aRelPos0.Y());
+ NbcMove(aSiz); // this also calls SetRectsDirty()
+}
+
+void SdrObjGroup::SetRelativePos(const Point& rPnt)
+{
+ Point aRelPos0(GetSnapRect().TopLeft()-m_aAnchor);
+ Size aSiz(rPnt.X()-aRelPos0.X(),rPnt.Y()-aRelPos0.Y());
+ if (aSiz.Width()!=0 || aSiz.Height()!=0) Move(aSiz); // this also calls SetRectsDirty() and Broadcast, ...
+}
+
+void SdrObjGroup::NbcReformatText()
+{
+ NbcReformatAllTextObjects();
+}
+
+SdrObjectUniquePtr SdrObjGroup::DoConvertToPolyObj(bool bBezier, bool bAddText) const
+{
+ SdrObjectUniquePtr pGroup( new SdrObjGroup(getSdrModelFromSdrObject()) );
+ const size_t nObjCount(GetObjCount());
+
+ for(size_t a=0; a < nObjCount; ++a)
+ {
+ SdrObject* pIterObj(GetObj(a));
+ SdrObjectUniquePtr pResult(pIterObj->DoConvertToPolyObj(bBezier, bAddText));
+
+ // pResult can be NULL e.g. for empty objects
+ if( pResult )
+ pGroup->GetSubList()->NbcInsertObject(pResult.release());
+ }
+
+ return pGroup;
+}
+
+void SdrObjGroup::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SdrObjGroup"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+
+ SdrObject::dumpAsXml(pWriter);
+ SdrObjList::dumpAsXml(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdomeas.cxx b/svx/source/svdraw/svdomeas.cxx
new file mode 100644
index 000000000..da9959c84
--- /dev/null
+++ b/svx/source/svdraw/svdomeas.cxx
@@ -0,0 +1,1429 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <editeng/editdata.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/flditem.hxx>
+#include <editeng/measfld.hxx>
+#include <editeng/outlobj.hxx>
+#include <math.h>
+#include <svl/style.hxx>
+
+#include <sdr/contact/viewcontactofsdrmeasureobj.hxx>
+#include <sdr/properties/measureproperties.hxx>
+#include <svx/svddrag.hxx>
+#include <svx/svdhdl.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svdomeas.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/svdoutl.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdtrans.hxx>
+#include <svx/svdview.hxx>
+#include <svx/sxmbritm.hxx>
+#include <svx/sxmlhitm.hxx>
+#include <sxmsitm.hxx>
+#include <sxmtaitm.hxx>
+#include <svx/sxmtfitm.hxx>
+#include <svx/sxmtpitm.hxx>
+#include <svx/sxmtritm.hxx>
+#include <svx/sxmuitm.hxx>
+#include <svx/xlnedcit.hxx>
+#include <svx/xlnedit.hxx>
+#include <svx/xlnedwit.hxx>
+#include <svx/xlnstcit.hxx>
+#include <svx/xlnstit.hxx>
+#include <svx/xlnstwit.hxx>
+#include <svx/xlnwtit.hxx>
+#include <svx/xpoly.hxx>
+#include <unotools/syslocale.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <vcl/ptrstyle.hxx>
+
+
+SdrMeasureObjGeoData::SdrMeasureObjGeoData() {}
+SdrMeasureObjGeoData::~SdrMeasureObjGeoData() {}
+
+OUString SdrMeasureObj::TakeRepresentation(SdrMeasureFieldKind eMeasureFieldKind) const
+{
+ OUString aStr;
+ Fraction aMeasureScale(1, 1);
+ bool bTextRota90(false);
+ bool bShowUnit(false);
+ FieldUnit eMeasureUnit(FieldUnit::NONE);
+ FieldUnit eModUIUnit(FieldUnit::NONE);
+
+ const SfxItemSet& rSet = GetMergedItemSet();
+ bTextRota90 = rSet.Get(SDRATTR_MEASURETEXTROTA90).GetValue();
+ eMeasureUnit = rSet.Get(SDRATTR_MEASUREUNIT).GetValue();
+ aMeasureScale = rSet.Get(SDRATTR_MEASURESCALE).GetValue();
+ bShowUnit = rSet.Get(SDRATTR_MEASURESHOWUNIT).GetValue();
+ sal_Int16 nNumDigits = rSet.Get(SDRATTR_MEASUREDECIMALPLACES).GetValue();
+
+ switch(eMeasureFieldKind)
+ {
+ case SdrMeasureFieldKind::Value:
+ {
+ eModUIUnit = getSdrModelFromSdrObject().GetUIUnit();
+
+ if(eMeasureUnit == FieldUnit::NONE)
+ eMeasureUnit = eModUIUnit;
+
+ sal_Int32 nLen(GetLen(aPt2 - aPt1));
+ Fraction aFact(1,1);
+
+ if(eMeasureUnit != eModUIUnit)
+ {
+ // for the unit conversion
+ aFact *= GetMapFactor(eModUIUnit, eMeasureUnit).X();
+ }
+
+ if(aMeasureScale.GetNumerator() != aMeasureScale.GetDenominator())
+ {
+ aFact *= aMeasureScale;
+ }
+
+ if(aFact.GetNumerator() != aFact.GetDenominator())
+ {
+ // scale via BigInt, to avoid overruns
+ nLen = BigMulDiv(nLen, aFact.GetNumerator(), aFact.GetDenominator());
+ }
+
+ if(!aFact.IsValid())
+ {
+ aStr = "?";
+ }
+ else
+ {
+ aStr = getSdrModelFromSdrObject().GetMetricString(nLen, true, nNumDigits);
+ }
+
+ SvtSysLocale aSysLocale;
+ const LocaleDataWrapper& rLocaleDataWrapper = aSysLocale.GetLocaleData();
+ sal_Unicode cDec(rLocaleDataWrapper.getNumDecimalSep()[0]);
+ sal_Unicode cDecAlt(rLocaleDataWrapper.getNumDecimalSepAlt().toChar());
+
+ if(aStr.indexOf(cDec) != -1 || (cDecAlt && aStr.indexOf(cDecAlt) != -1))
+ {
+ sal_Int32 nLen2(aStr.getLength() - 1);
+
+ while(aStr[nLen2] == '0')
+ {
+ aStr = aStr.copy(0, nLen2);
+ nLen2--;
+ }
+
+ if(aStr[nLen2] == cDec || (cDecAlt && aStr[nLen2] == cDecAlt))
+ {
+ aStr = aStr.copy(0, nLen2);
+ nLen2--;
+ }
+
+ if(aStr.isEmpty())
+ aStr += "0";
+ }
+
+ break;
+ }
+ case SdrMeasureFieldKind::Unit:
+ {
+ if(bShowUnit)
+ {
+ eModUIUnit = getSdrModelFromSdrObject().GetUIUnit();
+
+ if(eMeasureUnit == FieldUnit::NONE)
+ eMeasureUnit = eModUIUnit;
+
+ aStr = SdrModel::GetUnitString(eMeasureUnit);
+ }
+
+ break;
+ }
+ case SdrMeasureFieldKind::Rotate90Blanks:
+ {
+ if(bTextRota90)
+ {
+ aStr = " ";
+ }
+
+ break;
+ }
+ }
+ return aStr;
+}
+
+
+// BaseProperties section
+
+std::unique_ptr<sdr::properties::BaseProperties> SdrMeasureObj::CreateObjectSpecificProperties()
+{
+ return std::make_unique<sdr::properties::MeasureProperties>(*this);
+}
+
+
+// DrawContact section
+
+std::unique_ptr<sdr::contact::ViewContact> SdrMeasureObj::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::ViewContactOfSdrMeasureObj>(*this);
+}
+
+
+SdrMeasureObj::SdrMeasureObj(SdrModel& rSdrModel)
+: SdrTextObj(rSdrModel),
+ bTextDirty(false)
+{
+ // #i25616#
+ mbSupportTextIndentingOnLineWidthChange = false;
+}
+
+SdrMeasureObj::SdrMeasureObj(SdrModel& rSdrModel, SdrMeasureObj const & rSource)
+: SdrTextObj(rSdrModel, rSource),
+ bTextDirty(false)
+{
+ // #i25616#
+ mbSupportTextIndentingOnLineWidthChange = false;
+
+ aPt1 = rSource.aPt1;
+ aPt2 = rSource.aPt2;
+ bTextDirty = rSource.bTextDirty;
+}
+
+SdrMeasureObj::SdrMeasureObj(
+ SdrModel& rSdrModel,
+ const Point& rPt1,
+ const Point& rPt2)
+: SdrTextObj(rSdrModel),
+ aPt1(rPt1),
+ aPt2(rPt2),
+ bTextDirty(false)
+{
+ // #i25616#
+ mbSupportTextIndentingOnLineWidthChange = false;
+}
+
+SdrMeasureObj::~SdrMeasureObj()
+{
+}
+
+void SdrMeasureObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
+{
+ rInfo.bMoveAllowed =true;
+ rInfo.bResizeFreeAllowed=true;
+ rInfo.bResizePropAllowed=true;
+ rInfo.bRotateFreeAllowed=true;
+ rInfo.bRotate90Allowed =true;
+ rInfo.bMirrorFreeAllowed=true;
+ rInfo.bMirror45Allowed =true;
+ rInfo.bMirror90Allowed =true;
+ rInfo.bTransparenceAllowed = false;
+ rInfo.bShearAllowed =true;
+ rInfo.bEdgeRadiusAllowed=false;
+ rInfo.bNoOrthoDesired =true;
+ rInfo.bNoContortion =false;
+ rInfo.bCanConvToPath =false;
+ rInfo.bCanConvToPoly =true;
+ rInfo.bCanConvToPathLineToArea=false;
+ rInfo.bCanConvToPolyLineToArea=false;
+ rInfo.bCanConvToContour = LineGeometryUsageIsNecessary();
+}
+
+SdrObjKind SdrMeasureObj::GetObjIdentifier() const
+{
+ return SdrObjKind::Measure;
+}
+
+struct ImpMeasureRec : public SdrDragStatUserData
+{
+ Point aPt1;
+ Point aPt2;
+ css::drawing::MeasureTextHorzPos eWantTextHPos;
+ css::drawing::MeasureTextVertPos eWantTextVPos;
+ tools::Long nLineDist;
+ tools::Long nHelplineOverhang;
+ tools::Long nHelplineDist;
+ tools::Long nHelpline1Len;
+ tools::Long nHelpline2Len;
+ bool bBelowRefEdge;
+ bool bTextRota90;
+ bool bTextUpsideDown;
+ bool bTextAutoAngle;
+ Degree100 nTextAutoAngleView;
+};
+
+namespace {
+
+struct ImpLineRec
+{
+ Point aP1;
+ Point aP2;
+};
+
+}
+
+struct ImpMeasurePoly
+{
+ ImpLineRec aMainline1; // those with the 1st arrowhead
+ ImpLineRec aMainline2; // those with the 2nd arrowhead
+ ImpLineRec aMainline3; // those in between
+ ImpLineRec aHelpline1;
+ ImpLineRec aHelpline2;
+ Size aTextSize;
+ tools::Long nLineLen;
+ Degree100 nLineAngle;
+ Degree100 nTextAngle;
+ Degree100 nHlpAngle;
+ double nLineSin;
+ double nLineCos;
+ sal_uInt16 nMainlineCnt;
+ css::drawing::MeasureTextHorzPos eUsedTextHPos;
+ css::drawing::MeasureTextVertPos eUsedTextVPos;
+ tools::Long nLineWdt2; // half the line width
+ tools::Long nArrow1Len; // length of 1st arrowhead; for Center, use only half
+ tools::Long nArrow2Len; // length of 2nd arrowhead; for Center, use only half
+ tools::Long nArrow1Wdt; // width of 1st arrow
+ tools::Long nArrow2Wdt; // width of 2nd arrow
+ tools::Long nShortLineLen; // line length, if PfeileAussen (arrowheads on the outside)
+ bool bAutoUpsideDown; // UpsideDown via automation
+ bool bBreakedLine;
+};
+
+void SdrMeasureObj::ImpTakeAttr(ImpMeasureRec& rRec) const
+{
+ rRec.aPt1 = aPt1;
+ rRec.aPt2 = aPt2;
+
+ const SfxItemSet& rSet = GetObjectItemSet();
+ rRec.eWantTextHPos =rSet.Get(SDRATTR_MEASURETEXTHPOS ).GetValue();
+ rRec.eWantTextVPos =rSet.Get(SDRATTR_MEASURETEXTVPOS ).GetValue();
+ rRec.nLineDist =rSet.Get(SDRATTR_MEASURELINEDIST ).GetValue();
+ rRec.nHelplineOverhang =rSet.Get(SDRATTR_MEASUREHELPLINEOVERHANG).GetValue();
+ rRec.nHelplineDist =rSet.Get(SDRATTR_MEASUREHELPLINEDIST ).GetValue();
+ rRec.nHelpline1Len =rSet.Get(SDRATTR_MEASUREHELPLINE1LEN ).GetValue();
+ rRec.nHelpline2Len =rSet.Get(SDRATTR_MEASUREHELPLINE2LEN ).GetValue();
+ rRec.bBelowRefEdge =rSet.Get(SDRATTR_MEASUREBELOWREFEDGE ).GetValue();
+ rRec.bTextRota90 =rSet.Get(SDRATTR_MEASURETEXTROTA90 ).GetValue();
+ rRec.bTextUpsideDown =static_cast<const SdrMeasureTextUpsideDownItem& >(rSet.Get(SDRATTR_MEASURETEXTUPSIDEDOWN )).GetValue();
+ rRec.bTextAutoAngle =rSet.Get(SDRATTR_MEASURETEXTAUTOANGLE ).GetValue();
+ rRec.nTextAutoAngleView=static_cast<const SdrMeasureTextAutoAngleViewItem&>(rSet.Get(SDRATTR_MEASURETEXTAUTOANGLEVIEW)).GetValue();
+}
+
+static tools::Long impGetLineStartEndDistance(const basegfx::B2DPolyPolygon& rPolyPolygon, tools::Long nNewWidth, bool bCenter)
+{
+ const basegfx::B2DRange aPolygonRange(rPolyPolygon.getB2DRange());
+ const double fOldWidth(std::max(aPolygonRange.getWidth(), 1.0));
+ const double fScale(static_cast<double>(nNewWidth) / fOldWidth);
+ tools::Long nHeight(basegfx::fround(aPolygonRange.getHeight() * fScale));
+
+ if(bCenter)
+ {
+ nHeight /= 2;
+ }
+
+ return nHeight;
+}
+
+void SdrMeasureObj::ImpCalcGeometrics(const ImpMeasureRec& rRec, ImpMeasurePoly& rPol) const
+{
+ Point aP1(rRec.aPt1);
+ Point aP2(rRec.aPt2);
+ Point aDelt(aP2); aDelt-=aP1;
+
+ rPol.aTextSize=GetTextSize();
+ rPol.nLineLen=GetLen(aDelt);
+
+ rPol.nLineWdt2=0;
+ tools::Long nArrow1Len=0; bool bArrow1Center=false;
+ tools::Long nArrow2Len=0; bool bArrow2Center=false;
+ tools::Long nArrow1Wdt=0;
+ tools::Long nArrow2Wdt=0;
+ rPol.nArrow1Wdt=0;
+ rPol.nArrow2Wdt=0;
+ tools::Long nArrowNeed=0;
+ tools::Long nShortLen=0;
+ bool bPfeileAussen = false;
+
+ const SfxItemSet& rSet = GetObjectItemSet();
+ sal_Int32 nLineWdt = rSet.Get(XATTR_LINEWIDTH).GetValue(); // line width
+ rPol.nLineWdt2 = (nLineWdt + 1) / 2;
+
+ nArrow1Wdt = rSet.Get(XATTR_LINESTARTWIDTH).GetValue();
+ if(nArrow1Wdt < 0)
+ nArrow1Wdt = -nLineWdt * nArrow1Wdt / 100; // <0 = relative
+
+ nArrow2Wdt = rSet.Get(XATTR_LINEENDWIDTH).GetValue();
+ if(nArrow2Wdt < 0)
+ nArrow2Wdt = -nLineWdt * nArrow2Wdt / 100; // <0 = relative
+
+ basegfx::B2DPolyPolygon aPol1(rSet.Get(XATTR_LINESTART).GetLineStartValue());
+ basegfx::B2DPolyPolygon aPol2(rSet.Get(XATTR_LINEEND).GetLineEndValue());
+ bArrow1Center = rSet.Get(XATTR_LINESTARTCENTER).GetValue();
+ bArrow2Center = rSet.Get(XATTR_LINEENDCENTER).GetValue();
+ nArrow1Len = impGetLineStartEndDistance(aPol1, nArrow1Wdt, bArrow1Center) - 1;
+ nArrow2Len = impGetLineStartEndDistance(aPol2, nArrow2Wdt, bArrow2Center) - 1;
+
+ // nArrowLen is already halved at bCenter.
+ // In the case of 2 arrowheads each 4mm long, we can't go below 10mm.
+ nArrowNeed=nArrow1Len+nArrow2Len+(nArrow1Wdt+nArrow2Wdt)/2;
+ if (rPol.nLineLen<nArrowNeed) bPfeileAussen = true;
+ nShortLen=(nArrow1Len+nArrow1Wdt + nArrow2Len+nArrow2Wdt) /2;
+
+ rPol.eUsedTextHPos=rRec.eWantTextHPos;
+ rPol.eUsedTextVPos=rRec.eWantTextVPos;
+ if (rPol.eUsedTextVPos == css::drawing::MeasureTextVertPos_AUTO)
+ rPol.eUsedTextVPos = css::drawing::MeasureTextVertPos_EAST;
+ bool bBrkLine=false;
+ if (rPol.eUsedTextVPos == css::drawing::MeasureTextVertPos_CENTERED)
+ {
+ OutlinerParaObject* pOutlinerParaObject = SdrTextObj::GetOutlinerParaObject();
+ if (pOutlinerParaObject!=nullptr && pOutlinerParaObject->GetTextObject().GetParagraphCount()==1)
+ {
+ bBrkLine=true; // dashed line if there's only on paragraph.
+ }
+ }
+ rPol.bBreakedLine=bBrkLine;
+ if (rPol.eUsedTextHPos==css::drawing::MeasureTextHorzPos_AUTO) { // if text is too wide, push it outside
+ bool bOutside = false;
+ tools::Long nNeedSiz=!rRec.bTextRota90 ? rPol.aTextSize.Width() : rPol.aTextSize.Height();
+ if (nNeedSiz>rPol.nLineLen) bOutside = true; // text doesn't fit in between
+ if (bBrkLine) {
+ if (nNeedSiz+nArrowNeed>rPol.nLineLen) bPfeileAussen = true; // text fits in between, if arrowheads are on the outside
+ } else {
+ tools::Long nSmallNeed=nArrow1Len+nArrow2Len+(nArrow1Wdt+nArrow2Wdt)/2/4;
+ if (nNeedSiz+nSmallNeed>rPol.nLineLen) bPfeileAussen = true; // text fits in between, if arrowheads are on the outside
+ }
+ rPol.eUsedTextHPos=bOutside ? css::drawing::MeasureTextHorzPos_LEFTOUTSIDE : css::drawing::MeasureTextHorzPos_INSIDE;
+ }
+ if (rPol.eUsedTextHPos != css::drawing::MeasureTextHorzPos_INSIDE) bPfeileAussen = true;
+ rPol.nArrow1Wdt=nArrow1Wdt;
+ rPol.nArrow2Wdt=nArrow2Wdt;
+ rPol.nShortLineLen=nShortLen;
+ rPol.nArrow1Len=nArrow1Len;
+ rPol.nArrow2Len=nArrow2Len;
+
+ rPol.nLineAngle=GetAngle(aDelt);
+ double a = toRadians(rPol.nLineAngle);
+ double nLineSin=sin(a);
+ double nLineCos=cos(a);
+ rPol.nLineSin=nLineSin;
+ rPol.nLineCos=nLineCos;
+
+ rPol.nTextAngle=rPol.nLineAngle;
+ if (rRec.bTextRota90) rPol.nTextAngle+=9000_deg100;
+
+ rPol.bAutoUpsideDown=false;
+ if (rRec.bTextAutoAngle) {
+ Degree100 nTmpAngle=NormAngle36000(rPol.nTextAngle-rRec.nTextAutoAngleView);
+ if (nTmpAngle>=18000_deg100) {
+ rPol.nTextAngle+=18000_deg100;
+ rPol.bAutoUpsideDown=true;
+ }
+ }
+
+ if (rRec.bTextUpsideDown) rPol.nTextAngle+=18000_deg100;
+ rPol.nTextAngle=NormAngle36000(rPol.nTextAngle);
+ rPol.nHlpAngle=rPol.nLineAngle+9000_deg100;
+ if (rRec.bBelowRefEdge) rPol.nHlpAngle+=18000_deg100;
+ rPol.nHlpAngle=NormAngle36000(rPol.nHlpAngle);
+ double nHlpSin=nLineCos;
+ double nHlpCos=-nLineSin;
+ if (rRec.bBelowRefEdge) {
+ nHlpSin=-nHlpSin;
+ nHlpCos=-nHlpCos;
+ }
+
+ tools::Long nLineDist=rRec.nLineDist;
+ tools::Long nOverhang=rRec.nHelplineOverhang;
+ tools::Long nHelplineDist=rRec.nHelplineDist;
+
+ tools::Long dx= FRound(nLineDist*nHlpCos);
+ tools::Long dy=-FRound(nLineDist*nHlpSin);
+ tools::Long dxh1a= FRound((nHelplineDist-rRec.nHelpline1Len)*nHlpCos);
+ tools::Long dyh1a=-FRound((nHelplineDist-rRec.nHelpline1Len)*nHlpSin);
+ tools::Long dxh1b= FRound((nHelplineDist-rRec.nHelpline2Len)*nHlpCos);
+ tools::Long dyh1b=-FRound((nHelplineDist-rRec.nHelpline2Len)*nHlpSin);
+ tools::Long dxh2= FRound((nLineDist+nOverhang)*nHlpCos);
+ tools::Long dyh2=-FRound((nLineDist+nOverhang)*nHlpSin);
+
+ // extension line 1
+ rPol.aHelpline1.aP1=Point(aP1.X()+dxh1a,aP1.Y()+dyh1a);
+ rPol.aHelpline1.aP2=Point(aP1.X()+dxh2,aP1.Y()+dyh2);
+
+ // extension line 2
+ rPol.aHelpline2.aP1=Point(aP2.X()+dxh1b,aP2.Y()+dyh1b);
+ rPol.aHelpline2.aP2=Point(aP2.X()+dxh2,aP2.Y()+dyh2);
+
+ // dimension line
+ Point aMainlinePt1(aP1.X()+dx,aP1.Y()+dy);
+ Point aMainlinePt2(aP2.X()+dx,aP2.Y()+dy);
+ if (!bPfeileAussen) {
+ rPol.aMainline1.aP1=aMainlinePt1;
+ rPol.aMainline1.aP2=aMainlinePt2;
+ rPol.aMainline2=rPol.aMainline1;
+ rPol.aMainline3=rPol.aMainline1;
+ rPol.nMainlineCnt=1;
+ if (bBrkLine) {
+ tools::Long nNeedSiz=!rRec.bTextRota90 ? rPol.aTextSize.Width() : rPol.aTextSize.Height();
+ tools::Long nHalfLen=(rPol.nLineLen-nNeedSiz-nArrow1Wdt/4-nArrow2Wdt/4) /2;
+ rPol.nMainlineCnt=2;
+ rPol.aMainline1.aP2=aMainlinePt1;
+ rPol.aMainline1.aP2.AdjustX(nHalfLen );
+ RotatePoint(rPol.aMainline1.aP2,rPol.aMainline1.aP1,nLineSin,nLineCos);
+ rPol.aMainline2.aP1=aMainlinePt2;
+ rPol.aMainline2.aP1.AdjustX( -nHalfLen );
+ RotatePoint(rPol.aMainline2.aP1,rPol.aMainline2.aP2,nLineSin,nLineCos);
+ }
+ } else {
+ tools::Long nLen1=nShortLen; // arrowhead's width as line length outside of the arrowhead
+ tools::Long nLen2=nShortLen;
+ tools::Long nTextWdt=rRec.bTextRota90 ? rPol.aTextSize.Height() : rPol.aTextSize.Width();
+ if (!bBrkLine) {
+ if (rPol.eUsedTextHPos==css::drawing::MeasureTextHorzPos_LEFTOUTSIDE) nLen1=nArrow1Len+nTextWdt;
+ if (rPol.eUsedTextHPos==css::drawing::MeasureTextHorzPos_RIGHTOUTSIDE) nLen2=nArrow2Len+nTextWdt;
+ }
+ rPol.aMainline1.aP1=aMainlinePt1;
+ rPol.aMainline1.aP2=aMainlinePt1; rPol.aMainline1.aP2.AdjustX( -nLen1 ); RotatePoint(rPol.aMainline1.aP2,aMainlinePt1,nLineSin,nLineCos);
+ rPol.aMainline2.aP1=aMainlinePt2; rPol.aMainline2.aP1.AdjustX(nLen2 ); RotatePoint(rPol.aMainline2.aP1,aMainlinePt2,nLineSin,nLineCos);
+ rPol.aMainline2.aP2=aMainlinePt2;
+ rPol.aMainline3.aP1=aMainlinePt1;
+ rPol.aMainline3.aP2=aMainlinePt2;
+ rPol.nMainlineCnt=3;
+ if (bBrkLine && rPol.eUsedTextHPos==css::drawing::MeasureTextHorzPos_INSIDE) rPol.nMainlineCnt=2;
+ }
+}
+
+basegfx::B2DPolyPolygon SdrMeasureObj::ImpCalcXPoly(const ImpMeasurePoly& rPol)
+{
+ basegfx::B2DPolyPolygon aRetval;
+ basegfx::B2DPolygon aPartPolyA;
+ aPartPolyA.append(basegfx::B2DPoint(rPol.aMainline1.aP1.X(), rPol.aMainline1.aP1.Y()));
+ aPartPolyA.append(basegfx::B2DPoint(rPol.aMainline1.aP2.X(), rPol.aMainline1.aP2.Y()));
+ aRetval.append(aPartPolyA);
+
+ if(rPol.nMainlineCnt > 1)
+ {
+ aPartPolyA.clear();
+ aPartPolyA.append(basegfx::B2DPoint(rPol.aMainline2.aP1.X(), rPol.aMainline2.aP1.Y()));
+ aPartPolyA.append(basegfx::B2DPoint(rPol.aMainline2.aP2.X(), rPol.aMainline2.aP2.Y()));
+ aRetval.append(aPartPolyA);
+ }
+
+ if(rPol.nMainlineCnt > 2)
+ {
+ aPartPolyA.clear();
+ aPartPolyA.append(basegfx::B2DPoint(rPol.aMainline3.aP1.X(), rPol.aMainline3.aP1.Y()));
+ aPartPolyA.append(basegfx::B2DPoint(rPol.aMainline3.aP2.X(), rPol.aMainline3.aP2.Y()));
+ aRetval.append(aPartPolyA);
+ }
+
+ aPartPolyA.clear();
+ aPartPolyA.append(basegfx::B2DPoint(rPol.aHelpline1.aP1.X(), rPol.aHelpline1.aP1.Y()));
+ aPartPolyA.append(basegfx::B2DPoint(rPol.aHelpline1.aP2.X(), rPol.aHelpline1.aP2.Y()));
+ aRetval.append(aPartPolyA);
+
+ aPartPolyA.clear();
+ aPartPolyA.append(basegfx::B2DPoint(rPol.aHelpline2.aP1.X(), rPol.aHelpline2.aP1.Y()));
+ aPartPolyA.append(basegfx::B2DPoint(rPol.aHelpline2.aP2.X(), rPol.aHelpline2.aP2.Y()));
+ aRetval.append(aPartPolyA);
+
+ return aRetval;
+}
+
+bool SdrMeasureObj::CalcFieldValue(const SvxFieldItem& rField, sal_Int32 nPara, sal_uInt16 nPos,
+ bool bEdit,
+ std::optional<Color>& rpTxtColor, std::optional<Color>& rpFldColor, OUString& rRet) const
+{
+ const SvxFieldData* pField=rField.GetField();
+ const SdrMeasureField* pMeasureField=dynamic_cast<const SdrMeasureField*>( pField );
+ if (pMeasureField!=nullptr) {
+ rRet = TakeRepresentation(pMeasureField->GetMeasureFieldKind());
+ if (rpFldColor && !bEdit)
+ {
+ rpFldColor.reset();
+ }
+ return true;
+ } else {
+ return SdrTextObj::CalcFieldValue(rField,nPara,nPos,bEdit,rpTxtColor,rpFldColor,rRet);
+ }
+}
+
+void SdrMeasureObj::UndirtyText() const
+{
+ if (!bTextDirty)
+ return;
+
+ SdrOutliner& rOutliner=ImpGetDrawOutliner();
+ OutlinerParaObject* pOutlinerParaObject = SdrTextObj::GetOutlinerParaObject();
+ if(pOutlinerParaObject==nullptr)
+ {
+ rOutliner.QuickInsertField(SvxFieldItem(SdrMeasureField(SdrMeasureFieldKind::Rotate90Blanks), EE_FEATURE_FIELD), ESelection(0,0));
+ rOutliner.QuickInsertField(SvxFieldItem(SdrMeasureField(SdrMeasureFieldKind::Value), EE_FEATURE_FIELD),ESelection(0,1));
+ rOutliner.QuickInsertText(" ", ESelection(0,2));
+ rOutliner.QuickInsertField(SvxFieldItem(SdrMeasureField(SdrMeasureFieldKind::Unit), EE_FEATURE_FIELD),ESelection(0,3));
+ rOutliner.QuickInsertField(SvxFieldItem(SdrMeasureField(SdrMeasureFieldKind::Rotate90Blanks), EE_FEATURE_FIELD),ESelection(0,4));
+
+ if(GetStyleSheet())
+ rOutliner.SetStyleSheet(0, GetStyleSheet());
+
+ rOutliner.SetParaAttribs(0, GetObjectItemSet());
+
+ // cast to nonconst
+ const_cast<SdrMeasureObj*>(this)->NbcSetOutlinerParaObject( rOutliner.CreateParaObject() );
+ }
+ else
+ {
+ rOutliner.SetText(*pOutlinerParaObject);
+ }
+
+ rOutliner.SetUpdateLayout(true);
+ rOutliner.UpdateFields();
+ Size aSiz(rOutliner.CalcTextSize());
+ rOutliner.Clear();
+ // cast to nonconst three times
+ const_cast<SdrMeasureObj*>(this)->maTextSize = aSiz;
+ const_cast<SdrMeasureObj*>(this)->mbTextSizeDirty = false;
+ const_cast<SdrMeasureObj*>(this)->bTextDirty = false;
+}
+
+void SdrMeasureObj::TakeUnrotatedSnapRect(tools::Rectangle& rRect) const
+{
+ if (bTextDirty) UndirtyText();
+ ImpMeasureRec aRec;
+ ImpMeasurePoly aMPol;
+ ImpTakeAttr(aRec);
+ ImpCalcGeometrics(aRec,aMPol);
+
+ // determine TextSize including text frame margins
+ Size aTextSize2(aMPol.aTextSize);
+ if (aTextSize2.Width()<1) aTextSize2.setWidth(1 );
+ if (aTextSize2.Height()<1) aTextSize2.setHeight(1 );
+ aTextSize2.AdjustWidth(GetTextLeftDistance()+GetTextRightDistance() );
+ aTextSize2.AdjustHeight(GetTextUpperDistance()+GetTextLowerDistance() );
+
+ Point aPt1b(aMPol.aMainline1.aP1);
+ tools::Long nLen=aMPol.nLineLen;
+ tools::Long nLWdt=aMPol.nLineWdt2;
+ tools::Long nArr1Len=aMPol.nArrow1Len;
+ tools::Long nArr2Len=aMPol.nArrow2Len;
+ if (aMPol.bBreakedLine) {
+ // In the case of a dashed line and Outside, the text should be
+ // placed next to the line at the arrowhead instead of directly
+ // at the arrowhead.
+ nArr1Len=aMPol.nShortLineLen+aMPol.nArrow1Wdt/4;
+ nArr2Len=aMPol.nShortLineLen+aMPol.nArrow2Wdt/4;
+ }
+
+ Point aTextPos;
+ bool bRota90=aRec.bTextRota90;
+ bool bUpsideDown=aRec.bTextUpsideDown!=aMPol.bAutoUpsideDown;
+ bool bBelowRefEdge=aRec.bBelowRefEdge;
+ css::drawing::MeasureTextHorzPos eMH=aMPol.eUsedTextHPos;
+ css::drawing::MeasureTextVertPos eMV=aMPol.eUsedTextVPos;
+ if (!bRota90) {
+ switch (eMH) {
+ case css::drawing::MeasureTextHorzPos_LEFTOUTSIDE: aTextPos.setX(aPt1b.X()-aTextSize2.Width()-nArr1Len-nLWdt ); break;
+ case css::drawing::MeasureTextHorzPos_RIGHTOUTSIDE: aTextPos.setX(aPt1b.X()+nLen+nArr2Len+nLWdt ); break;
+ default: aTextPos.setX(aPt1b.X() ); aTextSize2.setWidth(nLen );
+ }
+ switch (eMV) {
+ case css::drawing::MeasureTextVertPos_CENTERED:
+ aTextPos.setY(aPt1b.Y()-aTextSize2.Height()/2 ); break;
+ case css::drawing::MeasureTextVertPos_WEST: {
+ if (!bUpsideDown) aTextPos.setY(aPt1b.Y()+nLWdt );
+ else aTextPos.setY(aPt1b.Y()-aTextSize2.Height()-nLWdt );
+ } break;
+ default: {
+ if (!bUpsideDown) aTextPos.setY(aPt1b.Y()-aTextSize2.Height()-nLWdt );
+ else aTextPos.setY(aPt1b.Y()+nLWdt );
+ }
+ }
+ if (bUpsideDown) {
+ aTextPos.AdjustX(aTextSize2.Width() );
+ aTextPos.AdjustY(aTextSize2.Height() );
+ }
+ } else { // also if bTextRota90==TRUE
+ switch (eMH) {
+ case css::drawing::MeasureTextHorzPos_LEFTOUTSIDE: aTextPos.setX(aPt1b.X()-aTextSize2.Height()-nArr1Len ); break;
+ case css::drawing::MeasureTextHorzPos_RIGHTOUTSIDE: aTextPos.setX(aPt1b.X()+nLen+nArr2Len ); break;
+ default: aTextPos.setX(aPt1b.X() ); aTextSize2.setHeight(nLen );
+ }
+ switch (eMV) {
+ case css::drawing::MeasureTextVertPos_CENTERED:
+ aTextPos.setY(aPt1b.Y()+aTextSize2.Width()/2 ); break;
+ case css::drawing::MeasureTextVertPos_WEST: {
+ if (!bBelowRefEdge) aTextPos.setY(aPt1b.Y()+aTextSize2.Width()+nLWdt );
+ else aTextPos.setY(aPt1b.Y()-nLWdt );
+ } break;
+ default: {
+ if (!bBelowRefEdge) aTextPos.setY(aPt1b.Y()-nLWdt );
+ else aTextPos.setY(aPt1b.Y()+aTextSize2.Width()+nLWdt );
+ }
+ }
+ if (bUpsideDown) {
+ aTextPos.AdjustX(aTextSize2.Height() );
+ aTextPos.AdjustY( -(aTextSize2.Width()) );
+ }
+ }
+ if (aMPol.nTextAngle != maGeo.nRotationAngle) {
+ const_cast<SdrMeasureObj*>(this)->maGeo.nRotationAngle=aMPol.nTextAngle;
+ const_cast<SdrMeasureObj*>(this)->maGeo.RecalcSinCos();
+ }
+ RotatePoint(aTextPos,aPt1b,aMPol.nLineSin,aMPol.nLineCos);
+ aTextSize2.AdjustWidth( 1 ); aTextSize2.AdjustHeight( 1 ); // because of the Rect-Ctor's odd behavior
+ rRect=tools::Rectangle(aTextPos,aTextSize2);
+ rRect.Justify();
+ const_cast<SdrMeasureObj*>(this)->maRect=rRect;
+
+ if (aMPol.nTextAngle != maGeo.nRotationAngle) {
+ const_cast<SdrMeasureObj*>(this)->maGeo.nRotationAngle=aMPol.nTextAngle;
+ const_cast<SdrMeasureObj*>(this)->maGeo.RecalcSinCos();
+ }
+}
+
+SdrMeasureObj* SdrMeasureObj::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new SdrMeasureObj(rTargetModel, *this);
+}
+
+OUString SdrMeasureObj::TakeObjNameSingul() const
+{
+ OUString sName(SvxResId(STR_ObjNameSingulMEASURE));
+
+ OUString aName( GetName() );
+ if (!aName.isEmpty())
+ sName += " '" + aName + "'";
+
+ return sName;
+}
+
+OUString SdrMeasureObj::TakeObjNamePlural() const
+{
+ return SvxResId(STR_ObjNamePluralMEASURE);
+}
+
+basegfx::B2DPolyPolygon SdrMeasureObj::TakeXorPoly() const
+{
+ ImpMeasureRec aRec;
+ ImpMeasurePoly aMPol;
+ ImpTakeAttr(aRec);
+ ImpCalcGeometrics(aRec,aMPol);
+ return ImpCalcXPoly(aMPol);
+}
+
+sal_uInt32 SdrMeasureObj::GetHdlCount() const
+{
+ return 6;
+}
+
+void SdrMeasureObj::AddToHdlList(SdrHdlList& rHdlList) const
+{
+ ImpMeasureRec aRec;
+ ImpMeasurePoly aMPol;
+ ImpTakeAttr(aRec);
+ aRec.nHelplineDist=0;
+ ImpCalcGeometrics(aRec,aMPol);
+
+ for (sal_uInt32 nHdlNum=0; nHdlNum<6; ++nHdlNum)
+ {
+ Point aPt;
+ switch (nHdlNum) {
+ case 0: aPt=aMPol.aHelpline1.aP1; break;
+ case 1: aPt=aMPol.aHelpline2.aP1; break;
+ case 2: aPt=aPt1; break;
+ case 3: aPt=aPt2; break;
+ case 4: aPt=aMPol.aHelpline1.aP2; break;
+ case 5: aPt=aMPol.aHelpline2.aP2; break;
+ } // switch
+ std::unique_ptr<SdrHdl> pHdl(new ImpMeasureHdl(aPt,SdrHdlKind::User));
+ pHdl->SetObjHdlNum(nHdlNum);
+ pHdl->SetRotationAngle(aMPol.nLineAngle);
+ rHdlList.AddHdl(std::move(pHdl));
+ }
+}
+
+
+bool SdrMeasureObj::hasSpecialDrag() const
+{
+ return true;
+}
+
+bool SdrMeasureObj::beginSpecialDrag(SdrDragStat& rDrag) const
+{
+ const SdrHdl* pHdl = rDrag.GetHdl();
+
+ if(pHdl)
+ {
+ const sal_uInt32 nHdlNum(pHdl->GetObjHdlNum());
+
+ if(nHdlNum != 2 && nHdlNum != 3)
+ {
+ rDrag.SetEndDragChangesAttributes(true);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+bool SdrMeasureObj::applySpecialDrag(SdrDragStat& rDrag)
+{
+ ImpMeasureRec aMeasureRec;
+ const SdrHdl* pHdl = rDrag.GetHdl();
+ const sal_uInt32 nHdlNum(pHdl->GetObjHdlNum());
+
+ ImpTakeAttr(aMeasureRec);
+ ImpEvalDrag(aMeasureRec, rDrag);
+
+ switch (nHdlNum)
+ {
+ case 2:
+ {
+ aPt1 = aMeasureRec.aPt1;
+ SetTextDirty();
+ break;
+ }
+ case 3:
+ {
+ aPt2 = aMeasureRec.aPt2;
+ SetTextDirty();
+ break;
+ }
+ default:
+ {
+ switch(nHdlNum)
+ {
+ case 0:
+ case 1:
+ {
+ ImpMeasureRec aOrigMeasureRec;
+ ImpTakeAttr(aOrigMeasureRec);
+
+ if(aMeasureRec.nHelpline1Len != aOrigMeasureRec.nHelpline1Len)
+ {
+ SetObjectItem(makeSdrMeasureHelpline1LenItem(aMeasureRec.nHelpline1Len));
+ }
+
+ if(aMeasureRec.nHelpline2Len != aOrigMeasureRec.nHelpline2Len)
+ {
+ SetObjectItem(makeSdrMeasureHelpline2LenItem(aMeasureRec.nHelpline2Len));
+ }
+
+ break;
+ }
+
+ case 4:
+ case 5:
+ {
+ ImpMeasureRec aOrigMeasureRec;
+ ImpTakeAttr(aOrigMeasureRec);
+
+ if(aMeasureRec.nLineDist != aOrigMeasureRec.nLineDist)
+ {
+ SetObjectItem(makeSdrMeasureLineDistItem(aMeasureRec.nLineDist));
+ }
+
+ if(aMeasureRec.bBelowRefEdge != aOrigMeasureRec.bBelowRefEdge)
+ {
+ SetObjectItem(SdrMeasureBelowRefEdgeItem(aMeasureRec.bBelowRefEdge));
+ }
+ }
+ }
+ }
+ } // switch
+
+ SetBoundAndSnapRectsDirty();
+ SetChanged();
+
+ return true;
+}
+
+OUString SdrMeasureObj::getSpecialDragComment(const SdrDragStat& /*rDrag*/) const
+{
+ return OUString();
+}
+
+void SdrMeasureObj::ImpEvalDrag(ImpMeasureRec& rRec, const SdrDragStat& rDrag) const
+{
+ Degree100 nLineAngle=GetAngle(rRec.aPt2-rRec.aPt1);
+ double a = toRadians(nLineAngle);
+ double nSin=sin(a);
+ double nCos=cos(a);
+
+ const SdrHdl* pHdl=rDrag.GetHdl();
+ sal_uInt32 nHdlNum(pHdl->GetObjHdlNum());
+ bool bOrtho=rDrag.GetView()!=nullptr && rDrag.GetView()->IsOrtho();
+ bool bBigOrtho=bOrtho && rDrag.GetView()->IsBigOrtho();
+ bool bBelow=rRec.bBelowRefEdge;
+ Point aPt(rDrag.GetNow());
+
+ switch (nHdlNum) {
+ case 0: {
+ RotatePoint(aPt,aPt1,nSin,-nCos);
+ rRec.nHelpline1Len=aPt1.Y()-aPt.Y();
+ if (bBelow) rRec.nHelpline1Len=-rRec.nHelpline1Len;
+ if (bOrtho) rRec.nHelpline2Len=rRec.nHelpline1Len;
+ } break;
+ case 1: {
+ RotatePoint(aPt,aPt2,nSin,-nCos);
+ rRec.nHelpline2Len=aPt2.Y()-aPt.Y();
+ if (bBelow) rRec.nHelpline2Len=-rRec.nHelpline2Len;
+ if (bOrtho) rRec.nHelpline1Len=rRec.nHelpline2Len;
+ } break;
+ case 2: case 3: {
+ bool bAnf=nHdlNum==2;
+ Point& rMov=bAnf ? rRec.aPt1 : rRec.aPt2;
+ Point aMov(rMov);
+ Point aFix(bAnf ? rRec.aPt2 : rRec.aPt1);
+ if (bOrtho) {
+ tools::Long ndx0=aMov.X()-aFix.X();
+ tools::Long ndy0=aMov.Y()-aFix.Y();
+ bool bHLin=ndy0==0;
+ bool bVLin=ndx0==0;
+ if (!bHLin || !bVLin) { // else aPt1==aPt2
+ tools::Long ndx=aPt.X()-aFix.X();
+ tools::Long ndy=aPt.Y()-aFix.Y();
+ double nXFact=0; if (!bVLin) nXFact=static_cast<double>(ndx)/static_cast<double>(ndx0);
+ double nYFact=0; if (!bHLin) nYFact=static_cast<double>(ndy)/static_cast<double>(ndy0);
+ bool bHor=bHLin || (!bVLin && (nXFact>nYFact) ==bBigOrtho);
+ bool bVer=bVLin || (!bHLin && (nXFact<=nYFact)==bBigOrtho);
+ if (bHor) ndy=tools::Long(ndy0*nXFact);
+ if (bVer) ndx=tools::Long(ndx0*nYFact);
+ aPt=aFix;
+ aPt.AdjustX(ndx );
+ aPt.AdjustY(ndy );
+ } // else Ortho8
+ }
+ rMov=aPt;
+ } break;
+ case 4: case 5: {
+ tools::Long nVal0=rRec.nLineDist;
+ RotatePoint(aPt,(nHdlNum==4 ? aPt1 : aPt2),nSin,-nCos);
+ rRec.nLineDist=aPt.Y()- (nHdlNum==4 ? aPt1.Y() : aPt2.Y());
+ if (bBelow) rRec.nLineDist=-rRec.nLineDist;
+ if (rRec.nLineDist<0) {
+ rRec.nLineDist=-rRec.nLineDist;
+ rRec.bBelowRefEdge=!bBelow;
+ }
+ rRec.nLineDist-=rRec.nHelplineOverhang;
+ if (bOrtho) rRec.nLineDist=nVal0;
+ } break;
+ } // switch
+}
+
+
+bool SdrMeasureObj::BegCreate(SdrDragStat& rStat)
+{
+ rStat.SetOrtho8Possible();
+ aPt1=rStat.GetStart();
+ aPt2=rStat.GetNow();
+ SetTextDirty();
+ return true;
+}
+
+bool SdrMeasureObj::MovCreate(SdrDragStat& rStat)
+{
+ SdrView* pView=rStat.GetView();
+ aPt1=rStat.GetStart();
+ aPt2=rStat.GetNow();
+ if (pView!=nullptr && pView->IsCreate1stPointAsCenter()) {
+ aPt1+=aPt1;
+ aPt1-=rStat.GetNow();
+ }
+ SetTextDirty();
+ SetBoundRectDirty();
+ m_bSnapRectDirty=true;
+ return true;
+}
+
+bool SdrMeasureObj::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd)
+{
+ SetTextDirty();
+ SetBoundAndSnapRectsDirty();
+ return (eCmd==SdrCreateCmd::ForceEnd || rStat.GetPointCount()>=2);
+}
+
+bool SdrMeasureObj::BckCreate(SdrDragStat& /*rStat*/)
+{
+ return false;
+}
+
+void SdrMeasureObj::BrkCreate(SdrDragStat& /*rStat*/)
+{
+}
+
+basegfx::B2DPolyPolygon SdrMeasureObj::TakeCreatePoly(const SdrDragStat& /*rDrag*/) const
+{
+ ImpMeasureRec aRec;
+ ImpMeasurePoly aMPol;
+
+ ImpTakeAttr(aRec);
+ ImpCalcGeometrics(aRec, aMPol);
+
+ return ImpCalcXPoly(aMPol);
+}
+
+PointerStyle SdrMeasureObj::GetCreatePointer() const
+{
+ return PointerStyle::Cross;
+}
+
+void SdrMeasureObj::NbcMove(const Size& rSiz)
+{
+ SdrTextObj::NbcMove(rSiz);
+ aPt1.Move(rSiz);
+ aPt2.Move(rSiz);
+}
+
+void SdrMeasureObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
+{
+ SdrTextObj::NbcResize(rRef,xFact,yFact);
+ ResizePoint(aPt1,rRef,xFact,yFact);
+ ResizePoint(aPt2,rRef,xFact,yFact);
+ SetTextDirty();
+}
+
+void SdrMeasureObj::NbcRotate(const Point& rRef, Degree100 nAngle, double sn, double cs)
+{
+ SdrTextObj::NbcRotate(rRef,nAngle,sn,cs);
+ tools::Long nLen0=GetLen(aPt2-aPt1);
+ RotatePoint(aPt1,rRef,sn,cs);
+ RotatePoint(aPt2,rRef,sn,cs);
+ tools::Long nLen1=GetLen(aPt2-aPt1);
+ if (nLen1!=nLen0) { // rounding error!
+ tools::Long dx=aPt2.X()-aPt1.X();
+ tools::Long dy=aPt2.Y()-aPt1.Y();
+ dx=BigMulDiv(dx,nLen0,nLen1);
+ dy=BigMulDiv(dy,nLen0,nLen1);
+ if (rRef==aPt2) {
+ aPt1.setX(aPt2.X()-dx );
+ aPt1.setY(aPt2.Y()-dy );
+ } else {
+ aPt2.setX(aPt1.X()+dx );
+ aPt2.setY(aPt1.Y()+dy );
+ }
+ }
+ SetBoundAndSnapRectsDirty();
+}
+
+void SdrMeasureObj::NbcMirror(const Point& rRef1, const Point& rRef2)
+{
+ SdrTextObj::NbcMirror(rRef1,rRef2);
+ MirrorPoint(aPt1,rRef1,rRef2);
+ MirrorPoint(aPt2,rRef1,rRef2);
+ SetBoundAndSnapRectsDirty();
+}
+
+void SdrMeasureObj::NbcShear(const Point& rRef, Degree100 nAngle, double tn, bool bVShear)
+{
+ SdrTextObj::NbcShear(rRef,nAngle,tn,bVShear);
+ ShearPoint(aPt1,rRef,tn,bVShear);
+ ShearPoint(aPt2,rRef,tn,bVShear);
+ SetBoundAndSnapRectsDirty();
+ SetTextDirty();
+}
+
+Degree100 SdrMeasureObj::GetRotateAngle() const
+{
+ return GetAngle(aPt2-aPt1);
+}
+
+void SdrMeasureObj::RecalcSnapRect()
+{
+ ImpMeasureRec aRec;
+ ImpMeasurePoly aMPol;
+ XPolyPolygon aXPP;
+
+ ImpTakeAttr(aRec);
+ ImpCalcGeometrics(aRec, aMPol);
+ aXPP = XPolyPolygon(ImpCalcXPoly(aMPol));
+ maSnapRect = aXPP.GetBoundRect();
+}
+
+sal_uInt32 SdrMeasureObj::GetSnapPointCount() const
+{
+ return 2;
+}
+
+Point SdrMeasureObj::GetSnapPoint(sal_uInt32 i) const
+{
+ if (i==0) return aPt1;
+ else return aPt2;
+}
+
+bool SdrMeasureObj::IsPolyObj() const
+{
+ return true;
+}
+
+sal_uInt32 SdrMeasureObj::GetPointCount() const
+{
+ return 2;
+}
+
+Point SdrMeasureObj::GetPoint(sal_uInt32 i) const
+{
+ return (0 == i) ? aPt1 : aPt2;
+}
+
+void SdrMeasureObj::NbcSetPoint(const Point& rPnt, sal_uInt32 i)
+{
+ if (0 == i)
+ aPt1=rPnt;
+ if (1 == i)
+ aPt2=rPnt;
+ SetBoundAndSnapRectsDirty();
+ SetTextDirty();
+}
+
+std::unique_ptr<SdrObjGeoData> SdrMeasureObj::NewGeoData() const
+{
+ return std::make_unique<SdrMeasureObjGeoData>();
+}
+
+void SdrMeasureObj::SaveGeoData(SdrObjGeoData& rGeo) const
+{
+ SdrTextObj::SaveGeoData(rGeo);
+ SdrMeasureObjGeoData& rMGeo=static_cast<SdrMeasureObjGeoData&>(rGeo);
+ rMGeo.aPt1=aPt1;
+ rMGeo.aPt2=aPt2;
+}
+
+void SdrMeasureObj::RestoreGeoData(const SdrObjGeoData& rGeo)
+{
+ SdrTextObj::RestoreGeoData(rGeo);
+ const SdrMeasureObjGeoData& rMGeo=static_cast<const SdrMeasureObjGeoData&>(rGeo);
+ aPt1=rMGeo.aPt1;
+ aPt2=rMGeo.aPt2;
+ SetTextDirty();
+}
+
+SdrObjectUniquePtr SdrMeasureObj::DoConvertToPolyObj(bool bBezier, bool bAddText) const
+{
+ // get XOR Poly as base
+ XPolyPolygon aTmpPolyPolygon(TakeXorPoly());
+
+ // get local ItemSet and StyleSheet
+ SfxItemSet aSet(GetObjectItemSet());
+ SfxStyleSheet* pStyleSheet = GetStyleSheet();
+
+ // prepare group
+ std::unique_ptr<SdrObjGroup,SdrObjectFreeOp> pGroup(new SdrObjGroup(getSdrModelFromSdrObject()));
+
+ // prepare parameters
+ basegfx::B2DPolyPolygon aPolyPoly;
+ SdrPathObj* pPath;
+ sal_uInt16 nCount(aTmpPolyPolygon.Count());
+ sal_uInt16 nLoopStart(0);
+
+ if(nCount == 3)
+ {
+ // three lines, first one is the middle one
+ aPolyPoly.clear();
+ aPolyPoly.append(aTmpPolyPolygon[0].getB2DPolygon());
+
+ pPath = new SdrPathObj(
+ getSdrModelFromSdrObject(),
+ SdrObjKind::PathLine,
+ aPolyPoly);
+
+ pPath->SetMergedItemSet(aSet);
+ pPath->SetStyleSheet(pStyleSheet, true);
+ pGroup->GetSubList()->NbcInsertObject(pPath);
+ aSet.Put(XLineStartWidthItem(0));
+ aSet.Put(XLineEndWidthItem(0));
+ nLoopStart = 1;
+ }
+ else if(nCount == 4)
+ {
+ // four lines, middle line with gap, so there are two lines used
+ // which have one arrow each
+ sal_Int32 nEndWidth = aSet.Get(XATTR_LINEENDWIDTH).GetValue();
+ aSet.Put(XLineEndWidthItem(0));
+
+ aPolyPoly.clear();
+ aPolyPoly.append(aTmpPolyPolygon[0].getB2DPolygon());
+ pPath = new SdrPathObj(
+ getSdrModelFromSdrObject(),
+ SdrObjKind::PathLine,
+ aPolyPoly);
+
+ pPath->SetMergedItemSet(aSet);
+ pPath->SetStyleSheet(pStyleSheet, true);
+
+ pGroup->GetSubList()->NbcInsertObject(pPath);
+
+ aSet.Put(XLineEndWidthItem(nEndWidth));
+ aSet.Put(XLineStartWidthItem(0));
+
+ aPolyPoly.clear();
+ aPolyPoly.append(aTmpPolyPolygon[1].getB2DPolygon());
+ pPath = new SdrPathObj(
+ getSdrModelFromSdrObject(),
+ SdrObjKind::PathLine,
+ aPolyPoly);
+
+ pPath->SetMergedItemSet(aSet);
+ pPath->SetStyleSheet(pStyleSheet, true);
+
+ pGroup->GetSubList()->NbcInsertObject(pPath);
+
+ aSet.Put(XLineEndWidthItem(0));
+ nLoopStart = 2;
+ }
+ else if(nCount == 5)
+ {
+ // five lines, first two are the outer ones
+ sal_Int32 nEndWidth = aSet.Get(XATTR_LINEENDWIDTH).GetValue();
+
+ aSet.Put(XLineEndWidthItem(0));
+
+ aPolyPoly.clear();
+ aPolyPoly.append(aTmpPolyPolygon[0].getB2DPolygon());
+ pPath = new SdrPathObj(
+ getSdrModelFromSdrObject(),
+ SdrObjKind::PathLine,
+ aPolyPoly);
+
+ pPath->SetMergedItemSet(aSet);
+ pPath->SetStyleSheet(pStyleSheet, true);
+
+ pGroup->GetSubList()->NbcInsertObject(pPath);
+
+ aSet.Put(XLineEndWidthItem(nEndWidth));
+ aSet.Put(XLineStartWidthItem(0));
+
+ aPolyPoly.clear();
+ aPolyPoly.append(aTmpPolyPolygon[1].getB2DPolygon());
+ pPath = new SdrPathObj(
+ getSdrModelFromSdrObject(),
+ SdrObjKind::PathLine,
+ aPolyPoly);
+
+ pPath->SetMergedItemSet(aSet);
+ pPath->SetStyleSheet(pStyleSheet, true);
+
+ pGroup->GetSubList()->NbcInsertObject(pPath);
+
+ aSet.Put(XLineEndWidthItem(0));
+ nLoopStart = 2;
+ }
+
+ for(;nLoopStart<nCount;nLoopStart++)
+ {
+ aPolyPoly.clear();
+ aPolyPoly.append(aTmpPolyPolygon[nLoopStart].getB2DPolygon());
+ pPath = new SdrPathObj(
+ getSdrModelFromSdrObject(),
+ SdrObjKind::PathLine,
+ aPolyPoly);
+
+ pPath->SetMergedItemSet(aSet);
+ pPath->SetStyleSheet(pStyleSheet, true);
+
+ pGroup->GetSubList()->NbcInsertObject(pPath);
+ }
+
+ if(bAddText)
+ {
+ return ImpConvertAddText(std::move(pGroup), bBezier);
+ }
+ else
+ {
+ return pGroup;
+ }
+}
+
+bool SdrMeasureObj::BegTextEdit(SdrOutliner& rOutl)
+{
+ UndirtyText();
+ return SdrTextObj::BegTextEdit(rOutl);
+}
+
+const Size& SdrMeasureObj::GetTextSize() const
+{
+ if (bTextDirty) UndirtyText();
+ return SdrTextObj::GetTextSize();
+}
+
+OutlinerParaObject* SdrMeasureObj::GetOutlinerParaObject() const
+{
+ if(bTextDirty)
+ UndirtyText();
+ return SdrTextObj::GetOutlinerParaObject();
+}
+
+void SdrMeasureObj::NbcSetOutlinerParaObject(std::optional<OutlinerParaObject> pTextObject)
+{
+ SdrTextObj::NbcSetOutlinerParaObject(std::move(pTextObject));
+ if(SdrTextObj::GetOutlinerParaObject())
+ SetTextDirty(); // recalculate text
+}
+
+void SdrMeasureObj::TakeTextRect( SdrOutliner& rOutliner, tools::Rectangle& rTextRect, bool bNoEditText,
+ tools::Rectangle* pAnchorRect, bool bLineWidth ) const
+{
+ if (bTextDirty) UndirtyText();
+ SdrTextObj::TakeTextRect( rOutliner, rTextRect, bNoEditText, pAnchorRect, bLineWidth );
+}
+
+void SdrMeasureObj::TakeTextAnchorRect(tools::Rectangle& rAnchorRect) const
+{
+ if (bTextDirty) UndirtyText();
+ SdrTextObj::TakeTextAnchorRect(rAnchorRect);
+}
+
+void SdrMeasureObj::TakeTextEditArea(Size* pPaperMin, Size* pPaperMax, tools::Rectangle* pViewInit, tools::Rectangle* pViewMin) const
+{
+ if (bTextDirty) UndirtyText();
+ SdrTextObj::TakeTextEditArea(pPaperMin,pPaperMax,pViewInit,pViewMin);
+}
+
+EEAnchorMode SdrMeasureObj::GetOutlinerViewAnchorMode() const
+{
+ if (bTextDirty) UndirtyText();
+ ImpMeasureRec aRec;
+ ImpMeasurePoly aMPol;
+ ImpTakeAttr(aRec);
+ ImpCalcGeometrics(aRec,aMPol);
+
+ SdrTextHorzAdjust eTH=GetTextHorizontalAdjust();
+ SdrTextVertAdjust eTV=GetTextVerticalAdjust();
+ css::drawing::MeasureTextHorzPos eMH = aMPol.eUsedTextHPos;
+ css::drawing::MeasureTextVertPos eMV = aMPol.eUsedTextVPos;
+ bool bTextRota90=aRec.bTextRota90;
+ bool bBelowRefEdge=aRec.bBelowRefEdge;
+
+ // TODO: bTextUpsideDown should be interpreted here!
+ if (!bTextRota90) {
+ if (eMH==css::drawing::MeasureTextHorzPos_LEFTOUTSIDE) eTH=SDRTEXTHORZADJUST_RIGHT;
+ if (eMH==css::drawing::MeasureTextHorzPos_RIGHTOUTSIDE) eTH=SDRTEXTHORZADJUST_LEFT;
+ // at eMH==css::drawing::MeasureTextHorzPos_INSIDE we can anchor horizontally
+ if (eMV==css::drawing::MeasureTextVertPos_EAST) eTV=SDRTEXTVERTADJUST_BOTTOM;
+ if (eMV==css::drawing::MeasureTextVertPos_WEST) eTV=SDRTEXTVERTADJUST_TOP;
+ if (eMV==css::drawing::MeasureTextVertPos_CENTERED) eTV=SDRTEXTVERTADJUST_CENTER;
+ } else {
+ if (eMH==css::drawing::MeasureTextHorzPos_LEFTOUTSIDE) eTV=SDRTEXTVERTADJUST_BOTTOM;
+ if (eMH==css::drawing::MeasureTextHorzPos_RIGHTOUTSIDE) eTV=SDRTEXTVERTADJUST_TOP;
+ // at eMH==css::drawing::MeasureTextHorzPos_INSIDE we can anchor vertically
+ if (!bBelowRefEdge) {
+ if (eMV==css::drawing::MeasureTextVertPos_EAST) eTH=SDRTEXTHORZADJUST_LEFT;
+ if (eMV==css::drawing::MeasureTextVertPos_WEST) eTH=SDRTEXTHORZADJUST_RIGHT;
+ } else {
+ if (eMV==css::drawing::MeasureTextVertPos_EAST) eTH=SDRTEXTHORZADJUST_RIGHT;
+ if (eMV==css::drawing::MeasureTextVertPos_WEST) eTH=SDRTEXTHORZADJUST_LEFT;
+ }
+ if (eMV==css::drawing::MeasureTextVertPos_CENTERED) eTH=SDRTEXTHORZADJUST_CENTER;
+ }
+
+ EEAnchorMode eRet=EEAnchorMode::BottomHCenter;
+ if (eTH==SDRTEXTHORZADJUST_LEFT) {
+ if (eTV==SDRTEXTVERTADJUST_TOP) eRet=EEAnchorMode::TopLeft;
+ else if (eTV==SDRTEXTVERTADJUST_BOTTOM) eRet=EEAnchorMode::BottomLeft;
+ else eRet=EEAnchorMode::VCenterLeft;
+ } else if (eTH==SDRTEXTHORZADJUST_RIGHT) {
+ if (eTV==SDRTEXTVERTADJUST_TOP) eRet=EEAnchorMode::TopRight;
+ else if (eTV==SDRTEXTVERTADJUST_BOTTOM) eRet=EEAnchorMode::BottomRight;
+ else eRet=EEAnchorMode::VCenterRight;
+ } else {
+ if (eTV==SDRTEXTVERTADJUST_TOP) eRet=EEAnchorMode::TopHCenter;
+ else if (eTV==SDRTEXTVERTADJUST_BOTTOM) eRet=EEAnchorMode::BottomHCenter;
+ else eRet=EEAnchorMode::VCenterHCenter;
+ }
+ return eRet;
+}
+
+
+// #i97878#
+// TRGetBaseGeometry/TRSetBaseGeometry needs to be based on two positions,
+// same as line geometry in SdrPathObj. Thus needs to be overridden and
+// implemented since currently it is derived from SdrTextObj which uses
+// a functionality based on SnapRect which is not useful here
+
+bool SdrMeasureObj::TRGetBaseGeometry(basegfx::B2DHomMatrix& rMatrix, basegfx::B2DPolyPolygon& /*rPolyPolygon*/) const
+{
+ // handle the same as a simple line since the definition is based on two points
+ const basegfx::B2DRange aRange(aPt1.X(), aPt1.Y(), aPt2.X(), aPt2.Y());
+ basegfx::B2DTuple aScale(aRange.getRange());
+ basegfx::B2DTuple aTranslate(aRange.getMinimum());
+
+ // position maybe relative to anchor position, convert
+ if( getSdrModelFromSdrObject().IsWriter() )
+ {
+ if(GetAnchorPos().X() || GetAnchorPos().Y())
+ {
+ aTranslate -= basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
+ }
+ }
+
+ // build return value matrix
+ rMatrix = basegfx::utils::createScaleTranslateB2DHomMatrix(aScale, aTranslate);
+
+ return true;
+}
+
+void SdrMeasureObj::TRSetBaseGeometry(const basegfx::B2DHomMatrix& rMatrix, const basegfx::B2DPolyPolygon& /*rPolyPolygon*/)
+{
+ // use given transformation to derive the two defining points from unit line
+ basegfx::B2DPoint aPosA(rMatrix * basegfx::B2DPoint(0.0, 0.0));
+ basegfx::B2DPoint aPosB(rMatrix * basegfx::B2DPoint(1.0, 0.0));
+
+ if( getSdrModelFromSdrObject().IsWriter() )
+ {
+ // if anchor is used, make position relative to it
+ if(GetAnchorPos().X() || GetAnchorPos().Y())
+ {
+ const basegfx::B2DVector aAnchorOffset(GetAnchorPos().X(), GetAnchorPos().Y());
+
+ aPosA += aAnchorOffset;
+ aPosB += aAnchorOffset;
+ }
+ }
+
+ // derive new model data
+ const Point aNewPt1(basegfx::fround(aPosA.getX()), basegfx::fround(aPosA.getY()));
+ const Point aNewPt2(basegfx::fround(aPosB.getX()), basegfx::fround(aPosB.getY()));
+
+ if(aNewPt1 == aPt1 && aNewPt2 == aPt2)
+ return;
+
+ // set model values and broadcast
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+
+ aPt1 = aNewPt1;
+ aPt2 = aNewPt2;
+
+ SetTextDirty();
+ ActionChanged();
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::MoveOnly,aBoundRect0);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdomedia.cxx b/svx/source/svdraw/svdomedia.cxx
new file mode 100644
index 000000000..46a35c2d6
--- /dev/null
+++ b/svx/source/svdraw/svdomedia.cxx
@@ -0,0 +1,457 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column:100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_features.h>
+
+#include <svx/svdomedia.hxx>
+
+#include <com/sun/star/text/GraphicCrop.hpp>
+
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+
+#include <ucbhelper/content.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/storagehelper.hxx>
+
+#include <vcl/svapp.hxx>
+
+#include <svx/svdmodel.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <svx/sdr/contact/viewcontactofsdrmediaobj.hxx>
+#include <avmedia/mediawindow.hxx>
+#include <tools/diagnose_ex.h>
+
+using namespace ::com::sun::star;
+
+
+struct SdrMediaObj::Impl
+{
+ ::avmedia::MediaItem m_MediaProperties;
+#if HAVE_FEATURE_AVMEDIA
+ // Note: the temp file is read only, until it is deleted!
+ // It may be shared between multiple documents in case of copy/paste,
+ // hence the shared_ptr.
+ std::shared_ptr< ::avmedia::MediaTempFile > m_pTempFile;
+#endif
+ uno::Reference< graphic::XGraphic > m_xCachedSnapshot;
+ rtl::Reference<avmedia::PlayerListener> m_xPlayerListener;
+ OUString m_LastFailedPkgURL;
+};
+
+SdrMediaObj::SdrMediaObj(SdrModel& rSdrModel)
+: SdrRectObj(rSdrModel)
+ ,m_xImpl( new Impl )
+{
+}
+
+SdrMediaObj::SdrMediaObj(SdrModel& rSdrModel, SdrMediaObj const & rSource)
+: SdrRectObj(rSdrModel, rSource)
+ ,m_xImpl( new Impl )
+{
+#if HAVE_FEATURE_AVMEDIA
+ m_xImpl->m_pTempFile = rSource.m_xImpl->m_pTempFile; // before props
+#endif
+ setMediaProperties( rSource.getMediaProperties() );
+ m_xImpl->m_xCachedSnapshot = rSource.m_xImpl->m_xCachedSnapshot;
+}
+
+SdrMediaObj::SdrMediaObj(
+ SdrModel& rSdrModel,
+ const tools::Rectangle& rRect)
+: SdrRectObj(rSdrModel, rRect)
+ ,m_xImpl( new Impl )
+{
+ const bool bUndo(rSdrModel.IsUndoEnabled());
+ rSdrModel.EnableUndo(false);
+ MakeNameUnique();
+ rSdrModel.EnableUndo(bUndo);
+}
+
+SdrMediaObj::~SdrMediaObj()
+{
+}
+
+bool SdrMediaObj::HasTextEdit() const
+{
+ return false;
+}
+
+std::unique_ptr<sdr::contact::ViewContact> SdrMediaObj::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::ViewContactOfSdrMediaObj>( *this );
+}
+
+void SdrMediaObj::TakeObjInfo( SdrObjTransformInfoRec& rInfo ) const
+{
+ rInfo.bMoveAllowed = true;
+ rInfo.bResizeFreeAllowed = true;
+ rInfo.bResizePropAllowed = true;
+ rInfo.bRotateFreeAllowed = false;
+ rInfo.bRotate90Allowed = false;
+ rInfo.bMirrorFreeAllowed = false;
+ rInfo.bMirror45Allowed = false;
+ rInfo.bMirror90Allowed = false;
+ rInfo.bTransparenceAllowed = false;
+ rInfo.bShearAllowed = false;
+ rInfo.bEdgeRadiusAllowed = false;
+ rInfo.bNoOrthoDesired = false;
+ rInfo.bNoContortion = false;
+ rInfo.bCanConvToPath = false;
+ rInfo.bCanConvToPoly = false;
+ rInfo.bCanConvToContour = false;
+ rInfo.bCanConvToPathLineToArea = false;
+ rInfo.bCanConvToPolyLineToArea = false;
+}
+
+SdrObjKind SdrMediaObj::GetObjIdentifier() const
+{
+ return SdrObjKind::Media;
+}
+
+OUString SdrMediaObj::TakeObjNameSingul() const
+{
+ OUString sName(SvxResId(STR_ObjNameSingulMEDIA));
+
+ OUString aName(GetName());
+
+ if (!aName.isEmpty())
+ sName += " '" + aName + "'";
+
+ return sName;
+}
+
+OUString SdrMediaObj::TakeObjNamePlural() const
+{
+ return SvxResId(STR_ObjNamePluralMEDIA);
+}
+
+SdrMediaObj* SdrMediaObj::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new SdrMediaObj(rTargetModel, *this);
+}
+
+uno::Reference< graphic::XGraphic > const & SdrMediaObj::getSnapshot() const
+{
+#if HAVE_FEATURE_AVMEDIA
+ if( !m_xImpl->m_xCachedSnapshot.is() )
+ {
+ Graphic aGraphic = m_xImpl->m_MediaProperties.getGraphic();
+ if (!aGraphic.IsNone())
+ {
+ Size aPref = aGraphic.GetPrefSize();
+ Size aPixel = aGraphic.GetSizePixel();
+ const text::GraphicCrop& rCrop = m_xImpl->m_MediaProperties.getCrop();
+ if (rCrop.Bottom > 0 || rCrop.Left > 0 || rCrop.Right > 0 || rCrop.Top > 0)
+ {
+ tools::Long nLeft = aPixel.getWidth() * rCrop.Left / aPref.getWidth();
+ tools::Long nTop = aPixel.getHeight() * rCrop.Top / aPref.getHeight();
+ tools::Long nRight = aPixel.getWidth() * rCrop.Right / aPref.getWidth();
+ tools::Long nBottom = aPixel.getHeight() * rCrop.Bottom / aPref.getHeight();
+ BitmapEx aBitmapEx = aGraphic.GetBitmapEx();
+ aBitmapEx.Crop({nLeft, nTop, aPixel.getWidth() - nRight, aPixel.getHeight() - nBottom});
+ aGraphic = aBitmapEx;
+ }
+
+ // We have an explicit graphic for this media object, then go with that instead of
+ // generating our own one.
+ m_xImpl->m_xCachedSnapshot = aGraphic.GetXGraphic();
+ return m_xImpl->m_xCachedSnapshot;
+ }
+
+ OUString aRealURL = m_xImpl->m_MediaProperties.getTempURL();
+ if( aRealURL.isEmpty() )
+ aRealURL = m_xImpl->m_MediaProperties.getURL();
+ OUString sReferer = m_xImpl->m_MediaProperties.getReferer();
+ OUString sMimeType = m_xImpl->m_MediaProperties.getMimeType();
+ uno::Reference<graphic::XGraphic> xCachedSnapshot = m_xImpl->m_xCachedSnapshot;
+
+ m_xImpl->m_xPlayerListener.set(new avmedia::PlayerListener(
+ [this, xCachedSnapshot, aRealURL, sReferer, sMimeType](const css::uno::Reference<css::media::XPlayer>& rPlayer){
+ SolarMutexGuard g;
+ uno::Reference<graphic::XGraphic> xGraphic
+ = m_xImpl->m_MediaProperties.getGraphic().GetXGraphic();
+ m_xImpl->m_xCachedSnapshot = avmedia::MediaWindow::grabFrame(rPlayer, xGraphic);
+ ActionChanged();
+ }));
+
+ avmedia::MediaWindow::grabFrame(aRealURL, sReferer, sMimeType, m_xImpl->m_xPlayerListener);
+ }
+#endif
+ return m_xImpl->m_xCachedSnapshot;
+}
+
+void SdrMediaObj::AdjustToMaxRect( const tools::Rectangle& rMaxRect, bool bShrinkOnly /* = false */ )
+{
+ Size aSize( Application::GetDefaultDevice()->PixelToLogic(
+ static_cast< sdr::contact::ViewContactOfSdrMediaObj& >( GetViewContact() ).getPreferredSize(),
+ MapMode(MapUnit::Map100thMM)) );
+ Size aMaxSize( rMaxRect.GetSize() );
+
+ if( aSize.IsEmpty() )
+ return;
+
+ Point aPos( rMaxRect.TopLeft() );
+
+ // if graphic is too large, fit it to the page
+ if ( (!bShrinkOnly ||
+ ( aSize.Height() > aMaxSize.Height() ) ||
+ ( aSize.Width() > aMaxSize.Width() ) )&&
+ aSize.Height() && aMaxSize.Height() )
+ {
+ float fGrfWH = static_cast<float>(aSize.Width()) /
+ static_cast<float>(aSize.Height());
+ float fWinWH = static_cast<float>(aMaxSize.Width()) /
+ static_cast<float>(aMaxSize.Height());
+
+ // scale graphic to page size
+ if ( fGrfWH < fWinWH )
+ {
+ aSize.setWidth( static_cast<tools::Long>(aMaxSize.Height() * fGrfWH) );
+ aSize.setHeight( aMaxSize.Height() );
+ }
+ else if ( fGrfWH > 0.F )
+ {
+ aSize.setWidth( aMaxSize.Width() );
+ aSize.setHeight( static_cast<tools::Long>(aMaxSize.Width() / fGrfWH) );
+ }
+
+ aPos = rMaxRect.Center();
+ }
+
+ if( bShrinkOnly )
+ aPos = maRect.TopLeft();
+
+ aPos.AdjustX( -(aSize.Width() / 2) );
+ aPos.AdjustY( -(aSize.Height() / 2) );
+ SetLogicRect( tools::Rectangle( aPos, aSize ) );
+}
+
+void SdrMediaObj::setURL( const OUString& rURL, const OUString& rReferer, const OUString& rMimeType )
+{
+ ::avmedia::MediaItem aURLItem;
+#if HAVE_FEATURE_AVMEDIA
+ if( !rMimeType.isEmpty() )
+ m_xImpl->m_MediaProperties.setMimeType(rMimeType);
+ aURLItem.setURL( rURL, "", rReferer );
+#else
+ (void) rMimeType;
+ (void) rURL;
+ (void) rReferer;
+#endif
+ setMediaProperties( aURLItem );
+}
+
+const OUString& SdrMediaObj::getURL() const
+{
+#if HAVE_FEATURE_AVMEDIA
+ return m_xImpl->m_MediaProperties.getURL();
+#else
+ static OUString ret;
+ return ret;
+#endif
+}
+
+void SdrMediaObj::setMediaProperties( const ::avmedia::MediaItem& rState )
+{
+ mediaPropertiesChanged( rState );
+ static_cast< sdr::contact::ViewContactOfSdrMediaObj& >( GetViewContact() ).executeMediaItem( getMediaProperties() );
+}
+
+const ::avmedia::MediaItem& SdrMediaObj::getMediaProperties() const
+{
+ return m_xImpl->m_MediaProperties;
+}
+
+uno::Reference<io::XInputStream> SdrMediaObj::GetInputStream() const
+{
+#if HAVE_FEATURE_AVMEDIA
+ if (!m_xImpl->m_pTempFile)
+ {
+ SAL_WARN("svx", "this is only intended for embedded media");
+ return nullptr;
+ }
+ ucbhelper::Content tempFile(m_xImpl->m_pTempFile->m_TempFileURL,
+ uno::Reference<ucb::XCommandEnvironment>(),
+ comphelper::getProcessComponentContext());
+ return tempFile.openStream();
+#else
+ return nullptr;
+#endif
+}
+
+void SdrMediaObj::SetInputStream(uno::Reference<io::XInputStream> const& xStream)
+{
+#if !HAVE_FEATURE_AVMEDIA
+ (void) xStream;
+#else
+ if (m_xImpl->m_pTempFile || m_xImpl->m_LastFailedPkgURL.isEmpty())
+ {
+ SAL_WARN("svx", "this is only intended for embedded media");
+ return;
+ }
+
+ OUString tempFileURL;
+ const bool bSuccess(
+ ::avmedia::CreateMediaTempFile(
+ xStream,
+ tempFileURL,
+ u""));
+
+ if (bSuccess)
+ {
+ m_xImpl->m_pTempFile = std::make_shared<::avmedia::MediaTempFile>(tempFileURL);
+ m_xImpl->m_MediaProperties.setURL(
+ m_xImpl->m_LastFailedPkgURL, tempFileURL, "");
+ }
+ m_xImpl->m_LastFailedPkgURL.clear(); // once only
+#endif
+}
+
+/// copy a stream from XStorage to temp file
+#if HAVE_FEATURE_AVMEDIA
+static bool lcl_HandlePackageURL(
+ OUString const & rURL,
+ const SdrModel& rModel,
+ OUString & o_rTempFileURL)
+{
+ ::comphelper::LifecycleProxy sourceProxy;
+ uno::Reference<io::XInputStream> xInStream;
+ try {
+ xInStream = rModel.GetDocumentStream(rURL, sourceProxy);
+ }
+ catch (container::NoSuchElementException const&)
+ {
+ SAL_INFO("svx", "not found: '" << rURL << "'");
+ return false;
+ }
+ catch (uno::Exception const&)
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ return false;
+ }
+ if (!xInStream.is())
+ {
+ SAL_WARN("svx", "no stream?");
+ return false;
+ }
+ // Make sure the temporary copy has the same file name extension as the original media file
+ // (like .mp4). That seems to be important for some AVFoundation APIs. For random extension-less
+ // file names, they don't seem to even bother looking inside the file.
+ sal_Int32 nLastDot = rURL.lastIndexOf('.');
+ sal_Int32 nLastSlash = rURL.lastIndexOf('/');
+ OUString sDesiredExtension;
+ if (nLastDot > nLastSlash && nLastDot+1 < rURL.getLength())
+ sDesiredExtension = rURL.copy(nLastDot);
+ return ::avmedia::CreateMediaTempFile(xInStream, o_rTempFileURL, sDesiredExtension);
+}
+#endif
+
+void SdrMediaObj::mediaPropertiesChanged( const ::avmedia::MediaItem& rNewProperties )
+{
+ bool bBroadcastChanged = false;
+#if HAVE_FEATURE_AVMEDIA
+ const AVMediaSetMask nMaskSet = rNewProperties.getMaskSet();
+
+ // use only a subset of MediaItem properties for own properties
+ if( AVMediaSetMask::MIME_TYPE & nMaskSet )
+ m_xImpl->m_MediaProperties.setMimeType( rNewProperties.getMimeType() );
+
+ if (nMaskSet & AVMediaSetMask::GRAPHIC)
+ {
+ m_xImpl->m_MediaProperties.setGraphic(rNewProperties.getGraphic());
+ }
+
+ if (nMaskSet & AVMediaSetMask::CROP)
+ {
+ m_xImpl->m_MediaProperties.setCrop(rNewProperties.getCrop());
+ }
+
+ if( ( AVMediaSetMask::URL & nMaskSet ) &&
+ ( rNewProperties.getURL() != getURL() ))
+ {
+ m_xImpl->m_xCachedSnapshot.clear();
+ m_xImpl->m_xPlayerListener.clear();
+ OUString const& url(rNewProperties.getURL());
+ if (url.startsWithIgnoreAsciiCase("vnd.sun.star.Package:"))
+ {
+ if ( !m_xImpl->m_pTempFile
+ || (m_xImpl->m_pTempFile->m_TempFileURL !=
+ rNewProperties.getTempURL()))
+ {
+ OUString tempFileURL;
+ const bool bSuccess(
+ lcl_HandlePackageURL(
+ url,
+ getSdrModelFromSdrObject(),
+ tempFileURL));
+
+ if (bSuccess)
+ {
+ m_xImpl->m_pTempFile =
+ std::make_shared<::avmedia::MediaTempFile>(tempFileURL);
+ m_xImpl->m_MediaProperties.setURL(url, tempFileURL, "");
+ }
+ else // this case is for Clone via operator=
+ {
+ m_xImpl->m_pTempFile.reset();
+ m_xImpl->m_MediaProperties.setURL("", "", "");
+ // UGLY: oox import also gets here, because unlike ODF
+ // getDocumentStorage() is not the imported file...
+ m_xImpl->m_LastFailedPkgURL = url;
+ }
+ }
+ else
+ {
+ m_xImpl->m_MediaProperties.setURL(url,
+ rNewProperties.getTempURL(), "");
+ }
+ }
+ else
+ {
+ m_xImpl->m_pTempFile.reset();
+ m_xImpl->m_MediaProperties.setURL(url, "", rNewProperties.getReferer());
+ }
+ bBroadcastChanged = true;
+ }
+
+ if( AVMediaSetMask::LOOP & nMaskSet )
+ m_xImpl->m_MediaProperties.setLoop( rNewProperties.isLoop() );
+
+ if( AVMediaSetMask::MUTE & nMaskSet )
+ m_xImpl->m_MediaProperties.setMute( rNewProperties.isMute() );
+
+ if( AVMediaSetMask::VOLUMEDB & nMaskSet )
+ m_xImpl->m_MediaProperties.setVolumeDB( rNewProperties.getVolumeDB() );
+
+ if( AVMediaSetMask::ZOOM & nMaskSet )
+ m_xImpl->m_MediaProperties.setZoom( rNewProperties.getZoom() );
+#else
+ (void) rNewProperties;
+#endif
+
+ if( bBroadcastChanged )
+ {
+ SetChanged();
+ BroadcastObjectChange();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdoole2.cxx b/svx/source/svdraw/svdoole2.cxx
new file mode 100644
index 000000000..110240782
--- /dev/null
+++ b/svx/source/svdraw/svdoole2.cxx
@@ -0,0 +1,1990 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/svdoole2.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/util/XModifyBroadcaster.hpp>
+#include <com/sun/star/util/XModifiable.hpp>
+#include <com/sun/star/embed/EmbedStates.hpp>
+#include <com/sun/star/embed/EmbedMisc.hpp>
+#include <com/sun/star/embed/Aspects.hpp>
+#include <com/sun/star/embed/ObjectSaveVetoException.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/embed/XEmbedPersist2.hpp>
+#include <com/sun/star/embed/XInplaceClient.hpp>
+#include <com/sun/star/embed/XInplaceObject.hpp>
+#include <com/sun/star/embed/XLinkageSupport.hpp>
+#include <com/sun/star/embed/NoVisualAreaSizeException.hpp>
+#include <com/sun/star/embed/XWindowSupplier.hpp>
+#include <com/sun/star/document/XEventListener.hpp>
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/document/XStorageBasedDocument.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+
+#include <cppuhelper/exc_hlp.hxx>
+
+#include <toolkit/helper/vclunohelper.hxx>
+#include <toolkit/helper/convert.hxx>
+
+#include <svtools/colorcfg.hxx>
+#include <svtools/embedhlp.hxx>
+
+#include <sfx2/objsh.hxx>
+#include <sfx2/ipclient.hxx>
+#include <sfx2/lnkbase.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <tools/debug.hxx>
+#include <tools/globname.hxx>
+#include <tools/diagnose_ex.h>
+#include <comphelper/classids.hxx>
+#include <comphelper/propertyvalue.hxx>
+
+#include <sot/formats.hxx>
+#include <cppuhelper/implbase.hxx>
+
+#include <vcl/svapp.hxx>
+
+#include <svx/svdmodel.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <svx/svdetc.hxx>
+#include <unomlstr.hxx>
+#include <sdr/contact/viewcontactofsdrole2obj.hxx>
+#include <svx/svdograf.hxx>
+#include <sdr/properties/oleproperties.hxx>
+#include <svx/unoshape.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/xlnclit.hxx>
+#include <svx/xbtmpit.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflbmtit.hxx>
+#include <svx/xflbstit.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <editeng/outlobj.hxx>
+#include <svx/svdpage.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/ref.hxx>
+#include <bitmaps.hlst>
+
+using namespace ::com::sun::star;
+
+static uno::Reference < beans::XPropertySet > lcl_getFrame_throw(const SdrOle2Obj* _pObject)
+{
+ uno::Reference < beans::XPropertySet > xFrame;
+ if ( _pObject )
+ {
+ uno::Reference< frame::XController> xController = _pObject->GetParentXModel()->getCurrentController();
+ if ( xController.is() )
+ {
+ xFrame.set( xController->getFrame(),uno::UNO_QUERY_THROW);
+ }
+ } // if ( _pObject )
+ return xFrame;
+}
+
+namespace {
+
+class SdrLightEmbeddedClient_Impl : public ::cppu::WeakImplHelper
+ < embed::XStateChangeListener
+ , document::XEventListener
+ , embed::XInplaceClient
+ , embed::XEmbeddedClient
+ , embed::XWindowSupplier
+ >
+{
+ uno::Reference< awt::XWindow > m_xWindow;
+ SdrOle2Obj* mpObj;
+
+ Fraction m_aScaleWidth;
+ Fraction m_aScaleHeight;
+
+
+public:
+ explicit SdrLightEmbeddedClient_Impl( SdrOle2Obj* pObj );
+ virtual ~SdrLightEmbeddedClient_Impl() override;
+
+ void SetSizeScale( const Fraction& aScaleWidth, const Fraction& aScaleHeight )
+ {
+ m_aScaleWidth = aScaleWidth;
+ m_aScaleHeight = aScaleHeight;
+ }
+
+ const Fraction& GetScaleWidth() const { return m_aScaleWidth; }
+ const Fraction& GetScaleHeight() const { return m_aScaleHeight; }
+
+ void setWindow(const uno::Reference< awt::XWindow >& _xWindow);
+
+ void disconnect();
+private:
+
+ tools::Rectangle impl_getScaledRect_nothrow() const;
+ // XStateChangeListener
+ virtual void SAL_CALL changingState( const css::lang::EventObject& aEvent, ::sal_Int32 nOldState, ::sal_Int32 nNewState ) override;
+ virtual void SAL_CALL stateChanged( const css::lang::EventObject& aEvent, ::sal_Int32 nOldState, ::sal_Int32 nNewState ) override;
+ virtual void SAL_CALL disposing( const css::lang::EventObject& aEvent ) override;
+
+ // document::XEventListener
+ virtual void SAL_CALL notifyEvent( const document::EventObject& aEvent ) override;
+
+ // XEmbeddedClient
+ virtual void SAL_CALL saveObject() override;
+ virtual void SAL_CALL visibilityChanged( sal_Bool bVisible ) override;
+
+ // XComponentSupplier
+ virtual uno::Reference< util::XCloseable > SAL_CALL getComponent() override;
+
+ // XInplaceClient
+ virtual sal_Bool SAL_CALL canInplaceActivate() override;
+ virtual void SAL_CALL activatingInplace() override;
+ virtual void SAL_CALL activatingUI() override;
+ virtual void SAL_CALL deactivatedInplace() override;
+ virtual void SAL_CALL deactivatedUI() override;
+ virtual uno::Reference< css::frame::XLayoutManager > SAL_CALL getLayoutManager() override;
+ virtual uno::Reference< frame::XDispatchProvider > SAL_CALL getInplaceDispatchProvider() override;
+ virtual awt::Rectangle SAL_CALL getPlacement() override;
+ virtual awt::Rectangle SAL_CALL getClipRectangle() override;
+ virtual void SAL_CALL translateAccelerators( const uno::Sequence< awt::KeyEvent >& aKeys ) override;
+ virtual void SAL_CALL scrollObject( const awt::Size& aOffset ) override;
+ virtual void SAL_CALL changedPlacement( const awt::Rectangle& aPosRect ) override;
+
+ // XWindowSupplier
+ virtual uno::Reference< awt::XWindow > SAL_CALL getWindow() override;
+};
+
+}
+
+SdrLightEmbeddedClient_Impl::SdrLightEmbeddedClient_Impl( SdrOle2Obj* pObj )
+: mpObj( pObj )
+{
+}
+SdrLightEmbeddedClient_Impl::~SdrLightEmbeddedClient_Impl()
+{
+ assert(!mpObj);
+}
+tools::Rectangle SdrLightEmbeddedClient_Impl::impl_getScaledRect_nothrow() const
+{
+ tools::Rectangle aLogicRect( mpObj->GetLogicRect() );
+ // apply scaling to object area and convert to pixels
+ aLogicRect.SetSize( Size( tools::Long( aLogicRect.GetWidth() * m_aScaleWidth),
+ tools::Long( aLogicRect.GetHeight() * m_aScaleHeight) ) );
+ return aLogicRect;
+}
+
+void SAL_CALL SdrLightEmbeddedClient_Impl::changingState( const css::lang::EventObject& /*aEvent*/, ::sal_Int32 /*nOldState*/, ::sal_Int32 /*nNewState*/ )
+{
+}
+
+void SAL_CALL SdrLightEmbeddedClient_Impl::stateChanged( const css::lang::EventObject& /*aEvent*/, ::sal_Int32 nOldState, ::sal_Int32 nNewState )
+{
+ SolarMutexGuard aGuard;
+
+ if ( mpObj && nOldState == embed::EmbedStates::LOADED && nNewState == embed::EmbedStates::RUNNING )
+ {
+ mpObj->ObjectLoaded();
+ GetSdrGlobalData().GetOLEObjCache().InsertObj(mpObj);
+ }
+ else if ( mpObj && nNewState == embed::EmbedStates::LOADED && nOldState == embed::EmbedStates::RUNNING )
+ {
+ GetSdrGlobalData().GetOLEObjCache().RemoveObj(mpObj);
+ }
+}
+
+void SdrLightEmbeddedClient_Impl::disconnect()
+{
+ SolarMutexGuard aGuard;
+ if (!mpObj)
+ return;
+ GetSdrGlobalData().GetOLEObjCache().RemoveObj(mpObj);
+ mpObj = nullptr;
+}
+
+void SAL_CALL SdrLightEmbeddedClient_Impl::disposing( const css::lang::EventObject& /*aEvent*/ )
+{
+ disconnect();
+}
+
+void SAL_CALL SdrLightEmbeddedClient_Impl::notifyEvent( const document::EventObject& aEvent )
+{
+ // TODO/LATER: when writer uses this implementation the code could be shared with SfxInPlaceClient_Impl
+
+ SolarMutexGuard aGuard;
+
+ // the code currently makes sense only in case there is no other client
+ if ( !(mpObj && mpObj->GetAspect() != embed::Aspects::MSOLE_ICON && aEvent.EventName == "OnVisAreaChanged"
+ && mpObj->GetObjRef().is() && mpObj->GetObjRef()->getClientSite() == uno::Reference< embed::XEmbeddedClient >( this )) )
+ return;
+
+ try
+ {
+ MapUnit aContainerMapUnit( MapUnit::Map100thMM );
+ uno::Reference< embed::XVisualObject > xParentVis( mpObj->GetParentXModel(), uno::UNO_QUERY );
+ if ( xParentVis.is() )
+ aContainerMapUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xParentVis->getMapUnit( mpObj->GetAspect() ) );
+
+ MapUnit aObjMapUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( mpObj->GetObjRef()->getMapUnit( mpObj->GetAspect() ) );
+
+ tools::Rectangle aVisArea;
+ awt::Size aSz;
+ try
+ {
+ aSz = mpObj->GetObjRef()->getVisualAreaSize( mpObj->GetAspect() );
+ }
+ catch( embed::NoVisualAreaSizeException& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.svdraw", "No visual area size!");
+ aSz.Width = 5000;
+ aSz.Height = 5000;
+ }
+ catch( uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.svdraw", "");
+ aSz.Width = 5000;
+ aSz.Height = 5000;
+ }
+
+ aVisArea.SetSize( Size( aSz.Width, aSz.Height ) );
+ aVisArea = OutputDevice::LogicToLogic(aVisArea, MapMode(aObjMapUnit), MapMode(aContainerMapUnit));
+ Size aScaledSize( static_cast< tools::Long >( m_aScaleWidth * Fraction( aVisArea.GetWidth() ) ),
+ static_cast< tools::Long >( m_aScaleHeight * Fraction( aVisArea.GetHeight() ) ) );
+ tools::Rectangle aLogicRect( mpObj->GetLogicRect() );
+
+ // react to the change if the difference is bigger than one pixel
+ Size aPixelDiff =
+ Application::GetDefaultDevice()->LogicToPixel(
+ Size( aLogicRect.GetWidth() - aScaledSize.Width(),
+ aLogicRect.GetHeight() - aScaledSize.Height() ),
+ MapMode(aContainerMapUnit));
+ if( aPixelDiff.Width() || aPixelDiff.Height() )
+ {
+ mpObj->SetLogicRect( tools::Rectangle( aLogicRect.TopLeft(), aScaledSize ) );
+ mpObj->BroadcastObjectChange();
+ }
+ else
+ mpObj->ActionChanged();
+ }
+ catch( uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.svdraw", "");
+ }
+}
+
+void SAL_CALL SdrLightEmbeddedClient_Impl::saveObject()
+{
+ // TODO/LATER: when writer uses this implementation the code could be shared with SfxInPlaceClient_Impl
+ uno::Reference< embed::XCommonEmbedPersist > xPersist;
+ uno::Reference< util::XModifiable > xModifiable;
+
+ {
+ SolarMutexGuard aGuard;
+
+ if ( !mpObj )
+ throw embed::ObjectSaveVetoException();
+
+ // the common persistence is supported by objects and links
+ xPersist.set( mpObj->GetObjRef(), uno::UNO_QUERY_THROW );
+ xModifiable.set( mpObj->GetParentXModel(), uno::UNO_QUERY );
+ }
+
+ xPersist->storeOwn();
+
+ if ( xModifiable.is() )
+ xModifiable->setModified( true );
+}
+
+void SAL_CALL SdrLightEmbeddedClient_Impl::visibilityChanged( sal_Bool /*bVisible*/ )
+{
+ // nothing to do currently
+ // TODO/LATER: when writer uses this implementation the code could be shared with SfxInPlaceClient_Impl
+ if ( mpObj )
+ {
+ tools::Rectangle aLogicRect( mpObj->GetLogicRect() );
+ Size aLogicSize( aLogicRect.GetWidth(), aLogicRect.GetHeight() );
+
+ if( mpObj->IsChart() )
+ {
+ //charts never should be stretched see #i84323# for example
+ mpObj->SetLogicRect( tools::Rectangle( aLogicRect.TopLeft(), aLogicSize ) );
+ mpObj->BroadcastObjectChange();
+ } // if( mpObj->IsChart() )
+ }
+}
+
+uno::Reference< util::XCloseable > SAL_CALL SdrLightEmbeddedClient_Impl::getComponent()
+{
+ uno::Reference< util::XCloseable > xResult;
+
+ SolarMutexGuard aGuard;
+ if ( mpObj )
+ xResult.set( mpObj->GetParentXModel(), uno::UNO_QUERY );
+
+ return xResult;
+}
+// XInplaceClient
+
+sal_Bool SAL_CALL SdrLightEmbeddedClient_Impl::canInplaceActivate()
+{
+ bool bRet = false;
+ SolarMutexGuard aGuard;
+ if ( mpObj )
+ {
+ uno::Reference< embed::XEmbeddedObject > xObject = mpObj->GetObjRef();
+ if ( !xObject.is() )
+ throw uno::RuntimeException();
+ // we don't want to switch directly from outplace to inplace mode
+ bRet = ( xObject->getCurrentState() != embed::EmbedStates::ACTIVE && mpObj->GetAspect() != embed::Aspects::MSOLE_ICON );
+ } // if ( mpObj )
+ return bRet;
+}
+
+void SAL_CALL SdrLightEmbeddedClient_Impl::activatingInplace()
+{
+}
+
+void SAL_CALL SdrLightEmbeddedClient_Impl::activatingUI()
+{
+ SolarMutexGuard aGuard;
+
+ uno::Reference < beans::XPropertySet > xFrame( lcl_getFrame_throw(mpObj));
+ uno::Reference < frame::XFrame > xOwnFrame( xFrame,uno::UNO_QUERY);
+ uno::Reference < frame::XFramesSupplier > xParentFrame = xOwnFrame->getCreator();
+ if ( xParentFrame.is() )
+ xParentFrame->setActiveFrame( xOwnFrame );
+
+ OLEObjCache& rObjCache = GetSdrGlobalData().GetOLEObjCache();
+ const size_t nCount = rObjCache.size();
+ for(sal_Int32 i = nCount-1 ; i >= 0;--i)
+ {
+ SdrOle2Obj* pObj = rObjCache[i];
+ if ( pObj != mpObj )
+ {
+ // only deactivate ole objects which belongs to the same frame
+ if ( xFrame == lcl_getFrame_throw(pObj) )
+ {
+ const uno::Reference< embed::XEmbeddedObject >& xObject = pObj->GetObjRef();
+ try
+ {
+ if ( xObject->getStatus( pObj->GetAspect() ) & embed::EmbedMisc::MS_EMBED_ACTIVATEWHENVISIBLE )
+ xObject->changeState( embed::EmbedStates::INPLACE_ACTIVE );
+ else
+ {
+ // the links should not stay in running state for long time because of locking
+ uno::Reference< embed::XLinkageSupport > xLink( xObject, uno::UNO_QUERY );
+ if ( xLink.is() && xLink->isLink() )
+ xObject->changeState( embed::EmbedStates::LOADED );
+ else
+ xObject->changeState( embed::EmbedStates::RUNNING );
+ }
+ }
+ catch (css::uno::Exception& )
+ {}
+ }
+ }
+ } // for(sal_Int32 i = nCount-1 ; i >= 0;--i)
+}
+
+void SAL_CALL SdrLightEmbeddedClient_Impl::deactivatedInplace()
+{
+}
+
+void SAL_CALL SdrLightEmbeddedClient_Impl::deactivatedUI()
+{
+ SolarMutexGuard aGuard;
+ css::uno::Reference< css::frame::XLayoutManager > xLayoutManager(getLayoutManager());
+ if ( xLayoutManager.is() )
+ {
+ static constexpr OUStringLiteral aMenuBarURL = u"private:resource/menubar/menubar";
+ if ( !xLayoutManager->isElementVisible( aMenuBarURL ) )
+ xLayoutManager->createElement( aMenuBarURL );
+ }
+}
+
+uno::Reference< css::frame::XLayoutManager > SAL_CALL SdrLightEmbeddedClient_Impl::getLayoutManager()
+{
+ uno::Reference< css::frame::XLayoutManager > xMan;
+ SolarMutexGuard aGuard;
+ uno::Reference < beans::XPropertySet > xFrame( lcl_getFrame_throw(mpObj));
+ try
+ {
+ xMan.set(xFrame->getPropertyValue("LayoutManager"),uno::UNO_QUERY);
+ }
+ catch ( uno::Exception& ex )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException( ex.Message,
+ nullptr, anyEx );
+ }
+
+ return xMan;
+}
+
+uno::Reference< frame::XDispatchProvider > SAL_CALL SdrLightEmbeddedClient_Impl::getInplaceDispatchProvider()
+{
+ SolarMutexGuard aGuard;
+ return uno::Reference < frame::XDispatchProvider >( lcl_getFrame_throw(mpObj), uno::UNO_QUERY_THROW );
+}
+
+awt::Rectangle SAL_CALL SdrLightEmbeddedClient_Impl::getPlacement()
+{
+ SolarMutexGuard aGuard;
+ if ( !mpObj )
+ throw uno::RuntimeException();
+
+ tools::Rectangle aLogicRect = impl_getScaledRect_nothrow();
+ MapUnit aContainerMapUnit( MapUnit::Map100thMM );
+ uno::Reference< embed::XVisualObject > xParentVis( mpObj->GetParentXModel(), uno::UNO_QUERY );
+ if ( xParentVis.is() )
+ aContainerMapUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xParentVis->getMapUnit( mpObj->GetAspect() ) );
+
+ aLogicRect = Application::GetDefaultDevice()->LogicToPixel(aLogicRect, MapMode(aContainerMapUnit));
+ return AWTRectangle( aLogicRect );
+}
+
+awt::Rectangle SAL_CALL SdrLightEmbeddedClient_Impl::getClipRectangle()
+{
+ return getPlacement();
+}
+
+void SAL_CALL SdrLightEmbeddedClient_Impl::translateAccelerators( const uno::Sequence< awt::KeyEvent >& /*aKeys*/ )
+{
+}
+
+void SAL_CALL SdrLightEmbeddedClient_Impl::scrollObject( const awt::Size& /*aOffset*/ )
+{
+}
+
+void SAL_CALL SdrLightEmbeddedClient_Impl::changedPlacement( const awt::Rectangle& aPosRect )
+{
+ SolarMutexGuard aGuard;
+ if ( !mpObj )
+ throw uno::RuntimeException();
+
+ uno::Reference< embed::XInplaceObject > xInplace( mpObj->GetObjRef(), uno::UNO_QUERY_THROW );
+
+ // check if the change is at least one pixel in size
+ awt::Rectangle aOldRect = getPlacement();
+ tools::Rectangle aNewPixelRect = VCLRectangle( aPosRect );
+ tools::Rectangle aOldPixelRect = VCLRectangle( aOldRect );
+ if ( aOldPixelRect == aNewPixelRect )
+ // nothing has changed
+ return;
+
+ // new scaled object area
+ MapUnit aContainerMapUnit( MapUnit::Map100thMM );
+ uno::Reference< embed::XVisualObject > xParentVis( mpObj->GetParentXModel(), uno::UNO_QUERY );
+ if ( xParentVis.is() )
+ aContainerMapUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xParentVis->getMapUnit( mpObj->GetAspect() ) );
+
+ tools::Rectangle aNewLogicRect = Application::GetDefaultDevice()->PixelToLogic(aNewPixelRect, MapMode(aContainerMapUnit));
+ tools::Rectangle aLogicRect = impl_getScaledRect_nothrow();
+
+ if ( aNewLogicRect == aLogicRect )
+ return;
+
+ // the calculation of the object area has not changed the object size
+ // it should be done here then
+ //SfxBooleanFlagGuard aGuard( m_bResizeNoScale, true );
+
+ // new size of the object area without scaling
+ Size aNewObjSize( tools::Long( aNewLogicRect.GetWidth() / m_aScaleWidth ),
+ tools::Long( aNewLogicRect.GetHeight() / m_aScaleHeight ) );
+
+ // now remove scaling from new placement and keep this at the new object area
+ aNewLogicRect.SetSize( aNewObjSize );
+ // react to the change if the difference is bigger than one pixel
+ Size aPixelDiff =
+ Application::GetDefaultDevice()->LogicToPixel(
+ Size( aLogicRect.GetWidth() - aNewObjSize.Width(),
+ aLogicRect.GetHeight() - aNewObjSize.Height() ),
+ MapMode(aContainerMapUnit));
+ if( aPixelDiff.Width() || aPixelDiff.Height() )
+ {
+ mpObj->SetLogicRect( tools::Rectangle( aLogicRect.TopLeft(), aNewObjSize ) );
+ mpObj->BroadcastObjectChange();
+ }
+ else
+ mpObj->ActionChanged();
+}
+// XWindowSupplier
+
+uno::Reference< awt::XWindow > SAL_CALL SdrLightEmbeddedClient_Impl::getWindow()
+{
+ SolarMutexGuard aGuard;
+ uno::Reference< awt::XWindow > xCurrent = m_xWindow;
+ if ( !xCurrent.is() )
+ {
+ if ( !mpObj )
+ throw uno::RuntimeException();
+ uno::Reference< frame::XFrame> xFrame(lcl_getFrame_throw(mpObj),uno::UNO_QUERY_THROW);
+ xCurrent = xFrame->getComponentWindow();
+ } // if ( !xCurrent.is() )
+ return xCurrent;
+}
+void SdrLightEmbeddedClient_Impl::setWindow(const uno::Reference< awt::XWindow >& _xWindow)
+{
+ m_xWindow = _xWindow;
+}
+
+SdrEmbedObjectLink::SdrEmbedObjectLink(SdrOle2Obj* pObject):
+ ::sfx2::SvBaseLink( ::SfxLinkUpdateMode::ONCALL, SotClipboardFormatId::SVXB ),
+ pObj(pObject)
+{
+ SetSynchron( false );
+}
+
+SdrEmbedObjectLink::~SdrEmbedObjectLink()
+{
+}
+
+::sfx2::SvBaseLink::UpdateResult SdrEmbedObjectLink::DataChanged(
+ const OUString& /*rMimeType*/, const css::uno::Any & /*rValue*/ )
+{
+ if ( !pObj->UpdateLinkURL_Impl() )
+ {
+ // the link URL was not changed
+ uno::Reference< embed::XEmbeddedObject > xObject = pObj->GetObjRef();
+ OSL_ENSURE( xObject.is(), "The object must exist always!" );
+ if ( xObject.is() )
+ {
+ // let the object reload the link
+ // TODO/LATER: reload call could be used for this case
+
+ try
+ {
+ sal_Int32 nState = xObject->getCurrentState();
+ if ( nState != embed::EmbedStates::LOADED )
+ {
+ // in some cases the linked file probably is not locked so it could be changed
+ xObject->changeState( embed::EmbedStates::LOADED );
+ xObject->changeState( nState );
+ }
+ }
+ catch ( uno::Exception& )
+ {
+ }
+ }
+ }
+
+ pObj->GetNewReplacement();
+ pObj->SetChanged();
+
+ return SUCCESS;
+}
+
+void SdrEmbedObjectLink::Closed()
+{
+ pObj->BreakFileLink_Impl();
+ SvBaseLink::Closed();
+}
+
+SdrIFrameLink::SdrIFrameLink(SdrOle2Obj* pObject)
+ : ::sfx2::SvBaseLink(::SfxLinkUpdateMode::ONCALL, SotClipboardFormatId::SVXB)
+ , m_pObject(pObject)
+{
+ SetSynchron( false );
+}
+
+::sfx2::SvBaseLink::UpdateResult SdrIFrameLink::DataChanged(
+ const OUString&, const uno::Any& )
+{
+ uno::Reference<embed::XEmbeddedObject> xObject = m_pObject->GetObjRef();
+ uno::Reference<embed::XCommonEmbedPersist> xPersObj(xObject, uno::UNO_QUERY);
+ if (xPersObj.is())
+ {
+ // let the IFrameObject reload the link
+ try
+ {
+ xPersObj->reload(uno::Sequence<beans::PropertyValue>(), uno::Sequence<beans::PropertyValue>());
+ }
+ catch (const uno::Exception&)
+ {
+ }
+
+ m_pObject->SetChanged();
+ }
+
+ return SUCCESS;
+}
+
+class SdrOle2ObjImpl
+{
+public:
+ svt::EmbeddedObjectRef mxObjRef;
+
+ std::unique_ptr<Graphic> mxGraphic;
+ OUString maProgName;
+ OUString aPersistName; // name of object in persist
+ rtl::Reference<SdrLightEmbeddedClient_Impl> mxLightClient; // must be registered as client only using AddOwnLightClient() call
+
+ bool mbFrame:1; // Due to compatibility at SdrTextObj for now
+ bool mbSuppressSetVisAreaSize:1; // #i118524#
+ mutable bool mbTypeAsked:1;
+ mutable bool mbIsChart:1;
+ bool mbLoadingOLEObjectFailed:1; // New local var to avoid repeated loading if load of OLE2 fails
+ bool mbConnected:1;
+
+ sfx2::SvBaseLink* mpObjectLink;
+ OUString maLinkURL;
+
+ rtl::Reference<SvxUnoShapeModifyListener> mxModifyListener;
+
+ explicit SdrOle2ObjImpl( bool bFrame ) :
+ mbFrame(bFrame),
+ mbSuppressSetVisAreaSize(false),
+ mbTypeAsked(false),
+ mbIsChart(false),
+ mbLoadingOLEObjectFailed(false),
+ mbConnected(false),
+ mpObjectLink(nullptr)
+ {
+ mxObjRef.Lock();
+ }
+
+ SdrOle2ObjImpl( bool bFrame, const svt::EmbeddedObjectRef& rObjRef ) :
+ mxObjRef(rObjRef),
+ mbFrame(bFrame),
+ mbSuppressSetVisAreaSize(false),
+ mbTypeAsked(false),
+ mbIsChart(false),
+ mbLoadingOLEObjectFailed(false),
+ mbConnected(false),
+ mpObjectLink(nullptr)
+ {
+ mxObjRef.Lock();
+ }
+
+ ~SdrOle2ObjImpl()
+ {
+ mxGraphic.reset();
+
+ if (mxModifyListener.is())
+ {
+ mxModifyListener->invalidate();
+ }
+ }
+};
+
+// Predicate determining whether the given OLE is an internal math
+// object
+static bool ImplIsMathObj( const uno::Reference < embed::XEmbeddedObject >& rObjRef )
+{
+ if ( !rObjRef.is() )
+ return false;
+
+ SvGlobalName aClassName( rObjRef->getClassID() );
+ return aClassName == SvGlobalName(SO3_SM_CLASSID_30) ||
+ aClassName == SvGlobalName(SO3_SM_CLASSID_40) ||
+ aClassName == SvGlobalName(SO3_SM_CLASSID_50) ||
+ aClassName == SvGlobalName(SO3_SM_CLASSID_60) ||
+ aClassName == SvGlobalName(SO3_SM_CLASSID);
+}
+
+// BaseProperties section
+
+std::unique_ptr<sdr::properties::BaseProperties> SdrOle2Obj::CreateObjectSpecificProperties()
+{
+ return std::make_unique<sdr::properties::OleProperties>(*this);
+}
+
+// DrawContact section
+
+std::unique_ptr<sdr::contact::ViewContact> SdrOle2Obj::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::ViewContactOfSdrOle2Obj>(*this);
+}
+
+void SdrOle2Obj::Init()
+{
+ // Stuff that was done from old SetModel:
+ // #i43086# #i85304 redo the change for charts for the above bugfix, as #i43086# does not occur anymore
+ // so maybe the ImpSetVisAreaSize call can be removed here completely
+ // Nevertheless I leave it in for other objects as I am not sure about the side effects when removing now
+ if(!getSdrModelFromSdrObject().isLocked() && !IsChart())
+ {
+ ImpSetVisAreaSize();
+ }
+
+ ::comphelper::IEmbeddedHelper* pDestPers(getSdrModelFromSdrObject().GetPersist());
+ if(pDestPers && !IsEmptyPresObj())
+ {
+ // object wasn't connected, now it should be
+ Connect_Impl();
+ }
+
+ AddListeners_Impl();
+}
+
+SdrOle2Obj::SdrOle2Obj(
+ SdrModel& rSdrModel,
+ bool bFrame_)
+: SdrRectObj(rSdrModel),
+ mpImpl(new SdrOle2ObjImpl(bFrame_))
+{
+ Init();
+}
+
+SdrOle2Obj::SdrOle2Obj(SdrModel& rSdrModel, SdrOle2Obj const & rSource)
+: SdrRectObj(rSdrModel, rSource),
+ mpImpl(new SdrOle2ObjImpl(/*bFrame*/false))
+{
+ Init();
+
+ // Manually copying bClosedObj attribute
+ SetClosedObj( rSource.IsClosedObj() );
+
+ mpImpl->aPersistName = rSource.mpImpl->aPersistName;
+ mpImpl->maProgName = rSource.mpImpl->maProgName;
+ mpImpl->mbFrame = rSource.mpImpl->mbFrame;
+
+ if (rSource.mpImpl->mxGraphic)
+ {
+ mpImpl->mxGraphic.reset(new Graphic(*rSource.mpImpl->mxGraphic));
+ }
+
+ if( IsEmptyPresObj() )
+ return;
+
+ ::comphelper::IEmbeddedHelper* pDestPers(getSdrModelFromSdrObject().GetPersist());
+ ::comphelper::IEmbeddedHelper* pSrcPers(rSource.getSdrModelFromSdrObject().GetPersist());
+ if( !(pDestPers && pSrcPers) )
+ return;
+
+ DBG_ASSERT( !mpImpl->mxObjRef.is(), "Object already existing!" );
+ comphelper::EmbeddedObjectContainer& rContainer = pSrcPers->getEmbeddedObjectContainer();
+ uno::Reference < embed::XEmbeddedObject > xObj = rContainer.GetEmbeddedObject( mpImpl->aPersistName );
+ if ( xObj.is() )
+ {
+ OUString aTmp;
+ mpImpl->mxObjRef.Assign( pDestPers->getEmbeddedObjectContainer().CopyAndGetEmbeddedObject(
+ rContainer, xObj, aTmp, pSrcPers->getDocumentBaseURL(), pDestPers->getDocumentBaseURL()), rSource.GetAspect());
+ mpImpl->mbTypeAsked = false;
+ mpImpl->aPersistName = aTmp;
+ CheckFileLink_Impl();
+ }
+
+ Connect();
+}
+
+SdrOle2Obj::SdrOle2Obj(
+ SdrModel& rSdrModel,
+ const svt::EmbeddedObjectRef& rNewObjRef,
+ const OUString& rNewObjName,
+ const tools::Rectangle& rNewRect)
+: SdrRectObj(rSdrModel, rNewRect),
+ mpImpl(new SdrOle2ObjImpl(false/*bFrame_*/, rNewObjRef))
+{
+ mpImpl->aPersistName = rNewObjName;
+
+ if (mpImpl->mxObjRef.is() && (mpImpl->mxObjRef->getStatus( GetAspect() ) & embed::EmbedMisc::EMBED_NEVERRESIZE ) )
+ m_bSizProt = true;
+
+ // For math objects, set closed state to transparent
+ SetClosedObj(!ImplIsMathObj( mpImpl->mxObjRef.GetObject() ));
+
+ Init();
+}
+
+OUString SdrOle2Obj::GetStyleString()
+{
+ OUString strStyle;
+ if (mpImpl->mxObjRef.is() && mpImpl->mxObjRef.IsChart())
+ {
+ strStyle = mpImpl->mxObjRef.GetChartType();
+ }
+ return strStyle;
+}
+
+SdrOle2Obj::~SdrOle2Obj()
+{
+ if ( mpImpl->mbConnected )
+ Disconnect();
+
+ DisconnectFileLink_Impl();
+
+ if (mpImpl->mxLightClient)
+ {
+ mpImpl->mxLightClient->disconnect();
+ mpImpl->mxLightClient.clear();
+ }
+}
+
+void SdrOle2Obj::SetAspect( sal_Int64 nAspect )
+{
+ mpImpl->mxObjRef.SetViewAspect( nAspect );
+}
+
+const svt::EmbeddedObjectRef& SdrOle2Obj::getEmbeddedObjectRef() const
+{
+ return mpImpl->mxObjRef;
+}
+
+sal_Int64 SdrOle2Obj::GetAspect() const
+{
+ return mpImpl->mxObjRef.GetViewAspect();
+}
+
+bool SdrOle2Obj::isInplaceActive() const
+{
+ return mpImpl->mxObjRef.is() && embed::EmbedStates::INPLACE_ACTIVE == mpImpl->mxObjRef->getCurrentState();
+}
+
+bool SdrOle2Obj::isUiActive() const
+{
+ return mpImpl->mxObjRef.is() && embed::EmbedStates::UI_ACTIVE == mpImpl->mxObjRef->getCurrentState();
+}
+
+void SdrOle2Obj::SetGraphic(const Graphic& rGrf)
+{
+ // only for setting a preview graphic
+ mpImpl->mxGraphic.reset(new Graphic(rGrf));
+
+ SetChanged();
+ BroadcastObjectChange();
+}
+
+void SdrOle2Obj::ClearGraphic()
+{
+ mpImpl->mxGraphic.reset();
+
+ SetChanged();
+ BroadcastObjectChange();
+}
+
+void SdrOle2Obj::SetProgName( const OUString& rName )
+{
+ mpImpl->maProgName = rName;
+}
+
+const OUString& SdrOle2Obj::GetProgName() const
+{
+ return mpImpl->maProgName;
+}
+
+bool SdrOle2Obj::IsEmpty() const
+{
+ return !mpImpl->mxObjRef.is();
+}
+
+void SdrOle2Obj::Connect(SvxOle2Shape* pCreator)
+{
+ if( IsEmptyPresObj() )
+ return;
+
+ if( mpImpl->mbConnected )
+ {
+ // currently there are situations where it seems to be unavoidable to have multiple connects
+ // changing this would need a larger code rewrite, so for now I remove the assertion
+ // OSL_FAIL("Connect() called on connected object!");
+ return;
+ }
+
+ Connect_Impl(pCreator);
+ AddListeners_Impl();
+}
+
+bool SdrOle2Obj::UpdateLinkURL_Impl()
+{
+ bool bResult = false;
+
+ if ( mpImpl->mpObjectLink )
+ {
+ sfx2::LinkManager* pLinkManager(getSdrModelFromSdrObject().GetLinkManager());
+
+ if ( pLinkManager )
+ {
+ OUString aNewLinkURL;
+ sfx2::LinkManager::GetDisplayNames( mpImpl->mpObjectLink, nullptr, &aNewLinkURL );
+ if ( !aNewLinkURL.equalsIgnoreAsciiCase( mpImpl->maLinkURL ) )
+ {
+ GetObjRef_Impl();
+ uno::Reference<embed::XCommonEmbedPersist> xPersObj( mpImpl->mxObjRef.GetObject(), uno::UNO_QUERY );
+ OSL_ENSURE( xPersObj.is(), "The object must exist!" );
+ if ( xPersObj.is() )
+ {
+ try
+ {
+ sal_Int32 nCurState = mpImpl->mxObjRef->getCurrentState();
+ if ( nCurState != embed::EmbedStates::LOADED )
+ mpImpl->mxObjRef->changeState(embed::EmbedStates::LOADED);
+
+ // TODO/LATER: there should be possible to get current mediadescriptor settings from the object
+ uno::Sequence< beans::PropertyValue > aArgs{ comphelper::makePropertyValue(
+ "URL", aNewLinkURL) };
+ xPersObj->reload( aArgs, uno::Sequence< beans::PropertyValue >() );
+
+ mpImpl->maLinkURL = aNewLinkURL;
+ bResult = true;
+
+ if ( nCurState != embed::EmbedStates::LOADED )
+ mpImpl->mxObjRef->changeState(nCurState);
+ }
+ catch( css::uno::Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "SdrOle2Obj::UpdateLinkURL_Impl()" );
+ }
+ }
+
+ if ( !bResult )
+ {
+ // TODO/LATER: return the old name to the link manager, is it possible?
+ }
+ }
+ }
+ }
+
+ return bResult;
+}
+
+void SdrOle2Obj::BreakFileLink_Impl()
+{
+ uno::Reference<document::XStorageBasedDocument> xDoc(getSdrModelFromSdrObject().getUnoModel(), uno::UNO_QUERY);
+
+ if ( !xDoc.is() )
+ return;
+
+ uno::Reference< embed::XStorage > xStorage = xDoc->getDocumentStorage();
+ if ( !xStorage.is() )
+ return;
+
+ try
+ {
+ uno::Reference< embed::XLinkageSupport > xLinkSupport( mpImpl->mxObjRef.GetObject(), uno::UNO_QUERY_THROW );
+ xLinkSupport->breakLink( xStorage, mpImpl->aPersistName );
+ DisconnectFileLink_Impl();
+ mpImpl->maLinkURL.clear();
+ }
+ catch( css::uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "SdrOle2Obj::BreakFileLink_Impl()" );
+ }
+}
+
+void SdrOle2Obj::DisconnectFileLink_Impl()
+{
+ sfx2::LinkManager* pLinkManager(getSdrModelFromSdrObject().GetLinkManager());
+
+ if ( pLinkManager && mpImpl->mpObjectLink )
+ {
+ pLinkManager->Remove( mpImpl->mpObjectLink );
+ mpImpl->mpObjectLink = nullptr;
+ }
+}
+
+void SdrOle2Obj::CheckFileLink_Impl()
+{
+ if (!mpImpl->mxObjRef.GetObject().is() || mpImpl->mpObjectLink)
+ return;
+
+ try
+ {
+ uno::Reference<embed::XEmbeddedObject> xObject = mpImpl->mxObjRef.GetObject();
+ if (!xObject)
+ return;
+
+ bool bIFrame = false;
+
+ OUString aLinkURL;
+ uno::Reference<embed::XLinkageSupport> xLinkSupport(xObject, uno::UNO_QUERY);
+ if (xLinkSupport)
+ {
+ if (xLinkSupport->isLink())
+ aLinkURL = xLinkSupport->getLinkURL();
+ }
+ else
+ {
+ // get IFrame (Floating Frames) listed and updatable from the
+ // manage links dialog
+ SvGlobalName aClassId(xObject->getClassID());
+ if (aClassId == SvGlobalName(SO3_IFRAME_CLASSID))
+ {
+ uno::Reference<beans::XPropertySet> xSet(xObject->getComponent(), uno::UNO_QUERY);
+ if (xSet.is())
+ xSet->getPropertyValue("FrameURL") >>= aLinkURL;
+ bIFrame = true;
+ }
+ }
+
+ if (!aLinkURL.isEmpty()) // this is a file link so the model link manager should handle it
+ {
+ sfx2::LinkManager* pLinkManager(getSdrModelFromSdrObject().GetLinkManager());
+
+ if ( pLinkManager )
+ {
+ SdrEmbedObjectLink* pEmbedObjectLink = nullptr;
+ if (!bIFrame)
+ {
+ pEmbedObjectLink = new SdrEmbedObjectLink(this);
+ mpImpl->mpObjectLink = pEmbedObjectLink;
+ }
+ else
+ mpImpl->mpObjectLink = new SdrIFrameLink(this);
+ mpImpl->maLinkURL = aLinkURL;
+ pLinkManager->InsertFileLink( *mpImpl->mpObjectLink, sfx2::SvBaseLinkObjectType::ClientOle, aLinkURL );
+ if (pEmbedObjectLink)
+ pEmbedObjectLink->Connect();
+ }
+ }
+ }
+ catch (const css::uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx", "SdrOle2Obj::CheckFileLink_Impl()");
+ }
+}
+
+void SdrOle2Obj::Connect_Impl(SvxOle2Shape* pCreator)
+{
+ if(mpImpl->aPersistName.isEmpty() )
+ return;
+
+ try
+ {
+ ::comphelper::IEmbeddedHelper* pPers(getSdrModelFromSdrObject().GetPersist());
+
+ if ( pPers )
+ {
+ comphelper::EmbeddedObjectContainer& rContainer = pPers->getEmbeddedObjectContainer();
+
+ if ( !rContainer.HasEmbeddedObject( mpImpl->aPersistName )
+ || ( mpImpl->mxObjRef.is() && !rContainer.HasEmbeddedObject( mpImpl->mxObjRef.GetObject() ) ) )
+ {
+ // object not known to container document
+ // No object -> disaster!
+ DBG_ASSERT( mpImpl->mxObjRef.is(), "No object in connect!");
+ if ( mpImpl->mxObjRef.is() )
+ {
+ // object came from the outside, now add it to the container
+ OUString aTmp;
+ rContainer.InsertEmbeddedObject( mpImpl->mxObjRef.GetObject(), aTmp );
+ mpImpl->aPersistName = aTmp;
+ }
+ }
+ else if ( !mpImpl->mxObjRef.is() )
+ {
+ mpImpl->mxObjRef.Assign( rContainer.GetEmbeddedObject( mpImpl->aPersistName ), mpImpl->mxObjRef.GetViewAspect() );
+ mpImpl->mbTypeAsked = false;
+ }
+
+ if ( mpImpl->mxObjRef.GetObject().is() )
+ {
+ mpImpl->mxObjRef.AssignToContainer( &rContainer, mpImpl->aPersistName );
+ mpImpl->mbConnected = true;
+ mpImpl->mxObjRef.Lock();
+ }
+ }
+
+ if (pCreator)
+ {
+ OUString sFrameURL(pCreator->GetAndClearInitialFrameURL());
+ if (!sFrameURL.isEmpty() && svt::EmbeddedObjectRef::TryRunningState(mpImpl->mxObjRef.GetObject()))
+ {
+ uno::Reference<beans::XPropertySet> xSet(mpImpl->mxObjRef->getComponent(), uno::UNO_QUERY);
+ if (xSet.is())
+ xSet->setPropertyValue("FrameURL", uno::Any(sFrameURL));
+ }
+ }
+
+ if ( mpImpl->mxObjRef.is() )
+ {
+ if ( !mpImpl->mxLightClient.is() )
+ mpImpl->mxLightClient = new SdrLightEmbeddedClient_Impl( this );
+
+ mpImpl->mxObjRef->addStateChangeListener( mpImpl->mxLightClient );
+ mpImpl->mxObjRef->addEventListener( mpImpl->mxLightClient );
+
+ if ( mpImpl->mxObjRef->getCurrentState() != embed::EmbedStates::LOADED )
+ GetSdrGlobalData().GetOLEObjCache().InsertObj(this);
+
+ CheckFileLink_Impl();
+
+ uno::Reference< container::XChild > xChild( mpImpl->mxObjRef.GetObject(), uno::UNO_QUERY );
+ if( xChild.is() )
+ {
+ uno::Reference< uno::XInterface > xParent( getSdrModelFromSdrObject().getUnoModel());
+ if( xParent.is())
+ xChild->setParent( getSdrModelFromSdrObject().getUnoModel() );
+ }
+
+ }
+ }
+ catch( css::uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "SdrOle2Obj::Connect_Impl()" );
+ }
+}
+
+void SdrOle2Obj::ObjectLoaded()
+{
+ AddListeners_Impl();
+}
+
+void SdrOle2Obj::AddListeners_Impl()
+{
+ if( !(mpImpl->mxObjRef.is() && mpImpl->mxObjRef->getCurrentState() != embed::EmbedStates::LOADED) )
+ return;
+
+ // register modify listener
+ if (!mpImpl->mxModifyListener.is())
+ {
+ mpImpl->mxModifyListener = new SvxUnoShapeModifyListener(this);
+ }
+
+ uno::Reference< util::XModifyBroadcaster > xBC( getXModel(), uno::UNO_QUERY );
+ if (xBC.is())
+ {
+ xBC->addModifyListener( mpImpl->mxModifyListener );
+ }
+}
+
+void SdrOle2Obj::Disconnect()
+{
+ if( IsEmptyPresObj() )
+ return;
+
+ if( !mpImpl->mbConnected )
+ {
+ OSL_FAIL("Disconnect() called on disconnected object!");
+ return;
+ }
+
+ RemoveListeners_Impl();
+ Disconnect_Impl();
+}
+
+void SdrOle2Obj::RemoveListeners_Impl()
+{
+ if ( !mpImpl->mxObjRef.is() || mpImpl->aPersistName.isEmpty() )
+ return;
+
+ try
+ {
+ sal_Int32 nState = mpImpl->mxObjRef->getCurrentState();
+ if ( nState != embed::EmbedStates::LOADED )
+ {
+ uno::Reference< util::XModifyBroadcaster > xBC( getXModel(), uno::UNO_QUERY );
+ if (xBC.is() && mpImpl->mxModifyListener.is())
+ {
+ xBC->removeModifyListener( mpImpl->mxModifyListener );
+ }
+ }
+ }
+ catch( css::uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "SdrOle2Obj::RemoveListeners_Impl()" );
+ }
+}
+
+void SdrOle2Obj::Disconnect_Impl()
+{
+ try
+ {
+ if ( !mpImpl->aPersistName.isEmpty() )
+ {
+ if( getSdrModelFromSdrObject().IsInDestruction() )
+ {
+ // TODO/LATER: here we must assume that the destruction of the model is enough to make clear that we will not
+ // remove the object from the container, even if the DrawingObject itself is not destroyed (unfortunately this
+ // There is no real need to do the following removing of the object from the container
+ // in case the model has correct persistence, but in case of problems such a removing
+ // would make the behavior of the office more stable
+
+ comphelper::EmbeddedObjectContainer* pContainer = mpImpl->mxObjRef.GetContainer();
+ if ( pContainer )
+ {
+ pContainer->CloseEmbeddedObject( mpImpl->mxObjRef.GetObject() );
+ mpImpl->mxObjRef.AssignToContainer( nullptr, mpImpl->aPersistName );
+ }
+
+ // happens later than the destruction of the model, so we can't assert that).
+ //DBG_ASSERT( bInDestruction, "Model is destroyed, but not me?!" );
+ //TODO/LATER: should be make sure that the ObjectShell also forgets the object, because we will close it soon?
+ /*
+ uno::Reference < util::XCloseable > xClose( xObjRef, uno::UNO_QUERY );
+ if ( xClose.is() )
+ {
+ try
+ {
+ xClose->close( true );
+ }
+ catch ( util::CloseVetoException& )
+ {
+ // there's still someone who needs the object!
+ }
+ }
+
+ xObjRef = NULL;*/
+ }
+ else if ( mpImpl->mxObjRef.is() )
+ {
+ if ( getSdrModelFromSdrObject().getUnoModel().is() )
+ {
+ // remove object, but don't close it (that's up to someone else)
+ comphelper::EmbeddedObjectContainer* pContainer = mpImpl->mxObjRef.GetContainer();
+ if ( pContainer )
+ {
+ pContainer->RemoveEmbeddedObject( mpImpl->mxObjRef.GetObject() );
+
+ // TODO/LATER: mpImpl->aPersistName contains outdated information, to keep it updated
+ // it should be returned from RemoveEmbeddedObject call. Currently it is no problem,
+ // since no container is adjusted, actually the empty string could be provided as a name here
+ mpImpl->mxObjRef.AssignToContainer( nullptr, mpImpl->aPersistName );
+ }
+
+ DisconnectFileLink_Impl();
+ }
+ }
+ }
+
+ if ( mpImpl->mxObjRef.is() && mpImpl->mxLightClient.is() )
+ {
+ mpImpl->mxObjRef->removeStateChangeListener ( mpImpl->mxLightClient );
+ mpImpl->mxObjRef->removeEventListener( mpImpl->mxLightClient );
+ mpImpl->mxObjRef->setClientSite( nullptr );
+
+ GetSdrGlobalData().GetOLEObjCache().RemoveObj(this);
+ }
+ }
+ catch( css::uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "SdrOle2Obj::Disconnect_Impl()" );
+ }
+
+ mpImpl->mbConnected = false;
+}
+
+SdrObjectUniquePtr SdrOle2Obj::createSdrGrafObjReplacement(bool bAddText) const
+{
+ const Graphic* pOLEGraphic = GetGraphic();
+
+ if(pOLEGraphic)
+ {
+ // #i118485# allow creating a SdrGrafObj representation
+ SdrGrafObj* pClone = new SdrGrafObj(
+ getSdrModelFromSdrObject(),
+ *pOLEGraphic);
+
+ // copy transformation
+ basegfx::B2DHomMatrix aMatrix;
+ basegfx::B2DPolyPolygon aPolyPolygon;
+
+ TRGetBaseGeometry(aMatrix, aPolyPolygon);
+ pClone->TRSetBaseGeometry(aMatrix, aPolyPolygon);
+
+ // copy all attributes to support graphic styles for OLEs
+ pClone->SetStyleSheet(GetStyleSheet(), false);
+ pClone->SetMergedItemSet(GetMergedItemSet());
+
+ if(bAddText)
+ {
+ // #i118485# copy text (Caution! Model needed, as guaranteed in aw080)
+ OutlinerParaObject* pOPO = GetOutlinerParaObject();
+
+ if(pOPO)
+ {
+ pClone->NbcSetOutlinerParaObject(*pOPO);
+ }
+ }
+
+ return SdrObjectUniquePtr(pClone);
+ }
+ else
+ {
+ // #i100710# pOLEGraphic may be zero (no visualisation available),
+ // so we need to use the OLE replacement graphic
+ SdrRectObj* pClone = new SdrRectObj(
+ getSdrModelFromSdrObject(),
+ GetSnapRect());
+
+ // gray outline
+ pClone->SetMergedItem(XLineStyleItem(css::drawing::LineStyle_SOLID));
+ const svtools::ColorConfig aColorConfig;
+ const svtools::ColorConfigValue aColor(aColorConfig.GetColorValue(svtools::OBJECTBOUNDARIES));
+ pClone->SetMergedItem(XLineColorItem(OUString(), aColor.nColor));
+
+ // bitmap fill
+ pClone->SetMergedItem(XFillStyleItem(drawing::FillStyle_BITMAP));
+ pClone->SetMergedItem(XFillBitmapItem(OUString(), GetEmptyOLEReplacementGraphic()));
+ pClone->SetMergedItem(XFillBmpTileItem(false));
+ pClone->SetMergedItem(XFillBmpStretchItem(false));
+
+ return SdrObjectUniquePtr(pClone);
+ }
+}
+
+SdrObjectUniquePtr SdrOle2Obj::DoConvertToPolyObj(bool bBezier, bool bAddText) const
+{
+ // #i118485# missing converter added
+ SdrObjectUniquePtr pRetval = createSdrGrafObjReplacement(true);
+
+ if(pRetval)
+ {
+ return pRetval->DoConvertToPolyObj(bBezier, bAddText);
+ }
+
+ return nullptr;
+}
+
+void SdrOle2Obj::handlePageChange(SdrPage* pOldPage, SdrPage* pNewPage)
+{
+ const bool bRemove(pNewPage == nullptr && pOldPage != nullptr);
+ const bool bInsert(pNewPage != nullptr && pOldPage == nullptr);
+
+ if (bRemove && mpImpl->mbConnected )
+ {
+ Disconnect();
+ }
+
+ // call parent
+ SdrRectObj::handlePageChange(pOldPage, pNewPage);
+
+ if (bInsert && !mpImpl->mbConnected )
+ {
+ Connect();
+ }
+}
+
+void SdrOle2Obj::SetObjRef( const css::uno::Reference < css::embed::XEmbeddedObject >& rNewObjRef )
+{
+ DBG_ASSERT( !rNewObjRef.is() || !mpImpl->mxObjRef.GetObject().is(), "SetObjRef called on already initialized object!");
+ if( rNewObjRef == mpImpl->mxObjRef.GetObject() )
+ return;
+
+ // the caller of the method is responsible to control the old object, it will not be closed here
+ // Otherwise WW8 import crashes because it transfers control to OLENode by this method
+ if ( mpImpl->mxObjRef.GetObject().is() )
+ mpImpl->mxObjRef.Lock( false );
+
+ // avoid removal of object in Disconnect! It is definitely a HACK to call SetObjRef(0)!
+ // This call will try to close the objects; so if anybody else wants to keep it, it must be locked by a CloseListener
+ mpImpl->mxObjRef.Clear();
+
+ if ( mpImpl->mbConnected )
+ Disconnect();
+
+ mpImpl->mxObjRef.Assign( rNewObjRef, GetAspect() );
+ mpImpl->mbTypeAsked = false;
+
+ if ( mpImpl->mxObjRef.is() )
+ {
+ mpImpl->mxGraphic.reset();
+
+ if ( mpImpl->mxObjRef->getStatus( GetAspect() ) & embed::EmbedMisc::EMBED_NEVERRESIZE )
+ SetResizeProtect(true);
+
+ // For math objects, set closed state to transparent
+ SetClosedObj(!ImplIsMathObj( rNewObjRef ));
+
+ Connect();
+ }
+
+ SetChanged();
+ BroadcastObjectChange();
+}
+
+void SdrOle2Obj::SetClosedObj( bool bIsClosed )
+{
+ // TODO/LATER: do we still need this hack?
+ // Allow changes to the closed state of OLE objects
+ m_bClosedObj = bIsClosed;
+}
+
+SdrObjectUniquePtr SdrOle2Obj::getFullDragClone() const
+{
+ // #i118485# use central replacement generator
+ return createSdrGrafObjReplacement(false);
+}
+
+void SdrOle2Obj::SetPersistName( const OUString& rPersistName, SvxOle2Shape* pCreator )
+{
+ DBG_ASSERT( mpImpl->aPersistName.isEmpty(), "Persist name changed!");
+
+ mpImpl->aPersistName = rPersistName;
+ mpImpl->mbLoadingOLEObjectFailed = false;
+
+ Connect(pCreator);
+ SetChanged();
+}
+
+void SdrOle2Obj::AbandonObject()
+{
+ mpImpl->aPersistName.clear();
+ mpImpl->mbLoadingOLEObjectFailed = false;
+ SetObjRef(nullptr);
+}
+
+const OUString& SdrOle2Obj::GetPersistName() const
+{
+ return mpImpl->aPersistName;
+}
+
+void SdrOle2Obj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
+{
+ // #i118485# Allowing much more attributes for OLEs
+ rInfo.bRotateFreeAllowed = true;
+ rInfo.bRotate90Allowed = true;
+ rInfo.bMirrorFreeAllowed = true;
+ rInfo.bMirror45Allowed = true;
+ rInfo.bMirror90Allowed = true;
+ rInfo.bTransparenceAllowed = true;
+ rInfo.bShearAllowed = true;
+ rInfo.bEdgeRadiusAllowed = false;
+ rInfo.bNoOrthoDesired = false;
+ rInfo.bCanConvToPath = true;
+ rInfo.bCanConvToPoly = true;
+ rInfo.bCanConvToPathLineToArea = false;
+ rInfo.bCanConvToPolyLineToArea = false;
+ rInfo.bCanConvToContour = true;
+}
+
+SdrObjKind SdrOle2Obj::GetObjIdentifier() const
+{
+ return mpImpl->mbFrame ? SdrObjKind::OLEPluginFrame : SdrObjKind::OLE2;
+}
+
+OUString SdrOle2Obj::TakeObjNameSingul() const
+{
+ OUStringBuffer sName(SvxResId(mpImpl->mbFrame ? STR_ObjNameSingulFrame : STR_ObjNameSingulOLE2));
+
+ const OUString aName(GetName());
+
+ if (!aName.isEmpty())
+ {
+ sName.append(" '");
+ sName.append(aName);
+ sName.append('\'');
+ }
+
+ return sName.makeStringAndClear();
+}
+
+OUString SdrOle2Obj::TakeObjNamePlural() const
+{
+ return SvxResId(mpImpl->mbFrame ? STR_ObjNamePluralFrame : STR_ObjNamePluralOLE2);
+}
+
+SdrOle2Obj* SdrOle2Obj::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new SdrOle2Obj(rTargetModel, *this);
+}
+
+void SdrOle2Obj::ImpSetVisAreaSize()
+{
+ // #i118524# do not again set VisAreaSize when the call comes from OLE client (e.g. ObjectAreaChanged)
+ if (mpImpl->mbSuppressSetVisAreaSize)
+ return;
+
+ // currently there is no need to recalculate scaling for iconified objects
+ // TODO/LATER: it might be needed in future when it is possible to change the icon
+ if ( GetAspect() == embed::Aspects::MSOLE_ICON )
+ return;
+
+ // the object area of an embedded object was changed, e.g. by user interaction an a selected object
+ GetObjRef();
+ if (!mpImpl->mxObjRef.is())
+ return;
+
+ sal_Int64 nMiscStatus = mpImpl->mxObjRef->getStatus( GetAspect() );
+
+ // the client is required to get access to scaling
+ SfxInPlaceClient* pClient(
+ SfxInPlaceClient::GetClient(
+ dynamic_cast<SfxObjectShell*>(
+ getSdrModelFromSdrObject().GetPersist()),
+ mpImpl->mxObjRef.GetObject()));
+ const bool bHasOwnClient(
+ mpImpl->mxLightClient.is() &&
+ mpImpl->mxObjRef->getClientSite() == uno::Reference< embed::XEmbeddedClient >( mpImpl->mxLightClient ) );
+
+ if ( pClient || bHasOwnClient )
+ {
+ // TODO: IMHO we need to do similar things when object is UIActive or OutplaceActive?!
+ if ( ((nMiscStatus & embed::EmbedMisc::MS_EMBED_RECOMPOSEONRESIZE) &&
+ svt::EmbeddedObjectRef::TryRunningState( mpImpl->mxObjRef.GetObject() ))
+ || mpImpl->mxObjRef->getCurrentState() == embed::EmbedStates::INPLACE_ACTIVE
+ )
+ {
+ Fraction aScaleWidth;
+ Fraction aScaleHeight;
+ if ( pClient )
+ {
+ aScaleWidth = pClient->GetScaleWidth();
+ aScaleHeight = pClient->GetScaleHeight();
+ }
+ else
+ {
+ aScaleWidth = mpImpl->mxLightClient->GetScaleWidth();
+ aScaleHeight = mpImpl->mxLightClient->GetScaleHeight();
+ }
+
+ // The object wants to resize itself (f.e. Chart wants to recalculate the layout)
+ // or object is inplace active and so has a window that must be resized also
+ // In these cases the change in the object area size will be reflected in a change of the
+ // objects' visual area. The scaling will not change, but it might exist already and must
+ // be used in calculations
+ MapUnit aMapUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( mpImpl->mxObjRef->getMapUnit( GetAspect() ) );
+ Size aVisSize;
+ if (sal_Int32(aScaleWidth) != 0 && sal_Int32(aScaleHeight) != 0) // avoid div by zero
+ aVisSize = Size( static_cast<tools::Long>( Fraction( maRect.GetWidth() ) / aScaleWidth ),
+ static_cast<tools::Long>( Fraction( maRect.GetHeight() ) / aScaleHeight ) );
+
+ aVisSize = OutputDevice::LogicToLogic(
+ aVisSize,
+ MapMode(getSdrModelFromSdrObject().GetScaleUnit()),
+ MapMode(aMapUnit));
+ awt::Size aSz;
+ aSz.Width = aVisSize.Width();
+ aSz.Height = aVisSize.Height();
+ mpImpl->mxObjRef->setVisualAreaSize( GetAspect(), aSz );
+
+ try
+ {
+ aSz = mpImpl->mxObjRef->getVisualAreaSize( GetAspect() );
+ }
+ catch( embed::NoVisualAreaSizeException& )
+ {}
+
+ tools::Rectangle aAcceptedVisArea;
+ aAcceptedVisArea.SetSize( Size( static_cast<tools::Long>( Fraction( tools::Long( aSz.Width ) ) * aScaleWidth ),
+ static_cast<tools::Long>( Fraction( tools::Long( aSz.Height ) ) * aScaleHeight ) ) );
+ if (aVisSize != aAcceptedVisArea.GetSize())
+ {
+ // server changed VisArea to its liking and the VisArea is different than the suggested one
+ // store the new value as given by the object
+ MapUnit aNewMapUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( mpImpl->mxObjRef->getMapUnit( GetAspect() ) );
+ maRect.SetSize(
+ OutputDevice::LogicToLogic(
+ aAcceptedVisArea.GetSize(),
+ MapMode(aNewMapUnit),
+ MapMode(getSdrModelFromSdrObject().GetScaleUnit())));
+ }
+
+ // make the new object area known to the client
+ // compared to the "else" branch aRect might have been changed by the object and no additional scaling was applied
+ // WHY this -> OSL_ASSERT( pClient );
+ if( pClient )
+ pClient->SetObjArea(maRect);
+
+ // we need a new replacement image as the object has resized itself
+
+ //#i79578# don't request a new replacement image for charts to often
+ //a chart sends a modified call to the framework if it was changed
+ //thus the replacement update is already handled there
+ if( !IsChart() )
+ mpImpl->mxObjRef.UpdateReplacement();
+ }
+ else
+ {
+ // The object isn't active and does not want to resize itself so the changed object area size
+ // will be reflected in a changed object scaling
+ Fraction aScaleWidth;
+ Fraction aScaleHeight;
+ Size aObjAreaSize;
+ if ( CalculateNewScaling( aScaleWidth, aScaleHeight, aObjAreaSize ) )
+ {
+ if ( pClient )
+ {
+ tools::Rectangle aScaleRect(maRect.TopLeft(), aObjAreaSize);
+ pClient->SetObjAreaAndScale( aScaleRect, aScaleWidth, aScaleHeight);
+ }
+ else
+ {
+ mpImpl->mxLightClient->SetSizeScale( aScaleWidth, aScaleHeight );
+ }
+ }
+ }
+ }
+ else if( (nMiscStatus & embed::EmbedMisc::MS_EMBED_RECOMPOSEONRESIZE) &&
+ svt::EmbeddedObjectRef::TryRunningState( mpImpl->mxObjRef.GetObject() ) )
+ {
+ //also handle not sfx based ole objects e.g. charts
+ //#i83860# resizing charts in impress distorts fonts
+ uno::Reference< embed::XVisualObject > xVisualObject( getXModel(), uno::UNO_QUERY );
+ if( xVisualObject.is() )
+ {
+ const MapUnit aMapUnit(
+ VCLUnoHelper::UnoEmbed2VCLMapUnit(
+ mpImpl->mxObjRef->getMapUnit(GetAspect())));
+ const Point aTL( maRect.TopLeft() );
+ const Point aBR( maRect.BottomRight() );
+ const Point aTL2(
+ OutputDevice::LogicToLogic(
+ aTL,
+ MapMode(getSdrModelFromSdrObject().GetScaleUnit()),
+ MapMode(aMapUnit)));
+ const Point aBR2(
+ OutputDevice::LogicToLogic(
+ aBR,
+ MapMode(getSdrModelFromSdrObject().GetScaleUnit()),
+ MapMode(aMapUnit)));
+ const tools::Rectangle aNewRect(
+ aTL2,
+ aBR2);
+
+ xVisualObject->setVisualAreaSize(
+ GetAspect(),
+ awt::Size(
+ aNewRect.GetWidth(),
+ aNewRect.GetHeight()));
+ }
+ }
+}
+
+void SdrOle2Obj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
+{
+ if(!getSdrModelFromSdrObject().isLocked())
+ {
+ GetObjRef();
+
+ if ( mpImpl->mxObjRef.is() && ( mpImpl->mxObjRef->getStatus( GetAspect() ) & embed::EmbedMisc::MS_EMBED_RECOMPOSEONRESIZE ) )
+ {
+ // if the object needs recompose on resize
+ // the client site should be created before the resize will take place
+ // check whether there is no client site and create it if necessary
+ AddOwnLightClient();
+ }
+ }
+
+ SdrRectObj::NbcResize(rRef,xFact,yFact);
+
+ if( !getSdrModelFromSdrObject().isLocked() )
+ ImpSetVisAreaSize();
+}
+
+void SdrOle2Obj::SetGeoData(const SdrObjGeoData& rGeo)
+{
+ SdrRectObj::SetGeoData(rGeo);
+
+ if( !getSdrModelFromSdrObject().isLocked() )
+ ImpSetVisAreaSize();
+}
+
+void SdrOle2Obj::NbcSetSnapRect(const tools::Rectangle& rRect)
+{
+ SdrRectObj::NbcSetSnapRect(rRect);
+
+ if( !getSdrModelFromSdrObject().isLocked() )
+ ImpSetVisAreaSize();
+
+ if ( mpImpl->mxObjRef.is() && IsChart() )
+ {
+ //#i103460# charts do not necessarily have an own size within ODF files,
+ //for this case they need to use the size settings from the surrounding frame,
+ //which is made available with this method as there is no other way
+ mpImpl->mxObjRef.SetDefaultSizeForChart( Size( rRect.GetWidth(), rRect.GetHeight() ) );
+ }
+}
+
+void SdrOle2Obj::NbcSetLogicRect(const tools::Rectangle& rRect)
+{
+ SdrRectObj::NbcSetLogicRect(rRect);
+
+ if( !getSdrModelFromSdrObject().isLocked() )
+ ImpSetVisAreaSize();
+}
+
+const Graphic* SdrOle2Obj::GetGraphic() const
+{
+ if ( mpImpl->mxObjRef.is() )
+ return mpImpl->mxObjRef.GetGraphic();
+ return mpImpl->mxGraphic.get();
+}
+
+void SdrOle2Obj::GetNewReplacement()
+{
+ if ( mpImpl->mxObjRef.is() )
+ mpImpl->mxObjRef.UpdateReplacement();
+}
+
+Size SdrOle2Obj::GetOrigObjSize( MapMode const * pTargetMapMode ) const
+{
+ return mpImpl->mxObjRef.GetSize( pTargetMapMode );
+}
+
+void SdrOle2Obj::setSuppressSetVisAreaSize( bool bNew )
+{
+ mpImpl->mbSuppressSetVisAreaSize = bNew;
+}
+
+void SdrOle2Obj::NbcMove(const Size& rSize)
+{
+ SdrRectObj::NbcMove(rSize);
+
+ if( !getSdrModelFromSdrObject().isLocked() )
+ ImpSetVisAreaSize();
+}
+
+bool SdrOle2Obj::CanUnloadRunningObj( const uno::Reference< embed::XEmbeddedObject >& xObj, sal_Int64 nAspect )
+{
+ uno::Reference<embed::XEmbedPersist2> xPersist(xObj, uno::UNO_QUERY);
+ if (xPersist.is())
+ {
+ if (!xPersist->isStored())
+ // It doesn't have persistent storage. We can't unload this.
+ return false;
+ }
+
+ bool bResult = false;
+
+ sal_Int32 nState = xObj->getCurrentState();
+ if ( nState == embed::EmbedStates::LOADED )
+ {
+ // the object is already unloaded
+ bResult = true;
+ }
+ else
+ {
+ uno::Reference < util::XModifiable > xModifiable( xObj->getComponent(), uno::UNO_QUERY );
+ if ( !xModifiable.is() )
+ bResult = true;
+ else
+ {
+ sal_Int64 nMiscStatus = xObj->getStatus( nAspect );
+
+ if ( embed::EmbedMisc::MS_EMBED_ALWAYSRUN != ( nMiscStatus & embed::EmbedMisc::MS_EMBED_ALWAYSRUN ) &&
+ embed::EmbedMisc::EMBED_ACTIVATEIMMEDIATELY != ( nMiscStatus & embed::EmbedMisc::EMBED_ACTIVATEIMMEDIATELY ) &&
+ !( xModifiable.is() && xModifiable->isModified() ) &&
+ !( nState == embed::EmbedStates::INPLACE_ACTIVE || nState == embed::EmbedStates::UI_ACTIVE || nState == embed::EmbedStates::ACTIVE ) )
+ {
+ bResult = true;
+ }
+ }
+ }
+
+ return bResult;
+}
+
+bool SdrOle2Obj::Unload( const uno::Reference< embed::XEmbeddedObject >& xObj, sal_Int64 nAspect )
+{
+ bool bResult = false;
+
+ if ( CanUnloadRunningObj( xObj, nAspect ) )
+ {
+ try
+ {
+ xObj->changeState( embed::EmbedStates::LOADED );
+ bResult = true;
+ }
+ catch( css::uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "SdrOle2Obj::Unload()" );
+ }
+ }
+
+ return bResult;
+}
+
+bool SdrOle2Obj::Unload()
+{
+ if (!mpImpl->mxObjRef.is())
+ // Already unloaded.
+ return true;
+
+ return Unload(mpImpl->mxObjRef.GetObject(), GetAspect());
+}
+
+void SdrOle2Obj::GetObjRef_Impl()
+{
+ if ( !mpImpl->mxObjRef.is() && !mpImpl->aPersistName.isEmpty() && getSdrModelFromSdrObject().GetPersist() )
+ {
+ // Only try loading if it did not went wrong up to now
+ if(!mpImpl->mbLoadingOLEObjectFailed)
+ {
+ mpImpl->mxObjRef.Assign(
+ getSdrModelFromSdrObject().GetPersist()->getEmbeddedObjectContainer().GetEmbeddedObject(mpImpl->aPersistName),
+ GetAspect());
+ mpImpl->mbTypeAsked = false;
+ CheckFileLink_Impl();
+
+ // If loading of OLE object failed, remember that to not invoke an endless
+ // loop trying to load it again and again.
+ if( mpImpl->mxObjRef.is() )
+ {
+ mpImpl->mbLoadingOLEObjectFailed = true;
+ }
+
+ // For math objects, set closed state to transparent
+ SetClosedObj(!ImplIsMathObj( mpImpl->mxObjRef.GetObject() ));
+ }
+
+ if ( mpImpl->mxObjRef.is() )
+ {
+ if( !IsEmptyPresObj() )
+ {
+ // remember modified status of model
+ const bool bWasChanged(getSdrModelFromSdrObject().IsChanged());
+
+ // perhaps preview not valid anymore
+ // This line changes the modified state of the model
+ ClearGraphic();
+
+ // if status was not set before, force it back
+ // to not set, so that SetGraphic(0) above does not
+ // set the modified state of the model.
+ if(!bWasChanged && getSdrModelFromSdrObject().IsChanged())
+ {
+ getSdrModelFromSdrObject().SetChanged( false );
+ }
+ }
+ }
+
+ if ( mpImpl->mxObjRef.is() )
+ Connect();
+ }
+
+ if ( mpImpl->mbConnected )
+ {
+ // move object to first position in cache
+ GetSdrGlobalData().GetOLEObjCache().InsertObj(this);
+ }
+}
+
+uno::Reference < embed::XEmbeddedObject > const & SdrOle2Obj::GetObjRef() const
+{
+ const_cast<SdrOle2Obj*>(this)->GetObjRef_Impl();
+ return mpImpl->mxObjRef.GetObject();
+}
+
+uno::Reference < embed::XEmbeddedObject > const & SdrOle2Obj::GetObjRef_NoInit() const
+{
+ return mpImpl->mxObjRef.GetObject();
+}
+
+uno::Reference< frame::XModel > SdrOle2Obj::getXModel() const
+{
+ if (svt::EmbeddedObjectRef::TryRunningState(GetObjRef()))
+ return uno::Reference< frame::XModel >( mpImpl->mxObjRef->getComponent(), uno::UNO_QUERY );
+ else
+ return uno::Reference< frame::XModel >();
+}
+
+bool SdrOle2Obj::IsChart() const
+{
+ if (!mpImpl->mbTypeAsked)
+ {
+ mpImpl->mbIsChart = mpImpl->mxObjRef.IsChart();
+ mpImpl->mbTypeAsked = true;
+ }
+ return mpImpl->mbIsChart;
+}
+
+void SdrOle2Obj::SetGraphicToObj( const Graphic& aGraphic )
+{
+ mpImpl->mxObjRef.SetGraphic( aGraphic, OUString() );
+ // if the object isn't valid, e.g. link to something that doesn't exist, set the fallback
+ // graphic as mxGraphic so SdrOle2Obj::GetGraphic will show the fallback
+ if (const Graphic* pObjGraphic = mpImpl->mxObjRef.is() ? nullptr : mpImpl->mxObjRef.GetGraphic())
+ mpImpl->mxGraphic.reset(new Graphic(*pObjGraphic));
+}
+
+void SdrOle2Obj::SetGraphicToObj( const uno::Reference< io::XInputStream >& xGrStream, const OUString& aMediaType )
+{
+ mpImpl->mxObjRef.SetGraphicStream( xGrStream, aMediaType );
+ // if the object isn't valid, e.g. link to something that doesn't exist, set the fallback
+ // graphic as mxGraphic so SdrOle2Obj::GetGraphic will show the fallback
+ if (const Graphic* pObjGraphic = mpImpl->mxObjRef.is() ? nullptr : mpImpl->mxObjRef.GetGraphic())
+ mpImpl->mxGraphic.reset(new Graphic(*pObjGraphic));
+}
+
+bool SdrOle2Obj::IsCalc() const
+{
+ if ( !mpImpl->mxObjRef.is() )
+ return false;
+
+ SvGlobalName aObjClsId( mpImpl->mxObjRef->getClassID() );
+ return SvGlobalName(SO3_SC_CLASSID_30) == aObjClsId
+ || SvGlobalName(SO3_SC_CLASSID_40) == aObjClsId
+ || SvGlobalName(SO3_SC_CLASSID_50) == aObjClsId
+ || SvGlobalName(SO3_SC_CLASSID_60) == aObjClsId
+ || SvGlobalName(SO3_SC_OLE_EMBED_CLASSID_60) == aObjClsId
+ || SvGlobalName(SO3_SC_OLE_EMBED_CLASSID_8) == aObjClsId
+ || SvGlobalName(SO3_SC_CLASSID) == aObjClsId;
+}
+
+uno::Reference< frame::XModel > SdrOle2Obj::GetParentXModel() const
+{
+ uno::Reference< frame::XModel > xDoc(getSdrModelFromSdrObject().getUnoModel(), uno::UNO_QUERY);
+ return xDoc;
+}
+
+bool SdrOle2Obj::CalculateNewScaling( Fraction& aScaleWidth, Fraction& aScaleHeight, Size& aObjAreaSize )
+{
+ // TODO/LEAN: to avoid rounding errors scaling always uses the VisArea.
+ // If we don't cache it for own objects also we must load the object here
+ if (!mpImpl->mxObjRef.is())
+ return false;
+
+ MapMode aMapMode(getSdrModelFromSdrObject().GetScaleUnit());
+ aObjAreaSize = mpImpl->mxObjRef.GetSize( &aMapMode );
+
+ Size aSize = maRect.GetSize();
+ aScaleWidth = Fraction(aSize.Width(), aObjAreaSize.Width() );
+ aScaleHeight = Fraction(aSize.Height(), aObjAreaSize.Height() );
+
+ // reduce to 10 binary digits
+ aScaleHeight.ReduceInaccurate(10);
+ aScaleWidth.ReduceInaccurate(10);
+
+ return true;
+}
+
+bool SdrOle2Obj::AddOwnLightClient()
+{
+ // The Own Light Client must be registered in object only using this method!
+ if ( !SfxInPlaceClient::GetClient( dynamic_cast<SfxObjectShell*>(getSdrModelFromSdrObject().GetPersist()), mpImpl->mxObjRef.GetObject() )
+ && !( mpImpl->mxLightClient.is() && mpImpl->mxObjRef->getClientSite() == uno::Reference< embed::XEmbeddedClient >( mpImpl->mxLightClient ) ) )
+ {
+ Connect();
+
+ if ( mpImpl->mxObjRef.is() && mpImpl->mxLightClient.is() )
+ {
+ Fraction aScaleWidth;
+ Fraction aScaleHeight;
+ Size aObjAreaSize;
+ if ( CalculateNewScaling( aScaleWidth, aScaleHeight, aObjAreaSize ) )
+ {
+ mpImpl->mxLightClient->SetSizeScale( aScaleWidth, aScaleHeight );
+ try {
+ mpImpl->mxObjRef->setClientSite( mpImpl->mxLightClient );
+ return true;
+ } catch( uno::Exception& )
+ {}
+ }
+
+ }
+
+ return false;
+ }
+
+ return true;
+}
+
+Graphic SdrOle2Obj::GetEmptyOLEReplacementGraphic()
+{
+ return Graphic(BitmapEx(BMP_SVXOLEOBJ));
+}
+
+void SdrOle2Obj::SetWindow(const css::uno::Reference < css::awt::XWindow >& _xWindow)
+{
+ if ( mpImpl->mxObjRef.is() && mpImpl->mxLightClient.is() )
+ {
+ mpImpl->mxLightClient->setWindow(_xWindow);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdopage.cxx b/svx/source/svdraw/svdopage.cxx
new file mode 100644
index 000000000..67c7a104e
--- /dev/null
+++ b/svx/source/svdraw/svdopage.cxx
@@ -0,0 +1,175 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <svx/svdopage.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <svx/svdmodel.hxx>
+#include <svx/svdpage.hxx>
+#include <sdr/properties/pageproperties.hxx>
+#include <sdr/contact/viewcontactofpageobj.hxx>
+
+
+// BaseProperties section
+
+std::unique_ptr<sdr::properties::BaseProperties> SdrPageObj::CreateObjectSpecificProperties()
+{
+ return std::make_unique<sdr::properties::PageProperties>(*this);
+}
+
+
+// DrawContact section
+
+std::unique_ptr<sdr::contact::ViewContact> SdrPageObj::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::ViewContactOfPageObj>(*this);
+}
+
+
+// this method is called from the destructor of the referenced page.
+// do all necessary action to forget the page. It is not necessary to call
+// RemovePageUser(), that is done from the destructor.
+void SdrPageObj::PageInDestruction(const SdrPage& rPage)
+{
+ if(mpShownPage && mpShownPage == &rPage)
+ {
+ // #i58769# Do not call ActionChanged() here, because that would
+ // lead to the construction of a view contact object for a page that
+ // is being destroyed.
+
+ mpShownPage = nullptr;
+ }
+}
+
+SdrPageObj::SdrPageObj(
+ SdrModel& rSdrModel,
+ SdrPage* pNewPage)
+: SdrObject(rSdrModel),
+ mpShownPage(pNewPage)
+{
+ if(mpShownPage)
+ {
+ mpShownPage->AddPageUser(*this);
+ }
+}
+
+SdrPageObj::SdrPageObj(SdrModel& rSdrModel, SdrPageObj const & rSource)
+: SdrObject(rSdrModel, rSource),
+ mpShownPage(nullptr)
+{
+ SetReferencedPage( rSource.GetReferencedPage());
+}
+
+SdrPageObj::SdrPageObj(
+ SdrModel& rSdrModel,
+ const tools::Rectangle& rRect,
+ SdrPage* pNewPage)
+: SdrObject(rSdrModel),
+ mpShownPage(pNewPage)
+{
+ if(mpShownPage)
+ {
+ mpShownPage->AddPageUser(*this);
+ }
+
+ m_aOutRect = rRect;
+}
+
+SdrPageObj::~SdrPageObj()
+{
+ if(mpShownPage)
+ {
+ mpShownPage->RemovePageUser(*this);
+ }
+}
+
+
+void SdrPageObj::SetReferencedPage(SdrPage* pNewPage)
+{
+ if(mpShownPage == pNewPage)
+ return;
+
+ if(mpShownPage)
+ {
+ mpShownPage->RemovePageUser(*this);
+ }
+
+ mpShownPage = pNewPage;
+
+ if(mpShownPage)
+ {
+ mpShownPage->AddPageUser(*this);
+ }
+
+ SetChanged();
+ BroadcastObjectChange();
+}
+
+// #i96598#
+void SdrPageObj::SetBoundRectDirty()
+{
+ // avoid resetting aOutRect which in case of this object is model data,
+ // not re-creatable view data
+}
+
+SdrObjKind SdrPageObj::GetObjIdentifier() const
+{
+ return SdrObjKind::Page;
+}
+
+void SdrPageObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
+{
+ rInfo.bRotateFreeAllowed=false;
+ rInfo.bRotate90Allowed =false;
+ rInfo.bMirrorFreeAllowed=false;
+ rInfo.bMirror45Allowed =false;
+ rInfo.bMirror90Allowed =false;
+ rInfo.bTransparenceAllowed = false;
+ rInfo.bShearAllowed =false;
+ rInfo.bEdgeRadiusAllowed=false;
+ rInfo.bNoOrthoDesired =false;
+ rInfo.bCanConvToPath =false;
+ rInfo.bCanConvToPoly =false;
+ rInfo.bCanConvToPathLineToArea=false;
+ rInfo.bCanConvToPolyLineToArea=false;
+}
+
+SdrPageObj* SdrPageObj::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new SdrPageObj(rTargetModel, *this);
+}
+
+OUString SdrPageObj::TakeObjNameSingul() const
+{
+ OUString sName(SvxResId(STR_ObjNameSingulPAGE));
+
+ OUString aName(GetName());
+ if (!aName.isEmpty())
+ sName += " '" + aName + "'";
+
+ return sName;
+}
+
+OUString SdrPageObj::TakeObjNamePlural() const
+{
+ return SvxResId(STR_ObjNamePluralPAGE);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdopath.cxx b/svx/source/svdraw/svdopath.cxx
new file mode 100644
index 000000000..9f338301e
--- /dev/null
+++ b/svx/source/svdraw/svdopath.cxx
@@ -0,0 +1,2994 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <o3tl/unit_conversion.hxx>
+#include <tools/bigint.hxx>
+#include <tools/helpers.hxx>
+#include <svx/svdopath.hxx>
+#include <math.h>
+#include <svx/xpoly.hxx>
+#include <svx/svdtrans.hxx>
+#include <svx/svddrag.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdhdl.hxx>
+#include <svx/svdview.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+
+#include <svx/polypolygoneditor.hxx>
+#include <sdr/contact/viewcontactofsdrpathobj.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/curve/b2dcubicbezier.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <sdr/attribute/sdrtextattribute.hxx>
+#include <sdr/primitive2d/sdrattributecreator.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <sdr/attribute/sdrformtextattribute.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <memory>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+using namespace sdr;
+
+static sal_uInt16 GetPrevPnt(sal_uInt16 nPnt, sal_uInt16 nPntMax, bool bClosed)
+{
+ if (nPnt>0) {
+ nPnt--;
+ } else {
+ nPnt=nPntMax;
+ if (bClosed) nPnt--;
+ }
+ return nPnt;
+}
+
+static sal_uInt16 GetNextPnt(sal_uInt16 nPnt, sal_uInt16 nPntMax, bool bClosed)
+{
+ nPnt++;
+ if (nPnt>nPntMax || (bClosed && nPnt>=nPntMax)) nPnt=0;
+ return nPnt;
+}
+
+namespace {
+
+struct ImpSdrPathDragData : public SdrDragStatUserData
+{
+ XPolygon aXP; // section of the original polygon
+ bool bValid; // FALSE = too few points
+ bool bClosed; // closed object?
+ sal_uInt16 nPoly; // number of the polygon in the PolyPolygon
+ sal_uInt16 nPnt; // number of point in the above polygon
+ sal_uInt16 nPointCount; // number of points of the polygon
+ bool bBegPnt; // dragged point is first point of a Polyline
+ bool bEndPnt; // dragged point is finishing point of a Polyline
+ sal_uInt16 nPrevPnt; // index of previous point
+ sal_uInt16 nNextPnt; // index of next point
+ bool bPrevIsBegPnt; // previous point is first point of a Polyline
+ bool bNextIsEndPnt; // next point is first point of a Polyline
+ sal_uInt16 nPrevPrevPnt; // index of point before previous point
+ sal_uInt16 nNextNextPnt; // index of point after next point
+ bool bControl; // point is a control point
+ bool bIsNextControl; // point is a control point after a support point
+ bool bPrevIsControl; // if nPnt is a support point: a control point comes before
+ bool bNextIsControl; // if nPnt is a support point: a control point comes after
+ sal_uInt16 nPrevPrevPnt0;
+ sal_uInt16 nPrevPnt0;
+ sal_uInt16 nPnt0;
+ sal_uInt16 nNextPnt0;
+ sal_uInt16 nNextNextPnt0;
+ bool bEliminate; // delete point? (is set by MovDrag)
+
+ bool mbMultiPointDrag;
+ const XPolyPolygon maOrig;
+ XPolyPolygon maMove;
+ std::vector<SdrHdl*> maHandles;
+
+public:
+ ImpSdrPathDragData(const SdrPathObj& rPO, const SdrHdl& rHdl, bool bMuPoDr, const SdrDragStat& rDrag);
+ void ResetPoly(const SdrPathObj& rPO);
+ bool IsMultiPointDrag() const { return mbMultiPointDrag; }
+};
+
+}
+
+ImpSdrPathDragData::ImpSdrPathDragData(const SdrPathObj& rPO, const SdrHdl& rHdl, bool bMuPoDr, const SdrDragStat& rDrag)
+ : aXP(5)
+ , bValid(false)
+ , bClosed(false)
+ , nPoly(0)
+ , nPnt(0)
+ , nPointCount(0)
+ , bBegPnt(false)
+ , bEndPnt(false)
+ , nPrevPnt(0)
+ , nNextPnt(0)
+ , bPrevIsBegPnt(false)
+ , bNextIsEndPnt(false)
+ , nPrevPrevPnt(0)
+ , nNextNextPnt(0)
+ , bControl(false)
+ , bIsNextControl(false)
+ , bPrevIsControl(false)
+ , bNextIsControl(false)
+ , nPrevPrevPnt0(0)
+ , nPrevPnt0(0)
+ , nPnt0(0)
+ , nNextPnt0(0)
+ , nNextNextPnt0(0)
+ , bEliminate(false)
+ , mbMultiPointDrag(bMuPoDr)
+ , maOrig(rPO.GetPathPoly())
+ , maHandles(0)
+{
+ if(mbMultiPointDrag)
+ {
+ const SdrMarkView& rMarkView = *rDrag.GetView();
+ const SdrHdlList& rHdlList = rMarkView.GetHdlList();
+ const size_t nHdlCount = rHdlList.GetHdlCount();
+ const SdrObject* pInteractionObject(nHdlCount && rHdlList.GetHdl(0) ? rHdlList.GetHdl(0)->GetObj() : nullptr);
+
+ for(size_t a = 0; a < nHdlCount; ++a)
+ {
+ SdrHdl* pTestHdl = rHdlList.GetHdl(a);
+
+ if(pTestHdl && pTestHdl->IsSelected() && pTestHdl->GetObj() == pInteractionObject)
+ {
+ maHandles.push_back(pTestHdl);
+ }
+ }
+
+ maMove = maOrig;
+ bValid = true;
+ }
+ else
+ {
+ sal_uInt16 nPntMax = 0; // maximum index
+ bValid=false;
+ bClosed=rPO.IsClosed(); // closed object?
+ nPoly=static_cast<sal_uInt16>(rHdl.GetPolyNum()); // number of the polygon in the PolyPolygon
+ nPnt=static_cast<sal_uInt16>(rHdl.GetPointNum()); // number of points in the above polygon
+ const XPolygon aTmpXP(rPO.GetPathPoly().getB2DPolygon(nPoly));
+ nPointCount=aTmpXP.GetPointCount(); // number of point of the polygon
+ if (nPointCount==0 || (bClosed && nPointCount==1)) return; // minimum of 1 points for Lines, minimum of 2 points for Polygon
+ nPntMax=nPointCount-1; // maximum index
+ bBegPnt=!bClosed && nPnt==0; // dragged point is first point of a Polyline
+ bEndPnt=!bClosed && nPnt==nPntMax; // dragged point is finishing point of a Polyline
+ if (bClosed && nPointCount<=3) { // if polygon is only a line
+ bBegPnt=(nPointCount<3) || nPnt==0;
+ bEndPnt=(nPointCount<3) || nPnt==nPntMax-1;
+ }
+ nPrevPnt=nPnt; // index of previous point
+ nNextPnt=nPnt; // index of next point
+ if (!bBegPnt) nPrevPnt=GetPrevPnt(nPnt,nPntMax,bClosed);
+ if (!bEndPnt) nNextPnt=GetNextPnt(nPnt,nPntMax,bClosed);
+ bPrevIsBegPnt=bBegPnt || (!bClosed && nPrevPnt==0);
+ bNextIsEndPnt=bEndPnt || (!bClosed && nNextPnt==nPntMax);
+ nPrevPrevPnt=nPnt; // index of point before previous point
+ nNextNextPnt=nPnt; // index of point after next point
+ if (!bPrevIsBegPnt) nPrevPrevPnt=GetPrevPnt(nPrevPnt,nPntMax,bClosed);
+ if (!bNextIsEndPnt) nNextNextPnt=GetNextPnt(nNextPnt,nPntMax,bClosed);
+ bControl=rHdl.IsPlusHdl(); // point is a control point
+ bIsNextControl=false; // point is a control point after a support point
+ bPrevIsControl=false; // if nPnt is a support point: a control point comes before
+ bNextIsControl=false; // if nPnt is a support point: a control point comes after
+ if (bControl) {
+ bIsNextControl=!aTmpXP.IsControl(nPrevPnt);
+ } else {
+ bPrevIsControl=!bBegPnt && !bPrevIsBegPnt && aTmpXP.GetFlags(nPrevPnt)==PolyFlags::Control;
+ bNextIsControl=!bEndPnt && !bNextIsEndPnt && aTmpXP.GetFlags(nNextPnt)==PolyFlags::Control;
+ }
+ nPrevPrevPnt0=nPrevPrevPnt;
+ nPrevPnt0 =nPrevPnt;
+ nPnt0 =nPnt;
+ nNextPnt0 =nNextPnt;
+ nNextNextPnt0=nNextNextPnt;
+ nPrevPrevPnt=0;
+ nPrevPnt=1;
+ nPnt=2;
+ nNextPnt=3;
+ nNextNextPnt=4;
+ bEliminate=false;
+ ResetPoly(rPO);
+ bValid=true;
+ }
+}
+
+void ImpSdrPathDragData::ResetPoly(const SdrPathObj& rPO)
+{
+ const XPolygon aTmpXP(rPO.GetPathPoly().getB2DPolygon(nPoly));
+ aXP[0]=aTmpXP[nPrevPrevPnt0]; aXP.SetFlags(0,aTmpXP.GetFlags(nPrevPrevPnt0));
+ aXP[1]=aTmpXP[nPrevPnt0]; aXP.SetFlags(1,aTmpXP.GetFlags(nPrevPnt0));
+ aXP[2]=aTmpXP[nPnt0]; aXP.SetFlags(2,aTmpXP.GetFlags(nPnt0));
+ aXP[3]=aTmpXP[nNextPnt0]; aXP.SetFlags(3,aTmpXP.GetFlags(nNextPnt0));
+ aXP[4]=aTmpXP[nNextNextPnt0]; aXP.SetFlags(4,aTmpXP.GetFlags(nNextNextPnt0));
+}
+
+namespace {
+
+struct ImpPathCreateUser : public SdrDragStatUserData
+{
+ Point aBezControl0;
+ Point aBezStart;
+ Point aBezCtrl1;
+ Point aBezCtrl2;
+ Point aBezEnd;
+ Point aCircStart;
+ Point aCircEnd;
+ Point aCircCenter;
+ Point aLineStart;
+ Point aLineEnd;
+ Point aRectP1;
+ Point aRectP2;
+ Point aRectP3;
+ tools::Long nCircRadius;
+ Degree100 nCircStAngle;
+ Degree100 nCircRelAngle;
+ bool bBezier;
+ bool bBezHasCtrl0;
+ bool bCircle;
+ bool bAngleSnap;
+ bool bLine;
+ bool bLine90;
+ bool bRect;
+ bool bMixedCreate;
+ sal_uInt16 nBezierStartPoint;
+ SdrObjKind eStartKind;
+ SdrObjKind eCurrentKind;
+
+public:
+ ImpPathCreateUser(): nCircRadius(0),nCircStAngle(0),nCircRelAngle(0),
+ bBezier(false),bBezHasCtrl0(false),bCircle(false),bAngleSnap(false),bLine(false),bLine90(false),bRect(false),
+ bMixedCreate(false),nBezierStartPoint(0),eStartKind(SdrObjKind::NONE),eCurrentKind(SdrObjKind::NONE) { }
+
+ void ResetFormFlags() { bBezier=false; bCircle=false; bLine=false; bRect=false; }
+ bool IsFormFlag() const { return bBezier || bCircle || bLine || bRect; }
+ XPolygon GetFormPoly() const;
+ void CalcBezier(const Point& rP1, const Point& rP2, const Point& rDir, bool bMouseDown);
+ XPolygon GetBezierPoly() const;
+ void CalcCircle(const Point& rP1, const Point& rP2, const Point& rDir, SdrView const * pView);
+ XPolygon GetCirclePoly() const;
+ void CalcLine(const Point& rP1, const Point& rP2, const Point& rDir, SdrView const * pView);
+ static Point CalcLine(const Point& rCsr, tools::Long nDirX, tools::Long nDirY, SdrView const * pView);
+ XPolygon GetLinePoly() const;
+ void CalcRect(const Point& rP1, const Point& rP2, const Point& rDir, SdrView const * pView);
+ XPolygon GetRectPoly() const;
+};
+
+}
+
+XPolygon ImpPathCreateUser::GetFormPoly() const
+{
+ if (bBezier) return GetBezierPoly();
+ if (bCircle) return GetCirclePoly();
+ if (bLine) return GetLinePoly();
+ if (bRect) return GetRectPoly();
+ return XPolygon();
+}
+
+void ImpPathCreateUser::CalcBezier(const Point& rP1, const Point& rP2, const Point& rDir, bool bMouseDown)
+{
+ aBezStart=rP1;
+ aBezCtrl1=rP1+rDir;
+ aBezCtrl2=rP2;
+
+ // #i21479#
+ // Also copy the end point when no end point is set yet
+ if (!bMouseDown || (0 == aBezEnd.X() && 0 == aBezEnd.Y())) aBezEnd=rP2;
+
+ bBezier=true;
+}
+
+XPolygon ImpPathCreateUser::GetBezierPoly() const
+{
+ XPolygon aXP(4);
+ aXP[0]=aBezStart; aXP.SetFlags(0,PolyFlags::Smooth);
+ aXP[1]=aBezCtrl1; aXP.SetFlags(1,PolyFlags::Control);
+ aXP[2]=aBezCtrl2; aXP.SetFlags(2,PolyFlags::Control);
+ aXP[3]=aBezEnd;
+ return aXP;
+}
+
+void ImpPathCreateUser::CalcCircle(const Point& rP1, const Point& rP2, const Point& rDir, SdrView const * pView)
+{
+ Degree100 nTangAngle=GetAngle(rDir);
+ aCircStart=rP1;
+ aCircEnd=rP2;
+ aCircCenter=rP1;
+ tools::Long dx=rP2.X()-rP1.X();
+ tools::Long dy=rP2.Y()-rP1.Y();
+ Degree100 dAngle=GetAngle(Point(dx,dy))-nTangAngle;
+ dAngle=NormAngle36000(dAngle);
+ Degree100 nTmpAngle=NormAngle36000(9000_deg100-dAngle);
+ bool bRet=nTmpAngle!=9000_deg100 && nTmpAngle!=27000_deg100;
+ tools::Long nRad=0;
+ if (bRet) {
+ double cs = cos(toRadians(nTmpAngle));
+ double nR=static_cast<double>(GetLen(Point(dx,dy)))/cs/2;
+ nRad=std::abs(FRound(nR));
+ }
+ if (dAngle<18000_deg100) {
+ nCircStAngle=NormAngle36000(nTangAngle-9000_deg100);
+ nCircRelAngle=NormAngle36000(2_deg100*dAngle);
+ aCircCenter.AdjustX(FRound(nRad * cos(toRadians(nTangAngle + 9000_deg100))));
+ aCircCenter.AdjustY(-(FRound(nRad * sin(toRadians(nTangAngle + 9000_deg100)))));
+ } else {
+ nCircStAngle=NormAngle36000(nTangAngle+9000_deg100);
+ nCircRelAngle=-NormAngle36000(36000_deg100-2_deg100*dAngle);
+ aCircCenter.AdjustX(FRound(nRad * cos(toRadians(nTangAngle - 9000_deg100))));
+ aCircCenter.AdjustY(-(FRound(nRad * sin(toRadians(nTangAngle - 9000_deg100)))));
+ }
+ bAngleSnap=pView!=nullptr && pView->IsAngleSnapEnabled();
+ if (bAngleSnap) {
+ Degree100 nSA=pView->GetSnapAngle();
+ if (nSA) { // angle snapping
+ bool bNeg=nCircRelAngle<0_deg100;
+ if (bNeg) nCircRelAngle=-nCircRelAngle;
+ nCircRelAngle+=nSA/2_deg100;
+ nCircRelAngle/=nSA;
+ nCircRelAngle*=nSA;
+ nCircRelAngle=NormAngle36000(nCircRelAngle);
+ if (bNeg) nCircRelAngle=-nCircRelAngle;
+ }
+ }
+ nCircRadius=nRad;
+ if (nRad==0 || abs(nCircRelAngle).get()<5) bRet=false;
+ bCircle=bRet;
+}
+
+XPolygon ImpPathCreateUser::GetCirclePoly() const
+{
+ if (nCircRelAngle>=0_deg100) {
+ XPolygon aXP(aCircCenter,nCircRadius,nCircRadius,
+ nCircStAngle, nCircStAngle+nCircRelAngle,false);
+ aXP[0]=aCircStart; aXP.SetFlags(0,PolyFlags::Smooth);
+ if (!bAngleSnap) aXP[aXP.GetPointCount()-1]=aCircEnd;
+ return aXP;
+ } else {
+ XPolygon aXP(aCircCenter,nCircRadius,nCircRadius,
+ NormAngle36000(nCircStAngle+nCircRelAngle), nCircStAngle,false);
+ sal_uInt16 nCount=aXP.GetPointCount();
+ for (sal_uInt16 nNum=nCount/2; nNum>0;) {
+ nNum--; // reverse XPoly's order of points
+ sal_uInt16 n2=nCount-nNum-1;
+ Point aPt(aXP[nNum]);
+ aXP[nNum]=aXP[n2];
+ aXP[n2]=aPt;
+ }
+ aXP[0]=aCircStart; aXP.SetFlags(0,PolyFlags::Smooth);
+ if (!bAngleSnap) aXP[aXP.GetPointCount()-1]=aCircEnd;
+ return aXP;
+ }
+}
+
+Point ImpPathCreateUser::CalcLine(const Point& aCsr, tools::Long nDirX, tools::Long nDirY, SdrView const * pView)
+{
+ tools::Long x=aCsr.X();
+ tools::Long y=aCsr.Y();
+ bool bHLin=nDirY==0;
+ bool bVLin=nDirX==0;
+ if (bHLin) y=0;
+ else if (bVLin) x=0;
+ else {
+ tools::Long x1=BigMulDiv(y,nDirX,nDirY);
+ tools::Long y1=y;
+ tools::Long x2=x;
+ tools::Long y2=BigMulDiv(x,nDirY,nDirX);
+ tools::Long l1=std::abs(x1)+std::abs(y1);
+ tools::Long l2=std::abs(x2)+std::abs(y2);
+ if ((l1<=l2) != (pView!=nullptr && pView->IsBigOrtho())) {
+ x=x1; y=y1;
+ } else {
+ x=x2; y=y2;
+ }
+ }
+ return Point(x,y);
+}
+
+void ImpPathCreateUser::CalcLine(const Point& rP1, const Point& rP2, const Point& rDir, SdrView const * pView)
+{
+ aLineStart=rP1;
+ aLineEnd=rP2;
+ bLine90=false;
+ if (rP1==rP2 || (rDir.X()==0 && rDir.Y()==0)) { bLine=false; return; }
+ Point aTmpPt(rP2-rP1);
+ tools::Long nDirX=rDir.X();
+ tools::Long nDirY=rDir.Y();
+ Point aP1(CalcLine(aTmpPt, nDirX, nDirY,pView)); aP1-=aTmpPt; tools::Long nQ1=std::abs(aP1.X())+std::abs(aP1.Y());
+ Point aP2(CalcLine(aTmpPt, nDirY,-nDirX,pView)); aP2-=aTmpPt; tools::Long nQ2=std::abs(aP2.X())+std::abs(aP2.Y());
+ if (pView!=nullptr && pView->IsOrtho()) nQ1=0; // Ortho turns off at right angle
+ bLine90=nQ1>2*nQ2;
+ if (!bLine90) { // smooth transition
+ aLineEnd+=aP1;
+ } else { // rectangular transition
+ aLineEnd+=aP2;
+ }
+ bLine=true;
+}
+
+XPolygon ImpPathCreateUser::GetLinePoly() const
+{
+ XPolygon aXP(2);
+ aXP[0]=aLineStart; if (!bLine90) aXP.SetFlags(0,PolyFlags::Smooth);
+ aXP[1]=aLineEnd;
+ return aXP;
+}
+
+void ImpPathCreateUser::CalcRect(const Point& rP1, const Point& rP2, const Point& rDir, SdrView const * pView)
+{
+ aRectP1=rP1;
+ aRectP2=rP1;
+ aRectP3=rP2;
+ if (rP1==rP2 || (rDir.X()==0 && rDir.Y()==0)) { bRect=false; return; }
+ Point aTmpPt(rP2-rP1);
+ tools::Long nDirX=rDir.X();
+ tools::Long nDirY=rDir.Y();
+ tools::Long x=aTmpPt.X();
+ tools::Long y=aTmpPt.Y();
+ bool bHLin=nDirY==0;
+ bool bVLin=nDirX==0;
+ if (bHLin) y=0;
+ else if (bVLin) x=0;
+ else {
+ y=BigMulDiv(x,nDirY,nDirX);
+ tools::Long nHypLen=aTmpPt.Y()-y;
+ Degree100 nTangAngle=-GetAngle(rDir);
+ // sin=g/h, g=h*sin
+ double a = toRadians(nTangAngle);
+ double sn=sin(a);
+ double cs=cos(a);
+ double nGKathLen=nHypLen*sn;
+ y+=FRound(nGKathLen*sn);
+ x+=FRound(nGKathLen*cs);
+ }
+ aRectP2.AdjustX(x );
+ aRectP2.AdjustY(y );
+ if (pView!=nullptr && pView->IsOrtho()) {
+ tools::Long dx1=aRectP2.X()-aRectP1.X(); tools::Long dx1a=std::abs(dx1);
+ tools::Long dy1=aRectP2.Y()-aRectP1.Y(); tools::Long dy1a=std::abs(dy1);
+ tools::Long dx2=aRectP3.X()-aRectP2.X(); tools::Long dx2a=std::abs(dx2);
+ tools::Long dy2=aRectP3.Y()-aRectP2.Y(); tools::Long dy2a=std::abs(dy2);
+ bool b1MoreThan2=dx1a+dy1a>dx2a+dy2a;
+ if (b1MoreThan2 != pView->IsBigOrtho()) {
+ tools::Long xtemp=dy2a-dx1a; if (dx1<0) xtemp=-xtemp;
+ tools::Long ytemp=dx2a-dy1a; if (dy1<0) ytemp=-ytemp;
+ aRectP2.AdjustX(xtemp );
+ aRectP2.AdjustY(ytemp );
+ aRectP3.AdjustX(xtemp );
+ aRectP3.AdjustY(ytemp );
+ } else {
+ tools::Long xtemp=dy1a-dx2a; if (dx2<0) xtemp=-xtemp;
+ tools::Long ytemp=dx1a-dy2a; if (dy2<0) ytemp=-ytemp;
+ aRectP3.AdjustX(xtemp );
+ aRectP3.AdjustY(ytemp );
+ }
+ }
+ bRect=true;
+}
+
+XPolygon ImpPathCreateUser::GetRectPoly() const
+{
+ XPolygon aXP(3);
+ aXP[0]=aRectP1; aXP.SetFlags(0,PolyFlags::Smooth);
+ aXP[1]=aRectP2;
+ if (aRectP3!=aRectP2) aXP[2]=aRectP3;
+ return aXP;
+}
+
+class ImpPathForDragAndCreate
+{
+ SdrPathObj& mrSdrPathObject;
+ XPolyPolygon aPathPolygon;
+ SdrObjKind meObjectKind;
+ std::unique_ptr<ImpSdrPathDragData>
+ mpSdrPathDragData;
+ bool mbCreating;
+
+public:
+ explicit ImpPathForDragAndCreate(SdrPathObj& rSdrPathObject);
+
+ // drag stuff
+ bool beginPathDrag( SdrDragStat const & rDrag ) const;
+ bool movePathDrag( SdrDragStat& rDrag ) const;
+ bool endPathDrag( SdrDragStat const & rDrag );
+ OUString getSpecialDragComment(const SdrDragStat& rDrag) const;
+ basegfx::B2DPolyPolygon getSpecialDragPoly(const SdrDragStat& rDrag) const;
+
+ // create stuff
+ void BegCreate(SdrDragStat& rStat);
+ bool MovCreate(SdrDragStat& rStat);
+ bool EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd);
+ bool BckCreate(SdrDragStat const & rStat);
+ void BrkCreate(SdrDragStat& rStat);
+ PointerStyle GetCreatePointer() const;
+
+ // helping stuff
+ static bool IsClosed(SdrObjKind eKind) { return eKind==SdrObjKind::Polygon || eKind==SdrObjKind::PathPoly || eKind==SdrObjKind::PathFill || eKind==SdrObjKind::FreehandFill || eKind==SdrObjKind::SplineFill; }
+ static bool IsFreeHand(SdrObjKind eKind) { return eKind==SdrObjKind::FreehandLine || eKind==SdrObjKind::FreehandFill; }
+ static bool IsBezier(SdrObjKind eKind) { return eKind==SdrObjKind::PathLine || eKind==SdrObjKind::PathFill; }
+ bool IsCreating() const { return mbCreating; }
+
+ // get the polygon
+ basegfx::B2DPolyPolygon TakeObjectPolyPolygon(const SdrDragStat& rDrag) const;
+ static basegfx::B2DPolyPolygon TakeDragPolyPolygon(const SdrDragStat& rDrag);
+ basegfx::B2DPolyPolygon getModifiedPolyPolygon() const { return aPathPolygon.getB2DPolyPolygon(); }
+};
+
+ImpPathForDragAndCreate::ImpPathForDragAndCreate(SdrPathObj& rSdrPathObject)
+: mrSdrPathObject(rSdrPathObject),
+ aPathPolygon(rSdrPathObject.GetPathPoly()),
+ meObjectKind(mrSdrPathObject.meKind),
+ mbCreating(false)
+{
+}
+
+bool ImpPathForDragAndCreate::beginPathDrag( SdrDragStat const & rDrag ) const
+{
+ const SdrHdl* pHdl=rDrag.GetHdl();
+ if(!pHdl)
+ return false;
+
+ bool bMultiPointDrag(true);
+
+ if(aPathPolygon[static_cast<sal_uInt16>(pHdl->GetPolyNum())].IsControl(static_cast<sal_uInt16>(pHdl->GetPointNum())))
+ bMultiPointDrag = false;
+
+ if(bMultiPointDrag)
+ {
+ const SdrMarkView& rMarkView = *rDrag.GetView();
+ const SdrHdlList& rHdlList = rMarkView.GetHdlList();
+ const size_t nHdlCount = rHdlList.GetHdlCount();
+ const SdrObject* pInteractionObject(nHdlCount && rHdlList.GetHdl(0) ? rHdlList.GetHdl(0)->GetObj() : nullptr);
+ sal_uInt32 nSelectedPoints(0);
+
+ for(size_t a = 0; a < nHdlCount; ++a)
+ {
+ SdrHdl* pTestHdl = rHdlList.GetHdl(a);
+
+ if(pTestHdl && pTestHdl->IsSelected() && pTestHdl->GetObj() == pInteractionObject)
+ {
+ nSelectedPoints++;
+ }
+ }
+
+ if(nSelectedPoints <= 1)
+ bMultiPointDrag = false;
+ }
+
+ const_cast<ImpPathForDragAndCreate*>(this)->mpSdrPathDragData.reset( new ImpSdrPathDragData(mrSdrPathObject,*pHdl,bMultiPointDrag,rDrag) );
+
+ if(!mpSdrPathDragData || !mpSdrPathDragData->bValid)
+ {
+ OSL_FAIL("ImpPathForDragAndCreate::BegDrag(): ImpSdrPathDragData is invalid.");
+ const_cast<ImpPathForDragAndCreate*>(this)->mpSdrPathDragData.reset();
+ return false;
+ }
+
+ return true;
+}
+
+bool ImpPathForDragAndCreate::movePathDrag( SdrDragStat& rDrag ) const
+{
+ if(!mpSdrPathDragData || !mpSdrPathDragData->bValid)
+ {
+ OSL_FAIL("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData is invalid.");
+ return false;
+ }
+
+ if(mpSdrPathDragData->IsMultiPointDrag())
+ {
+ Point aDelta(rDrag.GetNow() - rDrag.GetStart());
+
+ if(aDelta.X() || aDelta.Y())
+ {
+ for(SdrHdl* pHandle : mpSdrPathDragData->maHandles)
+ {
+ const sal_uInt16 nPolyIndex(static_cast<sal_uInt16>(pHandle->GetPolyNum()));
+ const sal_uInt16 nPointIndex(static_cast<sal_uInt16>(pHandle->GetPointNum()));
+ const XPolygon& rOrig = mpSdrPathDragData->maOrig[nPolyIndex];
+ XPolygon& rMove = mpSdrPathDragData->maMove[nPolyIndex];
+ const sal_uInt16 nPointCount(rOrig.GetPointCount());
+ bool bClosed(rOrig[0] == rOrig[nPointCount-1]);
+
+ // move point itself
+ rMove[nPointIndex] = rOrig[nPointIndex] + aDelta;
+
+ // when point is first and poly closed, move close point, too.
+ if(nPointCount > 0 && !nPointIndex && bClosed)
+ {
+ rMove[nPointCount - 1] = rOrig[nPointCount - 1] + aDelta;
+
+ // when moving the last point it may be necessary to move the
+ // control point in front of this one, too.
+ if(nPointCount > 1 && rOrig.IsControl(nPointCount - 2))
+ rMove[nPointCount - 2] = rOrig[nPointCount - 2] + aDelta;
+ }
+
+ // is a control point before this?
+ if(nPointIndex > 0 && rOrig.IsControl(nPointIndex - 1))
+ {
+ // Yes, move it, too
+ rMove[nPointIndex - 1] = rOrig[nPointIndex - 1] + aDelta;
+ }
+
+ // is a control point after this?
+ if(nPointIndex + 1 < nPointCount && rOrig.IsControl(nPointIndex + 1))
+ {
+ // Yes, move it, too
+ rMove[nPointIndex + 1] = rOrig[nPointIndex + 1] + aDelta;
+ }
+ }
+ }
+ }
+ else
+ {
+ mpSdrPathDragData->ResetPoly(mrSdrPathObject);
+
+ // copy certain data locally to use less code and have faster access times
+ bool bClosed =mpSdrPathDragData->bClosed ; // closed object?
+ sal_uInt16 nPnt =mpSdrPathDragData->nPnt ; // number of point in the above polygon
+ bool bBegPnt =mpSdrPathDragData->bBegPnt ; // dragged point is first point of a Polyline
+ bool bEndPnt =mpSdrPathDragData->bEndPnt ; // dragged point is last point of a Polyline
+ sal_uInt16 nPrevPnt =mpSdrPathDragData->nPrevPnt ; // index of previous point
+ sal_uInt16 nNextPnt =mpSdrPathDragData->nNextPnt ; // index of next point
+ bool bPrevIsBegPnt =mpSdrPathDragData->bPrevIsBegPnt ; // previous point is first point of a Polyline
+ bool bNextIsEndPnt =mpSdrPathDragData->bNextIsEndPnt ; // next point is last point of a Polyline
+ sal_uInt16 nPrevPrevPnt =mpSdrPathDragData->nPrevPrevPnt ; // index of the point before the previous point
+ sal_uInt16 nNextNextPnt =mpSdrPathDragData->nNextNextPnt ; // index if the point after the next point
+ bool bControl =mpSdrPathDragData->bControl ; // point is a control point
+ bool bIsNextControl =mpSdrPathDragData->bIsNextControl; // point is a control point after a support point
+ bool bPrevIsControl =mpSdrPathDragData->bPrevIsControl; // if nPnt is a support point: there's a control point before
+ bool bNextIsControl =mpSdrPathDragData->bNextIsControl; // if nPnt is a support point: there's a control point after
+
+ // Ortho for lines/polygons: keep angle
+ if (!bControl && rDrag.GetView()!=nullptr && rDrag.GetView()->IsOrtho()) {
+ bool bBigOrtho=rDrag.GetView()->IsBigOrtho();
+ Point aPos(rDrag.GetNow()); // current position
+ Point aPnt(mpSdrPathDragData->aXP[nPnt]); // the dragged point
+ sal_uInt16 nPnt1=0xFFFF,nPnt2=0xFFFF; // its neighboring points
+ Point aNewPos1,aNewPos2; // new alternative for aPos
+ bool bPnt1 = false, bPnt2 = false; // are these valid alternatives?
+ if (!bClosed && mpSdrPathDragData->nPointCount>=2) { // minimum of 2 points for lines
+ if (!bBegPnt) nPnt1=nPrevPnt;
+ if (!bEndPnt) nPnt2=nNextPnt;
+ }
+ if (bClosed && mpSdrPathDragData->nPointCount>=3) { // minimum of 3 points for polygon
+ nPnt1=nPrevPnt;
+ nPnt2=nNextPnt;
+ }
+ if (nPnt1!=0xFFFF && !bPrevIsControl) {
+ Point aPnt1=mpSdrPathDragData->aXP[nPnt1];
+ tools::Long ndx0=aPnt.X()-aPnt1.X();
+ tools::Long ndy0=aPnt.Y()-aPnt1.Y();
+ bool bHLin=ndy0==0;
+ bool bVLin=ndx0==0;
+ if (!bHLin || !bVLin) {
+ tools::Long ndx=aPos.X()-aPnt1.X();
+ tools::Long ndy=aPos.Y()-aPnt1.Y();
+ bPnt1=true;
+ double nXFact=0; if (!bVLin) nXFact=static_cast<double>(ndx)/static_cast<double>(ndx0);
+ double nYFact=0; if (!bHLin) nYFact=static_cast<double>(ndy)/static_cast<double>(ndy0);
+ bool bHor=bHLin || (!bVLin && (nXFact>nYFact) ==bBigOrtho);
+ bool bVer=bVLin || (!bHLin && (nXFact<=nYFact)==bBigOrtho);
+ if (bHor) ndy=tools::Long(ndy0*nXFact);
+ if (bVer) ndx=tools::Long(ndx0*nYFact);
+ aNewPos1=aPnt1;
+ aNewPos1.AdjustX(ndx );
+ aNewPos1.AdjustY(ndy );
+ }
+ }
+ if (nPnt2!=0xFFFF && !bNextIsControl) {
+ Point aPnt2=mpSdrPathDragData->aXP[nPnt2];
+ tools::Long ndx0=aPnt.X()-aPnt2.X();
+ tools::Long ndy0=aPnt.Y()-aPnt2.Y();
+ bool bHLin=ndy0==0;
+ bool bVLin=ndx0==0;
+ if (!bHLin || !bVLin) {
+ tools::Long ndx=aPos.X()-aPnt2.X();
+ tools::Long ndy=aPos.Y()-aPnt2.Y();
+ bPnt2=true;
+ double nXFact=0; if (!bVLin) nXFact=static_cast<double>(ndx)/static_cast<double>(ndx0);
+ double nYFact=0; if (!bHLin) nYFact=static_cast<double>(ndy)/static_cast<double>(ndy0);
+ bool bHor=bHLin || (!bVLin && (nXFact>nYFact) ==bBigOrtho);
+ bool bVer=bVLin || (!bHLin && (nXFact<=nYFact)==bBigOrtho);
+ if (bHor) ndy=tools::Long(ndy0*nXFact);
+ if (bVer) ndx=tools::Long(ndx0*nYFact);
+ aNewPos2=aPnt2;
+ aNewPos2.AdjustX(ndx );
+ aNewPos2.AdjustY(ndy );
+ }
+ }
+ if (bPnt1 && bPnt2) { // both alternatives exist (and compete)
+ BigInt nX1(aNewPos1.X()-aPos.X()); nX1*=nX1;
+ BigInt nY1(aNewPos1.Y()-aPos.Y()); nY1*=nY1;
+ BigInt nX2(aNewPos2.X()-aPos.X()); nX2*=nX2;
+ BigInt nY2(aNewPos2.Y()-aPos.Y()); nY2*=nY2;
+ nX1+=nY1; // correction distance to square
+ nX2+=nY2; // correction distance to square
+ // let the alternative that allows fewer correction win
+ if (nX1<nX2) bPnt2=false; else bPnt1=false;
+ }
+ if (bPnt1) rDrag.SetNow(aNewPos1);
+ if (bPnt2) rDrag.SetNow(aNewPos2);
+ }
+ rDrag.SetActionRect(tools::Rectangle(rDrag.GetNow(),rDrag.GetNow()));
+
+ // specially for IBM: Eliminate points if both adjoining lines form near 180 degrees angle anyway
+ if (!bControl && rDrag.GetView()!=nullptr && rDrag.GetView()->IsEliminatePolyPoints() &&
+ !bBegPnt && !bEndPnt && !bPrevIsControl && !bNextIsControl)
+ {
+ Point aPt(mpSdrPathDragData->aXP[nNextPnt]);
+ aPt-=rDrag.GetNow();
+ Degree100 nAngle1=GetAngle(aPt);
+ aPt=rDrag.GetNow();
+ aPt-=mpSdrPathDragData->aXP[nPrevPnt];
+ Degree100 nAngle2=GetAngle(aPt);
+ Degree100 nDiff=nAngle1-nAngle2;
+ nDiff=abs(nDiff);
+ mpSdrPathDragData->bEliminate=nDiff<=rDrag.GetView()->GetEliminatePolyPointLimitAngle();
+ if (mpSdrPathDragData->bEliminate) { // adapt position, Smooth is true for the ends
+ aPt=mpSdrPathDragData->aXP[nNextPnt];
+ aPt+=mpSdrPathDragData->aXP[nPrevPnt];
+ aPt/=2;
+ rDrag.SetNow(aPt);
+ }
+ }
+
+ // we dragged by this distance
+ Point aDiff(rDrag.GetNow()); aDiff-=mpSdrPathDragData->aXP[nPnt];
+
+ /* There are 8 possible cases:
+ X 1. A control point neither on the left nor on the right.
+ o--X--o 2. There are control points on the left and the right, we are dragging a support point.
+ o--X 3. There is a control point on the left, we are dragging a support point.
+ X--o 4. There is a control point on the right, we are dragging a support point.
+ x--O--o 5. There are control points on the left and the right, we are dragging the left one.
+ x--O 6. There is a control point on the left, we are dragging it.
+ o--O--x 7. There are control points on the left and the right, we are dragging the right one.
+ O--x 8. There is a control point on the right, we are dragging it.
+ Note: modifying a line (not a curve!) might create a curve on the other end of the line
+ if Smooth is set there (with control points aligned to line).
+ */
+
+ mpSdrPathDragData->aXP[nPnt]+=aDiff;
+
+ // now check symmetric plus handles
+ if (bControl) { // cases 5,6,7,8
+ sal_uInt16 nSt; // the associated support point
+ sal_uInt16 nFix; // the opposing control point
+ if (bIsNextControl) { // if the next one is a control point, the on before has to be a support point
+ nSt=nPrevPnt;
+ nFix=nPrevPrevPnt;
+ } else {
+ nSt=nNextPnt;
+ nFix=nNextNextPnt;
+ }
+ if (mpSdrPathDragData->aXP.IsSmooth(nSt)) {
+ mpSdrPathDragData->aXP.CalcSmoothJoin(nSt,nPnt,nFix);
+ }
+ }
+
+ if (!bControl) { // Cases 1,2,3,4. In case 1, nothing happens; in cases 3 and 4, there is more following below.
+ // move both control points
+ if (bPrevIsControl) mpSdrPathDragData->aXP[nPrevPnt]+=aDiff;
+ if (bNextIsControl) mpSdrPathDragData->aXP[nNextPnt]+=aDiff;
+ // align control point to line, if appropriate
+ if (mpSdrPathDragData->aXP.IsSmooth(nPnt)) {
+ if (bPrevIsControl && !bNextIsControl && !bEndPnt) { // case 3
+ mpSdrPathDragData->aXP.CalcSmoothJoin(nPnt,nNextPnt,nPrevPnt);
+ }
+ if (bNextIsControl && !bPrevIsControl && !bBegPnt) { // case 4
+ mpSdrPathDragData->aXP.CalcSmoothJoin(nPnt,nPrevPnt,nNextPnt);
+ }
+ }
+ // Now check the other ends of the line (nPnt+-1). If there is a
+ // curve (IsControl(nPnt+-2)) with SmoothJoin (nPnt+-1), the
+ // associated control point (nPnt+-2) has to be adapted.
+ if (!bBegPnt && !bPrevIsControl && !bPrevIsBegPnt && mpSdrPathDragData->aXP.IsSmooth(nPrevPnt)) {
+ if (mpSdrPathDragData->aXP.IsControl(nPrevPrevPnt)) {
+ mpSdrPathDragData->aXP.CalcSmoothJoin(nPrevPnt,nPnt,nPrevPrevPnt);
+ }
+ }
+ if (!bEndPnt && !bNextIsControl && !bNextIsEndPnt && mpSdrPathDragData->aXP.IsSmooth(nNextPnt)) {
+ if (mpSdrPathDragData->aXP.IsControl(nNextNextPnt)) {
+ mpSdrPathDragData->aXP.CalcSmoothJoin(nNextPnt,nPnt,nNextNextPnt);
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+bool ImpPathForDragAndCreate::endPathDrag(SdrDragStat const & rDrag)
+{
+ Point aLinePt1;
+ Point aLinePt2;
+ bool bLineGlueMirror(SdrObjKind::Line == meObjectKind);
+ if (bLineGlueMirror) {
+ XPolygon& rXP=aPathPolygon[0];
+ aLinePt1=rXP[0];
+ aLinePt2=rXP[1];
+ }
+
+ if(!mpSdrPathDragData || !mpSdrPathDragData->bValid)
+ {
+ OSL_FAIL("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData is invalid.");
+ return false;
+ }
+
+ if(mpSdrPathDragData->IsMultiPointDrag())
+ {
+ aPathPolygon = mpSdrPathDragData->maMove;
+ }
+ else
+ {
+ const SdrHdl* pHdl=rDrag.GetHdl();
+
+ // reference the polygon
+ XPolygon& rXP=aPathPolygon[static_cast<sal_uInt16>(pHdl->GetPolyNum())];
+
+ // the 5 points that might have changed
+ if (!mpSdrPathDragData->bPrevIsBegPnt) rXP[mpSdrPathDragData->nPrevPrevPnt0]=mpSdrPathDragData->aXP[mpSdrPathDragData->nPrevPrevPnt];
+ if (!mpSdrPathDragData->bNextIsEndPnt) rXP[mpSdrPathDragData->nNextNextPnt0]=mpSdrPathDragData->aXP[mpSdrPathDragData->nNextNextPnt];
+ if (!mpSdrPathDragData->bBegPnt) rXP[mpSdrPathDragData->nPrevPnt0] =mpSdrPathDragData->aXP[mpSdrPathDragData->nPrevPnt];
+ if (!mpSdrPathDragData->bEndPnt) rXP[mpSdrPathDragData->nNextPnt0] =mpSdrPathDragData->aXP[mpSdrPathDragData->nNextPnt];
+ rXP[mpSdrPathDragData->nPnt0] =mpSdrPathDragData->aXP[mpSdrPathDragData->nPnt];
+
+ // for closed objects: last point has to be equal to first point
+ if (mpSdrPathDragData->bClosed) rXP[rXP.GetPointCount()-1]=rXP[0];
+
+ if (mpSdrPathDragData->bEliminate)
+ {
+ basegfx::B2DPolyPolygon aTempPolyPolygon(aPathPolygon.getB2DPolyPolygon());
+ sal_uInt32 nPoly,nPnt;
+
+ if(PolyPolygonEditor::GetRelativePolyPoint(aTempPolyPolygon, rDrag.GetHdl()->GetSourceHdlNum(), nPoly, nPnt))
+ {
+ basegfx::B2DPolygon aCandidate(aTempPolyPolygon.getB2DPolygon(nPoly));
+ aCandidate.remove(nPnt);
+
+ if(aCandidate.count() < 2)
+ {
+ aTempPolyPolygon.remove(nPoly);
+ }
+ else
+ {
+ aTempPolyPolygon.setB2DPolygon(nPoly, aCandidate);
+ }
+ }
+
+ aPathPolygon = XPolyPolygon(aTempPolyPolygon);
+ }
+
+ // adapt angle for text beneath a simple line
+ if (bLineGlueMirror)
+ {
+ Point aLinePt1_(aPathPolygon[0][0]);
+ Point aLinePt2_(aPathPolygon[0][1]);
+ bool bXMirr=(aLinePt1_.X()>aLinePt2_.X())!=(aLinePt1.X()>aLinePt2.X());
+ bool bYMirr=(aLinePt1_.Y()>aLinePt2_.Y())!=(aLinePt1.Y()>aLinePt2.Y());
+ if (bXMirr || bYMirr) {
+ Point aRef1(mrSdrPathObject.GetSnapRect().Center());
+ if (bXMirr) {
+ Point aRef2(aRef1);
+ aRef2.AdjustY( 1 );
+ mrSdrPathObject.NbcMirrorGluePoints(aRef1,aRef2);
+ }
+ if (bYMirr) {
+ Point aRef2(aRef1);
+ aRef2.AdjustX( 1 );
+ mrSdrPathObject.NbcMirrorGluePoints(aRef1,aRef2);
+ }
+ }
+ }
+ }
+
+ mpSdrPathDragData.reset();
+
+ return true;
+}
+
+OUString ImpPathForDragAndCreate::getSpecialDragComment(const SdrDragStat& rDrag) const
+{
+ OUString aStr;
+ const SdrHdl* pHdl = rDrag.GetHdl();
+ const bool bCreateComment(rDrag.GetView() && &mrSdrPathObject == rDrag.GetView()->GetCreateObj());
+
+ if(bCreateComment && rDrag.GetUser())
+ {
+ // #i103058# re-add old creation comment mode
+ const ImpPathCreateUser* pU = static_cast<const ImpPathCreateUser*>(rDrag.GetUser());
+ const SdrObjKind eOriginalKind(meObjectKind);
+ mrSdrPathObject.meKind = pU->eCurrentKind;
+ aStr = mrSdrPathObject.ImpGetDescriptionStr(STR_ViewCreateObj);
+ mrSdrPathObject.meKind = eOriginalKind;
+
+ Point aPrev(rDrag.GetPrev());
+ Point aNow(rDrag.GetNow());
+
+ if(pU->bLine)
+ aNow = pU->aLineEnd;
+
+ aNow -= aPrev;
+ aStr += " (";
+
+ if(pU->bCircle)
+ {
+ aStr += SdrModel::GetAngleString(abs(pU->nCircRelAngle))
+ + " r="
+ + mrSdrPathObject.getSdrModelFromSdrObject().GetMetricString(pU->nCircRadius, true);
+ }
+
+ aStr += "dx="
+ + mrSdrPathObject.getSdrModelFromSdrObject().GetMetricString(aNow.X(), true)
+ + " dy="
+ + mrSdrPathObject.getSdrModelFromSdrObject().GetMetricString(aNow.Y(), true);
+
+ if(!IsFreeHand(meObjectKind))
+ {
+ sal_Int32 nLen(GetLen(aNow));
+ Degree100 nAngle(GetAngle(aNow));
+ aStr += " l="
+ + mrSdrPathObject.getSdrModelFromSdrObject().GetMetricString(nLen, true)
+ + " "
+ + SdrModel::GetAngleString(nAngle);
+ }
+
+ aStr += ")";
+ }
+ else if(!pHdl)
+ {
+ // #i103058# fallback when no model and/or Handle, both needed
+ // for else-path
+ aStr = mrSdrPathObject.ImpGetDescriptionStr(STR_DragPathObj);
+ }
+ else
+ {
+ // #i103058# standard for modification; model and handle needed
+ ImpSdrPathDragData* pDragData = mpSdrPathDragData.get();
+
+ if(!pDragData)
+ {
+ // getSpecialDragComment is also used from create, so fallback to GetUser()
+ // when mpSdrPathDragData is not set
+ pDragData = static_cast<ImpSdrPathDragData*>(rDrag.GetUser());
+ }
+
+ if(!pDragData)
+ {
+ OSL_FAIL("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData is invalid.");
+ return OUString();
+ }
+
+ if(!pDragData->IsMultiPointDrag() && pDragData->bEliminate)
+ {
+ // point of ...
+ aStr = mrSdrPathObject.ImpGetDescriptionStr(STR_ViewMarkedPoint);
+
+ // delete %O
+ OUString aStr2(SvxResId(STR_EditDelete));
+
+ // UNICODE: delete point of ...
+ aStr2 = aStr2.replaceFirst("%1", aStr);
+
+ return aStr2;
+ }
+
+ // dx=0.00 dy=0.00 -- both sides bezier
+ // dx=0.00 dy=0.00 l=0.00 0.00\302\260 -- one bezier/lever on one side, a start, or an ending
+ // dx=0.00 dy=0.00 l=0.00 0.00\302\260 / l=0.00 0.00\302\260 -- in between
+ Point aBeg(rDrag.GetStart());
+ Point aNow(rDrag.GetNow());
+
+ aStr.clear();
+ aStr += "dx="
+ + mrSdrPathObject.getSdrModelFromSdrObject().GetMetricString(aNow.X() - aBeg.X(), true)
+ + " dy="
+ + mrSdrPathObject.getSdrModelFromSdrObject().GetMetricString(aNow.Y() - aBeg.Y(), true);
+
+ if(!pDragData->IsMultiPointDrag())
+ {
+ sal_uInt16 nPntNum(static_cast<sal_uInt16>(pHdl->GetPointNum()));
+ const XPolygon& rXPoly = aPathPolygon[static_cast<sal_uInt16>(rDrag.GetHdl()->GetPolyNum())];
+ sal_uInt16 nPointCount(rXPoly.GetPointCount());
+ bool bClose(IsClosed(meObjectKind));
+
+ if(bClose)
+ nPointCount--;
+
+ if(pHdl->IsPlusHdl())
+ {
+ // lever
+ sal_uInt16 nRef(nPntNum);
+
+ if(rXPoly.IsControl(nPntNum + 1))
+ nRef--;
+ else
+ nRef++;
+
+ aNow -= rXPoly[nRef];
+
+ sal_Int32 nLen(GetLen(aNow));
+ Degree100 nAngle(GetAngle(aNow));
+ aStr += " l="
+ + mrSdrPathObject.getSdrModelFromSdrObject().GetMetricString(nLen, true)
+ + " "
+ + SdrModel::GetAngleString(nAngle);
+ }
+ else if(nPointCount > 1)
+ {
+ sal_uInt16 nPntMax(nPointCount - 1);
+ bool bIsClosed(IsClosed(meObjectKind));
+ bool bPt1(nPntNum > 0);
+ bool bPt2(nPntNum < nPntMax);
+
+ if(bIsClosed && nPointCount > 2)
+ {
+ bPt1 = true;
+ bPt2 = true;
+ }
+
+ sal_uInt16 nPt1,nPt2;
+
+ if(nPntNum > 0)
+ nPt1 = nPntNum - 1;
+ else
+ nPt1 = nPntMax;
+
+ if(nPntNum < nPntMax)
+ nPt2 = nPntNum + 1;
+ else
+ nPt2 = 0;
+
+ if(bPt1 && rXPoly.IsControl(nPt1))
+ bPt1 = false; // don't display
+
+ if(bPt2 && rXPoly.IsControl(nPt2))
+ bPt2 = false; // of bezier data
+
+ if(bPt1)
+ {
+ Point aPt(aNow);
+ aPt -= rXPoly[nPt1];
+
+ sal_Int32 nLen(GetLen(aPt));
+ Degree100 nAngle(GetAngle(aPt));
+ aStr += " l="
+ + mrSdrPathObject.getSdrModelFromSdrObject().GetMetricString(nLen, true)
+ + " "
+ + SdrModel::GetAngleString(nAngle);
+ }
+
+ if(bPt2)
+ {
+ if(bPt1)
+ aStr += " / ";
+ else
+ aStr += " ";
+
+ Point aPt(aNow);
+ aPt -= rXPoly[nPt2];
+
+ sal_Int32 nLen(GetLen(aPt));
+ Degree100 nAngle(GetAngle(aPt));
+ aStr += "l="
+ + mrSdrPathObject.getSdrModelFromSdrObject().GetMetricString(nLen, true)
+ + " "
+ + SdrModel::GetAngleString(nAngle);
+ }
+ }
+ }
+ }
+
+ return aStr;
+}
+
+basegfx::B2DPolyPolygon ImpPathForDragAndCreate::getSpecialDragPoly(const SdrDragStat& rDrag) const
+{
+ if(!mpSdrPathDragData || !mpSdrPathDragData->bValid)
+ {
+ OSL_FAIL("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData is invalid.");
+ return basegfx::B2DPolyPolygon();
+ }
+
+ XPolyPolygon aRetval;
+
+ if(mpSdrPathDragData->IsMultiPointDrag())
+ {
+ aRetval.Insert(mpSdrPathDragData->maMove);
+ }
+ else
+ {
+ const XPolygon& rXP=aPathPolygon[static_cast<sal_uInt16>(rDrag.GetHdl()->GetPolyNum())];
+ if (rXP.GetPointCount()<=2) {
+ XPolygon aXPoly(rXP);
+ aXPoly[static_cast<sal_uInt16>(rDrag.GetHdl()->GetPointNum())]=rDrag.GetNow();
+ aRetval.Insert(std::move(aXPoly));
+ return aRetval.getB2DPolyPolygon();
+ }
+ // copy certain data locally to use less code and have faster access times
+ bool bClosed =mpSdrPathDragData->bClosed ; // closed object?
+ sal_uInt16 nPointCount = mpSdrPathDragData->nPointCount; // number of points
+ sal_uInt16 nPnt =mpSdrPathDragData->nPnt ; // number of points in the polygon
+ bool bBegPnt =mpSdrPathDragData->bBegPnt ; // dragged point is the first point of a Polyline
+ bool bEndPnt =mpSdrPathDragData->bEndPnt ; // dragged point is the last point of a Polyline
+ sal_uInt16 nPrevPnt =mpSdrPathDragData->nPrevPnt ; // index of the previous point
+ sal_uInt16 nNextPnt =mpSdrPathDragData->nNextPnt ; // index of the next point
+ bool bPrevIsBegPnt =mpSdrPathDragData->bPrevIsBegPnt ; // previous point is first point of a Polyline
+ bool bNextIsEndPnt =mpSdrPathDragData->bNextIsEndPnt ; // next point is last point of a Polyline
+ sal_uInt16 nPrevPrevPnt =mpSdrPathDragData->nPrevPrevPnt ; // index of the point before the previous point
+ sal_uInt16 nNextNextPnt =mpSdrPathDragData->nNextNextPnt ; // index of the point after the last point
+ bool bControl =mpSdrPathDragData->bControl ; // point is a control point
+ bool bIsNextControl =mpSdrPathDragData->bIsNextControl; //point is a control point after a support point
+ bool bPrevIsControl =mpSdrPathDragData->bPrevIsControl; // if nPnt is a support point: there's a control point before
+ bool bNextIsControl =mpSdrPathDragData->bNextIsControl; // if nPnt is a support point: there's a control point after
+ XPolygon aXPoly(mpSdrPathDragData->aXP);
+ XPolygon aLine1(2);
+ XPolygon aLine2(2);
+ XPolygon aLine3(2);
+ XPolygon aLine4(2);
+ if (bControl) {
+ aLine1[1]=mpSdrPathDragData->aXP[nPnt];
+ if (bIsNextControl) { // is this a control point after the support point?
+ aLine1[0]=mpSdrPathDragData->aXP[nPrevPnt];
+ aLine2[0]=mpSdrPathDragData->aXP[nNextNextPnt];
+ aLine2[1]=mpSdrPathDragData->aXP[nNextPnt];
+ if (mpSdrPathDragData->aXP.IsSmooth(nPrevPnt) && !bPrevIsBegPnt && mpSdrPathDragData->aXP.IsControl(nPrevPrevPnt)) {
+ aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-1],PolyFlags::Control);
+ aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-2],PolyFlags::Normal);
+ // leverage lines for the opposing curve segment
+ aLine3[0]=mpSdrPathDragData->aXP[nPrevPnt];
+ aLine3[1]=mpSdrPathDragData->aXP[nPrevPrevPnt];
+ aLine4[0]=rXP[mpSdrPathDragData->nPrevPrevPnt0-2];
+ aLine4[1]=rXP[mpSdrPathDragData->nPrevPrevPnt0-1];
+ } else {
+ aXPoly.Remove(0,1);
+ }
+ } else { // else this is a control point before a support point
+ aLine1[0]=mpSdrPathDragData->aXP[nNextPnt];
+ aLine2[0]=mpSdrPathDragData->aXP[nPrevPrevPnt];
+ aLine2[1]=mpSdrPathDragData->aXP[nPrevPnt];
+ if (mpSdrPathDragData->aXP.IsSmooth(nNextPnt) && !bNextIsEndPnt && mpSdrPathDragData->aXP.IsControl(nNextNextPnt)) {
+ aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+1],PolyFlags::Control);
+ aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+2],PolyFlags::Normal);
+ // leverage lines for the opposing curve segment
+ aLine3[0]=mpSdrPathDragData->aXP[nNextPnt];
+ aLine3[1]=mpSdrPathDragData->aXP[nNextNextPnt];
+ aLine4[0]=rXP[mpSdrPathDragData->nNextNextPnt0+2];
+ aLine4[1]=rXP[mpSdrPathDragData->nNextNextPnt0+1];
+ } else {
+ aXPoly.Remove(aXPoly.GetPointCount()-1,1);
+ }
+ }
+ } else { // else is not a control point
+ if (mpSdrPathDragData->bEliminate) {
+ aXPoly.Remove(2,1);
+ }
+ if (bPrevIsControl) aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-1],PolyFlags::Normal);
+ else if (!bBegPnt && !bPrevIsBegPnt && mpSdrPathDragData->aXP.IsControl(nPrevPrevPnt)) {
+ aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-1],PolyFlags::Control);
+ aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-2],PolyFlags::Normal);
+ } else {
+ aXPoly.Remove(0,1);
+ if (bBegPnt) aXPoly.Remove(0,1);
+ }
+ if (bNextIsControl) aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+1],PolyFlags::Normal);
+ else if (!bEndPnt && !bNextIsEndPnt && mpSdrPathDragData->aXP.IsControl(nNextNextPnt)) {
+ aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+1],PolyFlags::Control);
+ aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+2],PolyFlags::Normal);
+ } else {
+ aXPoly.Remove(aXPoly.GetPointCount()-1,1);
+ if (bEndPnt) aXPoly.Remove(aXPoly.GetPointCount()-1,1);
+ }
+ if (bClosed) { // "pear problem": 2 lines, 1 curve, everything smoothed, a point between both lines is dragged
+ if (aXPoly.GetPointCount()>nPointCount && aXPoly.IsControl(1)) {
+ sal_uInt16 a=aXPoly.GetPointCount();
+ aXPoly[a-2]=aXPoly[2]; aXPoly.SetFlags(a-2,aXPoly.GetFlags(2));
+ aXPoly[a-1]=aXPoly[3]; aXPoly.SetFlags(a-1,aXPoly.GetFlags(3));
+ aXPoly.Remove(0,3);
+ }
+ }
+ }
+ aRetval.Insert(std::move(aXPoly));
+ if (aLine1.GetPointCount()>1) aRetval.Insert(std::move(aLine1));
+ if (aLine2.GetPointCount()>1) aRetval.Insert(std::move(aLine2));
+ if (aLine3.GetPointCount()>1) aRetval.Insert(std::move(aLine3));
+ if (aLine4.GetPointCount()>1) aRetval.Insert(std::move(aLine4));
+ }
+
+ return aRetval.getB2DPolyPolygon();
+}
+
+void ImpPathForDragAndCreate::BegCreate(SdrDragStat& rStat)
+{
+ bool bFreeHand(IsFreeHand(meObjectKind));
+ rStat.SetNoSnap(bFreeHand);
+ rStat.SetOrtho8Possible();
+ aPathPolygon.Clear();
+ mbCreating=true;
+ bool bMakeStartPoint = true;
+ SdrView* pView=rStat.GetView();
+ if (pView!=nullptr && pView->IsUseIncompatiblePathCreateInterface() &&
+ (meObjectKind==SdrObjKind::Polygon || meObjectKind==SdrObjKind::PolyLine || meObjectKind==SdrObjKind::PathLine || meObjectKind==SdrObjKind::PathFill)) {
+ bMakeStartPoint = false;
+ }
+ aPathPolygon.Insert(XPolygon());
+ aPathPolygon[0][0]=rStat.GetStart();
+ if (bMakeStartPoint) {
+ aPathPolygon[0][1]=rStat.GetNow();
+ }
+ std::unique_ptr<ImpPathCreateUser> pU(new ImpPathCreateUser);
+ pU->eStartKind=meObjectKind;
+ pU->eCurrentKind=meObjectKind;
+ rStat.SetUser(std::move(pU));
+}
+
+bool ImpPathForDragAndCreate::MovCreate(SdrDragStat& rStat)
+{
+ ImpPathCreateUser* pU=static_cast<ImpPathCreateUser*>(rStat.GetUser());
+ SdrView* pView=rStat.GetView();
+ XPolygon& rXPoly=aPathPolygon[aPathPolygon.Count()-1];
+ if (pView!=nullptr && pView->IsCreateMode()) {
+ // switch to different CreateTool, if appropriate
+ SdrObjKind nIdent;
+ SdrInventor nInvent;
+ pView->TakeCurrentObj(nIdent,nInvent);
+ if (nInvent==SdrInventor::Default && pU->eCurrentKind != nIdent) {
+ SdrObjKind eNewKind = nIdent;
+ switch (eNewKind) {
+ case SdrObjKind::CircleArc:
+ case SdrObjKind::CircleOrEllipse:
+ case SdrObjKind::CircleCut:
+ case SdrObjKind::CircleSection:
+ eNewKind=SdrObjKind::CircleArc;
+ [[fallthrough]];
+ case SdrObjKind::Rectangle:
+ case SdrObjKind::Line:
+ case SdrObjKind::PolyLine:
+ case SdrObjKind::Polygon:
+ case SdrObjKind::PathLine:
+ case SdrObjKind::PathFill:
+ case SdrObjKind::FreehandLine:
+ case SdrObjKind::FreehandFill:
+ case SdrObjKind::SplineLine:
+ case SdrObjKind::SplineFill: {
+ pU->eCurrentKind=eNewKind;
+ pU->bMixedCreate=true;
+ pU->nBezierStartPoint=rXPoly.GetPointCount();
+ if (pU->nBezierStartPoint>0) pU->nBezierStartPoint--;
+ } break;
+ default: break;
+ } // switch
+ }
+ }
+ sal_uInt16 nCurrentPoint=rXPoly.GetPointCount();
+ if (aPathPolygon.Count()>1 && rStat.IsMouseDown() && nCurrentPoint<2) {
+ rXPoly[0]=rStat.GetPos0();
+ rXPoly[1]=rStat.GetNow();
+ nCurrentPoint=2;
+ }
+ if (nCurrentPoint==0) {
+ rXPoly[0]=rStat.GetPos0();
+ } else nCurrentPoint--;
+ bool bFreeHand=IsFreeHand(pU->eCurrentKind);
+ rStat.SetNoSnap(bFreeHand);
+ rStat.SetOrtho8Possible(pU->eCurrentKind!=SdrObjKind::CircleArc && pU->eCurrentKind!=SdrObjKind::Rectangle && (!pU->bMixedCreate || pU->eCurrentKind!=SdrObjKind::Line));
+ rXPoly[nCurrentPoint]=rStat.GetNow();
+ if (!pU->bMixedCreate && pU->eStartKind==SdrObjKind::Line && rXPoly.GetPointCount()>=1) {
+ Point aPt(rStat.GetStart());
+ if (pView!=nullptr && pView->IsCreate1stPointAsCenter()) {
+ aPt+=aPt;
+ aPt-=rStat.GetNow();
+ }
+ rXPoly[0]=aPt;
+ }
+ OutputDevice* pOut=pView==nullptr ? nullptr : pView->GetFirstOutputDevice();
+ if (bFreeHand) {
+ if (pU->nBezierStartPoint>nCurrentPoint) pU->nBezierStartPoint=nCurrentPoint;
+ if (rStat.IsMouseDown() && nCurrentPoint>0) {
+ // don't allow two consecutive points to occupy too similar positions
+ tools::Long nMinDist=1;
+ if (pView!=nullptr) nMinDist=pView->GetFreeHandMinDistPix();
+ if (pOut!=nullptr) nMinDist=pOut->PixelToLogic(Size(nMinDist,0)).Width();
+ if (nMinDist<1) nMinDist=1;
+
+ Point aPt0(rXPoly[nCurrentPoint-1]);
+ Point aPt1(rStat.GetNow());
+ tools::Long dx=aPt0.X()-aPt1.X(); if (dx<0) dx=-dx;
+ tools::Long dy=aPt0.Y()-aPt1.Y(); if (dy<0) dy=-dy;
+ if (dx<nMinDist && dy<nMinDist) return false;
+
+ // TODO: the following is copied from EndCreate (with a few smaller modifications)
+ // and should be combined into a method with the code there.
+
+ if (nCurrentPoint-pU->nBezierStartPoint>=3 && ((nCurrentPoint-pU->nBezierStartPoint)%3)==0) {
+ rXPoly.PointsToBezier(nCurrentPoint-3);
+ rXPoly.SetFlags(nCurrentPoint-1,PolyFlags::Control);
+ rXPoly.SetFlags(nCurrentPoint-2,PolyFlags::Control);
+
+ if (nCurrentPoint>=6 && rXPoly.IsControl(nCurrentPoint-4)) {
+ rXPoly.CalcTangent(nCurrentPoint-3,nCurrentPoint-4,nCurrentPoint-2);
+ rXPoly.SetFlags(nCurrentPoint-3,PolyFlags::Smooth);
+ }
+ }
+ rXPoly[nCurrentPoint+1]=rStat.GetNow();
+ rStat.NextPoint();
+ } else {
+ pU->nBezierStartPoint=nCurrentPoint;
+ }
+ }
+
+ pU->ResetFormFlags();
+ if (IsBezier(pU->eCurrentKind)) {
+ if (nCurrentPoint>=2) {
+ pU->CalcBezier(rXPoly[nCurrentPoint-1],rXPoly[nCurrentPoint],rXPoly[nCurrentPoint-1]-rXPoly[nCurrentPoint-2],rStat.IsMouseDown());
+ } else if (pU->bBezHasCtrl0) {
+ pU->CalcBezier(rXPoly[nCurrentPoint-1],rXPoly[nCurrentPoint],pU->aBezControl0-rXPoly[nCurrentPoint-1],rStat.IsMouseDown());
+ }
+ }
+ if (pU->eCurrentKind==SdrObjKind::CircleArc && nCurrentPoint>=2) {
+ pU->CalcCircle(rXPoly[nCurrentPoint-1],rXPoly[nCurrentPoint],rXPoly[nCurrentPoint-1]-rXPoly[nCurrentPoint-2],pView);
+ }
+ if (pU->eCurrentKind==SdrObjKind::Line && nCurrentPoint>=2) {
+ pU->CalcLine(rXPoly[nCurrentPoint-1],rXPoly[nCurrentPoint],rXPoly[nCurrentPoint-1]-rXPoly[nCurrentPoint-2],pView);
+ }
+ if (pU->eCurrentKind==SdrObjKind::Rectangle && nCurrentPoint>=2) {
+ pU->CalcRect(rXPoly[nCurrentPoint-1],rXPoly[nCurrentPoint],rXPoly[nCurrentPoint-1]-rXPoly[nCurrentPoint-2],pView);
+ }
+
+ return true;
+}
+
+bool ImpPathForDragAndCreate::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd)
+{
+ ImpPathCreateUser* pU=static_cast<ImpPathCreateUser*>(rStat.GetUser());
+ bool bRet = false;
+ SdrView* pView=rStat.GetView();
+ bool bIncomp=pView!=nullptr && pView->IsUseIncompatiblePathCreateInterface();
+ XPolygon& rXPoly=aPathPolygon[aPathPolygon.Count()-1];
+ sal_uInt16 nCurrentPoint=rXPoly.GetPointCount()-1;
+ rXPoly[nCurrentPoint]=rStat.GetNow();
+ if (!pU->bMixedCreate && pU->eStartKind==SdrObjKind::Line) {
+ if (rStat.GetPointCount()>=2) eCmd=SdrCreateCmd::ForceEnd;
+ bRet = eCmd==SdrCreateCmd::ForceEnd;
+ if (bRet) {
+ mbCreating = false;
+ rStat.SetUser(nullptr);
+ }
+ return bRet;
+ }
+
+ if (!pU->bMixedCreate && IsFreeHand(pU->eStartKind)) {
+ if (rStat.GetPointCount()>=2) eCmd=SdrCreateCmd::ForceEnd;
+ bRet=eCmd==SdrCreateCmd::ForceEnd;
+ if (bRet) {
+ mbCreating=false;
+ rStat.SetUser(nullptr);
+ }
+ return bRet;
+ }
+ if (eCmd==SdrCreateCmd::NextPoint || eCmd==SdrCreateCmd::NextObject) {
+ // don't allow two consecutive points to occupy the same position
+ if (nCurrentPoint==0 || rStat.GetNow()!=rXPoly[nCurrentPoint-1]) {
+ if (bIncomp) {
+ if (pU->nBezierStartPoint>nCurrentPoint) pU->nBezierStartPoint=nCurrentPoint;
+ if (IsBezier(pU->eCurrentKind) && nCurrentPoint-pU->nBezierStartPoint>=3 && ((nCurrentPoint-pU->nBezierStartPoint)%3)==0) {
+ rXPoly.PointsToBezier(nCurrentPoint-3);
+ rXPoly.SetFlags(nCurrentPoint-1,PolyFlags::Control);
+ rXPoly.SetFlags(nCurrentPoint-2,PolyFlags::Control);
+
+ if (nCurrentPoint>=6 && rXPoly.IsControl(nCurrentPoint-4)) {
+ rXPoly.CalcTangent(nCurrentPoint-3,nCurrentPoint-4,nCurrentPoint-2);
+ rXPoly.SetFlags(nCurrentPoint-3,PolyFlags::Smooth);
+ }
+ }
+ } else {
+ if (nCurrentPoint==1 && IsBezier(pU->eCurrentKind) && !pU->bBezHasCtrl0) {
+ pU->aBezControl0=rStat.GetNow();
+ pU->bBezHasCtrl0=true;
+ nCurrentPoint--;
+ }
+ if (pU->IsFormFlag()) {
+ sal_uInt16 nPointCount0=rXPoly.GetPointCount();
+ rXPoly.Remove(nCurrentPoint-1,2); // remove last two points and replace by form
+ rXPoly.Insert(XPOLY_APPEND,pU->GetFormPoly());
+ sal_uInt16 nPointCount1=rXPoly.GetPointCount();
+ for (sal_uInt16 i=nPointCount0+1; i<nPointCount1-1; i++) { // to make BckAction work
+ if (!rXPoly.IsControl(i)) rStat.NextPoint();
+ }
+ nCurrentPoint=rXPoly.GetPointCount()-1;
+ }
+ }
+ nCurrentPoint++;
+ rXPoly[nCurrentPoint]=rStat.GetNow();
+ }
+ if (eCmd==SdrCreateCmd::NextObject) {
+ if (rXPoly.GetPointCount()>=2) {
+ pU->bBezHasCtrl0=false;
+ // only a singular polygon may be opened, so close this
+ rXPoly[nCurrentPoint]=rXPoly[0];
+ XPolygon aXP;
+ aXP[0]=rStat.GetNow();
+ aPathPolygon.Insert(std::move(aXP));
+ }
+ }
+ }
+
+ sal_uInt16 nPolyCount=aPathPolygon.Count();
+ if (nPolyCount!=0) {
+ // delete last point, if necessary
+ if (eCmd==SdrCreateCmd::ForceEnd) {
+ XPolygon& rXP=aPathPolygon[nPolyCount-1];
+ sal_uInt16 nPointCount=rXP.GetPointCount();
+ if (nPointCount>=2) {
+ if (!rXP.IsControl(nPointCount-2)) {
+ if (rXP[nPointCount-1]==rXP[nPointCount-2]) {
+ rXP.Remove(nPointCount-1,1);
+ }
+ } else {
+ if (rXP[nPointCount-3]==rXP[nPointCount-2]) {
+ rXP.Remove(nPointCount-3,3);
+ }
+ }
+ }
+ }
+ for (sal_uInt16 nPolyNum=nPolyCount; nPolyNum>0;) {
+ nPolyNum--;
+ XPolygon& rXP=aPathPolygon[nPolyNum];
+ sal_uInt16 nPointCount=rXP.GetPointCount();
+ // delete polygons with too few points
+ if (nPolyNum<nPolyCount-1 || eCmd==SdrCreateCmd::ForceEnd) {
+ if (nPointCount<2) aPathPolygon.Remove(nPolyNum);
+ }
+ }
+ }
+ pU->ResetFormFlags();
+ bRet=eCmd==SdrCreateCmd::ForceEnd;
+ if (bRet) {
+ mbCreating=false;
+ rStat.SetUser(nullptr);
+ }
+ return bRet;
+}
+
+bool ImpPathForDragAndCreate::BckCreate(SdrDragStat const & rStat)
+{
+ ImpPathCreateUser* pU=static_cast<ImpPathCreateUser*>(rStat.GetUser());
+ if (aPathPolygon.Count()>0) {
+ XPolygon& rXPoly=aPathPolygon[aPathPolygon.Count()-1];
+ sal_uInt16 nCurrentPoint=rXPoly.GetPointCount();
+ if (nCurrentPoint>0) {
+ nCurrentPoint--;
+ // make the last part of a bezier curve a line
+ rXPoly.Remove(nCurrentPoint,1);
+ if (nCurrentPoint>=3 && rXPoly.IsControl(nCurrentPoint-1)) {
+ // there should never be a bezier segment at the end, so this is just in case...
+ rXPoly.Remove(nCurrentPoint-1,1);
+ if (rXPoly.IsControl(nCurrentPoint-2)) rXPoly.Remove(nCurrentPoint-2,1);
+ }
+ }
+ nCurrentPoint=rXPoly.GetPointCount();
+ if (nCurrentPoint>=4) { // no bezier segment at the end
+ nCurrentPoint--;
+ if (rXPoly.IsControl(nCurrentPoint-1)) {
+ rXPoly.Remove(nCurrentPoint-1,1);
+ if (rXPoly.IsControl(nCurrentPoint-2)) rXPoly.Remove(nCurrentPoint-2,1);
+ }
+ }
+ if (rXPoly.GetPointCount()<2) {
+ aPathPolygon.Remove(aPathPolygon.Count()-1);
+ }
+ if (aPathPolygon.Count()>0) {
+ XPolygon& rLocalXPoly=aPathPolygon[aPathPolygon.Count()-1];
+ sal_uInt16 nLocalCurrentPoint=rLocalXPoly.GetPointCount();
+ if (nLocalCurrentPoint>0) {
+ nLocalCurrentPoint--;
+ rLocalXPoly[nLocalCurrentPoint]=rStat.GetNow();
+ }
+ }
+ }
+ pU->ResetFormFlags();
+ return aPathPolygon.Count()!=0;
+}
+
+void ImpPathForDragAndCreate::BrkCreate(SdrDragStat& rStat)
+{
+ aPathPolygon.Clear();
+ mbCreating=false;
+ rStat.SetUser(nullptr);
+}
+
+basegfx::B2DPolyPolygon ImpPathForDragAndCreate::TakeObjectPolyPolygon(const SdrDragStat& rDrag) const
+{
+ basegfx::B2DPolyPolygon aRetval(aPathPolygon.getB2DPolyPolygon());
+ SdrView* pView = rDrag.GetView();
+
+ if(pView && pView->IsUseIncompatiblePathCreateInterface())
+ return aRetval;
+
+ ImpPathCreateUser* pU = static_cast<ImpPathCreateUser*>(rDrag.GetUser());
+ basegfx::B2DPolygon aNewPolygon(aRetval.count() ? aRetval.getB2DPolygon(aRetval.count() - 1) : basegfx::B2DPolygon());
+
+ if(pU->IsFormFlag() && aNewPolygon.count() > 1)
+ {
+ // remove last segment and replace with current
+ // do not forget to rescue the previous control point which will be lost when
+ // the point it's associated with is removed
+ const sal_uInt32 nChangeIndex(aNewPolygon.count() - 2);
+ const basegfx::B2DPoint aSavedPrevCtrlPoint(aNewPolygon.getPrevControlPoint(nChangeIndex));
+
+ aNewPolygon.remove(nChangeIndex, 2);
+ aNewPolygon.append(pU->GetFormPoly().getB2DPolygon());
+
+ if(nChangeIndex < aNewPolygon.count())
+ {
+ // if really something was added, set the saved previous control point to the
+ // point where it belongs
+ aNewPolygon.setPrevControlPoint(nChangeIndex, aSavedPrevCtrlPoint);
+ }
+ }
+
+ if(aRetval.count())
+ {
+ aRetval.setB2DPolygon(aRetval.count() - 1, aNewPolygon);
+ }
+ else
+ {
+ aRetval.append(aNewPolygon);
+ }
+
+ return aRetval;
+}
+
+basegfx::B2DPolyPolygon ImpPathForDragAndCreate::TakeDragPolyPolygon(const SdrDragStat& rDrag)
+{
+ basegfx::B2DPolyPolygon aRetval;
+ SdrView* pView = rDrag.GetView();
+
+ if(pView && pView->IsUseIncompatiblePathCreateInterface())
+ return aRetval;
+
+ const ImpPathCreateUser* pU = static_cast<const ImpPathCreateUser*>(rDrag.GetUser());
+
+ if(pU && pU->bBezier && rDrag.IsMouseDown())
+ {
+ // no more XOR, no need for complicated helplines
+ basegfx::B2DPolygon aHelpline;
+ aHelpline.append(basegfx::B2DPoint(pU->aBezCtrl2.X(), pU->aBezCtrl2.Y()));
+ aHelpline.append(basegfx::B2DPoint(pU->aBezEnd.X(), pU->aBezEnd.Y()));
+ aRetval.append(aHelpline);
+ }
+
+ return aRetval;
+}
+
+PointerStyle ImpPathForDragAndCreate::GetCreatePointer() const
+{
+ switch (meObjectKind) {
+ case SdrObjKind::Line : return PointerStyle::DrawLine;
+ case SdrObjKind::Polygon : return PointerStyle::DrawPolygon;
+ case SdrObjKind::PolyLine : return PointerStyle::DrawPolygon;
+ case SdrObjKind::PathLine: return PointerStyle::DrawBezier;
+ case SdrObjKind::PathFill: return PointerStyle::DrawBezier;
+ case SdrObjKind::FreehandLine: return PointerStyle::DrawFreehand;
+ case SdrObjKind::FreehandFill: return PointerStyle::DrawFreehand;
+ case SdrObjKind::SplineLine: return PointerStyle::DrawFreehand;
+ case SdrObjKind::SplineFill: return PointerStyle::DrawFreehand;
+ case SdrObjKind::PathPoly: return PointerStyle::DrawPolygon;
+ case SdrObjKind::PathPolyLine: return PointerStyle::DrawPolygon;
+ default: break;
+ } // switch
+ return PointerStyle::Cross;
+}
+
+SdrPathObjGeoData::SdrPathObjGeoData()
+ : meKind(SdrObjKind::NONE)
+{
+}
+
+SdrPathObjGeoData::~SdrPathObjGeoData()
+{
+}
+
+// DrawContact section
+
+std::unique_ptr<sdr::contact::ViewContact> SdrPathObj::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::ViewContactOfSdrPathObj>(*this);
+}
+
+
+SdrPathObj::SdrPathObj(
+ SdrModel& rSdrModel,
+ SdrObjKind eNewKind)
+: SdrTextObj(rSdrModel),
+ meKind(eNewKind)
+{
+ m_bClosedObj = IsClosed();
+}
+
+SdrPathObj::SdrPathObj(SdrModel& rSdrModel, SdrPathObj const & rSource)
+: SdrTextObj(rSdrModel, rSource),
+ meKind(rSource.meKind)
+{
+ m_bClosedObj = IsClosed();
+ maPathPolygon = rSource.GetPathPoly();
+}
+
+SdrPathObj::SdrPathObj(
+ SdrModel& rSdrModel,
+ SdrObjKind eNewKind,
+ const basegfx::B2DPolyPolygon& rPathPoly)
+: SdrTextObj(rSdrModel),
+ maPathPolygon(rPathPoly),
+ meKind(eNewKind)
+{
+ m_bClosedObj = IsClosed();
+ ImpForceKind();
+}
+
+SdrPathObj::~SdrPathObj() = default;
+
+static bool lcl_ImpIsLine(const basegfx::B2DPolyPolygon& rPolyPolygon)
+{
+ return (1 == rPolyPolygon.count() && 2 == rPolyPolygon.getB2DPolygon(0).count());
+}
+
+static tools::Rectangle lcl_ImpGetBoundRect(const basegfx::B2DPolyPolygon& rPolyPolygon)
+{
+ basegfx::B2DRange aRange(basegfx::utils::getRange(rPolyPolygon));
+
+ if (aRange.isEmpty())
+ return tools::Rectangle();
+
+ return tools::Rectangle(
+ FRound(aRange.getMinX()), FRound(aRange.getMinY()),
+ FRound(aRange.getMaxX()), FRound(aRange.getMaxY()));
+}
+
+void SdrPathObj::ImpForceLineAngle()
+{
+ if(SdrObjKind::Line != meKind || !lcl_ImpIsLine(GetPathPoly()))
+ return;
+
+ const basegfx::B2DPolygon aPoly(GetPathPoly().getB2DPolygon(0));
+ const basegfx::B2DPoint aB2DPoint0(aPoly.getB2DPoint(0));
+ const basegfx::B2DPoint aB2DPoint1(aPoly.getB2DPoint(1));
+ const Point aPoint0(FRound(aB2DPoint0.getX()), FRound(aB2DPoint0.getY()));
+ const Point aPoint1(FRound(aB2DPoint1.getX()), FRound(aB2DPoint1.getY()));
+ const basegfx::B2DPoint aB2DDelt(aB2DPoint1 - aB2DPoint0);
+ const Point aDelt(FRound(aB2DDelt.getX()), FRound(aB2DDelt.getY()));
+
+ maGeo.nRotationAngle=GetAngle(aDelt);
+ maGeo.nShearAngle=0_deg100;
+ maGeo.RecalcSinCos();
+ maGeo.RecalcTan();
+
+ // for SdrTextObj, keep aRect up to date
+ maRect = tools::Rectangle::Justify(aPoint0, aPoint1);
+}
+
+void SdrPathObj::ImpForceKind()
+{
+ if (meKind==SdrObjKind::PathPolyLine) meKind=SdrObjKind::PolyLine;
+ if (meKind==SdrObjKind::PathPoly) meKind=SdrObjKind::Polygon;
+
+ if(GetPathPoly().areControlPointsUsed())
+ {
+ switch (meKind)
+ {
+ case SdrObjKind::Line: meKind=SdrObjKind::PathLine; break;
+ case SdrObjKind::PolyLine: meKind=SdrObjKind::PathLine; break;
+ case SdrObjKind::Polygon: meKind=SdrObjKind::PathFill; break;
+ default: break;
+ }
+ }
+ else
+ {
+ switch (meKind)
+ {
+ case SdrObjKind::PathLine: meKind=SdrObjKind::PolyLine; break;
+ case SdrObjKind::FreehandLine: meKind=SdrObjKind::PolyLine; break;
+ case SdrObjKind::PathFill: meKind=SdrObjKind::Polygon; break;
+ case SdrObjKind::FreehandFill: meKind=SdrObjKind::Polygon; break;
+ default: break;
+ }
+ }
+
+ if (meKind==SdrObjKind::Line && !lcl_ImpIsLine(GetPathPoly())) meKind=SdrObjKind::PolyLine;
+ if (meKind==SdrObjKind::PolyLine && lcl_ImpIsLine(GetPathPoly())) meKind=SdrObjKind::Line;
+
+ m_bClosedObj=IsClosed();
+
+ if (meKind==SdrObjKind::Line)
+ {
+ ImpForceLineAngle();
+ }
+ else
+ {
+ // #i10659#, for polys with more than 2 points.
+
+ // Here i again need to fix something, because when Path-Polys are Copy-Pasted
+ // between Apps with different measurements (e.g. 100TH_MM and TWIPS) there is
+ // a scaling loop started from SdrExchangeView::Paste. In itself, this is not
+ // wrong, but aRect is wrong here and not even updated by RecalcSnapRect(). If
+ // this is the case, some size needs to be set here in aRect to avoid that the cycle
+ // through Rect2Poly - Poly2Rect does something badly wrong since that cycle is
+ // BASED on aRect. That cycle is triggered in SdrTextObj::NbcResize() which is called
+ // from the local Resize() implementation.
+
+ // Basic problem is that the member aRect in SdrTextObj basically is a unrotated
+ // text rectangle for the text object itself and methods at SdrTextObj do handle it
+ // in that way. Many draw objects derived from SdrTextObj 'abuse' aRect as SnapRect
+ // which is basically wrong. To make the SdrText methods which deal with aRect directly
+ // work it is necessary to always keep aRect updated. This e.g. not done after a Clone()
+ // command for SdrPathObj. Since adding this update mechanism with #101412# to
+ // ImpForceLineAngle() for lines was very successful, i add it to where ImpForceLineAngle()
+ // was called, once here below and once on a 2nd place below.
+
+ // #i10659# for SdrTextObj, keep aRect up to date
+ if(GetPathPoly().count())
+ {
+ maRect = lcl_ImpGetBoundRect(GetPathPoly());
+ }
+ }
+
+ // #i75974# adapt polygon state to object type. This may include a reinterpretation
+ // of a closed geometry as open one, but with identical first and last point
+ for(auto& rPolygon : maPathPolygon)
+ {
+ if(IsClosed() != rPolygon.isClosed())
+ {
+ // #i80213# really change polygon geometry; else e.g. the last point which
+ // needs to be identical with the first one will be missing when opening
+ // due to OBJ_PATH type
+ if(rPolygon.isClosed())
+ {
+ basegfx::utils::openWithGeometryChange(rPolygon);
+ }
+ else
+ {
+ basegfx::utils::closeWithGeometryChange(rPolygon);
+ }
+ }
+ }
+}
+
+void SdrPathObj::ImpSetClosed(bool bClose)
+{
+ if(bClose)
+ {
+ switch (meKind)
+ {
+ case SdrObjKind::Line : meKind=SdrObjKind::Polygon; break;
+ case SdrObjKind::PolyLine : meKind=SdrObjKind::Polygon; break;
+ case SdrObjKind::PathLine: meKind=SdrObjKind::PathFill; break;
+ case SdrObjKind::FreehandLine: meKind=SdrObjKind::FreehandFill; break;
+ case SdrObjKind::SplineLine: meKind=SdrObjKind::SplineFill; break;
+ default: break;
+ }
+
+ m_bClosedObj = true;
+ }
+ else
+ {
+ switch (meKind)
+ {
+ case SdrObjKind::Polygon : meKind=SdrObjKind::PolyLine; break;
+ case SdrObjKind::PathFill: meKind=SdrObjKind::PathLine; break;
+ case SdrObjKind::FreehandFill: meKind=SdrObjKind::FreehandLine; break;
+ case SdrObjKind::SplineFill: meKind=SdrObjKind::SplineLine; break;
+ default: break;
+ }
+
+ m_bClosedObj = false;
+ }
+
+ ImpForceKind();
+}
+
+void SdrPathObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
+{
+ rInfo.bNoContortion=false;
+
+ bool bCanConv = !HasText() || ImpCanConvTextToCurve();
+ bool bIsPath = IsBezier() || IsSpline();
+
+ rInfo.bEdgeRadiusAllowed = false;
+ rInfo.bCanConvToPath = bCanConv && !bIsPath;
+ rInfo.bCanConvToPoly = bCanConv && bIsPath;
+ rInfo.bCanConvToContour = !IsFontwork() && (rInfo.bCanConvToPoly || LineGeometryUsageIsNecessary());
+}
+
+SdrObjKind SdrPathObj::GetObjIdentifier() const
+{
+ return meKind;
+}
+
+SdrPathObj* SdrPathObj::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new SdrPathObj(rTargetModel, *this);
+}
+
+OUString SdrPathObj::TakeObjNameSingul() const
+{
+ OUString sName;
+
+ if(SdrObjKind::Line == meKind)
+ {
+ TranslateId pId(STR_ObjNameSingulLINE);
+
+ if(lcl_ImpIsLine(GetPathPoly()))
+ {
+ const basegfx::B2DPolygon aPoly(GetPathPoly().getB2DPolygon(0));
+ const basegfx::B2DPoint aB2DPoint0(aPoly.getB2DPoint(0));
+ const basegfx::B2DPoint aB2DPoint1(aPoly.getB2DPoint(1));
+
+ if(aB2DPoint0 != aB2DPoint1)
+ {
+ if(aB2DPoint0.getY() == aB2DPoint1.getY())
+ {
+ pId = STR_ObjNameSingulLINE_Hori;
+ }
+ else if(aB2DPoint0.getX() == aB2DPoint1.getX())
+ {
+ pId = STR_ObjNameSingulLINE_Vert;
+ }
+ else
+ {
+ const double fDx(fabs(aB2DPoint0.getX() - aB2DPoint1.getX()));
+ const double fDy(fabs(aB2DPoint0.getY() - aB2DPoint1.getY()));
+
+ if(fDx == fDy)
+ {
+ pId = STR_ObjNameSingulLINE_Diag;
+ }
+ }
+ }
+ }
+
+ sName = SvxResId(pId);
+ }
+ else if(SdrObjKind::PolyLine == meKind || SdrObjKind::Polygon == meKind)
+ {
+ const bool bClosed(SdrObjKind::Polygon == meKind);
+ TranslateId pId;
+
+ if(mpDAC && mpDAC->IsCreating())
+ {
+ if(bClosed)
+ {
+ pId = STR_ObjNameSingulPOLY;
+ }
+ else
+ {
+ pId = STR_ObjNameSingulPLIN;
+ }
+
+ sName = SvxResId(pId);
+ }
+ else
+ {
+ // get point count
+ sal_uInt32 nPointCount(0);
+
+ for(auto const& rPolygon : GetPathPoly())
+ {
+ nPointCount += rPolygon.count();
+ }
+
+ if(bClosed)
+ {
+ pId = STR_ObjNameSingulPOLY_PointCount;
+ }
+ else
+ {
+ pId = STR_ObjNameSingulPLIN_PointCount;
+ }
+
+ // #i96537#
+ sName = SvxResId(pId).replaceFirst("%2", OUString::number(nPointCount));
+ }
+ }
+ else
+ {
+ switch (meKind)
+ {
+ case SdrObjKind::PathLine: sName = SvxResId(STR_ObjNameSingulPATHLINE); break;
+ case SdrObjKind::FreehandLine: sName = SvxResId(STR_ObjNameSingulFREELINE); break;
+ case SdrObjKind::SplineLine: sName = SvxResId(STR_ObjNameSingulNATSPLN); break;
+ case SdrObjKind::PathFill: sName = SvxResId(STR_ObjNameSingulPATHFILL); break;
+ case SdrObjKind::FreehandFill: sName = SvxResId(STR_ObjNameSingulFREEFILL); break;
+ case SdrObjKind::SplineFill: sName = SvxResId(STR_ObjNameSingulPERSPLN); break;
+ default: break;
+ }
+ }
+
+ OUString aName(GetName());
+ if (!aName.isEmpty())
+ sName += " '" + aName + "'";
+
+ return sName;
+}
+
+OUString SdrPathObj::TakeObjNamePlural() const
+{
+ OUString sName;
+ switch(meKind)
+ {
+ case SdrObjKind::Line : sName=SvxResId(STR_ObjNamePluralLINE ); break;
+ case SdrObjKind::PolyLine : sName=SvxResId(STR_ObjNamePluralPLIN ); break;
+ case SdrObjKind::Polygon : sName=SvxResId(STR_ObjNamePluralPOLY ); break;
+ case SdrObjKind::PathLine: sName=SvxResId(STR_ObjNamePluralPATHLINE); break;
+ case SdrObjKind::FreehandLine: sName=SvxResId(STR_ObjNamePluralFREELINE); break;
+ case SdrObjKind::SplineLine: sName=SvxResId(STR_ObjNamePluralNATSPLN); break;
+ case SdrObjKind::PathFill: sName=SvxResId(STR_ObjNamePluralPATHFILL); break;
+ case SdrObjKind::FreehandFill: sName=SvxResId(STR_ObjNamePluralFREEFILL); break;
+ case SdrObjKind::SplineFill: sName=SvxResId(STR_ObjNamePluralPERSPLN); break;
+ default: break;
+ }
+ return sName;
+}
+
+basegfx::B2DPolyPolygon SdrPathObj::TakeXorPoly() const
+{
+ return GetPathPoly();
+}
+
+sal_uInt32 SdrPathObj::GetHdlCount() const
+{
+ sal_uInt32 nRetval(0);
+
+ for(auto const& rPolygon : GetPathPoly())
+ {
+ nRetval += rPolygon.count();
+ }
+
+ return nRetval;
+}
+
+void SdrPathObj::AddToHdlList(SdrHdlList& rHdlList) const
+{
+ // keep old stuff to be able to keep old SdrHdl stuff, too
+ const XPolyPolygon aOldPathPolygon(GetPathPoly());
+ sal_uInt16 nPolyCnt=aOldPathPolygon.Count();
+ bool bClosed=IsClosed();
+ sal_uInt16 nIdx=0;
+
+ for (sal_uInt16 i=0; i<nPolyCnt; i++) {
+ const XPolygon& rXPoly=aOldPathPolygon.GetObject(i);
+ sal_uInt16 nPntCnt=rXPoly.GetPointCount();
+ if (bClosed && nPntCnt>1) nPntCnt--;
+
+ for (sal_uInt16 j=0; j<nPntCnt; j++) {
+ if (rXPoly.GetFlags(j)!=PolyFlags::Control) {
+ const Point& rPnt=rXPoly[j];
+ std::unique_ptr<SdrHdl> pHdl(new SdrHdl(rPnt,SdrHdlKind::Poly));
+ pHdl->SetPolyNum(i);
+ pHdl->SetPointNum(j);
+ pHdl->Set1PixMore(j==0);
+ pHdl->SetSourceHdlNum(nIdx);
+ nIdx++;
+ rHdlList.AddHdl(std::move(pHdl));
+ }
+ }
+ }
+}
+
+void SdrPathObj::AddToPlusHdlList(SdrHdlList& rHdlList, SdrHdl& rHdl) const
+{
+ // keep old stuff to be able to keep old SdrHdl stuff, too
+ const XPolyPolygon aOldPathPolygon(GetPathPoly());
+ sal_uInt16 nPnt = static_cast<sal_uInt16>(rHdl.GetPointNum());
+ sal_uInt16 nPolyNum = static_cast<sal_uInt16>(rHdl.GetPolyNum());
+
+ if (nPolyNum>=aOldPathPolygon.Count())
+ return;
+
+ const XPolygon& rXPoly = aOldPathPolygon[nPolyNum];
+ sal_uInt16 nPntMax = rXPoly.GetPointCount();
+
+ if (nPntMax<=0)
+ return;
+ nPntMax--;
+ if (nPnt>nPntMax)
+ return;
+
+ // calculate the number of plus points
+ sal_uInt16 nCnt = 0;
+ if (rXPoly.GetFlags(nPnt)!=PolyFlags::Control)
+ {
+ if (nPnt==0 && IsClosed())
+ nPnt=nPntMax;
+ if (nPnt>0 && rXPoly.GetFlags(nPnt-1)==PolyFlags::Control)
+ nCnt++;
+ if (nPnt==nPntMax && IsClosed())
+ nPnt=0;
+ if (nPnt<nPntMax && rXPoly.GetFlags(nPnt+1)==PolyFlags::Control)
+ nCnt++;
+ }
+
+ // construct the plus points
+ for (sal_uInt32 nPlusNum = 0; nPlusNum < nCnt; ++nPlusNum)
+ {
+ nPnt = static_cast<sal_uInt16>(rHdl.GetPointNum());
+ std::unique_ptr<SdrHdl> pHdl(new SdrHdlBezWgt(&rHdl));
+ pHdl->SetPolyNum(rHdl.GetPolyNum());
+
+ if (nPnt==0 && IsClosed())
+ nPnt=nPntMax;
+ if (nPnt>0 && rXPoly.GetFlags(nPnt-1)==PolyFlags::Control && nPlusNum==0)
+ {
+ pHdl->SetPos(rXPoly[nPnt-1]);
+ pHdl->SetPointNum(nPnt-1);
+ }
+ else
+ {
+ if (nPnt==nPntMax && IsClosed())
+ nPnt=0;
+ if (nPnt<rXPoly.GetPointCount()-1 && rXPoly.GetFlags(nPnt+1)==PolyFlags::Control)
+ {
+ pHdl->SetPos(rXPoly[nPnt+1]);
+ pHdl->SetPointNum(nPnt+1);
+ }
+ }
+
+ pHdl->SetSourceHdlNum(rHdl.GetSourceHdlNum());
+ pHdl->SetPlusHdl(true);
+ rHdlList.AddHdl(std::move(pHdl));
+ }
+}
+
+// tdf#123321: Make sure that SdrPathObj (e.g. line) has big enough extent for
+// visibility. This is realised by ensuring GetLogicRect() is the same as
+// GetSnapRect() for the SdrPathObj. Other SdrTextObj objects like
+// SdrObjCustomShape will still use a different version of this method that
+// does not consider the rotation. Otherwise, the rotated SdrObjCustomShape
+// would become mistakenly larger after save and reload (tdf#91687).
+// The invocation of the GetLogicRect() method that caused tdf#123321 was in
+// PlcDrawObj::WritePlc().
+const tools::Rectangle &SdrPathObj::GetLogicRect() const
+{
+ return GetSnapRect();
+}
+
+// dragging
+
+bool SdrPathObj::hasSpecialDrag() const
+{
+ return true;
+}
+
+bool SdrPathObj::beginSpecialDrag(SdrDragStat& rDrag) const
+{
+ ImpPathForDragAndCreate aDragAndCreate(*const_cast<SdrPathObj*>(this));
+
+ return aDragAndCreate.beginPathDrag(rDrag);
+}
+
+bool SdrPathObj::applySpecialDrag(SdrDragStat& rDrag)
+{
+ ImpPathForDragAndCreate aDragAndCreate(*this);
+ bool bRetval(aDragAndCreate.beginPathDrag(rDrag));
+
+ if(bRetval)
+ {
+ bRetval = aDragAndCreate.movePathDrag(rDrag);
+ }
+
+ if(bRetval)
+ {
+ bRetval = aDragAndCreate.endPathDrag(rDrag);
+ }
+
+ if(bRetval)
+ {
+ NbcSetPathPoly(aDragAndCreate.getModifiedPolyPolygon());
+ }
+
+ return bRetval;
+}
+
+OUString SdrPathObj::getSpecialDragComment(const SdrDragStat& rDrag) const
+{
+ OUString aRetval;
+
+ if(mpDAC)
+ {
+ // #i103058# also get a comment when in creation
+ const bool bCreateComment(rDrag.GetView() && this == rDrag.GetView()->GetCreateObj());
+
+ if(bCreateComment)
+ {
+ aRetval = mpDAC->getSpecialDragComment(rDrag);
+ }
+ }
+ else
+ {
+ ImpPathForDragAndCreate aDragAndCreate(*const_cast<SdrPathObj*>(this));
+ bool bDidWork(aDragAndCreate.beginPathDrag(rDrag));
+
+ if(bDidWork)
+ {
+ aRetval = aDragAndCreate.getSpecialDragComment(rDrag);
+ }
+ }
+
+ return aRetval;
+}
+
+basegfx::B2DPolyPolygon SdrPathObj::getSpecialDragPoly(const SdrDragStat& rDrag) const
+{
+ basegfx::B2DPolyPolygon aRetval;
+ ImpPathForDragAndCreate aDragAndCreate(*const_cast<SdrPathObj*>(this));
+ bool bDidWork(aDragAndCreate.beginPathDrag(rDrag));
+
+ if(bDidWork)
+ {
+ aRetval = aDragAndCreate.getSpecialDragPoly(rDrag);
+ }
+
+ return aRetval;
+}
+
+// creation
+
+bool SdrPathObj::BegCreate(SdrDragStat& rStat)
+{
+ mpDAC.reset();
+ impGetDAC().BegCreate(rStat);
+ return true;
+}
+
+bool SdrPathObj::MovCreate(SdrDragStat& rStat)
+{
+ return impGetDAC().MovCreate(rStat);
+}
+
+bool SdrPathObj::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd)
+{
+ bool bRetval(impGetDAC().EndCreate(rStat, eCmd));
+
+ if(bRetval && mpDAC)
+ {
+ SetPathPoly(mpDAC->getModifiedPolyPolygon());
+
+ // #i75974# Check for AutoClose feature. Moved here from ImpPathForDragAndCreate::EndCreate
+ // to be able to use the type-changing ImpSetClosed method
+ if(!IsClosedObj())
+ {
+ SdrView* pView = rStat.GetView();
+
+ if(pView && !pView->IsUseIncompatiblePathCreateInterface())
+ {
+ OutputDevice* pOut = pView->GetFirstOutputDevice();
+
+ if(pOut)
+ {
+ if(GetPathPoly().count())
+ {
+ const basegfx::B2DPolygon aCandidate(GetPathPoly().getB2DPolygon(0));
+
+ if(aCandidate.count() > 2)
+ {
+ // check distance of first and last point
+ const sal_Int32 nCloseDist(pOut->PixelToLogic(Size(pView->GetAutoCloseDistPix(), 0)).Width());
+ const basegfx::B2DVector aDistVector(aCandidate.getB2DPoint(aCandidate.count() - 1) - aCandidate.getB2DPoint(0));
+
+ if(aDistVector.getLength() <= static_cast<double>(nCloseDist))
+ {
+ // close it
+ ImpSetClosed(true);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ mpDAC.reset();
+ }
+
+ return bRetval;
+}
+
+bool SdrPathObj::BckCreate(SdrDragStat& rStat)
+{
+ return impGetDAC().BckCreate(rStat);
+}
+
+void SdrPathObj::BrkCreate(SdrDragStat& rStat)
+{
+ impGetDAC().BrkCreate(rStat);
+ mpDAC.reset();
+}
+
+// polygons
+
+basegfx::B2DPolyPolygon SdrPathObj::TakeCreatePoly(const SdrDragStat& rDrag) const
+{
+ basegfx::B2DPolyPolygon aRetval;
+
+ if(mpDAC)
+ {
+ aRetval = mpDAC->TakeObjectPolyPolygon(rDrag);
+ aRetval.append(ImpPathForDragAndCreate::TakeDragPolyPolygon(rDrag));
+ }
+
+ return aRetval;
+}
+
+// during drag or create, allow accessing the so-far created/modified polyPolygon
+basegfx::B2DPolyPolygon SdrPathObj::getObjectPolyPolygon(const SdrDragStat& rDrag) const
+{
+ basegfx::B2DPolyPolygon aRetval;
+
+ if(mpDAC)
+ {
+ aRetval = mpDAC->TakeObjectPolyPolygon(rDrag);
+ }
+
+ return aRetval;
+}
+
+basegfx::B2DPolyPolygon SdrPathObj::getDragPolyPolygon(const SdrDragStat& rDrag) const
+{
+ basegfx::B2DPolyPolygon aRetval;
+
+ if(mpDAC)
+ {
+ aRetval = ImpPathForDragAndCreate::TakeDragPolyPolygon(rDrag);
+ }
+
+ return aRetval;
+}
+
+PointerStyle SdrPathObj::GetCreatePointer() const
+{
+ return impGetDAC().GetCreatePointer();
+}
+
+void SdrPathObj::NbcMove(const Size& rSiz)
+{
+ maPathPolygon.transform(basegfx::utils::createTranslateB2DHomMatrix(rSiz.Width(), rSiz.Height()));
+
+ // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
+ SdrTextObj::NbcMove(rSiz);
+}
+
+void SdrPathObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
+{
+ const double fResizeX(xFact);
+ const double fResizeY(yFact);
+
+ if(basegfx::fTools::equal(fResizeX, 1.0) && basegfx::fTools::equal(fResizeY, 1.0))
+ {
+ // tdf#106792 avoid numerical unprecisions: If both scale factors are 1.0, do not
+ // manipulate at all - that may change maGeo rapidly (and wrongly) in
+ // SdrTextObj::NbcResize. Combined with the UNO API trying to not 'apply'
+ // a rotation but to manipulate the existing one, this is fatal. So just
+ // avoid this error as long as we have to deal with imprecise geometry
+ // manipulations
+ return;
+ }
+
+ basegfx::B2DHomMatrix aTrans(basegfx::utils::createTranslateB2DHomMatrix(-rRef.X(), -rRef.Y()));
+ aTrans = basegfx::utils::createScaleTranslateB2DHomMatrix(
+ double(xFact), double(yFact), rRef.X(), rRef.Y()) * aTrans;
+ maPathPolygon.transform(aTrans);
+
+ // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
+ SdrTextObj::NbcResize(rRef,xFact,yFact);
+}
+
+void SdrPathObj::NbcRotate(const Point& rRef, Degree100 nAngle, double sn, double cs)
+{
+ // Thank JOE, the angles are defined mirrored to the mathematical meanings
+ const basegfx::B2DHomMatrix aTrans(
+ basegfx::utils::createRotateAroundPoint(rRef.X(), rRef.Y(), -toRadians(nAngle)));
+ maPathPolygon.transform(aTrans);
+
+ // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
+ SdrTextObj::NbcRotate(rRef,nAngle,sn,cs);
+}
+
+void SdrPathObj::NbcShear(const Point& rRefPnt, Degree100 nAngle, double fTan, bool bVShear)
+{
+ basegfx::B2DHomMatrix aTrans(basegfx::utils::createTranslateB2DHomMatrix(-rRefPnt.X(), -rRefPnt.Y()));
+
+ if(bVShear)
+ {
+ // Thank JOE, the angles are defined mirrored to the mathematical meanings
+ aTrans.shearY(-fTan);
+ }
+ else
+ {
+ aTrans.shearX(-fTan);
+ }
+
+ aTrans.translate(rRefPnt.X(), rRefPnt.Y());
+ maPathPolygon.transform(aTrans);
+
+ // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
+ SdrTextObj::NbcShear(rRefPnt,nAngle,fTan,bVShear);
+}
+
+void SdrPathObj::NbcMirror(const Point& rRefPnt1, const Point& rRefPnt2)
+{
+ const double fDiffX(rRefPnt2.X() - rRefPnt1.X());
+ const double fDiffY(rRefPnt2.Y() - rRefPnt1.Y());
+ const double fRot(atan2(fDiffY, fDiffX));
+ basegfx::B2DHomMatrix aTrans(basegfx::utils::createTranslateB2DHomMatrix(-rRefPnt1.X(), -rRefPnt1.Y()));
+ aTrans.rotate(-fRot);
+ aTrans.scale(1.0, -1.0);
+ aTrans.rotate(fRot);
+ aTrans.translate(rRefPnt1.X(), rRefPnt1.Y());
+ maPathPolygon.transform(aTrans);
+
+ // Do Joe's special handling for lines when mirroring, too
+ ImpForceKind();
+
+ // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
+ SdrTextObj::NbcMirror(rRefPnt1,rRefPnt2);
+}
+
+void SdrPathObj::TakeUnrotatedSnapRect(tools::Rectangle& rRect) const
+{
+ if(!maGeo.nRotationAngle)
+ {
+ rRect = GetSnapRect();
+ }
+ else
+ {
+ XPolyPolygon aXPP(GetPathPoly());
+ RotateXPoly(aXPP,Point(),-maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle);
+ rRect=aXPP.GetBoundRect();
+ Point aTmp(rRect.TopLeft());
+ RotatePoint(aTmp,Point(),maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle);
+ aTmp-=rRect.TopLeft();
+ rRect.Move(aTmp.X(),aTmp.Y());
+ }
+}
+
+void SdrPathObj::RecalcSnapRect()
+{
+ if(GetPathPoly().count())
+ {
+ maSnapRect = lcl_ImpGetBoundRect(GetPathPoly());
+ }
+}
+
+void SdrPathObj::NbcSetSnapRect(const tools::Rectangle& rRect)
+{
+ tools::Rectangle aOld(GetSnapRect());
+ if (aOld.IsEmpty())
+ {
+ Fraction aX(1,1);
+ Fraction aY(1,1);
+ NbcResize(aOld.TopLeft(), aX, aY);
+ NbcMove(Size(rRect.Left() - aOld.Left(), rRect.Top() - aOld.Top()));
+ return;
+ }
+
+ // Take empty into account when calculating scale factors
+ tools::Long nMulX = rRect.IsWidthEmpty() ? 0 : rRect.Right() - rRect.Left();
+
+ tools::Long nDivX = aOld.Right() - aOld.Left();
+
+ // Take empty into account when calculating scale factors
+ tools::Long nMulY = rRect.IsHeightEmpty() ? 0 : rRect.Bottom() - rRect.Top();
+
+ tools::Long nDivY = aOld.Bottom() - aOld.Top();
+ if ( nDivX == 0 ) { nMulX = 1; nDivX = 1; }
+ if ( nDivY == 0 ) { nMulY = 1; nDivY = 1; }
+ if ( nDivX == nMulX ) { nMulX = 1; nDivX = 1; }
+ if ( nDivY == nMulY ) { nMulY = 1; nDivY = 1; }
+ Fraction aX(nMulX,nDivX);
+ Fraction aY(nMulY,nDivY);
+ NbcResize(aOld.TopLeft(), aX, aY);
+ NbcMove(Size(rRect.Left() - aOld.Left(), rRect.Top() - aOld.Top()));
+}
+
+sal_uInt32 SdrPathObj::GetSnapPointCount() const
+{
+ return GetHdlCount();
+}
+
+Point SdrPathObj::GetSnapPoint(sal_uInt32 nSnapPnt) const
+{
+ sal_uInt32 nPoly,nPnt;
+ if(!PolyPolygonEditor::GetRelativePolyPoint(GetPathPoly(), nSnapPnt, nPoly, nPnt))
+ {
+ SAL_WARN("svx", "SdrPathObj::GetSnapPoint: Point nSnapPnt does not exist.");
+ }
+
+ const basegfx::B2DPoint aB2DPoint(GetPathPoly().getB2DPolygon(nPoly).getB2DPoint(nPnt));
+ return Point(FRound(aB2DPoint.getX()), FRound(aB2DPoint.getY()));
+}
+
+bool SdrPathObj::IsPolyObj() const
+{
+ return true;
+}
+
+sal_uInt32 SdrPathObj::GetPointCount() const
+{
+ sal_uInt32 nRetval(0);
+
+ for(auto const& rPolygon : GetPathPoly())
+ {
+ nRetval += rPolygon.count();
+ }
+
+ return nRetval;
+}
+
+Point SdrPathObj::GetPoint(sal_uInt32 nHdlNum) const
+{
+ Point aRetval;
+ sal_uInt32 nPoly,nPnt;
+
+ if(PolyPolygonEditor::GetRelativePolyPoint(GetPathPoly(), nHdlNum, nPoly, nPnt))
+ {
+ const basegfx::B2DPolygon aPoly(GetPathPoly().getB2DPolygon(nPoly));
+ const basegfx::B2DPoint aPoint(aPoly.getB2DPoint(nPnt));
+ aRetval = Point(FRound(aPoint.getX()), FRound(aPoint.getY()));
+ }
+
+ return aRetval;
+}
+
+void SdrPathObj::NbcSetPoint(const Point& rPnt, sal_uInt32 nHdlNum)
+{
+ sal_uInt32 nPoly,nPnt;
+
+ if(!PolyPolygonEditor::GetRelativePolyPoint(GetPathPoly(), nHdlNum, nPoly, nPnt))
+ return;
+
+ basegfx::B2DPolygon aNewPolygon(GetPathPoly().getB2DPolygon(nPoly));
+ aNewPolygon.setB2DPoint(nPnt, basegfx::B2DPoint(rPnt.X(), rPnt.Y()));
+ maPathPolygon.setB2DPolygon(nPoly, aNewPolygon);
+
+ if(meKind==SdrObjKind::Line)
+ {
+ ImpForceLineAngle();
+ }
+ else
+ {
+ if(GetPathPoly().count())
+ {
+ // #i10659# for SdrTextObj, keep aRect up to date
+ maRect = lcl_ImpGetBoundRect(GetPathPoly());
+ }
+ }
+
+ SetBoundAndSnapRectsDirty();
+}
+
+sal_uInt32 SdrPathObj::NbcInsPointOld(const Point& rPos, bool bNewObj)
+{
+ sal_uInt32 nNewHdl;
+
+ if(bNewObj)
+ {
+ nNewHdl = NbcInsPoint(rPos, true);
+ }
+ else
+ {
+ // look for smallest distance data
+ const basegfx::B2DPoint aTestPoint(rPos.X(), rPos.Y());
+ sal_uInt32 nSmallestPolyIndex(0);
+ sal_uInt32 nSmallestEdgeIndex(0);
+ double fSmallestCut;
+ basegfx::utils::getSmallestDistancePointToPolyPolygon(GetPathPoly(), aTestPoint, nSmallestPolyIndex, nSmallestEdgeIndex, fSmallestCut);
+
+ nNewHdl = NbcInsPoint(rPos, false);
+ }
+
+ ImpForceKind();
+ return nNewHdl;
+}
+
+sal_uInt32 SdrPathObj::NbcInsPoint(const Point& rPos, bool bNewObj)
+{
+ sal_uInt32 nNewHdl;
+
+ if(bNewObj)
+ {
+ basegfx::B2DPolygon aNewPoly;
+ const basegfx::B2DPoint aPoint(rPos.X(), rPos.Y());
+ aNewPoly.append(aPoint);
+ aNewPoly.setClosed(IsClosed());
+ maPathPolygon.append(aNewPoly);
+ SetBoundAndSnapRectsDirty();
+ nNewHdl = GetHdlCount();
+ }
+ else
+ {
+ // look for smallest distance data
+ const basegfx::B2DPoint aTestPoint(rPos.X(), rPos.Y());
+ sal_uInt32 nSmallestPolyIndex(0);
+ sal_uInt32 nSmallestEdgeIndex(0);
+ double fSmallestCut;
+ basegfx::utils::getSmallestDistancePointToPolyPolygon(GetPathPoly(), aTestPoint, nSmallestPolyIndex, nSmallestEdgeIndex, fSmallestCut);
+ basegfx::B2DPolygon aCandidate(GetPathPoly().getB2DPolygon(nSmallestPolyIndex));
+ const bool bBefore(!aCandidate.isClosed() && 0 == nSmallestEdgeIndex && 0.0 == fSmallestCut);
+ const bool bAfter(!aCandidate.isClosed() && aCandidate.count() == nSmallestEdgeIndex + 2 && 1.0 == fSmallestCut);
+
+ if(bBefore)
+ {
+ // before first point
+ aCandidate.insert(0, aTestPoint);
+
+ if(aCandidate.areControlPointsUsed())
+ {
+ if(aCandidate.isNextControlPointUsed(1))
+ {
+ aCandidate.setNextControlPoint(0, interpolate(aTestPoint, aCandidate.getB2DPoint(1), (1.0 / 3.0)));
+ aCandidate.setPrevControlPoint(1, interpolate(aTestPoint, aCandidate.getB2DPoint(1), (2.0 / 3.0)));
+ }
+ }
+
+ nNewHdl = 0;
+ }
+ else if(bAfter)
+ {
+ // after last point
+ aCandidate.append(aTestPoint);
+
+ if(aCandidate.areControlPointsUsed())
+ {
+ if(aCandidate.isPrevControlPointUsed(aCandidate.count() - 2))
+ {
+ aCandidate.setNextControlPoint(aCandidate.count() - 2, interpolate(aCandidate.getB2DPoint(aCandidate.count() - 2), aTestPoint, (1.0 / 3.0)));
+ aCandidate.setPrevControlPoint(aCandidate.count() - 1, interpolate(aCandidate.getB2DPoint(aCandidate.count() - 2), aTestPoint, (2.0 / 3.0)));
+ }
+ }
+
+ nNewHdl = aCandidate.count() - 1;
+ }
+ else
+ {
+ // in between
+ bool bSegmentSplit(false);
+ const sal_uInt32 nNextIndex((nSmallestEdgeIndex + 1) % aCandidate.count());
+
+ if(aCandidate.areControlPointsUsed())
+ {
+ if(aCandidate.isNextControlPointUsed(nSmallestEdgeIndex) || aCandidate.isPrevControlPointUsed(nNextIndex))
+ {
+ bSegmentSplit = true;
+ }
+ }
+
+ if(bSegmentSplit)
+ {
+ // rebuild original segment to get the split data
+ basegfx::B2DCubicBezier aBezierA, aBezierB;
+ const basegfx::B2DCubicBezier aBezier(
+ aCandidate.getB2DPoint(nSmallestEdgeIndex),
+ aCandidate.getNextControlPoint(nSmallestEdgeIndex),
+ aCandidate.getPrevControlPoint(nNextIndex),
+ aCandidate.getB2DPoint(nNextIndex));
+
+ // split and insert hit point
+ aBezier.split(fSmallestCut, &aBezierA, &aBezierB);
+ aCandidate.insert(nSmallestEdgeIndex + 1, aTestPoint);
+
+ // since we inserted hit point and not split point, we need to add an offset
+ // to the control points to get the C1 continuity we want to achieve
+ const basegfx::B2DVector aOffset(aTestPoint - aBezierA.getEndPoint());
+ aCandidate.setNextControlPoint(nSmallestEdgeIndex, aBezierA.getControlPointA() + aOffset);
+ aCandidate.setPrevControlPoint(nSmallestEdgeIndex + 1, aBezierA.getControlPointB() + aOffset);
+ aCandidate.setNextControlPoint(nSmallestEdgeIndex + 1, aBezierB.getControlPointA() + aOffset);
+ aCandidate.setPrevControlPoint((nSmallestEdgeIndex + 2) % aCandidate.count(), aBezierB.getControlPointB() + aOffset);
+ }
+ else
+ {
+ aCandidate.insert(nSmallestEdgeIndex + 1, aTestPoint);
+ }
+
+ nNewHdl = nSmallestEdgeIndex + 1;
+ }
+
+ maPathPolygon.setB2DPolygon(nSmallestPolyIndex, aCandidate);
+
+ // create old polygon index from it
+ for(sal_uInt32 a(0); a < nSmallestPolyIndex; a++)
+ {
+ nNewHdl += GetPathPoly().getB2DPolygon(a).count();
+ }
+ }
+
+ ImpForceKind();
+ return nNewHdl;
+}
+
+SdrObject* SdrPathObj::RipPoint(sal_uInt32 nHdlNum, sal_uInt32& rNewPt0Index)
+{
+ SdrPathObj* pNewObj = nullptr;
+ const basegfx::B2DPolyPolygon aLocalPolyPolygon(GetPathPoly());
+ sal_uInt32 nPoly, nPnt;
+
+ if(PolyPolygonEditor::GetRelativePolyPoint(aLocalPolyPolygon, nHdlNum, nPoly, nPnt))
+ {
+ if(0 == nPoly)
+ {
+ const basegfx::B2DPolygon& aCandidate(aLocalPolyPolygon.getB2DPolygon(nPoly));
+ const sal_uInt32 nPointCount(aCandidate.count());
+
+ if(nPointCount)
+ {
+ if(IsClosed())
+ {
+ // when closed, RipPoint means to open the polygon at the selected point. To
+ // be able to do that, it is necessary to make the selected point the first one
+ basegfx::B2DPolygon aNewPolygon(basegfx::utils::makeStartPoint(aCandidate, nPnt));
+ SetPathPoly(basegfx::B2DPolyPolygon(aNewPolygon));
+ ToggleClosed();
+
+ // give back new position of old start point (historical reasons)
+ rNewPt0Index = (nPointCount - nPnt) % nPointCount;
+ }
+ else
+ {
+ if(nPointCount >= 3 && nPnt != 0 && nPnt + 1 < nPointCount)
+ {
+ // split in two objects at point nPnt
+ basegfx::B2DPolygon aSplitPolyA(aCandidate, 0, nPnt + 1);
+ SetPathPoly(basegfx::B2DPolyPolygon(aSplitPolyA));
+
+ pNewObj = CloneSdrObject(getSdrModelFromSdrObject());
+ basegfx::B2DPolygon aSplitPolyB(aCandidate, nPnt, nPointCount - nPnt);
+ pNewObj->SetPathPoly(basegfx::B2DPolyPolygon(aSplitPolyB));
+ }
+ }
+ }
+ }
+ }
+
+ return pNewObj;
+}
+
+SdrObjectUniquePtr SdrPathObj::DoConvertToPolyObj(bool bBezier, bool bAddText) const
+{
+ // #i89784# check for FontWork with activated HideContour
+ const drawinglayer::attribute::SdrTextAttribute aText(
+ drawinglayer::primitive2d::createNewSdrTextAttribute(GetObjectItemSet(), *getText(0)));
+ const bool bHideContour(
+ !aText.isDefault() && !aText.getSdrFormTextAttribute().isDefault() && aText.isHideContour());
+
+ SdrObjectUniquePtr pRet;
+
+ if(!bHideContour)
+ {
+ SdrPathObjUniquePtr pPath = ImpConvertMakeObj(GetPathPoly(), IsClosed(), bBezier);
+
+ if(pPath->GetPathPoly().areControlPointsUsed())
+ {
+ if(!bBezier)
+ {
+ // reduce all bezier curves
+ pPath->SetPathPoly(basegfx::utils::adaptiveSubdivideByAngle(pPath->GetPathPoly()));
+ }
+ }
+ else
+ {
+ if(bBezier)
+ {
+ // create bezier curves
+ pPath->SetPathPoly(basegfx::utils::expandToCurve(pPath->GetPathPoly()));
+ }
+ }
+ pRet = std::move(pPath);
+ }
+
+ if(bAddText)
+ {
+ pRet = ImpConvertAddText(std::move(pRet), bBezier);
+ }
+
+ return pRet;
+}
+
+std::unique_ptr<SdrObjGeoData> SdrPathObj::NewGeoData() const
+{
+ return std::make_unique<SdrPathObjGeoData>();
+}
+
+void SdrPathObj::SaveGeoData(SdrObjGeoData& rGeo) const
+{
+ SdrTextObj::SaveGeoData(rGeo);
+ SdrPathObjGeoData& rPGeo = static_cast<SdrPathObjGeoData&>( rGeo );
+ rPGeo.maPathPolygon=GetPathPoly();
+ rPGeo.meKind=meKind;
+}
+
+void SdrPathObj::RestoreGeoData(const SdrObjGeoData& rGeo)
+{
+ SdrTextObj::RestoreGeoData(rGeo);
+ const SdrPathObjGeoData& rPGeo=static_cast<const SdrPathObjGeoData&>(rGeo);
+ maPathPolygon=rPGeo.maPathPolygon;
+ meKind=rPGeo.meKind;
+ ImpForceKind(); // to set bClosed (among other things)
+}
+
+void SdrPathObj::NbcSetPathPoly(const basegfx::B2DPolyPolygon& rPathPoly)
+{
+ if(GetPathPoly() != rPathPoly)
+ {
+ maPathPolygon=rPathPoly;
+ ImpForceKind();
+ SetBoundAndSnapRectsDirty();
+ }
+}
+
+void SdrPathObj::SetPathPoly(const basegfx::B2DPolyPolygon& rPathPoly)
+{
+ if(GetPathPoly() != rPathPoly)
+ {
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ NbcSetPathPoly(rPathPoly);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+ }
+}
+
+void SdrPathObj::ToggleClosed()
+{
+ tools::Rectangle aBoundRect0;
+ if(m_pUserCall != nullptr)
+ aBoundRect0 = GetLastBoundRect();
+ ImpSetClosed(!IsClosed()); // set new ObjKind
+ ImpForceKind(); // because we want Line -> Poly -> PolyLine instead of Line -> Poly -> Line
+ SetBoundAndSnapRectsDirty();
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize, aBoundRect0);
+}
+
+ImpPathForDragAndCreate& SdrPathObj::impGetDAC() const
+{
+ if(!mpDAC)
+ {
+ const_cast<SdrPathObj*>(this)->mpDAC.reset(new ImpPathForDragAndCreate(*const_cast<SdrPathObj*>(this)));
+ }
+
+ return *mpDAC;
+}
+
+
+// transformation interface for StarOfficeAPI. This implements support for
+// homogeneous 3x3 matrices containing the transformation of the SdrObject. At the
+// moment it contains a shearX, rotation and translation, but for setting all linear
+// transforms like Scale, ShearX, ShearY, Rotate and Translate are supported.
+
+
+// gets base transformation and rectangle of object. If it's an SdrPathObj it fills the PolyPolygon
+// with the base geometry and returns TRUE. Otherwise it returns FALSE.
+bool SdrPathObj::TRGetBaseGeometry(basegfx::B2DHomMatrix& rMatrix, basegfx::B2DPolyPolygon& rPolyPolygon) const
+{
+ double fRotate(0.0);
+ double fShearX(0.0);
+ basegfx::B2DTuple aScale(1.0, 1.0);
+ basegfx::B2DTuple aTranslate(0.0, 0.0);
+
+ if(GetPathPoly().count())
+ {
+ // copy geometry
+ basegfx::B2DHomMatrix aMoveToZeroMatrix;
+ rPolyPolygon = GetPathPoly();
+
+ if(SdrObjKind::Line == meKind)
+ {
+ // ignore shear and rotate, just use scale and translate
+ OSL_ENSURE(GetPathPoly().count() > 0 && GetPathPoly().getB2DPolygon(0).count() > 1, "OBJ_LINE with too few polygons (!)");
+ // #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon
+ // itself, else this method will no longer return the full polygon information (curve will
+ // be lost)
+ const basegfx::B2DRange aPolyRangeNoCurve(basegfx::utils::getRange(rPolyPolygon));
+ aScale = aPolyRangeNoCurve.getRange();
+ aTranslate = aPolyRangeNoCurve.getMinimum();
+
+ // define matrix for move polygon to zero point
+ aMoveToZeroMatrix.translate(-aTranslate.getX(), -aTranslate.getY());
+ }
+ else
+ {
+ if(maGeo.nShearAngle || maGeo.nRotationAngle)
+ {
+ // get rotate and shear in drawingLayer notation
+ fRotate = toRadians(maGeo.nRotationAngle);
+ fShearX = toRadians(maGeo.nShearAngle);
+
+ // build mathematically correct (negative shear and rotate) object transform
+ // containing shear and rotate to extract unsheared, unrotated polygon
+ basegfx::B2DHomMatrix aObjectMatrix;
+ aObjectMatrix.shearX(-maGeo.mfTanShearAngle);
+ aObjectMatrix.rotate(toRadians(36000_deg100 - maGeo.nRotationAngle));
+
+ // create inverse from it and back-transform polygon
+ basegfx::B2DHomMatrix aInvObjectMatrix(aObjectMatrix);
+ aInvObjectMatrix.invert();
+ rPolyPolygon.transform(aInvObjectMatrix);
+
+ // get range from unsheared, unrotated polygon and extract scale and translate.
+ // transform topLeft from it back to transformed state to get original
+ // topLeft (rotation center)
+ // #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon
+ // itself, else this method will no longer return the full polygon information (curve will
+ // be lost)
+ const basegfx::B2DRange aCorrectedRangeNoCurve(basegfx::utils::getRange(rPolyPolygon));
+ aTranslate = aObjectMatrix * aCorrectedRangeNoCurve.getMinimum();
+ aScale = aCorrectedRangeNoCurve.getRange();
+
+ // define matrix for move polygon to zero point
+ // #i112280# Added missing minus for Y-Translation
+ aMoveToZeroMatrix.translate(-aCorrectedRangeNoCurve.getMinX(), -aCorrectedRangeNoCurve.getMinY());
+ }
+ else
+ {
+ // get scale and translate from unsheared, unrotated polygon
+ // #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon
+ // itself, else this method will no longer return the full polygon information (curve will
+ // be lost)
+ const basegfx::B2DRange aPolyRangeNoCurve(basegfx::utils::getRange(rPolyPolygon));
+ aScale = aPolyRangeNoCurve.getRange();
+ aTranslate = aPolyRangeNoCurve.getMinimum();
+
+ // define matrix for move polygon to zero point
+ aMoveToZeroMatrix.translate(-aTranslate.getX(), -aTranslate.getY());
+ }
+ }
+
+ // move polygon to zero point with pre-defined matrix
+ rPolyPolygon.transform(aMoveToZeroMatrix);
+ }
+
+ // position maybe relative to anchorpos, convert
+ if( getSdrModelFromSdrObject().IsWriter() )
+ {
+ if(GetAnchorPos().X() || GetAnchorPos().Y())
+ {
+ aTranslate -= basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
+ }
+ }
+
+ // build return value matrix
+ rMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ aScale,
+ basegfx::fTools::equalZero(fShearX) ? 0.0 : tan(fShearX),
+ basegfx::fTools::equalZero(fRotate) ? 0.0 : -fRotate,
+ aTranslate);
+
+ return true;
+}
+
+void SdrPathObj::SetHandleScale(bool bHandleScale)
+{
+ mbHandleScale = bHandleScale;
+}
+
+// Sets the base geometry of the object using infos contained in the homogeneous 3x3 matrix.
+// If it's an SdrPathObj it will use the provided geometry information. The Polygon has
+// to use (0,0) as upper left and will be scaled to the given size in the matrix.
+void SdrPathObj::TRSetBaseGeometry(const basegfx::B2DHomMatrix& rMatrix, const basegfx::B2DPolyPolygon& rPolyPolygon)
+{
+ // break up matrix
+ basegfx::B2DTuple aScale;
+ basegfx::B2DTuple aTranslate;
+ double fRotate, fShearX;
+ rMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // #i75086# Old DrawingLayer (GeoStat and geometry) does not support holding negative scalings
+ // in X and Y which equal a 180 degree rotation. Recognize it and react accordingly
+ if(basegfx::fTools::less(aScale.getX(), 0.0) && basegfx::fTools::less(aScale.getY(), 0.0))
+ {
+ aScale.setX(fabs(aScale.getX()));
+ aScale.setY(fabs(aScale.getY()));
+ fRotate = fmod(fRotate + M_PI, 2 * M_PI);
+ }
+
+ // copy poly
+ basegfx::B2DPolyPolygon aNewPolyPolygon(rPolyPolygon);
+
+ // reset object shear and rotations
+ maGeo.nRotationAngle = 0_deg100;
+ maGeo.RecalcSinCos();
+ maGeo.nShearAngle = 0_deg100;
+ maGeo.RecalcTan();
+
+ if( getSdrModelFromSdrObject().IsWriter() )
+ {
+ // if anchor is used, make position relative to it
+ if(GetAnchorPos().X() || GetAnchorPos().Y())
+ {
+ aTranslate += basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
+ }
+ }
+
+ // create transformation for polygon, set values at maGeo direct
+ basegfx::B2DHomMatrix aTransform;
+
+ // #i75086#
+ // Given polygon is already scaled (for historical reasons), but not mirrored yet.
+ // Thus, when scale is negative in X or Y, apply the needed mirroring accordingly.
+ double fScaleX(basegfx::fTools::less(aScale.getX(), 0.0) ? -1.0 : 1.0);
+ double fScaleY(basegfx::fTools::less(aScale.getY(), 0.0) ? -1.0 : 1.0);
+
+ // tdf#98565, tdf#98584. While loading a shape, svg:width and svg:height is used to scale
+ // the polygon. But draw:transform might introduce additional scaling factors, which need to
+ // be applied to the polygon too, so aScale cannot be ignored while loading.
+ // I use "maSnapRect.IsEmpty() && GetPathPoly().count()" to detect this case. Any better
+ // idea? The behavior in other cases is the same as it was before this fix.
+ if (maSnapRect.IsEmpty() && GetPathPoly().count() && mbHandleScale)
+ {
+ // In case of a Writer document, the scaling factors were converted to twips. That is not
+ // correct here, because width and height are already in the points coordinates and aScale
+ // is no length but only a factor here. Convert back.
+ if (getSdrModelFromSdrObject().IsWriter())
+ {
+ aScale.setX(o3tl::convert(aScale.getX(), o3tl::Length::twip, o3tl::Length::mm100));
+ aScale.setY(o3tl::convert(aScale.getY(), o3tl::Length::twip, o3tl::Length::mm100));
+ }
+ fScaleX *= fabs(aScale.getX());
+ fScaleY *= fabs(aScale.getY());
+ }
+
+ if (fScaleX != 1.0 || fScaleY != 1.0)
+ aTransform.scale(fScaleX, fScaleY);
+
+ if(!basegfx::fTools::equalZero(fShearX))
+ {
+ aTransform.shearX(tan(-atan(fShearX)));
+ maGeo.nShearAngle = Degree100(FRound(basegfx::rad2deg<100>(atan(fShearX))));
+ maGeo.RecalcTan();
+ }
+
+ if(!basegfx::fTools::equalZero(fRotate))
+ {
+ // #i78696#
+ // fRotate is mathematically correct for linear transformations, so it's
+ // the one to use for the geometry change
+ aTransform.rotate(fRotate);
+
+ // #i78696#
+ // fRotate is mathematically correct, but aGeoStat.nRotationAngle is
+ // mirrored -> mirror value here
+ maGeo.nRotationAngle = NormAngle36000(Degree100(FRound(-basegfx::rad2deg<100>(fRotate))));
+ maGeo.RecalcSinCos();
+ }
+
+ if(!aTranslate.equalZero())
+ {
+ // #i39529# absolute positioning, so get current position (without control points (!))
+ const basegfx::B2DRange aCurrentRange(basegfx::utils::getRange(aNewPolyPolygon));
+ aTransform.translate(aTranslate.getX() - aCurrentRange.getMinX(), aTranslate.getY() - aCurrentRange.getMinY());
+ }
+
+ // transform polygon and trigger change
+ aNewPolyPolygon.transform(aTransform);
+ SetPathPoly(aNewPolyPolygon);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdorect.cxx b/svx/source/svdraw/svdorect.cxx
new file mode 100644
index 000000000..2674464d2
--- /dev/null
+++ b/svx/source/svdraw/svdorect.cxx
@@ -0,0 +1,567 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/svdorect.hxx>
+#include <svx/xpoly.hxx>
+#include <svx/svdtrans.hxx>
+#include <svx/svddrag.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdview.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <sdr/properties/rectangleproperties.hxx>
+#include <sdr/contact/viewcontactofsdrrectobj.hxx>
+#include <tools/debug.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <osl/diagnose.h>
+
+using namespace com::sun::star;
+
+// BaseProperties section
+
+std::unique_ptr<sdr::properties::BaseProperties> SdrRectObj::CreateObjectSpecificProperties()
+{
+ return std::make_unique<sdr::properties::RectangleProperties>(*this);
+}
+
+
+// DrawContact section
+
+std::unique_ptr<sdr::contact::ViewContact> SdrRectObj::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::ViewContactOfSdrRectObj>(*this);
+}
+
+
+SdrRectObj::SdrRectObj(SdrModel& rSdrModel)
+: SdrTextObj(rSdrModel)
+{
+ m_bClosedObj=true;
+}
+
+SdrRectObj::SdrRectObj(SdrModel& rSdrModel, SdrRectObj const & rSource)
+: SdrTextObj(rSdrModel, rSource)
+{
+ m_bClosedObj=true;
+ mpXPoly = rSource.mpXPoly;
+}
+
+SdrRectObj::SdrRectObj(
+ SdrModel& rSdrModel,
+ const tools::Rectangle& rRect)
+: SdrTextObj(rSdrModel, rRect)
+{
+ m_bClosedObj=true;
+}
+
+SdrRectObj::SdrRectObj(
+ SdrModel& rSdrModel,
+ SdrObjKind eNewTextKind)
+: SdrTextObj(rSdrModel, eNewTextKind)
+{
+ DBG_ASSERT(meTextKind == SdrObjKind::Text ||
+ meTextKind == SdrObjKind::OutlineText || meTextKind == SdrObjKind::TitleText,
+ "SdrRectObj::SdrRectObj(SdrObjKind) can only be applied to text frames.");
+ m_bClosedObj=true;
+}
+
+SdrRectObj::SdrRectObj(
+ SdrModel& rSdrModel,
+ SdrObjKind eNewTextKind,
+ const tools::Rectangle& rRect)
+: SdrTextObj(rSdrModel, eNewTextKind, rRect)
+{
+ DBG_ASSERT(meTextKind == SdrObjKind::Text ||
+ meTextKind == SdrObjKind::OutlineText || meTextKind == SdrObjKind::TitleText,
+ "SdrRectObj::SdrRectObj(SdrObjKind,...) can only be applied to text frames.");
+ m_bClosedObj=true;
+}
+
+SdrRectObj::~SdrRectObj()
+{
+}
+
+void SdrRectObj::SetXPolyDirty()
+{
+ mpXPoly.reset();
+}
+
+XPolygon SdrRectObj::ImpCalcXPoly(const tools::Rectangle& rRect1, tools::Long nRad1) const
+{
+ XPolygon aXPoly(rRect1,nRad1,nRad1);
+ const sal_uInt16 nPointCnt(aXPoly.GetPointCount());
+ XPolygon aNewPoly(nPointCnt+1);
+ sal_uInt16 nShift=nPointCnt-2;
+ if (nRad1!=0) nShift=nPointCnt-5;
+ sal_uInt16 j=nShift;
+ for (sal_uInt16 i=1; i<nPointCnt; i++) {
+ aNewPoly[i]=aXPoly[j];
+ aNewPoly.SetFlags(i,aXPoly.GetFlags(j));
+ j++;
+ if (j>=nPointCnt) j=1;
+ }
+ aNewPoly[0]=rRect1.BottomCenter();
+ aNewPoly[nPointCnt]=aNewPoly[0];
+ aXPoly=aNewPoly;
+
+ // these angles always relate to the top left corner of aRect
+ if (maGeo.nShearAngle) ShearXPoly(aXPoly,maRect.TopLeft(),maGeo.mfTanShearAngle);
+ if (maGeo.nRotationAngle) RotateXPoly(aXPoly,maRect.TopLeft(),maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle);
+ return aXPoly;
+}
+
+void SdrRectObj::RecalcXPoly()
+{
+ mpXPoly = ImpCalcXPoly(maRect,GetEckenradius());
+}
+
+const XPolygon& SdrRectObj::GetXPoly() const
+{
+ if(!mpXPoly)
+ {
+ const_cast<SdrRectObj*>(this)->RecalcXPoly();
+ }
+
+ return *mpXPoly;
+}
+
+void SdrRectObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
+{
+ bool bNoTextFrame=!IsTextFrame();
+ rInfo.bResizeFreeAllowed=bNoTextFrame || ((maGeo.nRotationAngle.get() % 9000) == 0);
+ rInfo.bResizePropAllowed=true;
+ rInfo.bRotateFreeAllowed=true;
+ rInfo.bRotate90Allowed =true;
+ rInfo.bMirrorFreeAllowed=bNoTextFrame;
+ rInfo.bMirror45Allowed =bNoTextFrame;
+ rInfo.bMirror90Allowed =bNoTextFrame;
+
+ // allow transparency
+ rInfo.bTransparenceAllowed = true;
+
+ rInfo.bShearAllowed =bNoTextFrame;
+ rInfo.bEdgeRadiusAllowed=true;
+
+ bool bCanConv=!HasText() || ImpCanConvTextToCurve();
+ if (bCanConv && !bNoTextFrame && !HasText()) {
+ bCanConv=HasFill() || HasLine();
+ }
+ rInfo.bCanConvToPath =bCanConv;
+ rInfo.bCanConvToPoly =bCanConv;
+ rInfo.bCanConvToContour = (rInfo.bCanConvToPoly || LineGeometryUsageIsNecessary());
+}
+
+SdrObjKind SdrRectObj::GetObjIdentifier() const
+{
+ if (IsTextFrame())
+ return meTextKind;
+ else return SdrObjKind::Rectangle;
+}
+
+void SdrRectObj::TakeUnrotatedSnapRect(tools::Rectangle& rRect) const
+{
+ rRect = maRect;
+ if (maGeo.nShearAngle==0_deg100)
+ return;
+
+ tools::Long nDst=FRound((maRect.Bottom()-maRect.Top()) * maGeo.mfTanShearAngle);
+ if (maGeo.nShearAngle>0_deg100)
+ {
+ Point aRef(rRect.TopLeft());
+ rRect.AdjustLeft( -nDst );
+ Point aTmpPt(rRect.TopLeft());
+ RotatePoint(aTmpPt,aRef,maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle);
+ aTmpPt-=rRect.TopLeft();
+ rRect.Move(aTmpPt.X(),aTmpPt.Y());
+ }
+ else
+ {
+ rRect.AdjustRight( -nDst );
+ }
+}
+
+OUString SdrRectObj::TakeObjNameSingul() const
+{
+ if (IsTextFrame())
+ {
+ return SdrTextObj::TakeObjNameSingul();
+ }
+
+ bool bRounded = GetEckenradius() != 0; // rounded down
+ TranslateId pResId = bRounded ? STR_ObjNameSingulRECTRND : STR_ObjNameSingulRECT;
+ if (maGeo.nShearAngle)
+ {
+ pResId = bRounded ? STR_ObjNameSingulPARALRND : STR_ObjNameSingulPARAL; // parallelogram or, maybe, rhombus
+ }
+ else if (maRect.GetWidth() == maRect.GetHeight())
+ {
+ pResId = bRounded ? STR_ObjNameSingulQUADRND : STR_ObjNameSingulQUAD; // square
+ }
+ OUString sName(SvxResId(pResId));
+
+ OUString aName(GetName());
+ if (!aName.isEmpty())
+ sName += " '" + aName + "'";
+
+ return sName;
+}
+
+OUString SdrRectObj::TakeObjNamePlural() const
+{
+ if (IsTextFrame())
+ {
+ return SdrTextObj::TakeObjNamePlural();
+ }
+
+ bool bRounded = GetEckenradius() != 0; // rounded down
+ TranslateId pResId = bRounded ? STR_ObjNamePluralRECTRND : STR_ObjNamePluralRECT;
+ if (maGeo.nShearAngle)
+ {
+ pResId = bRounded ? STR_ObjNamePluralPARALRND : STR_ObjNamePluralPARAL; // parallelogram or rhombus
+ }
+ else if (maRect.GetWidth() == maRect.GetHeight())
+ {
+ pResId = bRounded ? STR_ObjNamePluralQUADRND : STR_ObjNamePluralQUAD; // square
+ }
+
+ return SvxResId(pResId);
+}
+
+SdrRectObj* SdrRectObj::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new SdrRectObj(rTargetModel, *this);
+}
+
+basegfx::B2DPolyPolygon SdrRectObj::TakeXorPoly() const
+{
+ XPolyPolygon aXPP;
+ aXPP.Insert(ImpCalcXPoly(maRect,GetEckenradius()));
+ return aXPP.getB2DPolyPolygon();
+}
+
+void SdrRectObj::RecalcSnapRect()
+{
+ tools::Long nEckRad=GetEckenradius();
+ if ((maGeo.nRotationAngle || maGeo.nShearAngle) && nEckRad!=0) {
+ maSnapRect=GetXPoly().GetBoundRect();
+ } else {
+ SdrTextObj::RecalcSnapRect();
+ }
+}
+
+void SdrRectObj::NbcSetSnapRect(const tools::Rectangle& rRect)
+{
+ SdrTextObj::NbcSetSnapRect(rRect);
+ SetXPolyDirty();
+}
+
+void SdrRectObj::NbcSetLogicRect(const tools::Rectangle& rRect)
+{
+ SdrTextObj::NbcSetLogicRect(rRect);
+ SetXPolyDirty();
+}
+
+sal_uInt32 SdrRectObj::GetHdlCount() const
+{
+ return IsTextFrame() ? 10 : 9;
+}
+
+void SdrRectObj::AddToHdlList(SdrHdlList& rHdlList) const
+{
+ // A text box has an additional (pseudo-)handle for the blinking frame.
+ if(IsTextFrame())
+ {
+ OSL_ENSURE(!IsTextEditActive(), "Do not use an ImpTextframeHdl for highlighting text in active text edit, this will collide with EditEngine paints (!)");
+ std::unique_ptr<SdrHdl> pH(new ImpTextframeHdl(maRect));
+ pH->SetObj(const_cast<SdrRectObj*>(this));
+ pH->SetRotationAngle(maGeo.nRotationAngle);
+ rHdlList.AddHdl(std::move(pH));
+ }
+
+ for(sal_Int32 nHdlNum = 1; nHdlNum <= 9; ++nHdlNum)
+ {
+ Point aPnt;
+ SdrHdlKind eKind = SdrHdlKind::Move;
+
+ switch(nHdlNum)
+ {
+ case 1: // Handle for changing the corner radius
+ {
+ tools::Long a = GetEckenradius();
+ tools::Long b = std::max(maRect.GetWidth(),maRect.GetHeight())/2; // rounded up, because GetWidth() adds 1
+ if (a>b) a=b;
+ if (a<0) a=0;
+ aPnt=maRect.TopLeft();
+ aPnt.AdjustX(a );
+ eKind = SdrHdlKind::Circle;
+ break;
+ }
+ case 2: aPnt=maRect.TopLeft(); eKind = SdrHdlKind::UpperLeft; break;
+ case 3: aPnt=maRect.TopCenter(); eKind = SdrHdlKind::Upper; break;
+ case 4: aPnt=maRect.TopRight(); eKind = SdrHdlKind::UpperRight; break;
+ case 5: aPnt=maRect.LeftCenter(); eKind = SdrHdlKind::Left ; break;
+ case 6: aPnt=maRect.RightCenter(); eKind = SdrHdlKind::Right; break;
+ case 7: aPnt=maRect.BottomLeft(); eKind = SdrHdlKind::LowerLeft; break;
+ case 8: aPnt=maRect.BottomCenter(); eKind = SdrHdlKind::Lower; break;
+ case 9: aPnt=maRect.BottomRight(); eKind = SdrHdlKind::LowerRight; break;
+ }
+
+ if (maGeo.nShearAngle)
+ {
+ ShearPoint(aPnt,maRect.TopLeft(),maGeo.mfTanShearAngle);
+ }
+ if (maGeo.nRotationAngle)
+ {
+ RotatePoint(aPnt,maRect.TopLeft(),maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle);
+ }
+
+ std::unique_ptr<SdrHdl> pH(new SdrHdl(aPnt,eKind));
+ pH->SetObj(const_cast<SdrRectObj*>(this));
+ pH->SetRotationAngle(maGeo.nRotationAngle);
+ rHdlList.AddHdl(std::move(pH));
+ }
+}
+
+bool SdrRectObj::hasSpecialDrag() const
+{
+ return true;
+}
+
+bool SdrRectObj::beginSpecialDrag(SdrDragStat& rDrag) const
+{
+ const bool bRad(rDrag.GetHdl() && SdrHdlKind::Circle == rDrag.GetHdl()->GetKind());
+
+ if(bRad)
+ {
+ rDrag.SetEndDragChangesAttributes(true);
+
+ return true;
+ }
+
+ return SdrTextObj::beginSpecialDrag(rDrag);
+}
+
+bool SdrRectObj::applySpecialDrag(SdrDragStat& rDrag)
+{
+ const bool bRad(rDrag.GetHdl() && SdrHdlKind::Circle == rDrag.GetHdl()->GetKind());
+
+ if (bRad)
+ {
+ Point aPt(rDrag.GetNow());
+
+ if (maGeo.nRotationAngle)
+ RotatePoint(aPt, maRect.TopLeft(), -maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
+
+ sal_Int32 nRad(aPt.X() - maRect.Left());
+
+ if (nRad < 0)
+ nRad = 0;
+
+ if(nRad != GetEckenradius())
+ {
+ NbcSetEckenradius(nRad);
+ }
+
+ return true;
+ }
+ else
+ {
+ return SdrTextObj::applySpecialDrag(rDrag);
+ }
+}
+
+OUString SdrRectObj::getSpecialDragComment(const SdrDragStat& rDrag) const
+{
+ const bool bCreateComment(rDrag.GetView() && this == rDrag.GetView()->GetCreateObj());
+
+ if(bCreateComment)
+ {
+ return OUString();
+ }
+ else
+ {
+ const bool bRad(rDrag.GetHdl() && SdrHdlKind::Circle == rDrag.GetHdl()->GetKind());
+
+ if(bRad)
+ {
+ Point aPt(rDrag.GetNow());
+
+ // -sin for reversal
+ if (maGeo.nRotationAngle)
+ RotatePoint(aPt, maRect.TopLeft(), -maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
+
+ sal_Int32 nRad(aPt.X() - maRect.Left());
+
+ if(nRad < 0)
+ nRad = 0;
+
+ return ImpGetDescriptionStr(STR_DragRectEckRad) +
+ " (" +
+ GetMetrStr(nRad) +
+ ")";
+ }
+ else
+ {
+ return SdrTextObj::getSpecialDragComment(rDrag);
+ }
+ }
+}
+
+
+basegfx::B2DPolyPolygon SdrRectObj::TakeCreatePoly(const SdrDragStat& rDrag) const
+{
+ tools::Rectangle aRect1;
+ rDrag.TakeCreateRect(aRect1);
+ aRect1.Justify();
+
+ basegfx::B2DPolyPolygon aRetval;
+ aRetval.append(ImpCalcXPoly(aRect1,GetEckenradius()).getB2DPolygon());
+ return aRetval;
+}
+
+PointerStyle SdrRectObj::GetCreatePointer() const
+{
+ if (IsTextFrame()) return PointerStyle::DrawText;
+ return PointerStyle::DrawRect;
+}
+
+void SdrRectObj::NbcMove(const Size& rSiz)
+{
+ SdrTextObj::NbcMove(rSiz);
+ SetXPolyDirty();
+}
+
+void SdrRectObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
+{
+ SdrTextObj::NbcResize(rRef,xFact,yFact);
+ SetXPolyDirty();
+}
+
+void SdrRectObj::NbcRotate(const Point& rRef, Degree100 nAngle, double sn, double cs)
+{
+ SdrTextObj::NbcRotate(rRef,nAngle,sn,cs);
+ SetXPolyDirty();
+}
+
+void SdrRectObj::NbcShear(const Point& rRef, Degree100 nAngle, double tn, bool bVShear)
+{
+ SdrTextObj::NbcShear(rRef,nAngle,tn,bVShear);
+ SetXPolyDirty();
+}
+
+void SdrRectObj::NbcMirror(const Point& rRef1, const Point& rRef2)
+{
+ SdrTextObj::NbcMirror(rRef1,rRef2);
+ SetXPolyDirty();
+}
+
+SdrGluePoint SdrRectObj::GetVertexGluePoint(sal_uInt16 nPosNum) const
+{
+ sal_Int32 nWdt = ImpGetLineWdt(); // #i25616#
+
+ // #i25616#
+ if(!LineIsOutsideGeometry())
+ {
+ nWdt++;
+ nWdt /= 2;
+ }
+
+ Point aPt;
+ switch (nPosNum) {
+ case 0: aPt=maRect.TopCenter(); aPt.AdjustY( -nWdt ); break;
+ case 1: aPt=maRect.RightCenter(); aPt.AdjustX(nWdt ); break;
+ case 2: aPt=maRect.BottomCenter(); aPt.AdjustY(nWdt ); break;
+ case 3: aPt=maRect.LeftCenter(); aPt.AdjustX( -nWdt ); break;
+ }
+ if (maGeo.nShearAngle) ShearPoint(aPt, maRect.TopLeft(), maGeo.mfTanShearAngle);
+ if (maGeo.nRotationAngle) RotatePoint(aPt, maRect.TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
+ aPt-=GetSnapRect().Center();
+ SdrGluePoint aGP(aPt);
+ aGP.SetPercent(false);
+ return aGP;
+}
+
+SdrGluePoint SdrRectObj::GetCornerGluePoint(sal_uInt16 nPosNum) const
+{
+ sal_Int32 nWdt = ImpGetLineWdt(); // #i25616#
+
+ // #i25616#
+ if(!LineIsOutsideGeometry())
+ {
+ nWdt++;
+ nWdt /= 2;
+ }
+
+ Point aPt;
+ switch (nPosNum) {
+ case 0: aPt=maRect.TopLeft(); aPt.AdjustX( -nWdt ); aPt.AdjustY( -nWdt ); break;
+ case 1: aPt=maRect.TopRight(); aPt.AdjustX(nWdt ); aPt.AdjustY( -nWdt ); break;
+ case 2: aPt=maRect.BottomRight(); aPt.AdjustX(nWdt ); aPt.AdjustY(nWdt ); break;
+ case 3: aPt=maRect.BottomLeft(); aPt.AdjustX( -nWdt ); aPt.AdjustY(nWdt ); break;
+ }
+ if (maGeo.nShearAngle) ShearPoint(aPt,maRect.TopLeft(),maGeo.mfTanShearAngle);
+ if (maGeo.nRotationAngle) RotatePoint(aPt,maRect.TopLeft(),maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle);
+ aPt-=GetSnapRect().Center();
+ SdrGluePoint aGP(aPt);
+ aGP.SetPercent(false);
+ return aGP;
+}
+
+SdrObjectUniquePtr SdrRectObj::DoConvertToPolyObj(bool bBezier, bool bAddText) const
+{
+ XPolygon aXP(ImpCalcXPoly(maRect,GetEckenradius()));
+ { // TODO: this is only for the moment, until we have the new TakeContour()
+ aXP.Remove(0,1);
+ aXP[aXP.GetPointCount()-1]=aXP[0];
+ }
+
+ basegfx::B2DPolyPolygon aPolyPolygon(aXP.getB2DPolygon());
+ aPolyPolygon.removeDoublePoints();
+ SdrObjectUniquePtr pRet;
+
+ // small correction: Do not create something when no fill and no line. To
+ // be sure to not damage something with non-text frames, do this only
+ // when used with bAddText==false from other converters
+ if((bAddText && !IsTextFrame()) || HasFill() || HasLine())
+ {
+ pRet = ImpConvertMakeObj(aPolyPolygon, true, bBezier);
+ }
+
+ if(bAddText)
+ {
+ pRet = ImpConvertAddText(std::move(pRet), bBezier);
+ }
+
+ return pRet;
+}
+
+void SdrRectObj::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
+{
+ SdrTextObj::Notify(rBC,rHint);
+ SetXPolyDirty(); // because of the corner radius
+}
+
+void SdrRectObj::RestoreGeoData(const SdrObjGeoData& rGeo)
+{
+ SdrTextObj::RestoreGeoData(rGeo);
+ SetXPolyDirty();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdotext.cxx b/svx/source/svdraw/svdotext.cxx
new file mode 100644
index 000000000..fe5e13446
--- /dev/null
+++ b/svx/source/svdraw/svdotext.cxx
@@ -0,0 +1,2174 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <comphelper/string.hxx>
+#include <svl/stritem.hxx>
+#include <svx/svdotext.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdoutl.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <editeng/writingmodeitem.hxx>
+#include <svx/sdtfchim.hxx>
+#include <editeng/editdata.hxx>
+#include <editeng/editstat.hxx>
+#include <editeng/outlobj.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/outliner.hxx>
+#include <textchain.hxx>
+#include <textchainflow.hxx>
+#include <tools/helpers.hxx>
+#include <svx/sderitm.hxx>
+#include <svx/sdooitm.hxx>
+#include <svx/sdshitm.hxx>
+#include <svx/sdtagitm.hxx>
+#include <svx/sdtfsitm.hxx>
+#include <svx/sdtmfitm.hxx>
+#include <svx/xtextit0.hxx>
+#include <sdr/properties/textproperties.hxx>
+#include <sdr/contact/viewcontactoftextobj.hxx>
+#include <basegfx/tuple/b2dtuple.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <vcl/gdimtf.hxx>
+#include <vcl/virdev.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <sal/log.hxx>
+#include <o3tl/temporary.hxx>
+#include <unotools/configmgr.hxx>
+
+using namespace com::sun::star;
+
+// BaseProperties section
+std::unique_ptr<sdr::properties::BaseProperties> SdrTextObj::CreateObjectSpecificProperties()
+{
+ return std::make_unique<sdr::properties::TextProperties>(*this);
+}
+
+// DrawContact section
+std::unique_ptr<sdr::contact::ViewContact> SdrTextObj::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::ViewContactOfTextObj>(*this);
+}
+
+SdrTextObj::SdrTextObj(SdrModel& rSdrModel)
+: SdrAttrObj(rSdrModel),
+ mpEditingOutliner(nullptr),
+ meTextKind(SdrObjKind::Text)
+{
+ mbTextSizeDirty = false;
+ mbTextFrame = false;
+ mbNoShear = false;
+ mbDisableAutoWidthOnDragging = false;
+
+ mbInEditMode = false;
+ mbTextAnimationAllowed = true;
+ maTextEditOffset = Point(0, 0);
+
+ // #i25616#
+ mbSupportTextIndentingOnLineWidthChange = true;
+ mbInDownScale = false;
+}
+
+SdrTextObj::SdrTextObj(SdrModel& rSdrModel, SdrTextObj const & rSource)
+: SdrAttrObj(rSdrModel, rSource),
+ mpEditingOutliner(nullptr)
+{
+ mbInEditMode = false;
+ mbTextAnimationAllowed = true;
+ maTextEditOffset = Point(0, 0);
+
+ // #i25616#
+ mbSupportTextIndentingOnLineWidthChange = true;
+ mbInDownScale = false;
+
+ maRect = rSource.maRect;
+ maGeo = rSource.maGeo;
+ meTextKind = rSource.meTextKind;
+ mbTextFrame = rSource.mbTextFrame;
+ maTextSize = rSource.maTextSize;
+ mbTextSizeDirty = rSource.mbTextSizeDirty;
+
+ // Not all of the necessary parameters were copied yet.
+ mbNoShear = rSource.mbNoShear;
+ mbDisableAutoWidthOnDragging = rSource.mbDisableAutoWidthOnDragging;
+ SdrText* pText = getActiveText();
+
+ if( pText && rSource.HasText() )
+ {
+ // before pNewOutlinerParaObject was created the same, but
+ // set at mpText (outside this scope), but mpText might be
+ // empty (this operator== seems not prepared for MultiText
+ // objects). In the current form it makes only sense to
+ // create locally and use locally on a known existing SdrText
+ const Outliner* pEO = rSource.mpEditingOutliner;
+ std::optional<OutlinerParaObject> pNewOutlinerParaObject;
+
+ if (pEO!=nullptr)
+ {
+ pNewOutlinerParaObject = pEO->CreateParaObject();
+ }
+ else if (nullptr != rSource.getActiveText()->GetOutlinerParaObject())
+ {
+ pNewOutlinerParaObject = *rSource.getActiveText()->GetOutlinerParaObject();
+ }
+
+ pText->SetOutlinerParaObject( std::move(pNewOutlinerParaObject) );
+ }
+
+ ImpSetTextStyleSheetListeners();
+}
+
+SdrTextObj::SdrTextObj(
+ SdrModel& rSdrModel,
+ const tools::Rectangle& rNewRect)
+: SdrAttrObj(rSdrModel),
+ maRect(rNewRect),
+ mpEditingOutliner(nullptr),
+ meTextKind(SdrObjKind::Text)
+{
+ mbTextSizeDirty = false;
+ mbTextFrame = false;
+ mbNoShear = false;
+ mbDisableAutoWidthOnDragging = false;
+ ImpJustifyRect(maRect);
+
+ mbInEditMode = false;
+ mbTextAnimationAllowed = true;
+ mbInDownScale = false;
+ maTextEditOffset = Point(0, 0);
+
+ // #i25616#
+ mbSupportTextIndentingOnLineWidthChange = true;
+}
+
+SdrTextObj::SdrTextObj(
+ SdrModel& rSdrModel,
+ SdrObjKind eNewTextKind)
+: SdrAttrObj(rSdrModel),
+ mpEditingOutliner(nullptr),
+ meTextKind(eNewTextKind)
+{
+ mbTextSizeDirty = false;
+ mbTextFrame = true;
+ mbNoShear = true;
+ mbDisableAutoWidthOnDragging = false;
+
+ mbInEditMode = false;
+ mbTextAnimationAllowed = true;
+ mbInDownScale = false;
+ maTextEditOffset = Point(0, 0);
+
+ // #i25616#
+ mbSupportTextIndentingOnLineWidthChange = true;
+}
+
+SdrTextObj::SdrTextObj(
+ SdrModel& rSdrModel,
+ SdrObjKind eNewTextKind,
+ const tools::Rectangle& rNewRect)
+: SdrAttrObj(rSdrModel),
+ maRect(rNewRect),
+ mpEditingOutliner(nullptr),
+ meTextKind(eNewTextKind)
+{
+ mbTextSizeDirty = false;
+ mbTextFrame = true;
+ mbNoShear = true;
+ mbDisableAutoWidthOnDragging = false;
+ ImpJustifyRect(maRect);
+
+ mbInEditMode = false;
+ mbTextAnimationAllowed = true;
+ mbInDownScale = false;
+ maTextEditOffset = Point(0, 0);
+
+ // #i25616#
+ mbSupportTextIndentingOnLineWidthChange = true;
+}
+
+SdrTextObj::~SdrTextObj()
+{
+ SdrOutliner& rOutl(getSdrModelFromSdrObject().GetHitTestOutliner());
+ if( rOutl.GetTextObj() == this )
+ rOutl.SetTextObj( nullptr );
+ mpText.reset();
+ ImpDeregisterLink();
+}
+
+void SdrTextObj::FitFrameToTextSize()
+{
+ ImpJustifyRect(maRect);
+
+ SdrText* pText = getActiveText();
+ if(pText==nullptr || !pText->GetOutlinerParaObject())
+ return;
+
+ SdrOutliner& rOutliner=ImpGetDrawOutliner();
+ rOutliner.SetPaperSize(Size(maRect.Right()-maRect.Left(),maRect.Bottom()-maRect.Top()));
+ rOutliner.SetUpdateLayout(true);
+ rOutliner.SetText(*pText->GetOutlinerParaObject());
+ Size aNewSize(rOutliner.CalcTextSize());
+ rOutliner.Clear();
+ aNewSize.AdjustWidth( 1 ); // because of possible rounding errors
+ aNewSize.AdjustWidth(GetTextLeftDistance()+GetTextRightDistance() );
+ aNewSize.AdjustHeight(GetTextUpperDistance()+GetTextLowerDistance() );
+ tools::Rectangle aNewRect(maRect);
+ aNewRect.SetSize(aNewSize);
+ ImpJustifyRect(aNewRect);
+ if (aNewRect!=maRect) {
+ SetLogicRect(aNewRect);
+ }
+}
+
+void SdrTextObj::NbcSetText(const OUString& rStr)
+{
+ SdrOutliner& rOutliner=ImpGetDrawOutliner();
+ rOutliner.SetStyleSheet( 0, GetStyleSheet());
+ rOutliner.SetText(rStr,rOutliner.GetParagraph( 0 ));
+ std::optional<OutlinerParaObject> pNewText=rOutliner.CreateParaObject();
+ NbcSetOutlinerParaObject(std::move(pNewText));
+ mbTextSizeDirty=true;
+}
+
+void SdrTextObj::SetText(const OUString& rStr)
+{
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ NbcSetText(rStr);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+}
+
+void SdrTextObj::NbcSetText(SvStream& rInput, const OUString& rBaseURL, EETextFormat eFormat)
+{
+ SdrOutliner& rOutliner=ImpGetDrawOutliner();
+ rOutliner.SetStyleSheet( 0, GetStyleSheet());
+ rOutliner.Read(rInput,rBaseURL,eFormat);
+ std::optional<OutlinerParaObject> pNewText=rOutliner.CreateParaObject();
+ rOutliner.SetUpdateLayout(true);
+ Size aSize(rOutliner.CalcTextSize());
+ rOutliner.Clear();
+ NbcSetOutlinerParaObject(std::move(pNewText));
+ maTextSize=aSize;
+ mbTextSizeDirty=false;
+}
+
+void SdrTextObj::SetText(SvStream& rInput, const OUString& rBaseURL, EETextFormat eFormat)
+{
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ NbcSetText(rInput,rBaseURL,eFormat);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+}
+
+const Size& SdrTextObj::GetTextSize() const
+{
+ if (mbTextSizeDirty)
+ {
+ Size aSiz;
+ SdrText* pText = getActiveText();
+ if( pText && pText->GetOutlinerParaObject ())
+ {
+ SdrOutliner& rOutliner=ImpGetDrawOutliner();
+ rOutliner.SetText(*pText->GetOutlinerParaObject());
+ rOutliner.SetUpdateLayout(true);
+ aSiz=rOutliner.CalcTextSize();
+ rOutliner.Clear();
+ }
+ // casting to nonconst twice
+ const_cast<SdrTextObj*>(this)->maTextSize = aSiz;
+ const_cast<SdrTextObj*>(this)->mbTextSizeDirty = false;
+ }
+ return maTextSize;
+}
+
+bool SdrTextObj::IsAutoGrowHeight() const
+{
+ if(!mbTextFrame)
+ return false; // AutoGrow only together with TextFrames
+
+ const SfxItemSet& rSet = GetObjectItemSet();
+ bool bRet = rSet.Get(SDRATTR_TEXT_AUTOGROWHEIGHT).GetValue();
+
+ if(bRet)
+ {
+ SdrTextAniKind eAniKind = rSet.Get(SDRATTR_TEXT_ANIKIND).GetValue();
+
+ if(eAniKind == SdrTextAniKind::Scroll || eAniKind == SdrTextAniKind::Alternate || eAniKind == SdrTextAniKind::Slide)
+ {
+ SdrTextAniDirection eDirection = rSet.Get(SDRATTR_TEXT_ANIDIRECTION).GetValue();
+
+ if(eDirection == SdrTextAniDirection::Up || eDirection == SdrTextAniDirection::Down)
+ {
+ bRet = false;
+ }
+ }
+ }
+ return bRet;
+}
+
+bool SdrTextObj::IsAutoGrowWidth() const
+{
+ if (!mbTextFrame)
+ return false; // AutoGrow only together with TextFrames
+
+ const SfxItemSet& rSet = GetObjectItemSet();
+ bool bRet = rSet.Get(SDRATTR_TEXT_AUTOGROWWIDTH).GetValue();
+
+ bool bInEditMOde = IsInEditMode();
+
+ if(!bInEditMOde && bRet)
+ {
+ SdrTextAniKind eAniKind = rSet.Get(SDRATTR_TEXT_ANIKIND).GetValue();
+
+ if(eAniKind == SdrTextAniKind::Scroll || eAniKind == SdrTextAniKind::Alternate || eAniKind == SdrTextAniKind::Slide)
+ {
+ SdrTextAniDirection eDirection = rSet.Get(SDRATTR_TEXT_ANIDIRECTION).GetValue();
+
+ if(eDirection == SdrTextAniDirection::Left || eDirection == SdrTextAniDirection::Right)
+ {
+ bRet = false;
+ }
+ }
+ }
+ return bRet;
+}
+
+SdrTextHorzAdjust SdrTextObj::GetTextHorizontalAdjust() const
+{
+ return GetTextHorizontalAdjust(GetObjectItemSet());
+}
+
+SdrTextHorzAdjust SdrTextObj::GetTextHorizontalAdjust(const SfxItemSet& rSet) const
+{
+ if(IsContourTextFrame())
+ return SDRTEXTHORZADJUST_BLOCK;
+
+ SdrTextHorzAdjust eRet = rSet.Get(SDRATTR_TEXT_HORZADJUST).GetValue();
+
+ bool bInEditMode = IsInEditMode();
+
+ if(!bInEditMode && eRet == SDRTEXTHORZADJUST_BLOCK)
+ {
+ SdrTextAniKind eAniKind = rSet.Get(SDRATTR_TEXT_ANIKIND).GetValue();
+
+ if(eAniKind == SdrTextAniKind::Scroll || eAniKind == SdrTextAniKind::Alternate || eAniKind == SdrTextAniKind::Slide)
+ {
+ SdrTextAniDirection eDirection = rSet.Get(SDRATTR_TEXT_ANIDIRECTION).GetValue();
+
+ if(eDirection == SdrTextAniDirection::Left || eDirection == SdrTextAniDirection::Right)
+ {
+ eRet = SDRTEXTHORZADJUST_LEFT;
+ }
+ }
+ }
+
+ return eRet;
+} // defaults: BLOCK (justify) for text frame, CENTER for captions of drawing objects
+
+SdrTextVertAdjust SdrTextObj::GetTextVerticalAdjust() const
+{
+ return GetTextVerticalAdjust(GetObjectItemSet());
+}
+
+SdrTextVertAdjust SdrTextObj::GetTextVerticalAdjust(const SfxItemSet& rSet) const
+{
+ if(IsContourTextFrame())
+ return SDRTEXTVERTADJUST_TOP;
+
+ // Take care for vertical text animation here
+ SdrTextVertAdjust eRet = rSet.Get(SDRATTR_TEXT_VERTADJUST).GetValue();
+ bool bInEditMode = IsInEditMode();
+
+ // Take care for vertical text animation here
+ if(!bInEditMode && eRet == SDRTEXTVERTADJUST_BLOCK)
+ {
+ SdrTextAniKind eAniKind = rSet.Get(SDRATTR_TEXT_ANIKIND).GetValue();
+
+ if(eAniKind == SdrTextAniKind::Scroll || eAniKind == SdrTextAniKind::Alternate || eAniKind == SdrTextAniKind::Slide)
+ {
+ SdrTextAniDirection eDirection = rSet.Get(SDRATTR_TEXT_ANIDIRECTION).GetValue();
+
+ if(eDirection == SdrTextAniDirection::Left || eDirection == SdrTextAniDirection::Right)
+ {
+ eRet = SDRTEXTVERTADJUST_TOP;
+ }
+ }
+ }
+
+ return eRet;
+} // defaults: TOP for text frame, CENTER for captions of drawing objects
+
+void SdrTextObj::ImpJustifyRect(tools::Rectangle& rRect)
+{
+ if (!rRect.IsEmpty()) {
+ rRect.Justify();
+ if (rRect.Left()==rRect.Right()) rRect.AdjustRight( 1 );
+ if (rRect.Top()==rRect.Bottom()) rRect.AdjustBottom( 1 );
+ }
+}
+
+void SdrTextObj::ImpCheckShear()
+{
+ if (mbNoShear && maGeo.nShearAngle)
+ {
+ maGeo.nShearAngle = 0_deg100;
+ maGeo.mfTanShearAngle = 0;
+ }
+}
+
+void SdrTextObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
+{
+ bool bNoTextFrame=!IsTextFrame();
+ rInfo.bResizeFreeAllowed=bNoTextFrame || ((maGeo.nRotationAngle.get() % 9000) == 0);
+ rInfo.bResizePropAllowed=true;
+ rInfo.bRotateFreeAllowed=true;
+ rInfo.bRotate90Allowed =true;
+ rInfo.bMirrorFreeAllowed=bNoTextFrame;
+ rInfo.bMirror45Allowed =bNoTextFrame;
+ rInfo.bMirror90Allowed =bNoTextFrame;
+
+ // allow transparency
+ rInfo.bTransparenceAllowed = true;
+
+ rInfo.bShearAllowed =bNoTextFrame;
+ rInfo.bEdgeRadiusAllowed=true;
+ bool bCanConv=ImpCanConvTextToCurve();
+ rInfo.bCanConvToPath =bCanConv;
+ rInfo.bCanConvToPoly =bCanConv;
+ rInfo.bCanConvToPathLineToArea=bCanConv;
+ rInfo.bCanConvToPolyLineToArea=bCanConv;
+ rInfo.bCanConvToContour = (rInfo.bCanConvToPoly || LineGeometryUsageIsNecessary());
+}
+
+SdrObjKind SdrTextObj::GetObjIdentifier() const
+{
+ return meTextKind;
+}
+
+bool SdrTextObj::HasTextImpl( SdrOutliner const * pOutliner )
+{
+ bool bRet=false;
+ if(pOutliner)
+ {
+ Paragraph* p1stPara=pOutliner->GetParagraph( 0 );
+ sal_Int32 nParaCount=pOutliner->GetParagraphCount();
+ if(p1stPara==nullptr)
+ nParaCount=0;
+
+ if(nParaCount==1)
+ {
+ // if it is only one paragraph, check if that paragraph is empty
+ if( pOutliner->GetText(p1stPara).isEmpty() )
+ nParaCount = 0;
+ }
+
+ bRet= nParaCount!=0;
+ }
+ return bRet;
+}
+
+void SdrTextObj::handlePageChange(SdrPage* pOldPage, SdrPage* pNewPage)
+{
+ const bool bRemove(pNewPage == nullptr && pOldPage != nullptr);
+ const bool bInsert(pNewPage != nullptr && pOldPage == nullptr);
+ const bool bLinked(IsLinkedText());
+
+ if (bLinked && bRemove)
+ {
+ ImpDeregisterLink();
+ }
+
+ // call parent
+ SdrAttrObj::handlePageChange(pOldPage, pNewPage);
+
+ if (bLinked && bInsert)
+ {
+ ImpRegisterLink();
+ }
+}
+
+void SdrTextObj::NbcSetEckenradius(tools::Long nRad)
+{
+ SetObjectItem(makeSdrEckenradiusItem(nRad));
+}
+
+// #115391# This implementation is based on the object size (aRect) and the
+// states of IsAutoGrowWidth/Height to correctly set TextMinFrameWidth/Height
+void SdrTextObj::AdaptTextMinSize()
+{
+ if (!mbTextFrame)
+ // Only do this for text frame.
+ return;
+
+ if (getSdrModelFromSdrObject().IsPasteResize())
+ // Don't do this during paste resize.
+ return;
+
+ const bool bW = IsAutoGrowWidth();
+ const bool bH = IsAutoGrowHeight();
+
+ if (!bW && !bH)
+ // No auto grow requested. Bail out.
+ return;
+
+ SfxItemSetFixed<SDRATTR_TEXT_MINFRAMEHEIGHT, SDRATTR_TEXT_AUTOGROWHEIGHT,
+ SDRATTR_TEXT_MINFRAMEWIDTH, SDRATTR_TEXT_AUTOGROWWIDTH> // contains SDRATTR_TEXT_MAXFRAMEWIDTH
+ aSet(*GetObjectItemSet().GetPool());
+
+ if(bW)
+ {
+ // Set minimum width.
+ const tools::Long nDist = GetTextLeftDistance() + GetTextRightDistance();
+ const tools::Long nW = std::max<tools::Long>(0, maRect.GetWidth() - 1 - nDist); // text width without margins
+
+ aSet.Put(makeSdrTextMinFrameWidthItem(nW));
+
+ if(!IsVerticalWriting() && mbDisableAutoWidthOnDragging)
+ {
+ mbDisableAutoWidthOnDragging = true;
+ aSet.Put(makeSdrTextAutoGrowWidthItem(false));
+ }
+ }
+
+ if(bH)
+ {
+ // Set Minimum height.
+ const tools::Long nDist = GetTextUpperDistance() + GetTextLowerDistance();
+ const tools::Long nH = std::max<tools::Long>(0, maRect.GetHeight() - 1 - nDist); // text height without margins
+
+ aSet.Put(makeSdrTextMinFrameHeightItem(nH));
+
+ if(IsVerticalWriting() && mbDisableAutoWidthOnDragging)
+ {
+ mbDisableAutoWidthOnDragging = false;
+ aSet.Put(makeSdrTextAutoGrowHeightItem(false));
+ }
+ }
+
+ SetObjectItemSet(aSet);
+}
+
+void SdrTextObj::ImpSetContourPolygon( SdrOutliner& rOutliner, tools::Rectangle const & rAnchorRect, bool bLineWidth ) const
+{
+ basegfx::B2DPolyPolygon aXorPolyPolygon(TakeXorPoly());
+ std::optional<basegfx::B2DPolyPolygon> pContourPolyPolygon;
+ basegfx::B2DHomMatrix aMatrix(basegfx::utils::createTranslateB2DHomMatrix(
+ -rAnchorRect.Left(), -rAnchorRect.Top()));
+
+ if(maGeo.nRotationAngle)
+ {
+ // Unrotate!
+ aMatrix.rotate(-toRadians(maGeo.nRotationAngle));
+ }
+
+ aXorPolyPolygon.transform(aMatrix);
+
+ if( bLineWidth )
+ {
+ // Take line width into account.
+ // When doing the hit test, avoid this. (Performance!)
+ pContourPolyPolygon.emplace();
+
+ // test if shadow needs to be avoided for TakeContour()
+ const SfxItemSet& rSet = GetObjectItemSet();
+ bool bShadowOn = rSet.Get(SDRATTR_SHADOW).GetValue();
+
+ // #i33696#
+ // Remember TextObject currently set at the DrawOutliner, it WILL be
+ // replaced during calculating the outline since it uses an own paint
+ // and that one uses the DrawOutliner, too.
+ const SdrTextObj* pLastTextObject = rOutliner.GetTextObj();
+
+ if(bShadowOn)
+ {
+ // force shadow off
+ SdrObject* pCopy(CloneSdrObject(getSdrModelFromSdrObject()));
+ pCopy->SetMergedItem(makeSdrShadowItem(false));
+ *pContourPolyPolygon = pCopy->TakeContour();
+ SdrObject::Free( pCopy );
+ }
+ else
+ {
+ *pContourPolyPolygon = TakeContour();
+ }
+
+ // #i33696#
+ // restore remembered text object
+ if(pLastTextObject != rOutliner.GetTextObj())
+ {
+ rOutliner.SetTextObj(pLastTextObject);
+ }
+
+ pContourPolyPolygon->transform(aMatrix);
+ }
+
+ rOutliner.SetPolygon(aXorPolyPolygon, pContourPolyPolygon ? &*pContourPolyPolygon : nullptr);
+}
+
+void SdrTextObj::TakeUnrotatedSnapRect(tools::Rectangle& rRect) const
+{
+ rRect=maRect;
+}
+
+// See also: <unnamed>::getTextAnchorRange in svx/source/sdr/primitive2d/sdrdecompositiontools.cxx
+void SdrTextObj::AdjustRectToTextDistance(tools::Rectangle& rAnchorRect) const
+{
+ const tools::Long nLeftDist = GetTextLeftDistance();
+ const tools::Long nRightDist = GetTextRightDistance();
+ const tools::Long nUpperDist = GetTextUpperDistance();
+ const tools::Long nLowerDist = GetTextLowerDistance();
+ if (!IsVerticalWriting())
+ {
+ rAnchorRect.AdjustLeft(nLeftDist);
+ rAnchorRect.AdjustTop(nUpperDist);
+ rAnchorRect.AdjustRight(-nRightDist);
+ rAnchorRect.AdjustBottom(-nLowerDist);
+ }
+ else if (IsTopToBottom())
+ {
+ rAnchorRect.AdjustLeft(nLowerDist);
+ rAnchorRect.AdjustTop(nLeftDist);
+ rAnchorRect.AdjustRight(-nUpperDist);
+ rAnchorRect.AdjustBottom(-nRightDist);
+ }
+ else
+ {
+ rAnchorRect.AdjustLeft(nUpperDist);
+ rAnchorRect.AdjustTop(nRightDist);
+ rAnchorRect.AdjustRight(-nLowerDist);
+ rAnchorRect.AdjustBottom(-nLeftDist);
+ }
+
+ // Since sizes may be bigger than the object bounds it is necessary to
+ // justify the rect now.
+ ImpJustifyRect(rAnchorRect);
+}
+
+void SdrTextObj::TakeTextAnchorRect(tools::Rectangle& rAnchorRect) const
+{
+ tools::Rectangle aAnkRect(maRect); // the rectangle in which we anchor
+ bool bFrame=IsTextFrame();
+ if (!bFrame) {
+ TakeUnrotatedSnapRect(aAnkRect);
+ }
+ Point aRotateRef(aAnkRect.TopLeft());
+ AdjustRectToTextDistance(aAnkRect);
+
+ if (bFrame) {
+ // TODO: Optimize this.
+ if (aAnkRect.GetWidth()<2) aAnkRect.SetRight(aAnkRect.Left()+1 ); // minimum size h and v: 2 px
+ if (aAnkRect.GetHeight()<2) aAnkRect.SetBottom(aAnkRect.Top()+1 );
+ }
+ if (maGeo.nRotationAngle) {
+ Point aTmpPt(aAnkRect.TopLeft());
+ RotatePoint(aTmpPt,aRotateRef,maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle);
+ aTmpPt-=aAnkRect.TopLeft();
+ aAnkRect.Move(aTmpPt.X(),aTmpPt.Y());
+ }
+ rAnchorRect=aAnkRect;
+}
+
+void SdrTextObj::TakeTextRect( SdrOutliner& rOutliner, tools::Rectangle& rTextRect, bool bNoEditText,
+ tools::Rectangle* pAnchorRect, bool bLineWidth ) const
+{
+ tools::Rectangle aAnkRect; // the rectangle in which we anchor
+ TakeTextAnchorRect(aAnkRect);
+ SdrTextVertAdjust eVAdj=GetTextVerticalAdjust();
+ SdrTextHorzAdjust eHAdj=GetTextHorizontalAdjust();
+ SdrTextAniKind eAniKind=GetTextAniKind();
+ SdrTextAniDirection eAniDirection=GetTextAniDirection();
+
+ bool bFitToSize(IsFitToSize());
+ bool bContourFrame=IsContourTextFrame();
+
+ bool bFrame=IsTextFrame();
+ EEControlBits nStat0=rOutliner.GetControlWord();
+ Size aNullSize;
+ if (!bContourFrame)
+ {
+ rOutliner.SetControlWord(nStat0|EEControlBits::AUTOPAGESIZE);
+ rOutliner.SetMinAutoPaperSize(aNullSize);
+ rOutliner.SetMaxAutoPaperSize(Size(1000000,1000000));
+ }
+
+ if (!bFitToSize && !bContourFrame)
+ {
+ tools::Long nAnkWdt=aAnkRect.GetWidth();
+ tools::Long nAnkHgt=aAnkRect.GetHeight();
+ if (bFrame)
+ {
+ tools::Long nWdt=nAnkWdt;
+ tools::Long nHgt=nAnkHgt;
+
+ bool bInEditMode = IsInEditMode();
+
+ if (!bInEditMode && (eAniKind==SdrTextAniKind::Scroll || eAniKind==SdrTextAniKind::Alternate || eAniKind==SdrTextAniKind::Slide))
+ {
+ // unlimited paper size for ticker text
+ if (eAniDirection==SdrTextAniDirection::Left || eAniDirection==SdrTextAniDirection::Right) nWdt=1000000;
+ if (eAniDirection==SdrTextAniDirection::Up || eAniDirection==SdrTextAniDirection::Down) nHgt=1000000;
+ }
+
+ bool bChainedFrame = IsChainable();
+ // Might be required for overflow check working: do limit height to frame if box is chainable.
+ if (!bChainedFrame) {
+ // #i119885# Do not limit/force height to geometrical frame (vice versa for vertical writing)
+
+ if(IsVerticalWriting())
+ {
+ nWdt = 1000000;
+ }
+ else
+ {
+ nHgt = 1000000;
+ }
+ }
+
+ rOutliner.SetMaxAutoPaperSize(Size(nWdt,nHgt));
+ }
+
+ // New try with _BLOCK for hor and ver after completely
+ // supporting full width for vertical text.
+ if(SDRTEXTHORZADJUST_BLOCK == eHAdj && !IsVerticalWriting())
+ {
+ rOutliner.SetMinAutoPaperSize(Size(nAnkWdt, 0));
+ rOutliner.SetMinColumnWrapHeight(nAnkHgt);
+ }
+
+ if(SDRTEXTVERTADJUST_BLOCK == eVAdj && IsVerticalWriting())
+ {
+ rOutliner.SetMinAutoPaperSize(Size(0, nAnkHgt));
+ rOutliner.SetMinColumnWrapHeight(nAnkWdt);
+ }
+ }
+
+ rOutliner.SetPaperSize(aNullSize);
+ if (bContourFrame)
+ ImpSetContourPolygon( rOutliner, aAnkRect, bLineWidth );
+
+ // put text into the outliner, if available from the edit outliner
+ SdrText* pText = getActiveText();
+ OutlinerParaObject* pOutlinerParaObject = pText ? pText->GetOutlinerParaObject() : nullptr;
+ std::optional<OutlinerParaObject> pPara;
+ if (mpEditingOutliner && !bNoEditText)
+ pPara = mpEditingOutliner->CreateParaObject();
+ else if (pOutlinerParaObject)
+ pPara = *pOutlinerParaObject;
+
+ if (pPara)
+ {
+ const bool bHitTest(&getSdrModelFromSdrObject().GetHitTestOutliner() == &rOutliner);
+ const SdrTextObj* pTestObj = rOutliner.GetTextObj();
+
+ if( !pTestObj || !bHitTest || pTestObj != this ||
+ pTestObj->GetOutlinerParaObject() != pOutlinerParaObject )
+ {
+ if( bHitTest ) // #i33696# take back fix #i27510#
+ {
+ rOutliner.SetTextObj( this );
+ rOutliner.SetFixedCellHeight(GetMergedItem(SDRATTR_TEXT_USEFIXEDCELLHEIGHT).GetValue());
+ }
+
+ rOutliner.SetUpdateLayout(true);
+ rOutliner.SetText(*pPara);
+ }
+ }
+ else
+ {
+ rOutliner.SetTextObj( nullptr );
+ }
+
+ rOutliner.SetUpdateLayout(true);
+ rOutliner.SetControlWord(nStat0);
+
+ if( pText )
+ pText->CheckPortionInfo(rOutliner);
+
+ Point aTextPos(aAnkRect.TopLeft());
+ Size aTextSiz(rOutliner.GetPaperSize()); // GetPaperSize() adds a little tolerance, right?
+
+ // For draw objects containing text correct hor/ver alignment if text is bigger
+ // than the object itself. Without that correction, the text would always be
+ // formatted to the left edge (or top edge when vertical) of the draw object.
+ if(!IsTextFrame())
+ {
+ if(aAnkRect.GetWidth() < aTextSiz.Width() && !IsVerticalWriting())
+ {
+ // Horizontal case here. Correct only if eHAdj == SDRTEXTHORZADJUST_BLOCK,
+ // else the alignment is wanted.
+ if(SDRTEXTHORZADJUST_BLOCK == eHAdj)
+ {
+ eHAdj = SDRTEXTHORZADJUST_CENTER;
+ }
+ }
+
+ if(aAnkRect.GetHeight() < aTextSiz.Height() && IsVerticalWriting())
+ {
+ // Vertical case here. Correct only if eHAdj == SDRTEXTVERTADJUST_BLOCK,
+ // else the alignment is wanted.
+ if(SDRTEXTVERTADJUST_BLOCK == eVAdj)
+ {
+ eVAdj = SDRTEXTVERTADJUST_CENTER;
+ }
+ }
+ }
+
+ if (eHAdj==SDRTEXTHORZADJUST_CENTER || eHAdj==SDRTEXTHORZADJUST_RIGHT)
+ {
+ tools::Long nFreeWdt=aAnkRect.GetWidth()-aTextSiz.Width();
+ if (eHAdj==SDRTEXTHORZADJUST_CENTER)
+ aTextPos.AdjustX(nFreeWdt/2 );
+ if (eHAdj==SDRTEXTHORZADJUST_RIGHT)
+ aTextPos.AdjustX(nFreeWdt );
+ }
+ if (eVAdj==SDRTEXTVERTADJUST_CENTER || eVAdj==SDRTEXTVERTADJUST_BOTTOM)
+ {
+ tools::Long nFreeHgt=aAnkRect.GetHeight()-aTextSiz.Height();
+ if (eVAdj==SDRTEXTVERTADJUST_CENTER)
+ aTextPos.AdjustY(nFreeHgt/2 );
+ if (eVAdj==SDRTEXTVERTADJUST_BOTTOM)
+ aTextPos.AdjustY(nFreeHgt );
+ }
+ if (maGeo.nRotationAngle)
+ RotatePoint(aTextPos,aAnkRect.TopLeft(),maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle);
+
+ if (pAnchorRect)
+ *pAnchorRect=aAnkRect;
+
+ // rTextRect might not be correct in some cases at ContourFrame
+ rTextRect=tools::Rectangle(aTextPos,aTextSiz);
+ if (bContourFrame)
+ rTextRect=aAnkRect;
+}
+
+bool SdrTextObj::CanCreateEditOutlinerParaObject() const
+{
+ if( HasTextImpl( mpEditingOutliner ) )
+ {
+ return mpEditingOutliner->GetParagraphCount() > 0;
+ }
+ return false;
+}
+
+std::optional<OutlinerParaObject> SdrTextObj::CreateEditOutlinerParaObject() const
+{
+ std::optional<OutlinerParaObject> pPara;
+ if( HasTextImpl( mpEditingOutliner ) )
+ {
+ sal_Int32 nParaCount = mpEditingOutliner->GetParagraphCount();
+ pPara = mpEditingOutliner->CreateParaObject(0, nParaCount);
+ }
+ return pPara;
+}
+
+void SdrTextObj::ImpSetCharStretching(SdrOutliner& rOutliner, const Size& rTextSize, const Size& rShapeSize, Fraction& rFitXCorrection)
+{
+ OutputDevice* pOut = rOutliner.GetRefDevice();
+ bool bNoStretching(false);
+
+ if(pOut && pOut->GetOutDevType() == OUTDEV_PRINTER)
+ {
+ // check whether CharStretching is possible at all
+ GDIMetaFile* pMtf = pOut->GetConnectMetaFile();
+ OUString aTestString(u'J');
+
+ if(pMtf && (!pMtf->IsRecord() || pMtf->IsPause()))
+ pMtf = nullptr;
+
+ if(pMtf)
+ pMtf->Pause(true);
+
+ vcl::Font aOriginalFont(pOut->GetFont());
+ vcl::Font aTmpFont( OutputDevice::GetDefaultFont( DefaultFontType::SERIF, LANGUAGE_SYSTEM, GetDefaultFontFlags::OnlyOne ) );
+
+ aTmpFont.SetFontSize(Size(0,100));
+ pOut->SetFont(aTmpFont);
+ Size aSize1(pOut->GetTextWidth(aTestString), pOut->GetTextHeight());
+ aTmpFont.SetFontSize(Size(800,100));
+ pOut->SetFont(aTmpFont);
+ Size aSize2(pOut->GetTextWidth(aTestString), pOut->GetTextHeight());
+ pOut->SetFont(aOriginalFont);
+
+ if(pMtf)
+ pMtf->Pause(false);
+
+ bNoStretching = (aSize1 == aSize2);
+
+#ifdef _WIN32
+ // Windows zooms the font proportionally when using Size(100,500),
+ // we don't like that.
+ if(aSize2.Height() >= aSize1.Height() * 2)
+ {
+ bNoStretching = true;
+ }
+#endif
+ }
+ unsigned nLoopCount=0;
+ bool bNoMoreLoop = false;
+ tools::Long nXDiff0=0x7FFFFFFF;
+ tools::Long nWantWdt=rShapeSize.Width();
+ tools::Long nIsWdt=rTextSize.Width();
+ if (nIsWdt==0) nIsWdt=1;
+
+ tools::Long nWantHgt=rShapeSize.Height();
+ tools::Long nIsHgt=rTextSize.Height();
+ if (nIsHgt==0) nIsHgt=1;
+
+ tools::Long nXTolPl=nWantWdt/100; // tolerance: +1%
+ tools::Long nXTolMi=nWantWdt/25; // tolerance: -4%
+ tools::Long nXCorr =nWantWdt/20; // correction scale: 5%
+
+ tools::Long nX=(nWantWdt*100) /nIsWdt; // calculate X stretching
+ tools::Long nY=(nWantHgt*100) /nIsHgt; // calculate Y stretching
+ bool bChkX = true;
+ if (bNoStretching) { // might only be possible proportionally
+ if (nX>nY) { nX=nY; bChkX=false; }
+ else { nY=nX; }
+ }
+
+ while (nLoopCount<5 && !bNoMoreLoop) {
+ if (nX<0) nX=-nX;
+ if (nX<1) { nX=1; bNoMoreLoop = true; }
+ if (nX>65535) { nX=65535; bNoMoreLoop = true; }
+
+ if (nY<0) nY=-nY;
+ if (nY<1) { nY=1; bNoMoreLoop = true; }
+ if (nY>65535) { nY=65535; bNoMoreLoop = true; }
+
+ // exception, there is no text yet (horizontal case)
+ if(nIsWdt <= 1)
+ {
+ nX = nY;
+ bNoMoreLoop = true;
+ }
+
+ // exception, there is no text yet (vertical case)
+ if(nIsHgt <= 1)
+ {
+ nY = nX;
+ bNoMoreLoop = true;
+ }
+
+ rOutliner.SetGlobalCharStretching(static_cast<sal_uInt16>(nX),static_cast<sal_uInt16>(nY));
+ nLoopCount++;
+ Size aSiz(rOutliner.CalcTextSize());
+ tools::Long nXDiff=aSiz.Width()-nWantWdt;
+ rFitXCorrection=Fraction(nWantWdt,aSiz.Width());
+ if (((nXDiff>=nXTolMi || !bChkX) && nXDiff<=nXTolPl) || nXDiff==nXDiff0) {
+ bNoMoreLoop = true;
+ } else {
+ // correct stretching factors
+ tools::Long nMul=nWantWdt;
+ tools::Long nDiv=aSiz.Width();
+ if (std::abs(nXDiff)<=2*nXCorr) {
+ if (nMul>nDiv) nDiv+=(nMul-nDiv)/2; // but only add half of what we calculated,
+ else nMul+=(nDiv-nMul)/2; // because the EditEngine calculates wrongly later on
+ }
+ nX=nX*nMul/nDiv;
+ if (bNoStretching) nY=nX;
+ }
+ nXDiff0=nXDiff;
+ }
+}
+
+OUString SdrTextObj::TakeObjNameSingul() const
+{
+ OUString aStr;
+
+ switch(meTextKind)
+ {
+ case SdrObjKind::OutlineText:
+ {
+ aStr = SvxResId(STR_ObjNameSingulOUTLINETEXT);
+ break;
+ }
+
+ case SdrObjKind::TitleText :
+ {
+ aStr = SvxResId(STR_ObjNameSingulTITLETEXT);
+ break;
+ }
+
+ default:
+ {
+ if(IsLinkedText())
+ aStr = SvxResId(STR_ObjNameSingulTEXTLNK);
+ else
+ aStr = SvxResId(STR_ObjNameSingulTEXT);
+ break;
+ }
+ }
+
+ OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject();
+ if(pOutlinerParaObject && meTextKind != SdrObjKind::OutlineText)
+ {
+ // shouldn't currently cause any problems at OUTLINETEXT
+ OUString aStr2(comphelper::string::stripStart(pOutlinerParaObject->GetTextObject().GetText(0), ' '));
+
+ // avoid non expanded text portions in object name
+ // (second condition is new)
+ if(!aStr2.isEmpty() && aStr2.indexOf(u'\x00FF') == -1)
+ {
+ // space between ResStr and content text
+ aStr += " \'";
+
+ if(aStr2.getLength() > 10)
+ {
+ aStr2 = OUString::Concat(aStr2.subView(0, 8)) + "...";
+ }
+
+ aStr += aStr2 + "\'";
+ }
+ }
+
+ OUString sName(aStr);
+
+ OUString aName(GetName());
+ if (!aName.isEmpty())
+ sName += " '" + aName + "'";
+
+ return sName;
+}
+
+OUString SdrTextObj::TakeObjNamePlural() const
+{
+ OUString sName;
+ switch (meTextKind)
+ {
+ case SdrObjKind::OutlineText: sName=SvxResId(STR_ObjNamePluralOUTLINETEXT); break;
+ case SdrObjKind::TitleText : sName=SvxResId(STR_ObjNamePluralTITLETEXT); break;
+ default: {
+ if (IsLinkedText()) {
+ sName=SvxResId(STR_ObjNamePluralTEXTLNK);
+ } else {
+ sName=SvxResId(STR_ObjNamePluralTEXT);
+ }
+ } break;
+ } // switch
+ return sName;
+}
+
+SdrTextObj* SdrTextObj::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new SdrTextObj(rTargetModel, *this);
+}
+
+basegfx::B2DPolyPolygon SdrTextObj::TakeXorPoly() const
+{
+ tools::Polygon aPol(maRect);
+ if (maGeo.nShearAngle) ShearPoly(aPol,maRect.TopLeft(),maGeo.mfTanShearAngle);
+ if (maGeo.nRotationAngle) RotatePoly(aPol,maRect.TopLeft(),maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle);
+
+ basegfx::B2DPolyPolygon aRetval;
+ aRetval.append(aPol.getB2DPolygon());
+ return aRetval;
+}
+
+basegfx::B2DPolyPolygon SdrTextObj::TakeContour() const
+{
+ basegfx::B2DPolyPolygon aRetval(SdrAttrObj::TakeContour());
+
+ // and now add the BoundRect of the text, if necessary
+ if ( GetOutlinerParaObject() && !IsFontwork() && !IsContourTextFrame() )
+ {
+ // using Clone()-Paint() strategy inside TakeContour() leaves a destroyed
+ // SdrObject as pointer in DrawOutliner. Set *this again in fetching the outliner
+ // in every case
+ SdrOutliner& rOutliner=ImpGetDrawOutliner();
+
+ tools::Rectangle aAnchor2;
+ tools::Rectangle aR;
+ TakeTextRect(rOutliner,aR,false,&aAnchor2);
+ rOutliner.Clear();
+ bool bFitToSize(IsFitToSize());
+ if (bFitToSize) aR=aAnchor2;
+ tools::Polygon aPol(aR);
+ if (maGeo.nRotationAngle) RotatePoly(aPol,aR.TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
+
+ aRetval.append(aPol.getB2DPolygon());
+ }
+
+ return aRetval;
+}
+
+void SdrTextObj::RecalcSnapRect()
+{
+ if (maGeo.nRotationAngle || maGeo.nShearAngle)
+ {
+ maSnapRect = Rect2Poly(maRect, maGeo).GetBoundRect();
+ } else {
+ maSnapRect = maRect;
+ }
+}
+
+sal_uInt32 SdrTextObj::GetSnapPointCount() const
+{
+ return 4;
+}
+
+Point SdrTextObj::GetSnapPoint(sal_uInt32 i) const
+{
+ Point aP;
+ switch (i) {
+ case 0: aP=maRect.TopLeft(); break;
+ case 1: aP=maRect.TopRight(); break;
+ case 2: aP=maRect.BottomLeft(); break;
+ case 3: aP=maRect.BottomRight(); break;
+ default: aP=maRect.Center(); break;
+ }
+ if (maGeo.nShearAngle) ShearPoint(aP,maRect.TopLeft(),maGeo.mfTanShearAngle);
+ if (maGeo.nRotationAngle) RotatePoint(aP,maRect.TopLeft(),maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle);
+ return aP;
+}
+
+// Extracted from ImpGetDrawOutliner()
+void SdrTextObj::ImpInitDrawOutliner( SdrOutliner& rOutl ) const
+{
+ rOutl.SetUpdateLayout(false);
+ OutlinerMode nOutlinerMode = OutlinerMode::OutlineObject;
+ if ( !IsOutlText() )
+ nOutlinerMode = OutlinerMode::TextObject;
+ rOutl.Init( nOutlinerMode );
+
+ rOutl.SetGlobalCharStretching();
+ EEControlBits nStat=rOutl.GetControlWord();
+ nStat &= ~EEControlBits(EEControlBits::STRETCHING|EEControlBits::AUTOPAGESIZE);
+ rOutl.SetControlWord(nStat);
+ Size aMaxSize(100000,100000);
+ rOutl.SetMinAutoPaperSize(Size());
+ rOutl.SetMaxAutoPaperSize(aMaxSize);
+ rOutl.SetPaperSize(aMaxSize);
+ rOutl.ClearPolygon();
+}
+
+SdrOutliner& SdrTextObj::ImpGetDrawOutliner() const
+{
+ SdrOutliner& rOutl(getSdrModelFromSdrObject().GetDrawOutliner(this));
+
+ // Code extracted to ImpInitDrawOutliner()
+ ImpInitDrawOutliner( rOutl );
+
+ return rOutl;
+}
+
+// Extracted from Paint()
+void SdrTextObj::ImpSetupDrawOutlinerForPaint( bool bContourFrame,
+ SdrOutliner& rOutliner,
+ tools::Rectangle& rTextRect,
+ tools::Rectangle& rAnchorRect,
+ tools::Rectangle& rPaintRect,
+ Fraction& rFitXCorrection ) const
+{
+ if (!bContourFrame)
+ {
+ // FitToSize can't be used together with ContourFrame for now
+ if (IsFitToSize() || IsAutoFit())
+ {
+ EEControlBits nStat=rOutliner.GetControlWord();
+ nStat|=EEControlBits::STRETCHING|EEControlBits::AUTOPAGESIZE;
+ rOutliner.SetControlWord(nStat);
+ }
+ }
+ rOutliner.SetFixedCellHeight(GetMergedItem(SDRATTR_TEXT_USEFIXEDCELLHEIGHT).GetValue());
+ TakeTextRect(rOutliner, rTextRect, false, &rAnchorRect);
+ rPaintRect = rTextRect;
+
+ if (bContourFrame)
+ return;
+
+ // FitToSize can't be used together with ContourFrame for now
+ if (IsFitToSize())
+ {
+ ImpSetCharStretching(rOutliner,rTextRect.GetSize(),rAnchorRect.GetSize(),rFitXCorrection);
+ rPaintRect=rAnchorRect;
+ }
+ else if (IsAutoFit())
+ {
+ ImpAutoFitText(rOutliner);
+ }
+}
+
+sal_uInt16 SdrTextObj::GetFontScaleY() const
+{
+ SdrOutliner& rOutliner = ImpGetDrawOutliner();
+ // This eventually calls ImpAutoFitText
+ UpdateOutlinerFormatting(rOutliner, o3tl::temporary(tools::Rectangle()));
+
+ sal_uInt16 nStretchY;
+ rOutliner.GetGlobalCharStretching(o3tl::temporary(sal_uInt16()), nStretchY);
+ return nStretchY;
+}
+
+void SdrTextObj::ImpAutoFitText( SdrOutliner& rOutliner ) const
+{
+ const Size aShapeSize=GetSnapRect().GetSize();
+ ImpAutoFitText( rOutliner,
+ Size(aShapeSize.Width()-GetTextLeftDistance()-GetTextRightDistance(),
+ aShapeSize.Height()-GetTextUpperDistance()-GetTextLowerDistance()),
+ IsVerticalWriting() );
+}
+
+void SdrTextObj::ImpAutoFitText(SdrOutliner& rOutliner, const Size& rTextSize,
+ bool bIsVerticalWriting) const
+{
+ // EditEngine formatting is unstable enough for
+ // line-breaking text that we need some more samples
+
+ // loop early-exits if we detect an already attained value
+ sal_uInt16 nMinStretchX=0, nMinStretchY=0;
+ sal_uInt16 aOldStretchXVals[]={0,0,0,0,0,0,0,0,0,0};
+ const size_t aStretchArySize=SAL_N_ELEMENTS(aOldStretchXVals);
+ for(unsigned int i=0; i<aStretchArySize; ++i)
+ {
+ const Size aCurrTextSize = rOutliner.CalcTextSizeNTP();
+ double fFactor(1.0);
+ if( bIsVerticalWriting )
+ {
+ if (aCurrTextSize.Width() != 0)
+ {
+ fFactor = double(rTextSize.Width())/aCurrTextSize.Width();
+ }
+ }
+ else if (aCurrTextSize.Height() != 0)
+ {
+ fFactor = double(rTextSize.Height())/aCurrTextSize.Height();
+ }
+ // fFactor scales in both x and y directions
+ // - this is fine for bulleted words
+ // - but it scales too much for a long paragraph
+ // - taking sqrt scales long paragraphs the best
+ // - bulleted words will have to go through more iterations
+ fFactor = std::sqrt(fFactor);
+
+ sal_uInt16 nCurrStretchX, nCurrStretchY;
+ rOutliner.GetGlobalCharStretching(nCurrStretchX, nCurrStretchY);
+
+ if (fFactor >= 1.0 )
+ {
+ // resulting text area fits into available shape rect -
+ // err on the larger stretching, to optimally fill area
+ nMinStretchX = std::max(nMinStretchX,nCurrStretchX);
+ nMinStretchY = std::max(nMinStretchY,nCurrStretchY);
+ }
+
+ aOldStretchXVals[i] = nCurrStretchX;
+ if( std::find(aOldStretchXVals, aOldStretchXVals+i, nCurrStretchX) != aOldStretchXVals+i )
+ break; // same value already attained once; algo is looping, exit
+
+ if (fFactor < 1.0 || nCurrStretchX != 100)
+ {
+ nCurrStretchX = sal::static_int_cast<sal_uInt16>(nCurrStretchX*fFactor);
+ nCurrStretchY = sal::static_int_cast<sal_uInt16>(nCurrStretchY*fFactor);
+ rOutliner.SetGlobalCharStretching(std::min(sal_uInt16(100),nCurrStretchX),
+ std::min(sal_uInt16(100),nCurrStretchY));
+ SAL_INFO("svx", "zoom is " << nCurrStretchX);
+ }
+ }
+
+ const SdrTextFitToSizeTypeItem& rItem = GetObjectItem(SDRATTR_TEXT_FITTOSIZE);
+ if (rItem.GetMaxScale() > 0)
+ {
+ nMinStretchX = std::min<sal_uInt16>(rItem.GetMaxScale(), nMinStretchX);
+ nMinStretchY = std::min<sal_uInt16>(rItem.GetMaxScale(), nMinStretchY);
+ }
+
+ SAL_INFO("svx", "final zoom is " << nMinStretchX);
+ rOutliner.SetGlobalCharStretching(std::min(sal_uInt16(100),nMinStretchX),
+ std::min(sal_uInt16(100),nMinStretchY));
+}
+
+void SdrTextObj::SetupOutlinerFormatting( SdrOutliner& rOutl, tools::Rectangle& rPaintRect ) const
+{
+ ImpInitDrawOutliner( rOutl );
+ UpdateOutlinerFormatting( rOutl, rPaintRect );
+}
+
+void SdrTextObj::UpdateOutlinerFormatting( SdrOutliner& rOutl, tools::Rectangle& rPaintRect ) const
+{
+ tools::Rectangle aTextRect;
+ tools::Rectangle aAnchorRect;
+ Fraction aFitXCorrection(1,1);
+
+ const bool bContourFrame(IsContourTextFrame());
+ const MapMode aMapMode(
+ getSdrModelFromSdrObject().GetScaleUnit(),
+ Point(0,0),
+ getSdrModelFromSdrObject().GetScaleFraction(),
+ getSdrModelFromSdrObject().GetScaleFraction());
+
+ rOutl.SetRefMapMode(aMapMode);
+ ImpSetupDrawOutlinerForPaint(
+ bContourFrame,
+ rOutl,
+ aTextRect,
+ aAnchorRect,
+ rPaintRect,
+ aFitXCorrection);
+}
+
+
+OutlinerParaObject* SdrTextObj::GetOutlinerParaObject() const
+{
+ SdrText* pText = getActiveText();
+ if( pText )
+ return pText->GetOutlinerParaObject();
+ else
+ return nullptr;
+}
+
+void SdrTextObj::NbcSetOutlinerParaObject(std::optional<OutlinerParaObject> pTextObject)
+{
+ NbcSetOutlinerParaObjectForText( std::move(pTextObject), getActiveText() );
+}
+
+namespace
+{
+ bool IsAutoGrow(const SdrTextObj& rObj)
+ {
+ bool bAutoGrow = rObj.IsAutoGrowHeight() || rObj.IsAutoGrowWidth();
+ return bAutoGrow && !utl::ConfigManager::IsFuzzing();
+ }
+}
+
+void SdrTextObj::NbcSetOutlinerParaObjectForText( std::optional<OutlinerParaObject> pTextObject, SdrText* pText )
+{
+ if( pText )
+ pText->SetOutlinerParaObject( std::move(pTextObject) );
+
+ if (pText && pText->GetOutlinerParaObject())
+ {
+ SvxWritingModeItem aWritingMode(pText->GetOutlinerParaObject()->IsEffectivelyVertical() && pText->GetOutlinerParaObject()->IsTopToBottom()
+ ? css::text::WritingMode_TB_RL
+ : css::text::WritingMode_LR_TB,
+ SDRATTR_TEXTDIRECTION);
+ GetProperties().SetObjectItemDirect(aWritingMode);
+ }
+
+ SetTextSizeDirty();
+ if (IsTextFrame() && IsAutoGrow(*this))
+ { // adapt text frame!
+ NbcAdjustTextFrameWidthAndHeight();
+ }
+ if (!IsTextFrame())
+ {
+ // the SnapRect keeps its size
+ SetBoundAndSnapRectsDirty(true);
+ }
+
+ // always invalidate BoundRect on change
+ SetBoundRectDirty();
+ ActionChanged();
+
+ ImpSetTextStyleSheetListeners();
+}
+
+void SdrTextObj::NbcReformatText()
+{
+ SdrText* pText = getActiveText();
+ if( !(pText && pText->GetOutlinerParaObject()) )
+ return;
+
+ pText->ReformatText();
+ if (mbTextFrame)
+ {
+ NbcAdjustTextFrameWidthAndHeight();
+ }
+ else
+ {
+ // the SnapRect keeps its size
+ SetBoundRectDirty();
+ SetBoundAndSnapRectsDirty(/*bNotMyself*/true);
+ }
+ SetTextSizeDirty();
+ ActionChanged();
+ // i22396
+ // Necessary here since we have no compare operator at the outliner
+ // para object which may detect changes regarding the combination
+ // of outliner para data and configuration (e.g., change of
+ // formatting of text numerals)
+ GetViewContact().flushViewObjectContacts(false);
+}
+
+std::unique_ptr<SdrObjGeoData> SdrTextObj::NewGeoData() const
+{
+ return std::make_unique<SdrTextObjGeoData>();
+}
+
+void SdrTextObj::SaveGeoData(SdrObjGeoData& rGeo) const
+{
+ SdrAttrObj::SaveGeoData(rGeo);
+ SdrTextObjGeoData& rTGeo=static_cast<SdrTextObjGeoData&>(rGeo);
+ rTGeo.maRect = maRect;
+ rTGeo.maGeo = maGeo;
+}
+
+void SdrTextObj::RestoreGeoData(const SdrObjGeoData& rGeo)
+{ // RectsDirty is called by SdrObject
+ SdrAttrObj::RestoreGeoData(rGeo);
+ const SdrTextObjGeoData& rTGeo=static_cast<const SdrTextObjGeoData&>(rGeo);
+ NbcSetLogicRect(rTGeo.maRect);
+ maGeo = rTGeo.maGeo;
+ SetTextSizeDirty();
+}
+
+drawing::TextFitToSizeType SdrTextObj::GetFitToSize() const
+{
+ drawing::TextFitToSizeType eType = drawing::TextFitToSizeType_NONE;
+
+ if(!IsAutoGrowWidth())
+ eType = GetObjectItem(SDRATTR_TEXT_FITTOSIZE).GetValue();
+
+ return eType;
+}
+
+const tools::Rectangle& SdrTextObj::GetGeoRect() const
+{
+ return maRect;
+}
+
+void SdrTextObj::ForceOutlinerParaObject()
+{
+ SdrText* pText = getActiveText();
+ if( pText && (pText->GetOutlinerParaObject() == nullptr) )
+ {
+ OutlinerMode nOutlMode = OutlinerMode::TextObject;
+ if( IsTextFrame() && meTextKind == SdrObjKind::OutlineText )
+ nOutlMode = OutlinerMode::OutlineObject;
+
+ pText->ForceOutlinerParaObject( nOutlMode );
+ }
+}
+
+TextChain *SdrTextObj::GetTextChain() const
+{
+ //if (!IsChainable())
+ // return NULL;
+
+ return getSdrModelFromSdrObject().GetTextChain();
+}
+
+bool SdrTextObj::IsVerticalWriting() const
+{
+ if(mpEditingOutliner)
+ {
+ return mpEditingOutliner->IsVertical();
+ }
+
+ OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject();
+ if(pOutlinerParaObject)
+ {
+ return pOutlinerParaObject->IsEffectivelyVertical();
+ }
+
+ return false;
+}
+
+void SdrTextObj::SetVerticalWriting(bool bVertical)
+{
+ OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject();
+
+ if( !pOutlinerParaObject && bVertical )
+ {
+ // we only need to force an outliner para object if the default of
+ // horizontal text is changed
+ ForceOutlinerParaObject();
+ pOutlinerParaObject = GetOutlinerParaObject();
+ }
+
+ if (!pOutlinerParaObject ||
+ (pOutlinerParaObject->IsEffectivelyVertical() == bVertical))
+ return;
+
+ // get item settings
+ const SfxItemSet& rSet = GetObjectItemSet();
+ bool bAutoGrowWidth = rSet.Get(SDRATTR_TEXT_AUTOGROWWIDTH).GetValue();
+ bool bAutoGrowHeight = rSet.Get(SDRATTR_TEXT_AUTOGROWHEIGHT).GetValue();
+
+ // Also exchange hor/ver adjust items
+ SdrTextHorzAdjust eHorz = rSet.Get(SDRATTR_TEXT_HORZADJUST).GetValue();
+ SdrTextVertAdjust eVert = rSet.Get(SDRATTR_TEXT_VERTADJUST).GetValue();
+
+ // rescue object size
+ tools::Rectangle aObjectRect = GetSnapRect();
+
+ // prepare ItemSet to set exchanged width and height items
+ SfxItemSetFixed<SDRATTR_TEXT_AUTOGROWHEIGHT, SDRATTR_TEXT_AUTOGROWHEIGHT,
+ // Expanded item ranges to also support hor and ver adjust.
+ SDRATTR_TEXT_VERTADJUST, SDRATTR_TEXT_VERTADJUST,
+ SDRATTR_TEXT_AUTOGROWWIDTH, SDRATTR_TEXT_HORZADJUST> aNewSet(*rSet.GetPool());
+
+ aNewSet.Put(rSet);
+ aNewSet.Put(makeSdrTextAutoGrowWidthItem(bAutoGrowHeight));
+ aNewSet.Put(makeSdrTextAutoGrowHeightItem(bAutoGrowWidth));
+
+ // Exchange horz and vert adjusts
+ switch (eVert)
+ {
+ case SDRTEXTVERTADJUST_TOP: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_RIGHT)); break;
+ case SDRTEXTVERTADJUST_CENTER: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_CENTER)); break;
+ case SDRTEXTVERTADJUST_BOTTOM: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_LEFT)); break;
+ case SDRTEXTVERTADJUST_BLOCK: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_BLOCK)); break;
+ }
+ switch (eHorz)
+ {
+ case SDRTEXTHORZADJUST_LEFT: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_BOTTOM)); break;
+ case SDRTEXTHORZADJUST_CENTER: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_CENTER)); break;
+ case SDRTEXTHORZADJUST_RIGHT: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_TOP)); break;
+ case SDRTEXTHORZADJUST_BLOCK: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_BLOCK)); break;
+ }
+
+ SetObjectItemSet(aNewSet);
+
+ pOutlinerParaObject = GetOutlinerParaObject();
+ if (pOutlinerParaObject)
+ {
+ // set ParaObject orientation accordingly
+ pOutlinerParaObject->SetVertical(bVertical);
+ }
+
+ // restore object size
+ SetSnapRect(aObjectRect);
+}
+
+bool SdrTextObj::IsTopToBottom() const
+{
+ if (mpEditingOutliner)
+ return mpEditingOutliner->IsTopToBottom();
+
+ if (OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject())
+ return pOutlinerParaObject->IsTopToBottom();
+
+ return false;
+}
+
+// transformation interface for StarOfficeAPI. This implements support for
+// homogeneous 3x3 matrices containing the transformation of the SdrObject. At the
+// moment it contains a shearX, rotation and translation, but for setting all linear
+// transforms like Scale, ShearX, ShearY, Rotate and Translate are supported.
+
+
+// gets base transformation and rectangle of object. If it's an SdrPathObj it fills the PolyPolygon
+// with the base geometry and returns TRUE. Otherwise it returns FALSE.
+bool SdrTextObj::TRGetBaseGeometry(basegfx::B2DHomMatrix& rMatrix, basegfx::B2DPolyPolygon& /*rPolyPolygon*/) const
+{
+ // get turn and shear
+ double fRotate = toRadians(maGeo.nRotationAngle);
+ double fShearX = toRadians(maGeo.nShearAngle);
+
+ // get aRect, this is the unrotated snaprect
+ tools::Rectangle aRectangle(maRect);
+
+ // fill other values
+ basegfx::B2DTuple aScale(aRectangle.GetWidth(), aRectangle.GetHeight());
+ basegfx::B2DTuple aTranslate(aRectangle.Left(), aRectangle.Top());
+
+ // position maybe relative to anchorpos, convert
+ if( getSdrModelFromSdrObject().IsWriter() )
+ {
+ if(GetAnchorPos().X() || GetAnchorPos().Y())
+ {
+ aTranslate -= basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
+ }
+ }
+
+ // build matrix
+ rMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ aScale,
+ basegfx::fTools::equalZero(fShearX) ? 0.0 : tan(fShearX),
+ basegfx::fTools::equalZero(fRotate) ? 0.0 : -fRotate,
+ aTranslate);
+
+ return false;
+}
+
+// sets the base geometry of the object using infos contained in the homogeneous 3x3 matrix.
+// If it's an SdrPathObj it will use the provided geometry information. The Polygon has
+// to use (0,0) as upper left and will be scaled to the given size in the matrix.
+void SdrTextObj::TRSetBaseGeometry(const basegfx::B2DHomMatrix& rMatrix, const basegfx::B2DPolyPolygon& /*rPolyPolygon*/)
+{
+ // break up matrix
+ basegfx::B2DTuple aScale;
+ basegfx::B2DTuple aTranslate;
+ double fRotate(0.0);
+ double fShearX(0.0);
+ rMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // flip?
+ bool bFlipX = aScale.getX() < 0.0,
+ bFlipY = aScale.getY() < 0.0;
+ if (bFlipX)
+ {
+ aScale.setX(fabs(aScale.getX()));
+ }
+ if (bFlipY)
+ {
+ aScale.setY(fabs(aScale.getY()));
+ }
+
+ // reset object shear and rotations
+ maGeo.nRotationAngle = 0_deg100;
+ maGeo.RecalcSinCos();
+ maGeo.nShearAngle = 0_deg100;
+ maGeo.RecalcTan();
+
+ // if anchor is used, make position relative to it
+ if( getSdrModelFromSdrObject().IsWriter() )
+ {
+ if(GetAnchorPos().X() || GetAnchorPos().Y())
+ {
+ aTranslate += basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
+ }
+ }
+
+ // build and set BaseRect (use scale)
+ Size aSize(FRound(aScale.getX()), FRound(aScale.getY()));
+ tools::Rectangle aBaseRect(Point(), aSize);
+ SetSnapRect(aBaseRect);
+
+ // flip?
+ if (bFlipX)
+ {
+ Mirror(Point(), Point(0, 1));
+ }
+ if (bFlipY)
+ {
+ Mirror(Point(), Point(1, 0));
+ }
+
+ // shear?
+ if(!basegfx::fTools::equalZero(fShearX))
+ {
+ GeoStat aGeoStat;
+ aGeoStat.nShearAngle = Degree100(FRound(basegfx::rad2deg<100>(atan(fShearX))));
+ aGeoStat.RecalcTan();
+ Shear(Point(), aGeoStat.nShearAngle, aGeoStat.mfTanShearAngle, false);
+ }
+
+ // rotation?
+ if(!basegfx::fTools::equalZero(fRotate))
+ {
+ GeoStat aGeoStat;
+
+ // #i78696#
+ // fRotate is matematically correct, but aGeoStat.nRotationAngle is
+ // mirrored -> mirror value here
+ aGeoStat.nRotationAngle = NormAngle36000(Degree100(FRound(-basegfx::rad2deg<100>(fRotate))));
+ aGeoStat.RecalcSinCos();
+ Rotate(Point(), aGeoStat.nRotationAngle, aGeoStat.mfSinRotationAngle, aGeoStat.mfCosRotationAngle);
+ }
+
+ // translate?
+ if(!aTranslate.equalZero())
+ {
+ Move(Size(FRound(aTranslate.getX()), FRound(aTranslate.getY())));
+ }
+}
+
+bool SdrTextObj::IsReallyEdited() const
+{
+ return mpEditingOutliner && mpEditingOutliner->IsModified();
+}
+
+// moved inlines here form hxx
+
+tools::Long SdrTextObj::GetEckenradius() const
+{
+ return GetObjectItemSet().Get(SDRATTR_CORNER_RADIUS).GetValue();
+}
+
+tools::Long SdrTextObj::GetMinTextFrameHeight() const
+{
+ return GetObjectItemSet().Get(SDRATTR_TEXT_MINFRAMEHEIGHT).GetValue();
+}
+
+tools::Long SdrTextObj::GetMaxTextFrameHeight() const
+{
+ return GetObjectItemSet().Get(SDRATTR_TEXT_MAXFRAMEHEIGHT).GetValue();
+}
+
+tools::Long SdrTextObj::GetMinTextFrameWidth() const
+{
+ return GetObjectItemSet().Get(SDRATTR_TEXT_MINFRAMEWIDTH).GetValue();
+}
+
+tools::Long SdrTextObj::GetMaxTextFrameWidth() const
+{
+ return GetObjectItemSet().Get(SDRATTR_TEXT_MAXFRAMEWIDTH).GetValue();
+}
+
+bool SdrTextObj::IsFontwork() const
+{
+ return !mbTextFrame // Default is FALSE
+ && GetObjectItemSet().Get(XATTR_FORMTXTSTYLE).GetValue() != XFormTextStyle::NONE;
+}
+
+bool SdrTextObj::IsHideContour() const
+{
+ return !mbTextFrame // Default is: no, don't HideContour; HideContour not together with TextFrames
+ && GetObjectItemSet().Get(XATTR_FORMTXTHIDEFORM).GetValue();
+}
+
+bool SdrTextObj::IsContourTextFrame() const
+{
+ return !mbTextFrame // ContourFrame not together with normal TextFrames
+ && GetObjectItemSet().Get(SDRATTR_TEXT_CONTOURFRAME).GetValue();
+}
+
+tools::Long SdrTextObj::GetTextLeftDistance() const
+{
+ return GetObjectItemSet().Get(SDRATTR_TEXT_LEFTDIST).GetValue();
+}
+
+tools::Long SdrTextObj::GetTextRightDistance() const
+{
+ return GetObjectItemSet().Get(SDRATTR_TEXT_RIGHTDIST).GetValue();
+}
+
+tools::Long SdrTextObj::GetTextUpperDistance() const
+{
+ return GetObjectItemSet().Get(SDRATTR_TEXT_UPPERDIST).GetValue();
+}
+
+tools::Long SdrTextObj::GetTextLowerDistance() const
+{
+ return GetObjectItemSet().Get(SDRATTR_TEXT_LOWERDIST).GetValue();
+}
+
+SdrTextAniKind SdrTextObj::GetTextAniKind() const
+{
+ return GetObjectItemSet().Get(SDRATTR_TEXT_ANIKIND).GetValue();
+}
+
+SdrTextAniDirection SdrTextObj::GetTextAniDirection() const
+{
+ return GetObjectItemSet().Get(SDRATTR_TEXT_ANIDIRECTION).GetValue();
+}
+
+bool SdrTextObj::HasTextColumnsNumber() const
+{
+ return GetObjectItemSet().HasItem(SDRATTR_TEXTCOLUMNS_NUMBER);
+}
+
+sal_Int16 SdrTextObj::GetTextColumnsNumber() const
+{
+ return GetObjectItemSet().Get(SDRATTR_TEXTCOLUMNS_NUMBER).GetValue();
+}
+
+void SdrTextObj::SetTextColumnsNumber(sal_Int16 nColumns)
+{
+ SetObjectItem(SfxInt16Item(SDRATTR_TEXTCOLUMNS_NUMBER, nColumns));
+}
+
+bool SdrTextObj::HasTextColumnsSpacing() const
+{
+ return GetObjectItemSet().HasItem(SDRATTR_TEXTCOLUMNS_SPACING);
+}
+
+sal_Int32 SdrTextObj::GetTextColumnsSpacing() const
+{
+ return GetObjectItemSet().Get(SDRATTR_TEXTCOLUMNS_SPACING).GetValue();
+}
+
+void SdrTextObj::SetTextColumnsSpacing(sal_Int32 nSpacing)
+{
+ SetObjectItem(SdrMetricItem(SDRATTR_TEXTCOLUMNS_SPACING, nSpacing));
+}
+
+// Get necessary data for text scroll animation. ATM base it on a Text-Metafile and a
+// painting rectangle. Rotation is excluded from the returned values.
+GDIMetaFile* SdrTextObj::GetTextScrollMetaFileAndRectangle(
+ tools::Rectangle& rScrollRectangle, tools::Rectangle& rPaintRectangle)
+{
+ GDIMetaFile* pRetval = nullptr;
+ SdrOutliner& rOutliner = ImpGetDrawOutliner();
+ tools::Rectangle aTextRect;
+ tools::Rectangle aAnchorRect;
+ tools::Rectangle aPaintRect;
+ Fraction aFitXCorrection(1,1);
+ bool bContourFrame(IsContourTextFrame());
+
+ // get outliner set up. To avoid getting a somehow rotated MetaFile,
+ // temporarily disable object rotation.
+ Degree100 nAngle(maGeo.nRotationAngle);
+ maGeo.nRotationAngle = 0_deg100;
+ ImpSetupDrawOutlinerForPaint( bContourFrame, rOutliner, aTextRect, aAnchorRect, aPaintRect, aFitXCorrection );
+ maGeo.nRotationAngle = nAngle;
+
+ tools::Rectangle aScrollFrameRect(aPaintRect);
+ const SfxItemSet& rSet = GetObjectItemSet();
+ SdrTextAniDirection eDirection = rSet.Get(SDRATTR_TEXT_ANIDIRECTION).GetValue();
+
+ if(SdrTextAniDirection::Left == eDirection || SdrTextAniDirection::Right == eDirection)
+ {
+ aScrollFrameRect.SetLeft( aAnchorRect.Left() );
+ aScrollFrameRect.SetRight( aAnchorRect.Right() );
+ }
+
+ if(SdrTextAniDirection::Up == eDirection || SdrTextAniDirection::Down == eDirection)
+ {
+ aScrollFrameRect.SetTop( aAnchorRect.Top() );
+ aScrollFrameRect.SetBottom( aAnchorRect.Bottom() );
+ }
+
+ // create the MetaFile
+ pRetval = new GDIMetaFile;
+ ScopedVclPtrInstance< VirtualDevice > pBlackHole;
+ pBlackHole->EnableOutput(false);
+ pRetval->Record(pBlackHole);
+ Point aPaintPos = aPaintRect.TopLeft();
+
+ rOutliner.Draw(*pBlackHole, aPaintPos);
+
+ pRetval->Stop();
+ pRetval->WindStart();
+
+ // return PaintRectanglePixel and pRetval;
+ rScrollRectangle = aScrollFrameRect;
+ rPaintRectangle = aPaintRect;
+
+ return pRetval;
+}
+
+// Access to TextAnimationAllowed flag
+bool SdrTextObj::IsAutoFit() const
+{
+ return GetFitToSize() == drawing::TextFitToSizeType_AUTOFIT;
+}
+
+bool SdrTextObj::IsFitToSize() const
+{
+ const drawing::TextFitToSizeType eFit = GetFitToSize();
+ return (eFit == drawing::TextFitToSizeType_PROPORTIONAL
+ || eFit == drawing::TextFitToSizeType_ALLLINES);
+}
+
+void SdrTextObj::SetTextAnimationAllowed(bool bNew)
+{
+ if(mbTextAnimationAllowed != bNew)
+ {
+ mbTextAnimationAllowed = bNew;
+ ActionChanged();
+ }
+}
+
+/** called from the SdrObjEditView during text edit when the status of the edit outliner changes */
+void SdrTextObj::onEditOutlinerStatusEvent( EditStatus* pEditStatus )
+{
+ const EditStatusFlags nStat = pEditStatus->GetStatusWord();
+ const bool bGrowX = bool(nStat & EditStatusFlags::TEXTWIDTHCHANGED);
+ const bool bGrowY = bool(nStat & EditStatusFlags::TextHeightChanged);
+ if(!(mbTextFrame && (bGrowX || bGrowY)))
+ return;
+
+ if ((bGrowX && IsAutoGrowWidth()) || (bGrowY && IsAutoGrowHeight()))
+ {
+ AdjustTextFrameWidthAndHeight();
+ }
+ else if ( (IsAutoFit() || IsFitToSize()) && !mbInDownScale)
+ {
+ assert(mpEditingOutliner);
+ mbInDownScale = true;
+
+ // sucks that we cannot disable paints via
+ // mpEditingOutliner->SetUpdateMode(FALSE) - but EditEngine skips
+ // formatting as well, then.
+ ImpAutoFitText(*mpEditingOutliner);
+ mbInDownScale = false;
+ }
+}
+
+/* Begin chaining code */
+
+// XXX: Make it a method somewhere?
+static SdrObject *ImpGetObjByName(SdrObjList const *pObjList, std::u16string_view aObjName)
+{
+ // scan the whole list
+ size_t nObjCount = pObjList->GetObjCount();
+ for (size_t i = 0; i < nObjCount; i++) {
+ SdrObject *pCurObj = pObjList->GetObj(i);
+
+ if (pCurObj->GetName() == aObjName) {
+ return pCurObj;
+ }
+ }
+ // not found
+ return nullptr;
+}
+
+// XXX: Make it a (private) method of SdrTextObj
+static void ImpUpdateChainLinks(SdrTextObj *pTextObj, std::u16string_view aNextLinkName)
+{
+ // XXX: Current implementation constraints text boxes to be on the same page
+
+ // No next link
+ if (aNextLinkName.empty()) {
+ pTextObj->SetNextLinkInChain(nullptr);
+ return;
+ }
+
+ SdrPage *pPage(pTextObj->getSdrPageFromSdrObject());
+ assert(pPage);
+ SdrTextObj *pNextTextObj = dynamic_cast< SdrTextObj * >
+ (ImpGetObjByName(pPage, aNextLinkName));
+ if (!pNextTextObj) {
+ SAL_INFO("svx.chaining", "[CHAINING] Can't find object as next link.");
+ return;
+ }
+
+ pTextObj->SetNextLinkInChain(pNextTextObj);
+}
+
+bool SdrTextObj::IsChainable() const
+{
+ // Read it as item
+ const SfxItemSet& rSet = GetObjectItemSet();
+ OUString aNextLinkName = rSet.Get(SDRATTR_TEXT_CHAINNEXTNAME).GetValue();
+
+ // Update links if any inconsistency is found
+ bool bNextLinkUnsetYet = !aNextLinkName.isEmpty() && !mpNextInChain;
+ bool bInconsistentNextLink = mpNextInChain && mpNextInChain->GetName() != aNextLinkName;
+ // if the link is not set despite there should be one OR if it has changed
+ if (bNextLinkUnsetYet || bInconsistentNextLink) {
+ ImpUpdateChainLinks(const_cast<SdrTextObj *>(this), aNextLinkName);
+ }
+
+ return !aNextLinkName.isEmpty(); // XXX: Should we also check for GetNilChainingEvent? (see old code below)
+
+/*
+ // Check that no overflow is going on
+ if (!GetTextChain() || GetTextChain()->GetNilChainingEvent(this))
+ return false;
+*/
+}
+
+void SdrTextObj::onChainingEvent()
+{
+ if (!mpEditingOutliner)
+ return;
+
+ // Outliner for text transfer
+ SdrOutliner &aDrawOutliner = ImpGetDrawOutliner();
+
+ EditingTextChainFlow aTxtChainFlow(this);
+ aTxtChainFlow.CheckForFlowEvents(mpEditingOutliner);
+
+ if (aTxtChainFlow.IsOverflow()) {
+ SAL_INFO("svx.chaining", "[CHAINING] Overflow going on");
+ // One outliner is for non-overflowing text, the other for overflowing text
+ // We remove text directly from the editing outliner
+ aTxtChainFlow.ExecuteOverflow(mpEditingOutliner, &aDrawOutliner);
+ } else if (aTxtChainFlow.IsUnderflow()) {
+ SAL_INFO("svx.chaining", "[CHAINING] Underflow going on");
+ // underflow-induced overflow
+ aTxtChainFlow.ExecuteUnderflow(&aDrawOutliner);
+ bool bIsOverflowFromUnderflow = aTxtChainFlow.IsOverflow();
+ // handle overflow
+ if (bIsOverflowFromUnderflow) {
+ SAL_INFO("svx.chaining", "[CHAINING] Overflow going on (underflow induced)");
+ // prevents infinite loops when setting text for editing outliner
+ aTxtChainFlow.ExecuteOverflow(&aDrawOutliner, &aDrawOutliner);
+ }
+ }
+}
+
+SdrTextObj* SdrTextObj::GetNextLinkInChain() const
+{
+ /*
+ if (GetTextChain())
+ return GetTextChain()->GetNextLink(this);
+
+ return NULL;
+ */
+
+ return mpNextInChain;
+}
+
+void SdrTextObj::SetNextLinkInChain(SdrTextObj *pNextObj)
+{
+ // Basically a doubly linked list implementation
+
+ SdrTextObj *pOldNextObj = mpNextInChain;
+
+ // Replace next link
+ mpNextInChain = pNextObj;
+ // Deal with old next link's prev link
+ if (pOldNextObj) {
+ pOldNextObj->mpPrevInChain = nullptr;
+ }
+
+ // Deal with new next link's prev link
+ if (mpNextInChain) {
+ // If there is a prev already at all and this is not already the current object
+ if (mpNextInChain->mpPrevInChain &&
+ mpNextInChain->mpPrevInChain != this)
+ mpNextInChain->mpPrevInChain->mpNextInChain = nullptr;
+ mpNextInChain->mpPrevInChain = this;
+ }
+
+ // TODO: Introduce check for circular chains
+
+}
+
+SdrTextObj* SdrTextObj::GetPrevLinkInChain() const
+{
+ /*
+ if (GetTextChain())
+ return GetTextChain()->GetPrevLink(this);
+
+ return NULL;
+ */
+
+ return mpPrevInChain;
+}
+
+bool SdrTextObj::GetPreventChainable() const
+{
+ // Prevent chaining it 1) during dragging && 2) when we are editing next link
+ return mbIsUnchainableClone || (GetNextLinkInChain() && GetNextLinkInChain()->IsInEditMode());
+}
+
+SdrObjectUniquePtr SdrTextObj::getFullDragClone() const
+{
+ SdrObjectUniquePtr pClone = SdrAttrObj::getFullDragClone();
+ SdrTextObj *pTextObjClone = dynamic_cast<SdrTextObj *>(pClone.get());
+ if (pTextObjClone != nullptr) {
+ // Avoid transferring of text for chainable object during dragging
+ pTextObjClone->mbIsUnchainableClone = true;
+ }
+
+ return pClone;
+ }
+
+/* End chaining code */
+
+/** returns the currently active text. */
+SdrText* SdrTextObj::getActiveText() const
+{
+ if( !mpText )
+ return getText( 0 );
+ else
+ return mpText.get();
+}
+
+/** returns the nth available text. */
+SdrText* SdrTextObj::getText( sal_Int32 nIndex ) const
+{
+ if( nIndex == 0 )
+ {
+ if( !mpText )
+ const_cast< SdrTextObj* >(this)->mpText.reset( new SdrText( *const_cast< SdrTextObj* >(this) ) );
+ return mpText.get();
+ }
+ else
+ {
+ return nullptr;
+ }
+}
+
+/** returns the number of texts available for this object. */
+sal_Int32 SdrTextObj::getTextCount() const
+{
+ return 1;
+}
+
+/** changes the current active text */
+void SdrTextObj::setActiveText( sal_Int32 /*nIndex*/ )
+{
+}
+
+/** returns the index of the text that contains the given point or -1 */
+sal_Int32 SdrTextObj::CheckTextHit(const Point& /*rPnt*/) const
+{
+ return 0;
+}
+
+void SdrTextObj::SetObjectItemNoBroadcast(const SfxPoolItem& rItem)
+{
+ static_cast< sdr::properties::TextProperties& >(GetProperties()).SetObjectItemNoBroadcast(rItem);
+}
+
+
+// The concept of the text object:
+// ~~~~~~~~~~~~~~~~~~~~~~~~
+// Attributes/Variations:
+// - bool text frame / graphics object with caption
+// - bool FontWork (if it is not a text frame and not a ContourTextFrame)
+// - bool ContourTextFrame (if it is not a text frame and not Fontwork)
+// - long rotation angle (if it is not FontWork)
+// - long text frame margins (if it is not FontWork)
+// - bool FitToSize (if it is not FontWork)
+// - bool AutoGrowingWidth/Height (if it is not FitToSize and not FontWork)
+// - long Min/MaxFrameWidth/Height (if AutoGrowingWidth/Height)
+// - enum horizontal text anchoring left,center,right,justify/block,Stretch(ni)
+// - enum vertical text anchoring top, middle, bottom, block, stretch(ni)
+// - enum ticker text (if it is not FontWork)
+
+// Every derived object is either a text frame (mbTextFrame=true)
+// or a drawing object with a caption (mbTextFrame=false).
+
+// Default anchoring for text frames:
+// SDRTEXTHORZADJUST_BLOCK, SDRTEXTVERTADJUST_TOP
+// = static Pool defaults
+// Default anchoring for drawing objects with a caption:
+// SDRTEXTHORZADJUST_CENTER, SDRTEXTVERTADJUST_CENTER
+// via "hard" attribution of SdrAttrObj
+
+// Every object derived from SdrTextObj must return an "UnrotatedSnapRect"
+// (->TakeUnrotatedSnapRect()) (the reference point for the rotation is the top
+// left of the rectangle (maGeo.nRotationAngle)) which is the basis for anchoring
+// text. We then subtract the text frame margins from this rectangle, as a re-
+// sult we get the anchoring area (->TakeTextAnchorRect()). Within this area, we
+// calculate the anchoring point and the painting area, depending on the hori-
+// zontal and vertical adjustment of the text (SdrTextVertAdjust,
+// SdrTextHorzAdjust).
+// In the case of drawing objects with a caption the painting area might well
+// be larger than the anchoring area, for text frames on the other hand, it is
+// always of the same or a smaller size (except when there are negative text
+// frame margins).
+
+// FitToSize takes priority over text anchoring and AutoGrowHeight/Width. When
+// FitToSize is turned on, the painting area is always equal to the anchoring
+// area. Additionally, FitToSize doesn't allow automatic line breaks.
+
+// ContourTextFrame:
+// - long rotation angle
+// - long text frame margins (maybe later)
+// - bool FitToSize (maybe later)
+// - bool AutoGrowingWidth/Height (maybe much later)
+// - long Min/MaxFrameWidth/Height (maybe much later)
+// - enum horizontal text anchoring (maybe later, for now: left, centered)
+// - enum vertical text anchoring (maybe later, for now: top)
+// - enum ticker text (maybe later, maybe even with correct clipping)
+
+// When making changes, check these:
+// - Paint
+// - HitTest
+// - ConvertToPoly
+// - Edit
+// - Printing, Saving, Painting in neighboring View while editing
+// - ModelChanged (e. g. through a neighboring View or rulers) while editing
+// - FillColorChanged while editing
+// - and many more...
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdotextdecomposition.cxx b/svx/source/svdraw/svdotextdecomposition.cxx
new file mode 100644
index 000000000..522375b1f
--- /dev/null
+++ b/svx/source/svdraw/svdotextdecomposition.cxx
@@ -0,0 +1,1698 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <svx/svdetc.hxx>
+#include <svx/svdoutl.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdotext.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/sdasitm.hxx>
+#include <textchain.hxx>
+#include <textchainflow.hxx>
+#include <svx/sdtacitm.hxx>
+#include <svx/sdtayitm.hxx>
+#include <svx/sdtaiitm.hxx>
+#include <svx/sdtaaitm.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xbtmpit.hxx>
+#include <basegfx/vector/b2dvector.hxx>
+#include <sdr/primitive2d/sdrtextprimitive2d.hxx>
+#include <drawinglayer/primitive2d/textprimitive2d.hxx>
+#include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/editstat.hxx>
+#include <tools/helpers.hxx>
+#include <svl/itemset.hxx>
+#include <drawinglayer/animation/animationtiming.hxx>
+#include <basegfx/color/bcolor.hxx>
+#include <vcl/svapp.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/svxenum.hxx>
+#include <editeng/flditem.hxx>
+#include <editeng/adjustitem.hxx>
+#include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx>
+#include <drawinglayer/primitive2d/wrongspellprimitive2d.hxx>
+#include <drawinglayer/primitive2d/graphicprimitive2d.hxx>
+#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
+#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
+#include <svx/unoapi.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <editeng/outlobj.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+using namespace com::sun::star;
+
+// helpers
+
+namespace
+{
+ class impTextBreakupHandler
+ {
+ private:
+ drawinglayer::primitive2d::Primitive2DContainer maTextPortionPrimitives;
+ drawinglayer::primitive2d::Primitive2DContainer maLinePrimitives;
+ drawinglayer::primitive2d::Primitive2DContainer maParagraphPrimitives;
+
+ SdrOutliner& mrOutliner;
+ basegfx::B2DHomMatrix maNewTransformA;
+ basegfx::B2DHomMatrix maNewTransformB;
+
+ // the visible area for contour text decomposition
+ basegfx::B2DVector maScale;
+
+ // ClipRange for BlockText decomposition; only text portions completely
+ // inside are to be accepted, so this is different from geometric clipping
+ // (which would allow e.g. upper parts of portions to remain). Only used for
+ // BlockText (see there)
+ basegfx::B2DRange maClipRange;
+
+ DECL_LINK(decomposeContourTextPrimitive, DrawPortionInfo*, void);
+ DECL_LINK(decomposeBlockTextPrimitive, DrawPortionInfo*, void);
+ DECL_LINK(decomposeStretchTextPrimitive, DrawPortionInfo*, void);
+
+ DECL_LINK(decomposeContourBulletPrimitive, DrawBulletInfo*, void);
+ DECL_LINK(decomposeBlockBulletPrimitive, DrawBulletInfo*, void);
+ DECL_LINK(decomposeStretchBulletPrimitive, DrawBulletInfo*, void);
+
+ void impCreateTextPortionPrimitive(const DrawPortionInfo& rInfo);
+ static rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> impCheckFieldPrimitive(drawinglayer::primitive2d::BasePrimitive2D* pPrimitive, const DrawPortionInfo& rInfo);
+ void impFlushTextPortionPrimitivesToLinePrimitives();
+ void impFlushLinePrimitivesToParagraphPrimitives(sal_Int32 nPara);
+ void impHandleDrawPortionInfo(const DrawPortionInfo& rInfo);
+ void impHandleDrawBulletInfo(const DrawBulletInfo& rInfo);
+
+ public:
+ explicit impTextBreakupHandler(SdrOutliner& rOutliner)
+ : mrOutliner(rOutliner)
+ {
+ }
+
+ void decomposeContourTextPrimitive(const basegfx::B2DHomMatrix& rNewTransformA, const basegfx::B2DHomMatrix& rNewTransformB, const basegfx::B2DVector& rScale)
+ {
+ maScale = rScale;
+ maNewTransformA = rNewTransformA;
+ maNewTransformB = rNewTransformB;
+ mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, decomposeContourTextPrimitive));
+ mrOutliner.SetDrawBulletHdl(LINK(this, impTextBreakupHandler, decomposeContourBulletPrimitive));
+ mrOutliner.StripPortions();
+ mrOutliner.SetDrawPortionHdl(Link<DrawPortionInfo*,void>());
+ mrOutliner.SetDrawBulletHdl(Link<DrawBulletInfo*,void>());
+ }
+
+ void decomposeBlockTextPrimitive(
+ const basegfx::B2DHomMatrix& rNewTransformA,
+ const basegfx::B2DHomMatrix& rNewTransformB,
+ const basegfx::B2DRange& rClipRange)
+ {
+ maNewTransformA = rNewTransformA;
+ maNewTransformB = rNewTransformB;
+ maClipRange = rClipRange;
+ mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, decomposeBlockTextPrimitive));
+ mrOutliner.SetDrawBulletHdl(LINK(this, impTextBreakupHandler, decomposeBlockBulletPrimitive));
+ mrOutliner.StripPortions();
+ mrOutliner.SetDrawPortionHdl(Link<DrawPortionInfo*,void>());
+ mrOutliner.SetDrawBulletHdl(Link<DrawBulletInfo*,void>());
+ }
+
+ void decomposeStretchTextPrimitive(const basegfx::B2DHomMatrix& rNewTransformA, const basegfx::B2DHomMatrix& rNewTransformB)
+ {
+ maNewTransformA = rNewTransformA;
+ maNewTransformB = rNewTransformB;
+ mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, decomposeStretchTextPrimitive));
+ mrOutliner.SetDrawBulletHdl(LINK(this, impTextBreakupHandler, decomposeStretchBulletPrimitive));
+ mrOutliner.StripPortions();
+ mrOutliner.SetDrawPortionHdl(Link<DrawPortionInfo*,void>());
+ mrOutliner.SetDrawBulletHdl(Link<DrawBulletInfo*,void>());
+ }
+
+ drawinglayer::primitive2d::Primitive2DContainer extractPrimitive2DSequence();
+ };
+
+ void impTextBreakupHandler::impCreateTextPortionPrimitive(const DrawPortionInfo& rInfo)
+ {
+ if(rInfo.maText.isEmpty() || !rInfo.mnTextLen)
+ return;
+
+ OUString caseMappedText = rInfo.mrFont.CalcCaseMap( rInfo.maText );
+ basegfx::B2DVector aFontScaling;
+ drawinglayer::attribute::FontAttribute aFontAttribute(
+ drawinglayer::primitive2d::getFontAttributeFromVclFont(
+ aFontScaling,
+ rInfo.mrFont,
+ rInfo.IsRTL(),
+ false));
+ basegfx::B2DHomMatrix aNewTransform;
+
+ // add font scale to new transform
+ aNewTransform.scale(aFontScaling.getX(), aFontScaling.getY());
+
+ // look for proportional font scaling, if necessary, scale accordingly
+ sal_Int8 nPropr(rInfo.mrFont.GetPropr());
+ if(100 != nPropr)
+ {
+ const double fFactor(rInfo.mrFont.GetPropr() / 100.0);
+ aNewTransform.scale(fFactor, fFactor);
+ }
+
+ // apply font rotate
+ if(rInfo.mrFont.GetOrientation())
+ {
+ aNewTransform.rotate(-toRadians(rInfo.mrFont.GetOrientation()));
+ }
+
+ // look for escapement, if necessary, translate accordingly
+ if(rInfo.mrFont.GetEscapement())
+ {
+ sal_Int16 nEsc(rInfo.mrFont.GetEscapement());
+
+ if(DFLT_ESC_AUTO_SUPER == nEsc)
+ {
+ nEsc = .8 * (100 - nPropr);
+ assert (nEsc == DFLT_ESC_SUPER && "I'm sure this formula needs to be changed, but how to confirm that???");
+ nEsc = DFLT_ESC_SUPER;
+ }
+ else if(DFLT_ESC_AUTO_SUB == nEsc)
+ {
+ nEsc = .2 * -(100 - nPropr);
+ assert (nEsc == -20 && "I'm sure this formula needs to be changed, but how to confirm that???");
+ nEsc = -20;
+ }
+
+ if(nEsc > MAX_ESC_POS)
+ {
+ nEsc = MAX_ESC_POS;
+ }
+ else if(nEsc < -MAX_ESC_POS)
+ {
+ nEsc = -MAX_ESC_POS;
+ }
+
+ const double fEscapement(nEsc / -100.0);
+ aNewTransform.translate(0.0, fEscapement * aFontScaling.getY());
+ }
+
+ // apply transformA
+ aNewTransform *= maNewTransformA;
+
+ // apply local offset
+ aNewTransform.translate(rInfo.mrStartPos.X(), rInfo.mrStartPos.Y());
+
+ // also apply embedding object's transform
+ aNewTransform *= maNewTransformB;
+
+ // prepare DXArray content. To make it independent from font size (and such from
+ // the text transformation), scale it to unit coordinates
+ ::std::vector< double > aDXArray;
+
+ if(!rInfo.mpDXArray.empty() && rInfo.mnTextLen)
+ {
+ aDXArray.reserve(rInfo.mnTextLen);
+
+ for(sal_Int32 a=0; a < rInfo.mnTextLen; a++)
+ {
+ aDXArray.push_back(static_cast<double>(rInfo.mpDXArray[a]));
+ }
+ }
+
+ // create complex text primitive and append
+ const Color aFontColor(rInfo.mrFont.GetColor());
+ const basegfx::BColor aBFontColor(aFontColor.getBColor());
+
+ const Color aTextFillColor(rInfo.mrFont.GetFillColor());
+
+ // prepare wordLineMode (for underline and strikeout)
+ // NOT for bullet texts. It is set (this may be an error by itself), but needs to be suppressed to hinder e.g. '1)'
+ // to be split which would not look like the original
+ const bool bWordLineMode(rInfo.mrFont.IsWordLineMode() && !rInfo.mbEndOfBullet);
+
+ // prepare new primitive
+ rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pNewPrimitive;
+ const bool bDecoratedIsNeeded(
+ LINESTYLE_NONE != rInfo.mrFont.GetOverline()
+ || LINESTYLE_NONE != rInfo.mrFont.GetUnderline()
+ || STRIKEOUT_NONE != rInfo.mrFont.GetStrikeout()
+ || FontEmphasisMark::NONE != (rInfo.mrFont.GetEmphasisMark() & FontEmphasisMark::Style)
+ || FontRelief::NONE != rInfo.mrFont.GetRelief()
+ || rInfo.mrFont.IsShadow()
+ || bWordLineMode);
+
+ if(bDecoratedIsNeeded)
+ {
+ // TextDecoratedPortionPrimitive2D needed, prepare some more data
+ // get overline and underline color. If it's on automatic (0xffffffff) use FontColor instead
+ const Color aUnderlineColor(rInfo.maTextLineColor);
+ const basegfx::BColor aBUnderlineColor((aUnderlineColor == COL_AUTO) ? aBFontColor : aUnderlineColor.getBColor());
+ const Color aOverlineColor(rInfo.maOverlineColor);
+ const basegfx::BColor aBOverlineColor((aOverlineColor == COL_AUTO) ? aBFontColor : aOverlineColor.getBColor());
+
+ // prepare overline and underline data
+ const drawinglayer::primitive2d::TextLine eFontOverline(
+ drawinglayer::primitive2d::mapFontLineStyleToTextLine(rInfo.mrFont.GetOverline()));
+ const drawinglayer::primitive2d::TextLine eFontLineStyle(
+ drawinglayer::primitive2d::mapFontLineStyleToTextLine(rInfo.mrFont.GetUnderline()));
+
+ // check UnderlineAbove
+ const bool bUnderlineAbove(
+ drawinglayer::primitive2d::TEXT_LINE_NONE != eFontLineStyle && rInfo.mrFont.IsUnderlineAbove());
+
+ // prepare strikeout data
+ const drawinglayer::primitive2d::TextStrikeout eTextStrikeout(
+ drawinglayer::primitive2d::mapFontStrikeoutToTextStrikeout(rInfo.mrFont.GetStrikeout()));
+
+ // prepare emphasis mark data
+ drawinglayer::primitive2d::TextEmphasisMark eTextEmphasisMark(drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_NONE);
+
+ switch(rInfo.mrFont.GetEmphasisMark() & FontEmphasisMark::Style)
+ {
+ case FontEmphasisMark::Dot : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_DOT; break;
+ case FontEmphasisMark::Circle : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_CIRCLE; break;
+ case FontEmphasisMark::Disc : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_DISC; break;
+ case FontEmphasisMark::Accent : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_ACCENT; break;
+ default: break;
+ }
+
+ const bool bEmphasisMarkAbove(rInfo.mrFont.GetEmphasisMark() & FontEmphasisMark::PosAbove);
+ const bool bEmphasisMarkBelow(rInfo.mrFont.GetEmphasisMark() & FontEmphasisMark::PosBelow);
+
+ // prepare font relief data
+ drawinglayer::primitive2d::TextRelief eTextRelief(drawinglayer::primitive2d::TEXT_RELIEF_NONE);
+
+ switch(rInfo.mrFont.GetRelief())
+ {
+ case FontRelief::Embossed : eTextRelief = drawinglayer::primitive2d::TEXT_RELIEF_EMBOSSED; break;
+ case FontRelief::Engraved : eTextRelief = drawinglayer::primitive2d::TEXT_RELIEF_ENGRAVED; break;
+ default : break; // RELIEF_NONE, FontRelief_FORCE_EQUAL_SIZE
+ }
+
+ // prepare shadow/outline data
+ const bool bShadow(rInfo.mrFont.IsShadow());
+
+ // TextDecoratedPortionPrimitive2D is needed, create one
+ pNewPrimitive = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D(
+
+ // attributes for TextSimplePortionPrimitive2D
+ aNewTransform,
+ caseMappedText,
+ rInfo.mnTextStart,
+ rInfo.mnTextLen,
+ std::vector(aDXArray),
+ aFontAttribute,
+ rInfo.mpLocale ? *rInfo.mpLocale : css::lang::Locale(),
+ aBFontColor,
+ aTextFillColor,
+
+ // attributes for TextDecoratedPortionPrimitive2D
+ aBOverlineColor,
+ aBUnderlineColor,
+ eFontOverline,
+ eFontLineStyle,
+ bUnderlineAbove,
+ eTextStrikeout,
+ bWordLineMode,
+ eTextEmphasisMark,
+ bEmphasisMarkAbove,
+ bEmphasisMarkBelow,
+ eTextRelief,
+ bShadow);
+ }
+ else
+ {
+ // TextSimplePortionPrimitive2D is enough
+ pNewPrimitive = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
+ aNewTransform,
+ caseMappedText,
+ rInfo.mnTextStart,
+ rInfo.mnTextLen,
+ std::vector(aDXArray),
+ aFontAttribute,
+ rInfo.mpLocale ? *rInfo.mpLocale : css::lang::Locale(),
+ aBFontColor,
+ rInfo.mbFilled,
+ rInfo.mnWidthToFill,
+ aTextFillColor);
+ }
+
+ if (aFontColor.IsTransparent())
+ {
+ // Handle semi-transparent text for both the decorated and simple case here.
+ pNewPrimitive = new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
+ drawinglayer::primitive2d::Primitive2DContainer{ pNewPrimitive },
+ (255 - aFontColor.GetAlpha()) / 255.0);
+ }
+
+ if(rInfo.mbEndOfBullet)
+ {
+ // embed in TextHierarchyBulletPrimitive2D
+ drawinglayer::primitive2d::Primitive2DReference aNewReference(pNewPrimitive);
+ drawinglayer::primitive2d::Primitive2DContainer aNewSequence { aNewReference } ;
+ pNewPrimitive = new drawinglayer::primitive2d::TextHierarchyBulletPrimitive2D(std::move(aNewSequence));
+ }
+
+ if(rInfo.mpFieldData)
+ {
+ pNewPrimitive = impCheckFieldPrimitive(pNewPrimitive.get(), rInfo);
+ }
+
+ maTextPortionPrimitives.push_back(pNewPrimitive);
+
+ // support for WrongSpellVector. Create WrongSpellPrimitives as needed
+ if(!rInfo.mpWrongSpellVector || aDXArray.empty())
+ return;
+
+ const sal_Int32 nSize(rInfo.mpWrongSpellVector->size());
+ const sal_Int32 nDXCount(aDXArray.size());
+ const basegfx::BColor aSpellColor(1.0, 0.0, 0.0); // red, hard coded
+
+ for(sal_Int32 a(0); a < nSize; a++)
+ {
+ const EEngineData::WrongSpellClass& rCandidate = (*rInfo.mpWrongSpellVector)[a];
+
+ if(rCandidate.nStart >= rInfo.mnTextStart && rCandidate.nEnd >= rInfo.mnTextStart && rCandidate.nEnd > rCandidate.nStart)
+ {
+ const sal_Int32 nStart(rCandidate.nStart - rInfo.mnTextStart);
+ const sal_Int32 nEnd(rCandidate.nEnd - rInfo.mnTextStart);
+ double fStart(0.0);
+ double fEnd(0.0);
+
+ if(nStart > 0 && nStart - 1 < nDXCount)
+ {
+ fStart = aDXArray[nStart - 1];
+ }
+
+ if(nEnd > 0 && nEnd - 1 < nDXCount)
+ {
+ fEnd = aDXArray[nEnd - 1];
+ }
+
+ if(!basegfx::fTools::equal(fStart, fEnd))
+ {
+ if(rInfo.IsRTL())
+ {
+ // #i98523#
+ // When the portion is RTL, mirror the redlining using the
+ // full portion width
+ const double fTextWidth(aDXArray[aDXArray.size() - 1]);
+
+ fStart = fTextWidth - fStart;
+ fEnd = fTextWidth - fEnd;
+ }
+
+ // need to take FontScaling out of values; it's already part of
+ // aNewTransform and would be double applied
+ const double fFontScaleX(aFontScaling.getX());
+
+ if(!basegfx::fTools::equal(fFontScaleX, 1.0)
+ && !basegfx::fTools::equalZero(fFontScaleX))
+ {
+ fStart /= fFontScaleX;
+ fEnd /= fFontScaleX;
+ }
+
+ maTextPortionPrimitives.push_back(new drawinglayer::primitive2d::WrongSpellPrimitive2D(
+ aNewTransform,
+ fStart,
+ fEnd,
+ aSpellColor));
+ }
+ }
+ }
+ }
+
+ rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> impTextBreakupHandler::impCheckFieldPrimitive(drawinglayer::primitive2d::BasePrimitive2D* pPrimitive, const DrawPortionInfo& rInfo)
+ {
+ rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> xRet = pPrimitive;
+ if(rInfo.mpFieldData)
+ {
+ // Support for FIELD_SEQ_BEGIN, FIELD_SEQ_END. If used, create a TextHierarchyFieldPrimitive2D
+ // which holds the field type and, if applicable, the URL
+ const SvxURLField* pURLField = dynamic_cast< const SvxURLField* >(rInfo.mpFieldData);
+ const SvxPageField* pPageField = dynamic_cast< const SvxPageField* >(rInfo.mpFieldData);
+
+ // embed current primitive to a sequence
+ drawinglayer::primitive2d::Primitive2DContainer aSequence;
+
+ if(pPrimitive)
+ {
+ aSequence.resize(1);
+ aSequence[0] = drawinglayer::primitive2d::Primitive2DReference(pPrimitive);
+ }
+
+ if(pURLField)
+ {
+ // extended this to hold more of the contents of the original
+ // SvxURLField since that stuff is still used in HitTest and e.g. Calc
+ std::vector< std::pair< OUString, OUString>> meValues;
+ meValues.emplace_back("URL", pURLField->GetURL());
+ meValues.emplace_back("Representation", pURLField->GetRepresentation());
+ meValues.emplace_back("TargetFrame", pURLField->GetTargetFrame());
+ meValues.emplace_back("SvxURLFormat", OUString::number(static_cast<sal_uInt16>(pURLField->GetFormat())));
+ xRet = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(std::move(aSequence), drawinglayer::primitive2d::FIELD_TYPE_URL, &meValues);
+ }
+ else if(pPageField)
+ {
+ xRet = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(std::move(aSequence), drawinglayer::primitive2d::FIELD_TYPE_PAGE);
+ }
+ else
+ {
+ xRet = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(std::move(aSequence), drawinglayer::primitive2d::FIELD_TYPE_COMMON);
+ }
+ }
+
+ return xRet;
+ }
+
+ void impTextBreakupHandler::impFlushTextPortionPrimitivesToLinePrimitives()
+ {
+ // only create a line primitive when we had content; there is no need for
+ // empty line primitives (contrary to paragraphs, see below).
+ if(!maTextPortionPrimitives.empty())
+ {
+ maLinePrimitives.push_back(new drawinglayer::primitive2d::TextHierarchyLinePrimitive2D(std::move(maTextPortionPrimitives)));
+ }
+ }
+
+ void impTextBreakupHandler::impFlushLinePrimitivesToParagraphPrimitives(sal_Int32 nPara)
+ {
+ sal_Int16 nDepth = mrOutliner.GetDepth(nPara);
+ EBulletInfo eInfo = mrOutliner.GetBulletInfo(nPara);
+ // Pass -1 to signal VclMetafileProcessor2D that there is no active
+ // bullets/numbering in this paragraph (i.e. this is normal text)
+ const sal_Int16 nOutlineLevel( eInfo.bVisible ? nDepth : -1);
+
+ // ALWAYS create a paragraph primitive, even when no content was added. This is done to
+ // have the correct paragraph count even with empty paragraphs. Those paragraphs will
+ // have an empty sub-PrimitiveSequence.
+ maParagraphPrimitives.push_back(
+ new drawinglayer::primitive2d::TextHierarchyParagraphPrimitive2D(
+ std::move(maLinePrimitives),
+ nOutlineLevel));
+ }
+
+ void impTextBreakupHandler::impHandleDrawPortionInfo(const DrawPortionInfo& rInfo)
+ {
+ impCreateTextPortionPrimitive(rInfo);
+
+ if(rInfo.mbEndOfLine || rInfo.mbEndOfParagraph)
+ {
+ impFlushTextPortionPrimitivesToLinePrimitives();
+ }
+
+ if(rInfo.mbEndOfParagraph)
+ {
+ impFlushLinePrimitivesToParagraphPrimitives(rInfo.mnPara);
+ }
+ }
+
+ void impTextBreakupHandler::impHandleDrawBulletInfo(const DrawBulletInfo& rInfo)
+ {
+ basegfx::B2DHomMatrix aNewTransform;
+
+ // add size to new transform
+ aNewTransform.scale(rInfo.maBulletSize.getWidth(), rInfo.maBulletSize.getHeight());
+
+ // apply transformA
+ aNewTransform *= maNewTransformA;
+
+ // apply local offset
+ aNewTransform.translate(rInfo.maBulletPosition.X(), rInfo.maBulletPosition.Y());
+
+ // also apply embedding object's transform
+ aNewTransform *= maNewTransformB;
+
+ // prepare empty GraphicAttr
+ const GraphicAttr aGraphicAttr;
+
+ // create GraphicPrimitive2D
+ const drawinglayer::primitive2d::Primitive2DReference aNewReference(new drawinglayer::primitive2d::GraphicPrimitive2D(
+ aNewTransform,
+ rInfo.maBulletGraphicObject,
+ aGraphicAttr));
+
+ // embed in TextHierarchyBulletPrimitive2D
+ drawinglayer::primitive2d::Primitive2DContainer aNewSequence { aNewReference };
+ rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pNewPrimitive = new drawinglayer::primitive2d::TextHierarchyBulletPrimitive2D(std::move(aNewSequence));
+
+ // add to output
+ maTextPortionPrimitives.push_back(pNewPrimitive);
+ }
+
+ IMPL_LINK(impTextBreakupHandler, decomposeContourTextPrimitive, DrawPortionInfo*, pInfo, void)
+ {
+ // for contour text, ignore (clip away) all portions which are below
+ // the visible area given by maScale
+ if(pInfo && static_cast<double>(pInfo->mrStartPos.Y()) < maScale.getY())
+ {
+ impHandleDrawPortionInfo(*pInfo);
+ }
+ }
+
+ IMPL_LINK(impTextBreakupHandler, decomposeBlockTextPrimitive, DrawPortionInfo*, pInfo, void)
+ {
+ if(!pInfo)
+ return;
+
+ // Is clipping wanted? This is text clipping; only accept a portion
+ // if it's completely in the range
+ if(!maClipRange.isEmpty())
+ {
+ // Test start position first; this allows to not get the text range at
+ // all if text is far outside
+ const basegfx::B2DPoint aStartPosition(pInfo->mrStartPos.X(), pInfo->mrStartPos.Y());
+
+ if(!maClipRange.isInside(aStartPosition))
+ {
+ return;
+ }
+
+ // Start position is inside. Get TextBoundRect and TopLeft next
+ drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
+ aTextLayouterDevice.setFont(pInfo->mrFont);
+
+ const basegfx::B2DRange aTextBoundRect(
+ aTextLayouterDevice.getTextBoundRect(
+ pInfo->maText, pInfo->mnTextStart, pInfo->mnTextLen));
+ const basegfx::B2DPoint aTopLeft(aTextBoundRect.getMinimum() + aStartPosition);
+
+ if(!maClipRange.isInside(aTopLeft))
+ {
+ return;
+ }
+
+ // TopLeft is inside. Get BottomRight and check
+ const basegfx::B2DPoint aBottomRight(aTextBoundRect.getMaximum() + aStartPosition);
+
+ if(!maClipRange.isInside(aBottomRight))
+ {
+ return;
+ }
+
+ // all inside, clip was successful
+ }
+ impHandleDrawPortionInfo(*pInfo);
+ }
+
+ IMPL_LINK(impTextBreakupHandler, decomposeStretchTextPrimitive, DrawPortionInfo*, pInfo, void)
+ {
+ if(pInfo)
+ {
+ impHandleDrawPortionInfo(*pInfo);
+ }
+ }
+
+ IMPL_LINK(impTextBreakupHandler, decomposeContourBulletPrimitive, DrawBulletInfo*, pInfo, void)
+ {
+ if(pInfo)
+ {
+ impHandleDrawBulletInfo(*pInfo);
+ }
+ }
+
+ IMPL_LINK(impTextBreakupHandler, decomposeBlockBulletPrimitive, DrawBulletInfo*, pInfo, void)
+ {
+ if(pInfo)
+ {
+ impHandleDrawBulletInfo(*pInfo);
+ }
+ }
+
+ IMPL_LINK(impTextBreakupHandler, decomposeStretchBulletPrimitive, DrawBulletInfo*, pInfo, void)
+ {
+ if(pInfo)
+ {
+ impHandleDrawBulletInfo(*pInfo);
+ }
+ }
+
+ drawinglayer::primitive2d::Primitive2DContainer impTextBreakupHandler::extractPrimitive2DSequence()
+ {
+ if(!maTextPortionPrimitives.empty())
+ {
+ // collect non-closed lines
+ impFlushTextPortionPrimitivesToLinePrimitives();
+ }
+
+ if(!maLinePrimitives.empty())
+ {
+ // collect non-closed paragraphs
+ impFlushLinePrimitivesToParagraphPrimitives(mrOutliner.GetParagraphCount() - 1);
+ }
+
+ return std::move(maParagraphPrimitives);
+ }
+} // end of anonymous namespace
+
+
+// primitive decompositions
+
+void SdrTextObj::impDecomposeContourTextPrimitive(
+ drawinglayer::primitive2d::Primitive2DContainer& rTarget,
+ const drawinglayer::primitive2d::SdrContourTextPrimitive2D& rSdrContourTextPrimitive,
+ const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
+{
+ // decompose matrix to have position and size of text
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ rSdrContourTextPrimitive.getObjectTransform().decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // prepare contour polygon, force to non-mirrored for laying out
+ basegfx::B2DPolyPolygon aPolyPolygon(rSdrContourTextPrimitive.getUnitPolyPolygon());
+ aPolyPolygon.transform(basegfx::utils::createScaleB2DHomMatrix(fabs(aScale.getX()), fabs(aScale.getY())));
+
+ // prepare outliner
+ SolarMutexGuard aSolarGuard;
+ SdrOutliner& rOutliner = ImpGetDrawOutliner();
+ const Size aNullSize;
+ rOutliner.SetPaperSize(aNullSize);
+ rOutliner.SetPolygon(aPolyPolygon);
+ rOutliner.SetUpdateLayout(true);
+ rOutliner.SetText(rSdrContourTextPrimitive.getOutlinerParaObject());
+
+ // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
+ rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
+
+ // prepare matrices to apply to newly created primitives
+ basegfx::B2DHomMatrix aNewTransformA;
+
+ // mirroring. We are now in the polygon sizes. When mirroring in X and Y,
+ // move the null point which was top left to bottom right.
+ const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
+ const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
+
+ // in-between the translations of the single primitives will take place. Afterwards,
+ // the object's transformations need to be applied
+ const basegfx::B2DHomMatrix aNewTransformB(basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0,
+ fShearX, fRotate, aTranslate.getX(), aTranslate.getY()));
+
+ // now break up text primitives.
+ impTextBreakupHandler aConverter(rOutliner);
+ aConverter.decomposeContourTextPrimitive(aNewTransformA, aNewTransformB, aScale);
+
+ // cleanup outliner
+ rOutliner.Clear();
+ rOutliner.setVisualizedPage(nullptr);
+
+ rTarget = aConverter.extractPrimitive2DSequence();
+}
+
+void SdrTextObj::impDecomposeAutoFitTextPrimitive(
+ drawinglayer::primitive2d::Primitive2DContainer& rTarget,
+ const drawinglayer::primitive2d::SdrAutoFitTextPrimitive2D& rSdrAutofitTextPrimitive,
+ const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
+{
+ // decompose matrix to have position and size of text
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ rSdrAutofitTextPrimitive.getTextRangeTransform().decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // use B2DRange aAnchorTextRange for calculations
+ basegfx::B2DRange aAnchorTextRange(aTranslate);
+ aAnchorTextRange.expand(aTranslate + aScale);
+
+ // prepare outliner
+ const SfxItemSet& rTextItemSet = rSdrAutofitTextPrimitive.getSdrText()->GetItemSet();
+ SolarMutexGuard aSolarGuard;
+ SdrOutliner& rOutliner = ImpGetDrawOutliner();
+ SdrTextVertAdjust eVAdj = GetTextVerticalAdjust(rTextItemSet);
+ SdrTextHorzAdjust eHAdj = GetTextHorizontalAdjust(rTextItemSet);
+ const EEControlBits nOriginalControlWord(rOutliner.GetControlWord());
+ const Size aNullSize;
+
+ // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
+ rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
+
+ rOutliner.SetControlWord(nOriginalControlWord|EEControlBits::AUTOPAGESIZE|EEControlBits::STRETCHING);
+ rOutliner.SetMinAutoPaperSize(aNullSize);
+ rOutliner.SetMaxAutoPaperSize(Size(1000000,1000000));
+
+ // That color needs to be restored on leaving this method
+ Color aOriginalBackColor(rOutliner.GetBackgroundColor());
+ setSuitableOutlinerBg(rOutliner);
+
+ // add one to rage sizes to get back to the old Rectangle and outliner measurements
+ const sal_uInt32 nAnchorTextWidth(FRound(aAnchorTextRange.getWidth() + 1));
+ const sal_uInt32 nAnchorTextHeight(FRound(aAnchorTextRange.getHeight() + 1));
+ const OutlinerParaObject* pOutlinerParaObject = rSdrAutofitTextPrimitive.getSdrText()->GetOutlinerParaObject();
+ OSL_ENSURE(pOutlinerParaObject, "impDecomposeBlockTextPrimitive used with no OutlinerParaObject (!)");
+ const bool bVerticalWriting(pOutlinerParaObject->IsEffectivelyVertical());
+ const bool bTopToBottom(pOutlinerParaObject->IsTopToBottom());
+ const Size aAnchorTextSize(Size(nAnchorTextWidth, nAnchorTextHeight));
+
+ if(rSdrAutofitTextPrimitive.getWordWrap() || IsTextFrame())
+ {
+ rOutliner.SetMaxAutoPaperSize(aAnchorTextSize);
+ }
+
+ if(SDRTEXTHORZADJUST_BLOCK == eHAdj && !bVerticalWriting)
+ {
+ rOutliner.SetMinAutoPaperSize(Size(nAnchorTextWidth, 0));
+ rOutliner.SetMinColumnWrapHeight(nAnchorTextHeight);
+ }
+
+ if(SDRTEXTVERTADJUST_BLOCK == eVAdj && bVerticalWriting)
+ {
+ rOutliner.SetMinAutoPaperSize(Size(0, nAnchorTextHeight));
+ rOutliner.SetMinColumnWrapHeight(nAnchorTextWidth);
+ }
+
+ rOutliner.SetPaperSize(aNullSize);
+ rOutliner.SetUpdateLayout(true);
+ rOutliner.SetText(*pOutlinerParaObject);
+ ImpAutoFitText(rOutliner,aAnchorTextSize,bVerticalWriting);
+
+ // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
+ rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
+
+ // now get back the layouted text size from outliner
+ const Size aOutlinerTextSize(rOutliner.GetPaperSize());
+ const basegfx::B2DVector aOutlinerScale(aOutlinerTextSize.Width(), aOutlinerTextSize.Height());
+ basegfx::B2DVector aAdjustTranslate(0.0, 0.0);
+
+ // correct horizontal translation using the now known text size
+ if(SDRTEXTHORZADJUST_CENTER == eHAdj || SDRTEXTHORZADJUST_RIGHT == eHAdj)
+ {
+ const double fFree(aAnchorTextRange.getWidth() - aOutlinerScale.getX());
+
+ if(SDRTEXTHORZADJUST_CENTER == eHAdj)
+ {
+ aAdjustTranslate.setX(fFree / 2.0);
+ }
+
+ if(SDRTEXTHORZADJUST_RIGHT == eHAdj)
+ {
+ aAdjustTranslate.setX(fFree);
+ }
+ }
+
+ // correct vertical translation using the now known text size
+ if(SDRTEXTVERTADJUST_CENTER == eVAdj || SDRTEXTVERTADJUST_BOTTOM == eVAdj)
+ {
+ const double fFree(aAnchorTextRange.getHeight() - aOutlinerScale.getY());
+
+ if(SDRTEXTVERTADJUST_CENTER == eVAdj)
+ {
+ aAdjustTranslate.setY(fFree / 2.0);
+ }
+
+ if(SDRTEXTVERTADJUST_BOTTOM == eVAdj)
+ {
+ aAdjustTranslate.setY(fFree);
+ }
+ }
+
+ // prepare matrices to apply to newly created primitives. aNewTransformA
+ // will get coordinates in aOutlinerScale size and positive in X, Y.
+ basegfx::B2DHomMatrix aNewTransformA;
+ basegfx::B2DHomMatrix aNewTransformB;
+
+ // translate relative to given primitive to get same rotation and shear
+ // as the master shape we are working on. For vertical, use the top-right
+ // corner
+ const double fStartInX(bVerticalWriting && bTopToBottom ? aAdjustTranslate.getX() + aOutlinerScale.getX() : aAdjustTranslate.getX());
+ const double fStartInY(bVerticalWriting && !bTopToBottom ? aAdjustTranslate.getY() + aOutlinerScale.getY() : aAdjustTranslate.getY());
+ aNewTransformA.translate(fStartInX, fStartInY);
+
+ // mirroring. We are now in aAnchorTextRange sizes. When mirroring in X and Y,
+ // move the null point which was top left to bottom right.
+ const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
+ const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
+ aNewTransformB.scale(bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0);
+
+ // in-between the translations of the single primitives will take place. Afterwards,
+ // the object's transformations need to be applied
+ aNewTransformB.shearX(fShearX);
+ aNewTransformB.rotate(fRotate);
+ aNewTransformB.translate(aTranslate.getX(), aTranslate.getY());
+
+ basegfx::B2DRange aClipRange;
+
+ // now break up text primitives.
+ impTextBreakupHandler aConverter(rOutliner);
+ aConverter.decomposeBlockTextPrimitive(aNewTransformA, aNewTransformB, aClipRange);
+
+ // cleanup outliner
+ rOutliner.SetBackgroundColor(aOriginalBackColor);
+ rOutliner.Clear();
+ rOutliner.setVisualizedPage(nullptr);
+ rOutliner.SetControlWord(nOriginalControlWord);
+
+ rTarget = aConverter.extractPrimitive2DSequence();
+}
+
+// Resolves: fdo#35779 set background color of this shape as the editeng background if there
+// is one. Check the shape itself, then the host page, then that page's master page.
+bool SdrObject::setSuitableOutlinerBg(::Outliner& rOutliner) const
+{
+ const SfxItemSet* pBackgroundFillSet = getBackgroundFillSet();
+ if (drawing::FillStyle_NONE != pBackgroundFillSet->Get(XATTR_FILLSTYLE).GetValue())
+ {
+ Color aColor(rOutliner.GetBackgroundColor());
+ GetDraftFillColor(*pBackgroundFillSet, aColor);
+ rOutliner.SetBackgroundColor(aColor);
+ return true;
+ }
+ return false;
+}
+
+const SfxItemSet* SdrObject::getBackgroundFillSet() const
+{
+ const SfxItemSet* pBackgroundFillSet = &GetObjectItemSet();
+
+ if (drawing::FillStyle_NONE == pBackgroundFillSet->Get(XATTR_FILLSTYLE).GetValue())
+ {
+ SdrPage* pOwnerPage(getSdrPageFromSdrObject());
+ if (pOwnerPage)
+ {
+ pBackgroundFillSet = &pOwnerPage->getSdrPageProperties().GetItemSet();
+
+ if (drawing::FillStyle_NONE == pBackgroundFillSet->Get(XATTR_FILLSTYLE).GetValue())
+ {
+ if (!pOwnerPage->IsMasterPage() && pOwnerPage->TRG_HasMasterPage())
+ {
+ pBackgroundFillSet = &pOwnerPage->TRG_GetMasterPage().getSdrPageProperties().GetItemSet();
+ }
+ }
+ }
+ }
+ return pBackgroundFillSet;
+}
+
+const Graphic* SdrObject::getFillGraphic() const
+{
+ if(IsGroupObject()) // Doesn't make sense, and GetObjectItemSet() asserts.
+ return nullptr;
+ const SfxItemSet* pBackgroundFillSet = getBackgroundFillSet();
+ if (drawing::FillStyle_BITMAP != pBackgroundFillSet->Get(XATTR_FILLSTYLE).GetValue())
+ return nullptr;
+ return &pBackgroundFillSet->Get(XATTR_FILLBITMAP).GetGraphicObject().GetGraphic();
+}
+
+void SdrTextObj::impDecomposeBlockTextPrimitive(
+ drawinglayer::primitive2d::Primitive2DContainer& rTarget,
+ const drawinglayer::primitive2d::SdrBlockTextPrimitive2D& rSdrBlockTextPrimitive,
+ const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
+{
+ // decompose matrix to have position and size of text
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ rSdrBlockTextPrimitive.getTextRangeTransform().decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // use B2DRange aAnchorTextRange for calculations
+ basegfx::B2DRange aAnchorTextRange(aTranslate);
+ aAnchorTextRange.expand(aTranslate + aScale);
+
+ // prepare outliner
+ const bool bIsCell(rSdrBlockTextPrimitive.getCellText());
+ SolarMutexGuard aSolarGuard;
+ SdrOutliner& rOutliner = ImpGetDrawOutliner();
+ SdrTextHorzAdjust eHAdj = rSdrBlockTextPrimitive.getSdrTextHorzAdjust();
+ SdrTextVertAdjust eVAdj = rSdrBlockTextPrimitive.getSdrTextVertAdjust();
+ const EEControlBits nOriginalControlWord(rOutliner.GetControlWord());
+ const Size aNullSize;
+
+ // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
+ rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
+ rOutliner.SetFixedCellHeight(rSdrBlockTextPrimitive.isFixedCellHeight());
+ rOutliner.SetControlWord(nOriginalControlWord|EEControlBits::AUTOPAGESIZE);
+ rOutliner.SetMinAutoPaperSize(aNullSize);
+ rOutliner.SetMaxAutoPaperSize(Size(1000000,1000000));
+
+ // That color needs to be restored on leaving this method
+ Color aOriginalBackColor(rOutliner.GetBackgroundColor());
+ setSuitableOutlinerBg(rOutliner);
+
+ // add one to rage sizes to get back to the old Rectangle and outliner measurements
+ const sal_uInt32 nAnchorTextWidth(FRound(aAnchorTextRange.getWidth() + 1));
+ const sal_uInt32 nAnchorTextHeight(FRound(aAnchorTextRange.getHeight() + 1));
+ const bool bVerticalWriting(rSdrBlockTextPrimitive.getOutlinerParaObject().IsEffectivelyVertical());
+ const bool bTopToBottom(rSdrBlockTextPrimitive.getOutlinerParaObject().IsTopToBottom());
+ const Size aAnchorTextSize(Size(nAnchorTextWidth, nAnchorTextHeight));
+
+ if(bIsCell)
+ {
+ // cell text is formatted neither like a text object nor like an object
+ // text, so use a special setup here
+ rOutliner.SetMaxAutoPaperSize(aAnchorTextSize);
+
+ // #i106214# To work with an unchangeable PaperSize (CellSize in
+ // this case) Set(Min|Max)AutoPaperSize and SetPaperSize have to be used.
+ // #i106214# This was not completely correct; to still measure the real
+ // text height to allow vertical adjust (and vice versa for VerticalWritintg)
+ // only one aspect has to be set, but the other one to zero
+ if(bVerticalWriting)
+ {
+ // measure the horizontal text size
+ rOutliner.SetMinAutoPaperSize(Size(0, aAnchorTextSize.Height()));
+ }
+ else
+ {
+ // measure the vertical text size
+ rOutliner.SetMinAutoPaperSize(Size(aAnchorTextSize.Width(), 0));
+ }
+
+ rOutliner.SetPaperSize(aAnchorTextSize);
+ rOutliner.SetUpdateLayout(true);
+ rOutliner.SetText(rSdrBlockTextPrimitive.getOutlinerParaObject());
+ }
+ else
+ {
+ // check if block text is used (only one of them can be true)
+ const bool bHorizontalIsBlock(SDRTEXTHORZADJUST_BLOCK == eHAdj && !bVerticalWriting);
+ const bool bVerticalIsBlock(SDRTEXTVERTADJUST_BLOCK == eVAdj && bVerticalWriting);
+
+ // set minimal paper size horizontally/vertically if needed
+ if(bHorizontalIsBlock)
+ {
+ rOutliner.SetMinAutoPaperSize(Size(nAnchorTextWidth, 0));
+ rOutliner.SetMinColumnWrapHeight(nAnchorTextHeight);
+ }
+ else if(bVerticalIsBlock)
+ {
+ rOutliner.SetMinAutoPaperSize(Size(0, nAnchorTextHeight));
+ rOutliner.SetMinColumnWrapHeight(nAnchorTextWidth);
+ }
+
+ if((rSdrBlockTextPrimitive.getWordWrap() || IsTextFrame()) && !rSdrBlockTextPrimitive.getUnlimitedPage())
+ {
+ // #i103454# maximal paper size hor/ver needs to be limited to text
+ // frame size. If it's block text, still allow the 'other' direction
+ // to grow to get a correct real text size when using GetPaperSize().
+ // When just using aAnchorTextSize as maximum, GetPaperSize()
+ // would just return aAnchorTextSize again: this means, the wanted
+ // 'measurement' of the real size of block text would not work
+ Size aMaxAutoPaperSize(aAnchorTextSize);
+
+ // Usual processing - always grow in one of directions
+ bool bAllowGrowVertical = !bVerticalWriting;
+ bool bAllowGrowHorizontal = bVerticalWriting;
+
+ // Compatibility mode for tdf#99729
+ if (getSdrModelFromSdrObject().IsAnchoredTextOverflowLegacy())
+ {
+ bAllowGrowVertical = bHorizontalIsBlock;
+ bAllowGrowHorizontal = bVerticalIsBlock;
+ }
+
+ if (bAllowGrowVertical)
+ {
+ // allow to grow vertical for horizontal texts
+ aMaxAutoPaperSize.setHeight(1000000);
+ }
+ else if (bAllowGrowHorizontal)
+ {
+ // allow to grow horizontal for vertical texts
+ aMaxAutoPaperSize.setWidth(1000000);
+ }
+
+ rOutliner.SetMaxAutoPaperSize(aMaxAutoPaperSize);
+ }
+
+ rOutliner.SetPaperSize(aNullSize);
+ rOutliner.SetUpdateLayout(true);
+ rOutliner.SetText(rSdrBlockTextPrimitive.getOutlinerParaObject());
+ }
+
+ rOutliner.SetControlWord(nOriginalControlWord);
+
+ // now get back the layouted text size from outliner
+ const Size aOutlinerTextSize(rOutliner.GetPaperSize());
+ const basegfx::B2DVector aOutlinerScale(aOutlinerTextSize.Width(), aOutlinerTextSize.Height());
+ basegfx::B2DVector aAdjustTranslate(0.0, 0.0);
+
+ // For draw objects containing text correct hor/ver alignment if text is bigger
+ // than the object itself. Without that correction, the text would always be
+ // formatted to the left edge (or top edge when vertical) of the draw object.
+ if(!IsTextFrame() && !bIsCell)
+ {
+ if(aAnchorTextRange.getWidth() < aOutlinerScale.getX() && !bVerticalWriting)
+ {
+ // Horizontal case here. Correct only if eHAdj == SDRTEXTHORZADJUST_BLOCK,
+ // else the alignment is wanted.
+ if(SDRTEXTHORZADJUST_BLOCK == eHAdj)
+ {
+ SvxAdjust eAdjust = GetObjectItemSet().Get(EE_PARA_JUST).GetAdjust();
+ switch(eAdjust)
+ {
+ case SvxAdjust::Left: eHAdj = SDRTEXTHORZADJUST_LEFT; break;
+ case SvxAdjust::Right: eHAdj = SDRTEXTHORZADJUST_RIGHT; break;
+ case SvxAdjust::Center: eHAdj = SDRTEXTHORZADJUST_CENTER; break;
+ default: break;
+ }
+ }
+ }
+
+ if(aAnchorTextRange.getHeight() < aOutlinerScale.getY() && bVerticalWriting)
+ {
+ // Vertical case here. Correct only if eHAdj == SDRTEXTVERTADJUST_BLOCK,
+ // else the alignment is wanted.
+ if(SDRTEXTVERTADJUST_BLOCK == eVAdj)
+ {
+ eVAdj = SDRTEXTVERTADJUST_CENTER;
+ }
+ }
+ }
+
+ // correct horizontal translation using the now known text size
+ if(SDRTEXTHORZADJUST_CENTER == eHAdj || SDRTEXTHORZADJUST_RIGHT == eHAdj)
+ {
+ const double fFree(aAnchorTextRange.getWidth() - aOutlinerScale.getX());
+
+ if(SDRTEXTHORZADJUST_CENTER == eHAdj)
+ {
+ aAdjustTranslate.setX(fFree / 2.0);
+ }
+
+ if(SDRTEXTHORZADJUST_RIGHT == eHAdj)
+ {
+ aAdjustTranslate.setX(fFree);
+ }
+ }
+
+ // correct vertical translation using the now known text size
+ if(SDRTEXTVERTADJUST_CENTER == eVAdj || SDRTEXTVERTADJUST_BOTTOM == eVAdj)
+ {
+ const double fFree(aAnchorTextRange.getHeight() - aOutlinerScale.getY());
+
+ if(SDRTEXTVERTADJUST_CENTER == eVAdj)
+ {
+ aAdjustTranslate.setY(fFree / 2.0);
+ }
+
+ if(SDRTEXTVERTADJUST_BOTTOM == eVAdj)
+ {
+ aAdjustTranslate.setY(fFree);
+ }
+ }
+
+ // prepare matrices to apply to newly created primitives. aNewTransformA
+ // will get coordinates in aOutlinerScale size and positive in X, Y.
+ // Translate relative to given primitive to get same rotation and shear
+ // as the master shape we are working on. For vertical, use the top-right
+ // corner
+ const double fStartInX(bVerticalWriting && bTopToBottom ? aAdjustTranslate.getX() + aOutlinerScale.getX() : aAdjustTranslate.getX());
+ const double fStartInY(bVerticalWriting && !bTopToBottom ? aAdjustTranslate.getY() + aOutlinerScale.getY() : aAdjustTranslate.getY());
+ basegfx::B2DHomMatrix aNewTransformA(basegfx::utils::createTranslateB2DHomMatrix(fStartInX, fStartInY));
+
+ // Apply the camera rotation. It have to be applied after adjustment of
+ // the text (top, bottom, center, left, right).
+ if(GetCameraZRotation() != 0)
+ {
+ // First find the text rect.
+ basegfx::B2DRange aTextRectangle(/*x1=*/aTranslate.getX() + aAdjustTranslate.getX(),
+ /*y1=*/aTranslate.getY() + aAdjustTranslate.getY(),
+ /*x2=*/aTranslate.getX() + aOutlinerScale.getX() - aAdjustTranslate.getX(),
+ /*y2=*/aTranslate.getY() + aOutlinerScale.getY() - aAdjustTranslate.getY());
+
+ // Rotate the text from the center point.
+ basegfx::B2DVector aTranslateToCenter(aTextRectangle.getWidth() / 2, aTextRectangle.getHeight() / 2);
+ aNewTransformA.translate(-aTranslateToCenter.getX(), -aTranslateToCenter.getY());
+ aNewTransformA.rotate(basegfx::deg2rad(360.0 - GetCameraZRotation() ));
+ aNewTransformA.translate(aTranslateToCenter.getX(), aTranslateToCenter.getY());
+ }
+
+ // mirroring. We are now in aAnchorTextRange sizes. When mirroring in X and Y,
+ // move the null point which was top left to bottom right.
+ const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
+ const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
+
+ // in-between the translations of the single primitives will take place. Afterwards,
+ // the object's transformations need to be applied
+ const basegfx::B2DHomMatrix aNewTransformB(basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0,
+ fShearX, fRotate, aTranslate.getX(), aTranslate.getY()));
+
+
+ // create ClipRange (if needed)
+ basegfx::B2DRange aClipRange;
+
+ // now break up text primitives.
+ impTextBreakupHandler aConverter(rOutliner);
+ aConverter.decomposeBlockTextPrimitive(aNewTransformA, aNewTransformB, aClipRange);
+
+ // cleanup outliner
+ rOutliner.SetBackgroundColor(aOriginalBackColor);
+ rOutliner.Clear();
+ rOutliner.setVisualizedPage(nullptr);
+
+ rTarget = aConverter.extractPrimitive2DSequence();
+}
+
+void SdrTextObj::impDecomposeStretchTextPrimitive(
+ drawinglayer::primitive2d::Primitive2DContainer& rTarget,
+ const drawinglayer::primitive2d::SdrStretchTextPrimitive2D& rSdrStretchTextPrimitive,
+ const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
+{
+ // decompose matrix to have position and size of text
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ rSdrStretchTextPrimitive.getTextRangeTransform().decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // prepare outliner
+ SolarMutexGuard aSolarGuard;
+ SdrOutliner& rOutliner = ImpGetDrawOutliner();
+ const EEControlBits nOriginalControlWord(rOutliner.GetControlWord());
+ const Size aNullSize;
+
+ rOutliner.SetControlWord(nOriginalControlWord|EEControlBits::STRETCHING|EEControlBits::AUTOPAGESIZE);
+ rOutliner.SetFixedCellHeight(rSdrStretchTextPrimitive.isFixedCellHeight());
+ rOutliner.SetMinAutoPaperSize(aNullSize);
+ rOutliner.SetMaxAutoPaperSize(Size(1000000,1000000));
+ rOutliner.SetPaperSize(aNullSize);
+ rOutliner.SetUpdateLayout(true);
+ rOutliner.SetText(rSdrStretchTextPrimitive.getOutlinerParaObject());
+
+ // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
+ rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
+
+ // now get back the laid out text size from outliner
+ const Size aOutlinerTextSize(rOutliner.CalcTextSize());
+ const basegfx::B2DVector aOutlinerScale(
+ aOutlinerTextSize.Width() == tools::Long(0) ? 1.0 : aOutlinerTextSize.Width(),
+ aOutlinerTextSize.Height() == tools::Long(0) ? 1.0 : aOutlinerTextSize.Height());
+
+ // prepare matrices to apply to newly created primitives
+ basegfx::B2DHomMatrix aNewTransformA;
+
+ // #i101957# Check for vertical text. If used, aNewTransformA
+ // needs to translate the text initially around object width to orient
+ // it relative to the topper right instead of the topper left
+ const bool bVertical(rSdrStretchTextPrimitive.getOutlinerParaObject().IsEffectivelyVertical());
+ const bool bTopToBottom(rSdrStretchTextPrimitive.getOutlinerParaObject().IsTopToBottom());
+
+ if(bVertical)
+ {
+ if(bTopToBottom)
+ aNewTransformA.translate(aScale.getX(), 0.0);
+ else
+ aNewTransformA.translate(0.0, aScale.getY());
+ }
+
+ // calculate global char stretching scale parameters. Use non-mirrored sizes
+ // to layout without mirroring
+ const double fScaleX(fabs(aScale.getX()) / aOutlinerScale.getX());
+ const double fScaleY(fabs(aScale.getY()) / aOutlinerScale.getY());
+ rOutliner.SetGlobalCharStretching(static_cast<sal_Int16>(FRound(fScaleX * 100.0)), static_cast<sal_Int16>(FRound(fScaleY * 100.0)));
+
+ // When mirroring in X and Y,
+ // move the null point which was top left to bottom right.
+ const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
+ const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
+
+ // in-between the translations of the single primitives will take place. Afterwards,
+ // the object's transformations need to be applied
+ const basegfx::B2DHomMatrix aNewTransformB(basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0,
+ fShearX, fRotate, aTranslate.getX(), aTranslate.getY()));
+
+ // now break up text primitives.
+ impTextBreakupHandler aConverter(rOutliner);
+ aConverter.decomposeStretchTextPrimitive(aNewTransformA, aNewTransformB);
+
+ // cleanup outliner
+ rOutliner.SetControlWord(nOriginalControlWord);
+ rOutliner.Clear();
+ rOutliner.setVisualizedPage(nullptr);
+
+ rTarget = aConverter.extractPrimitive2DSequence();
+}
+
+
+// timing generators
+#define ENDLESS_LOOP (0xffffffff)
+#define ENDLESS_TIME (double(0xffffffff))
+#define PIXEL_DPI (96.0)
+
+void SdrTextObj::impGetBlinkTextTiming(drawinglayer::animation::AnimationEntryList& rAnimList) const
+{
+ if(SdrTextAniKind::Blink != GetTextAniKind())
+ return;
+
+ // get values
+ const SfxItemSet& rSet = GetObjectItemSet();
+ const sal_uInt32 nRepeat(static_cast<sal_uInt32>(rSet.Get(SDRATTR_TEXT_ANICOUNT).GetValue()));
+ double fDelay(static_cast<double>(rSet.Get(SDRATTR_TEXT_ANIDELAY).GetValue()));
+
+ if(0.0 == fDelay)
+ {
+ // use default
+ fDelay = 250.0;
+ }
+
+ // prepare loop and add
+ drawinglayer::animation::AnimationEntryLoop aLoop(nRepeat ? nRepeat : ENDLESS_LOOP);
+ drawinglayer::animation::AnimationEntryFixed aStart(fDelay, 0.0);
+ aLoop.append(aStart);
+ drawinglayer::animation::AnimationEntryFixed aEnd(fDelay, 1.0);
+ aLoop.append(aEnd);
+ rAnimList.append(aLoop);
+
+ // add stopped state if loop is not endless
+ if(0 != nRepeat)
+ {
+ bool bVisibleWhenStopped(rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE).GetValue());
+ drawinglayer::animation::AnimationEntryFixed aStop(ENDLESS_TIME, bVisibleWhenStopped ? 0.0 : 1.0);
+ rAnimList.append(aStop);
+ }
+}
+
+static void impCreateScrollTiming(const SfxItemSet& rSet, drawinglayer::animation::AnimationEntryList& rAnimList, bool bForward, double fTimeFullPath, double fFrequency)
+{
+ bool bVisibleWhenStopped(rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE).GetValue());
+ bool bVisibleWhenStarted(rSet.Get(SDRATTR_TEXT_ANISTARTINSIDE).GetValue());
+ const sal_uInt32 nRepeat(rSet.Get(SDRATTR_TEXT_ANICOUNT).GetValue());
+
+ if(bVisibleWhenStarted)
+ {
+ // move from center to outside
+ drawinglayer::animation::AnimationEntryLinear aInOut(fTimeFullPath * 0.5, fFrequency, 0.5, bForward ? 1.0 : 0.0);
+ rAnimList.append(aInOut);
+ }
+
+ // loop. In loop, move through
+ drawinglayer::animation::AnimationEntryLoop aLoop(nRepeat ? nRepeat : ENDLESS_LOOP);
+ drawinglayer::animation::AnimationEntryLinear aThrough(fTimeFullPath, fFrequency, bForward ? 0.0 : 1.0, bForward ? 1.0 : 0.0);
+ aLoop.append(aThrough);
+ rAnimList.append(aLoop);
+
+ if(0 != nRepeat && bVisibleWhenStopped)
+ {
+ // move from outside to center
+ drawinglayer::animation::AnimationEntryLinear aOutIn(fTimeFullPath * 0.5, fFrequency, bForward ? 0.0 : 1.0, 0.5);
+ rAnimList.append(aOutIn);
+
+ // add timing for staying at the end
+ drawinglayer::animation::AnimationEntryFixed aEnd(ENDLESS_TIME, 0.5);
+ rAnimList.append(aEnd);
+ }
+}
+
+static void impCreateAlternateTiming(const SfxItemSet& rSet, drawinglayer::animation::AnimationEntryList& rAnimList, double fRelativeTextLength, bool bForward, double fTimeFullPath, double fFrequency)
+{
+ if(basegfx::fTools::more(fRelativeTextLength, 0.5))
+ {
+ // this is the case when fTextLength > fFrameLength, text is bigger than animation frame.
+ // In that case, correct direction
+ bForward = !bForward;
+ }
+
+ const double fStartPosition(bForward ? fRelativeTextLength : 1.0 - fRelativeTextLength);
+ const double fEndPosition(bForward ? 1.0 - fRelativeTextLength : fRelativeTextLength);
+ bool bVisibleWhenStarted(rSet.Get(SDRATTR_TEXT_ANISTARTINSIDE).GetValue());
+ const sal_uInt32 nRepeat(rSet.Get(SDRATTR_TEXT_ANICOUNT).GetValue());
+
+ if(!bVisibleWhenStarted)
+ {
+ // move from outside to center
+ drawinglayer::animation::AnimationEntryLinear aOutIn(fTimeFullPath * 0.5, fFrequency, bForward ? 0.0 : 1.0, 0.5);
+ rAnimList.append(aOutIn);
+ }
+
+ // loop. In loop, move out and in again. fInnerMovePath may be negative when text is bigger then frame,
+ // so use absolute value
+ const double fInnerMovePath(fabs(1.0 - (fRelativeTextLength * 2.0)));
+ const double fTimeForInnerPath(fTimeFullPath * fInnerMovePath);
+ const double fHalfInnerPath(fTimeForInnerPath * 0.5);
+ const sal_uInt32 nDoubleRepeat(nRepeat / 2L);
+
+ if(nDoubleRepeat || 0 == nRepeat)
+ {
+ // double forth and back loop
+ drawinglayer::animation::AnimationEntryLoop aLoop(nDoubleRepeat ? nDoubleRepeat : ENDLESS_LOOP);
+ drawinglayer::animation::AnimationEntryLinear aTime0(fHalfInnerPath, fFrequency, 0.5, fEndPosition);
+ aLoop.append(aTime0);
+ drawinglayer::animation::AnimationEntryLinear aTime1(fTimeForInnerPath, fFrequency, fEndPosition, fStartPosition);
+ aLoop.append(aTime1);
+ drawinglayer::animation::AnimationEntryLinear aTime2(fHalfInnerPath, fFrequency, fStartPosition, 0.5);
+ aLoop.append(aTime2);
+ rAnimList.append(aLoop);
+ }
+
+ if(nRepeat % 2L)
+ {
+ // repeat is uneven, so we need one more forth and back to center
+ drawinglayer::animation::AnimationEntryLinear aTime0(fHalfInnerPath, fFrequency, 0.5, fEndPosition);
+ rAnimList.append(aTime0);
+ drawinglayer::animation::AnimationEntryLinear aTime1(fHalfInnerPath, fFrequency, fEndPosition, 0.5);
+ rAnimList.append(aTime1);
+ }
+
+ if(0 == nRepeat)
+ return;
+
+ bool bVisibleWhenStopped(rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE).GetValue());
+ if(bVisibleWhenStopped)
+ {
+ // add timing for staying at the end
+ drawinglayer::animation::AnimationEntryFixed aEnd(ENDLESS_TIME, 0.5);
+ rAnimList.append(aEnd);
+ }
+ else
+ {
+ // move from center to outside
+ drawinglayer::animation::AnimationEntryLinear aInOut(fTimeFullPath * 0.5, fFrequency, 0.5, bForward ? 1.0 : 0.0);
+ rAnimList.append(aInOut);
+ }
+}
+
+static void impCreateSlideTiming(const SfxItemSet& rSet, drawinglayer::animation::AnimationEntryList& rAnimList, bool bForward, double fTimeFullPath, double fFrequency)
+{
+ // move in from outside, start outside
+ const double fStartPosition(bForward ? 0.0 : 1.0);
+ const sal_uInt32 nRepeat(rSet.Get(SDRATTR_TEXT_ANICOUNT).GetValue());
+
+ // move from outside to center
+ drawinglayer::animation::AnimationEntryLinear aOutIn(fTimeFullPath * 0.5, fFrequency, fStartPosition, 0.5);
+ rAnimList.append(aOutIn);
+
+ // loop. In loop, move out and in again
+ if(nRepeat > 1 || 0 == nRepeat)
+ {
+ drawinglayer::animation::AnimationEntryLoop aLoop(nRepeat ? nRepeat - 1 : ENDLESS_LOOP);
+ drawinglayer::animation::AnimationEntryLinear aTime0(fTimeFullPath * 0.5, fFrequency, 0.5, fStartPosition);
+ aLoop.append(aTime0);
+ drawinglayer::animation::AnimationEntryLinear aTime1(fTimeFullPath * 0.5, fFrequency, fStartPosition, 0.5);
+ aLoop.append(aTime1);
+ rAnimList.append(aLoop);
+ }
+
+ // always visible when stopped, so add timing for staying at the end when not endless
+ if(0 != nRepeat)
+ {
+ drawinglayer::animation::AnimationEntryFixed aEnd(ENDLESS_TIME, 0.5);
+ rAnimList.append(aEnd);
+ }
+}
+
+void SdrTextObj::impGetScrollTextTiming(drawinglayer::animation::AnimationEntryList& rAnimList, double fFrameLength, double fTextLength) const
+{
+ const SdrTextAniKind eAniKind(GetTextAniKind());
+
+ if(SdrTextAniKind::Scroll != eAniKind && SdrTextAniKind::Alternate != eAniKind && SdrTextAniKind::Slide != eAniKind)
+ return;
+
+ // get data. Goal is to calculate fTimeFullPath which is the time needed to
+ // move animation from (0.0) to (1.0) state
+ const SfxItemSet& rSet = GetObjectItemSet();
+ double fAnimationDelay(static_cast<double>(rSet.Get(SDRATTR_TEXT_ANIDELAY).GetValue()));
+ double fSingleStepWidth(static_cast<double>(rSet.Get(SDRATTR_TEXT_ANIAMOUNT).GetValue()));
+ const SdrTextAniDirection eDirection(GetTextAniDirection());
+ const bool bForward(SdrTextAniDirection::Right == eDirection || SdrTextAniDirection::Down == eDirection);
+
+ if(basegfx::fTools::equalZero(fAnimationDelay))
+ {
+ // default to 1/20 second
+ fAnimationDelay = 50.0;
+ }
+
+ if(basegfx::fTools::less(fSingleStepWidth, 0.0))
+ {
+ // data is in pixels, convert to logic. Imply PIXEL_DPI dpi.
+ // It makes no sense to keep the view-transformation centered
+ // definitions, so get rid of them here.
+ fSingleStepWidth = (-fSingleStepWidth * (2540.0 / PIXEL_DPI));
+ }
+
+ if(basegfx::fTools::equalZero(fSingleStepWidth))
+ {
+ // default to 1 millimeter
+ fSingleStepWidth = 100.0;
+ }
+
+ // use the length of the full animation path and the number of steps
+ // to get the full path time
+ const double fFullPathLength(fFrameLength + fTextLength);
+ const double fNumberOfSteps(fFullPathLength / fSingleStepWidth);
+ double fTimeFullPath(fNumberOfSteps * fAnimationDelay);
+
+ if(fTimeFullPath < fAnimationDelay)
+ {
+ fTimeFullPath = fAnimationDelay;
+ }
+
+ switch(eAniKind)
+ {
+ case SdrTextAniKind::Scroll :
+ {
+ impCreateScrollTiming(rSet, rAnimList, bForward, fTimeFullPath, fAnimationDelay);
+ break;
+ }
+ case SdrTextAniKind::Alternate :
+ {
+ double fRelativeTextLength(fTextLength / (fFrameLength + fTextLength));
+ impCreateAlternateTiming(rSet, rAnimList, fRelativeTextLength, bForward, fTimeFullPath, fAnimationDelay);
+ break;
+ }
+ case SdrTextAniKind::Slide :
+ {
+ impCreateSlideTiming(rSet, rAnimList, bForward, fTimeFullPath, fAnimationDelay);
+ break;
+ }
+ default : break; // SdrTextAniKind::NONE, SdrTextAniKind::Blink
+ }
+}
+
+void SdrTextObj::impHandleChainingEventsDuringDecomposition(SdrOutliner &rOutliner) const
+{
+ if (GetTextChain()->GetNilChainingEvent(this))
+ return;
+
+ GetTextChain()->SetNilChainingEvent(this, true);
+
+ TextChainFlow aTxtChainFlow(const_cast<SdrTextObj*>(this));
+ bool bIsOverflow;
+
+#ifdef DBG_UTIL
+ // Some debug output
+ size_t nObjCount(getSdrPageFromSdrObject()->GetObjCount());
+ for (size_t i = 0; i < nObjCount; i++)
+ {
+ SdrTextObj* pCurObj(dynamic_cast< SdrTextObj* >(getSdrPageFromSdrObject()->GetObj(i)));
+ if(pCurObj == this)
+ {
+ SAL_INFO("svx.chaining", "Working on TextBox " << i);
+ break;
+ }
+ }
+#endif
+
+ aTxtChainFlow.CheckForFlowEvents(&rOutliner);
+
+ if (aTxtChainFlow.IsUnderflow() && !IsInEditMode())
+ {
+ // underflow-induced overflow
+ aTxtChainFlow.ExecuteUnderflow(&rOutliner);
+ bIsOverflow = aTxtChainFlow.IsOverflow();
+ } else {
+ // standard overflow (no underflow before)
+ bIsOverflow = aTxtChainFlow.IsOverflow();
+ }
+
+ if (bIsOverflow && !IsInEditMode()) {
+ // Initialize Chaining Outliner
+ SdrOutliner &rChainingOutl(getSdrModelFromSdrObject().GetChainingOutliner(this));
+ ImpInitDrawOutliner( rChainingOutl );
+ rChainingOutl.SetUpdateLayout(true);
+ // We must pass the chaining outliner otherwise we would mess up decomposition
+ aTxtChainFlow.ExecuteOverflow(&rOutliner, &rChainingOutl);
+ }
+
+ GetTextChain()->SetNilChainingEvent(this, false);
+}
+
+void SdrTextObj::impDecomposeChainedTextPrimitive(
+ drawinglayer::primitive2d::Primitive2DContainer& rTarget,
+ const drawinglayer::primitive2d::SdrChainedTextPrimitive2D& rSdrChainedTextPrimitive,
+ const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
+{
+ // decompose matrix to have position and size of text
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ rSdrChainedTextPrimitive.getTextRangeTransform().decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // use B2DRange aAnchorTextRange for calculations
+ basegfx::B2DRange aAnchorTextRange(aTranslate);
+ aAnchorTextRange.expand(aTranslate + aScale);
+
+ // prepare outliner
+ const SfxItemSet& rTextItemSet = rSdrChainedTextPrimitive.getSdrText()->GetItemSet();
+ SolarMutexGuard aSolarGuard;
+ SdrOutliner& rOutliner = ImpGetDrawOutliner();
+
+ SdrTextVertAdjust eVAdj = GetTextVerticalAdjust(rTextItemSet);
+ SdrTextHorzAdjust eHAdj = GetTextHorizontalAdjust(rTextItemSet);
+ const EEControlBits nOriginalControlWord(rOutliner.GetControlWord());
+ const Size aNullSize;
+
+ // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
+ rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
+
+ rOutliner.SetControlWord(nOriginalControlWord|EEControlBits::AUTOPAGESIZE|EEControlBits::STRETCHING);
+ rOutliner.SetMinAutoPaperSize(aNullSize);
+ rOutliner.SetMaxAutoPaperSize(Size(1000000,1000000));
+
+ // add one to rage sizes to get back to the old Rectangle and outliner measurements
+ const sal_uInt32 nAnchorTextWidth(FRound(aAnchorTextRange.getWidth() + 1));
+ const sal_uInt32 nAnchorTextHeight(FRound(aAnchorTextRange.getHeight() + 1));
+
+ // Text
+ const OutlinerParaObject* pOutlinerParaObject = rSdrChainedTextPrimitive.getSdrText()->GetOutlinerParaObject();
+ OSL_ENSURE(pOutlinerParaObject, "impDecomposeBlockTextPrimitive used with no OutlinerParaObject (!)");
+
+ const bool bVerticalWriting(pOutlinerParaObject->IsEffectivelyVertical());
+ const bool bTopToBottom(pOutlinerParaObject->IsTopToBottom());
+ const Size aAnchorTextSize(Size(nAnchorTextWidth, nAnchorTextHeight));
+
+ if(IsTextFrame())
+ {
+ rOutliner.SetMaxAutoPaperSize(aAnchorTextSize);
+ }
+
+ if(SDRTEXTHORZADJUST_BLOCK == eHAdj && !bVerticalWriting)
+ {
+ rOutliner.SetMinAutoPaperSize(Size(nAnchorTextWidth, 0));
+ }
+
+ if(SDRTEXTVERTADJUST_BLOCK == eVAdj && bVerticalWriting)
+ {
+ rOutliner.SetMinAutoPaperSize(Size(0, nAnchorTextHeight));
+ }
+
+ rOutliner.SetPaperSize(aNullSize);
+ rOutliner.SetUpdateLayout(true);
+ // Sets original text
+ rOutliner.SetText(*pOutlinerParaObject);
+
+ /* Begin overflow/underflow handling */
+
+ impHandleChainingEventsDuringDecomposition(rOutliner);
+
+ /* End overflow/underflow handling */
+
+ // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
+ rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
+
+ // now get back the layouted text size from outliner
+ const Size aOutlinerTextSize(rOutliner.GetPaperSize());
+ const basegfx::B2DVector aOutlinerScale(aOutlinerTextSize.Width(), aOutlinerTextSize.Height());
+ basegfx::B2DVector aAdjustTranslate(0.0, 0.0);
+
+ // correct horizontal translation using the now known text size
+ if(SDRTEXTHORZADJUST_CENTER == eHAdj || SDRTEXTHORZADJUST_RIGHT == eHAdj)
+ {
+ const double fFree(aAnchorTextRange.getWidth() - aOutlinerScale.getX());
+
+ if(SDRTEXTHORZADJUST_CENTER == eHAdj)
+ {
+ aAdjustTranslate.setX(fFree / 2.0);
+ }
+
+ if(SDRTEXTHORZADJUST_RIGHT == eHAdj)
+ {
+ aAdjustTranslate.setX(fFree);
+ }
+ }
+
+ // correct vertical translation using the now known text size
+ if(SDRTEXTVERTADJUST_CENTER == eVAdj || SDRTEXTVERTADJUST_BOTTOM == eVAdj)
+ {
+ const double fFree(aAnchorTextRange.getHeight() - aOutlinerScale.getY());
+
+ if(SDRTEXTVERTADJUST_CENTER == eVAdj)
+ {
+ aAdjustTranslate.setY(fFree / 2.0);
+ }
+
+ if(SDRTEXTVERTADJUST_BOTTOM == eVAdj)
+ {
+ aAdjustTranslate.setY(fFree);
+ }
+ }
+
+ // prepare matrices to apply to newly created primitives. aNewTransformA
+ // will get coordinates in aOutlinerScale size and positive in X, Y.
+ basegfx::B2DHomMatrix aNewTransformA;
+ basegfx::B2DHomMatrix aNewTransformB;
+
+ // translate relative to given primitive to get same rotation and shear
+ // as the master shape we are working on. For vertical, use the top-right
+ // corner
+ const double fStartInX(bVerticalWriting && bTopToBottom ? aAdjustTranslate.getX() + aOutlinerScale.getX() : aAdjustTranslate.getX());
+ const double fStartInY(bVerticalWriting && !bTopToBottom ? aAdjustTranslate.getY() + aOutlinerScale.getY() : aAdjustTranslate.getY());
+ aNewTransformA.translate(fStartInX, fStartInY);
+
+ // mirroring. We are now in aAnchorTextRange sizes. When mirroring in X and Y,
+ // move the null point which was top left to bottom right.
+ const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
+ const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
+ aNewTransformB.scale(bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0);
+
+ // in-between the translations of the single primitives will take place. Afterwards,
+ // the object's transformations need to be applied
+ aNewTransformB.shearX(fShearX);
+ aNewTransformB.rotate(fRotate);
+ aNewTransformB.translate(aTranslate.getX(), aTranslate.getY());
+
+ basegfx::B2DRange aClipRange;
+
+ // now break up text primitives.
+ impTextBreakupHandler aConverter(rOutliner);
+ aConverter.decomposeBlockTextPrimitive(aNewTransformA, aNewTransformB, aClipRange);
+
+ // cleanup outliner
+ rOutliner.Clear();
+ rOutliner.setVisualizedPage(nullptr);
+ rOutliner.SetControlWord(nOriginalControlWord);
+
+ rTarget = aConverter.extractPrimitive2DSequence();
+}
+
+// Direct decomposer for text visualization when you already have a prepared
+// Outliner containing all the needed information
+void SdrTextObj::impDecomposeBlockTextPrimitiveDirect(
+ drawinglayer::primitive2d::Primitive2DContainer& rTarget,
+ SdrOutliner& rOutliner,
+ const basegfx::B2DHomMatrix& rNewTransformA,
+ const basegfx::B2DHomMatrix& rNewTransformB,
+ const basegfx::B2DRange& rClipRange)
+{
+ impTextBreakupHandler aConverter(rOutliner);
+ aConverter.decomposeBlockTextPrimitive(rNewTransformA, rNewTransformB, rClipRange);
+ rTarget.append(aConverter.extractPrimitive2DSequence());
+}
+
+double SdrTextObj::GetCameraZRotation() const
+{
+ const css::uno::Any* pAny;
+ double fTextCameraZRotateAngle = 0.0;
+ const SfxItemSet& rSet = GetObjectItemSet();
+ const SdrCustomShapeGeometryItem& rGeometryItem(rSet.Get(SDRATTR_CUSTOMSHAPE_GEOMETRY));
+
+ pAny = rGeometryItem.GetPropertyValueByName("TextCameraZRotateAngle");
+
+ if ( pAny )
+ *pAny >>= fTextCameraZRotateAngle;
+
+ return fTextCameraZRotateAngle;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdotextpathdecomposition.cxx b/svx/source/svdraw/svdotextpathdecomposition.cxx
new file mode 100644
index 000000000..14bbd73ca
--- /dev/null
+++ b/svx/source/svdraw/svdotextpathdecomposition.cxx
@@ -0,0 +1,748 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <o3tl/safeint.hxx>
+#include <svx/svdotext.hxx>
+#include <svx/svdoutl.hxx>
+#include <basegfx/vector/b2dvector.hxx>
+#include <sdr/primitive2d/sdrtextprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <algorithm>
+#include <com/sun/star/i18n/BreakIterator.hpp>
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/i18n/CharacterIteratorMode.hpp>
+#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
+#include <drawinglayer/primitive2d/textprimitive2d.hxx>
+#include <basegfx/color/bcolor.hxx>
+
+
+// primitive decomposition helpers
+
+#include <drawinglayer/attribute/strokeattribute.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
+#include <svx/unoapi.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <sdr/attribute/sdrformtextoutlineattribute.hxx>
+
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::i18n;
+
+
+// PathTextPortion helper
+
+namespace
+{
+ class impPathTextPortion
+ {
+ basegfx::B2DVector maOffset;
+ OUString maText;
+ sal_Int32 mnTextStart;
+ sal_Int32 mnTextLength;
+ sal_Int32 mnParagraph;
+ SvxFont maFont;
+ ::std::vector< double > maDblDXArray; // double DXArray, font size independent -> unit coordinate system
+ css::lang::Locale maLocale;
+
+ bool mbRTL : 1;
+
+ public:
+ explicit impPathTextPortion(const DrawPortionInfo& rInfo)
+ : maOffset(rInfo.mrStartPos.X(), rInfo.mrStartPos.Y()),
+ maText(rInfo.maText),
+ mnTextStart(rInfo.mnTextStart),
+ mnTextLength(rInfo.mnTextLen),
+ mnParagraph(rInfo.mnPara),
+ maFont(rInfo.mrFont),
+ maLocale(rInfo.mpLocale ? *rInfo.mpLocale : css::lang::Locale()),
+ mbRTL(!rInfo.mrFont.IsVertical() && rInfo.IsRTL())
+ {
+ if(mnTextLength && !rInfo.mpDXArray.empty())
+ {
+ maDblDXArray.reserve(mnTextLength);
+
+ for(sal_Int32 a=0; a < mnTextLength; a++)
+ {
+ maDblDXArray.push_back(static_cast<double>(rInfo.mpDXArray[a]));
+ }
+ }
+ }
+
+ // for ::std::sort
+ bool operator<(const impPathTextPortion& rComp) const
+ {
+ if(mnParagraph < rComp.mnParagraph)
+ {
+ return true;
+ }
+
+ if(maOffset.getX() < rComp.maOffset.getX())
+ {
+ return true;
+ }
+
+ return (maOffset.getY() < rComp.maOffset.getY());
+ }
+
+ const OUString& getText() const { return maText; }
+ sal_Int32 getTextStart() const { return mnTextStart; }
+ sal_Int32 getTextLength() const { return mnTextLength; }
+ sal_Int32 getParagraph() const { return mnParagraph; }
+ const SvxFont& getFont() const { return maFont; }
+ bool isRTL() const { return mbRTL; }
+ const ::std::vector< double >& getDoubleDXArray() const { return maDblDXArray; }
+ const css::lang::Locale& getLocale() const { return maLocale; }
+
+ sal_Int32 getPortionIndex(sal_Int32 nIndex, sal_Int32 nLength) const
+ {
+ if(mbRTL)
+ {
+ return (mnTextStart + (mnTextLength - (nIndex + nLength)));
+ }
+ else
+ {
+ return (mnTextStart + nIndex);
+ }
+ }
+
+ double getDisplayLength(sal_Int32 nIndex, sal_Int32 nLength) const
+ {
+ drawinglayer::primitive2d::TextLayouterDevice aTextLayouter;
+ double fRetval(0.0);
+
+ if(maFont.IsVertical())
+ {
+ fRetval = aTextLayouter.getTextHeight() * static_cast<double>(nLength);
+ }
+ else
+ {
+ fRetval = aTextLayouter.getTextWidth(maText, getPortionIndex(nIndex, nLength), nLength);
+ }
+
+ return fRetval;
+ }
+ };
+} // end of anonymous namespace
+
+
+// TextBreakup helper
+
+namespace
+{
+ class impTextBreakupHandler
+ {
+ SdrOutliner& mrOutliner;
+ ::std::vector< impPathTextPortion > maPathTextPortions;
+
+ DECL_LINK(decompositionPathTextPrimitive, DrawPortionInfo*, void );
+
+ public:
+ explicit impTextBreakupHandler(SdrOutliner& rOutliner)
+ : mrOutliner(rOutliner)
+ {
+ }
+
+ const ::std::vector< impPathTextPortion >& decompositionPathTextPrimitive()
+ {
+ // strip portions to maPathTextPortions
+ mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, decompositionPathTextPrimitive));
+ mrOutliner.StripPortions();
+
+ if(!maPathTextPortions.empty())
+ {
+ // sort portions by paragraph, x and y
+ ::std::sort(maPathTextPortions.begin(), maPathTextPortions.end());
+ }
+
+ return maPathTextPortions;
+ }
+ };
+
+ IMPL_LINK(impTextBreakupHandler, decompositionPathTextPrimitive, DrawPortionInfo*, pInfo, void)
+ {
+ maPathTextPortions.emplace_back(*pInfo);
+ }
+} // end of anonymous namespace
+
+
+// TextBreakup one poly and one paragraph helper
+
+namespace
+{
+ class impPolygonParagraphHandler
+ {
+ const drawinglayer::attribute::SdrFormTextAttribute maSdrFormTextAttribute; // FormText parameters
+ drawinglayer::primitive2d::Primitive2DContainer& mrDecomposition; // destination primitive list
+ drawinglayer::primitive2d::Primitive2DContainer& mrShadowDecomposition; // destination primitive list for shadow
+ Reference < css::i18n::XBreakIterator > mxBreak; // break iterator
+
+ static double getParagraphTextLength(const ::std::vector< const impPathTextPortion* >& rTextPortions)
+ {
+ drawinglayer::primitive2d::TextLayouterDevice aTextLayouter;
+ double fRetval(0.0);
+
+ for(const impPathTextPortion* pCandidate : rTextPortions)
+ {
+ if(pCandidate && pCandidate->getTextLength())
+ {
+ aTextLayouter.setFont(pCandidate->getFont());
+ fRetval += pCandidate->getDisplayLength(0, pCandidate->getTextLength());
+ }
+ }
+
+ return fRetval;
+ }
+
+ sal_Int32 getNextGlyphLen(const impPathTextPortion* pCandidate, sal_Int32 nPosition, const css::lang::Locale& rFontLocale)
+ {
+ sal_Int32 nNextGlyphLen(1);
+
+ if(mxBreak.is())
+ {
+ sal_Int32 nDone(0);
+ nNextGlyphLen = mxBreak->nextCharacters(pCandidate->getText(), nPosition,
+ rFontLocale, CharacterIteratorMode::SKIPCELL, 1, nDone) - nPosition;
+ }
+
+ return nNextGlyphLen;
+ }
+
+ public:
+ impPolygonParagraphHandler(
+ const drawinglayer::attribute::SdrFormTextAttribute& rSdrFormTextAttribute,
+ drawinglayer::primitive2d::Primitive2DContainer& rDecomposition,
+ drawinglayer::primitive2d::Primitive2DContainer& rShadowDecomposition)
+ : maSdrFormTextAttribute(rSdrFormTextAttribute),
+ mrDecomposition(rDecomposition),
+ mrShadowDecomposition(rShadowDecomposition)
+ {
+ // prepare BreakIterator
+ Reference < XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ mxBreak = css::i18n::BreakIterator::create(xContext);
+ }
+
+ void HandlePair(const basegfx::B2DPolygon& rPolygonCandidate, const ::std::vector< const impPathTextPortion* >& rTextPortions)
+ {
+ // prepare polygon geometry, take into account as many parameters as possible
+ basegfx::B2DPolygon aPolygonCandidate(rPolygonCandidate);
+ const double fPolyLength(basegfx::utils::getLength(aPolygonCandidate));
+ double fPolyEnd(fPolyLength);
+ double fPolyStart(0.0);
+ double fAutosizeScaleFactor(1.0);
+ bool bAutosizeScale(false);
+
+ if(maSdrFormTextAttribute.getFormTextMirror())
+ {
+ aPolygonCandidate.flip();
+ }
+
+ if(maSdrFormTextAttribute.getFormTextStart()
+ && (XFormTextAdjust::Left == maSdrFormTextAttribute.getFormTextAdjust()
+ || XFormTextAdjust::Right == maSdrFormTextAttribute.getFormTextAdjust()))
+ {
+ if(XFormTextAdjust::Left == maSdrFormTextAttribute.getFormTextAdjust())
+ {
+ fPolyStart += maSdrFormTextAttribute.getFormTextStart();
+
+ if(fPolyStart > fPolyEnd)
+ {
+ fPolyStart = fPolyEnd;
+ }
+ }
+ else
+ {
+ fPolyEnd -= maSdrFormTextAttribute.getFormTextStart();
+
+ if(fPolyEnd < fPolyStart)
+ {
+ fPolyEnd = fPolyStart;
+ }
+ }
+ }
+
+ if(XFormTextAdjust::Left != maSdrFormTextAttribute.getFormTextAdjust())
+ {
+ // calculate total text length of this paragraph, some layout needs to be done
+ const double fParagraphTextLength(getParagraphTextLength(rTextPortions));
+
+ // check if text is too long for paragraph. If yes, handle as if left aligned (default),
+ // but still take care of XFormTextAdjust::AutoSize in that case
+ const bool bTextTooLong(fParagraphTextLength > (fPolyEnd - fPolyStart));
+
+ if(XFormTextAdjust::Right == maSdrFormTextAttribute.getFormTextAdjust())
+ {
+ if(!bTextTooLong)
+ {
+ // if right aligned, add difference to polygon start
+ fPolyStart += ((fPolyEnd - fPolyStart) - fParagraphTextLength);
+ }
+ }
+ else if(XFormTextAdjust::Center == maSdrFormTextAttribute.getFormTextAdjust())
+ {
+ if(!bTextTooLong)
+ {
+ // if centered, add half of difference to polygon start
+ fPolyStart += ((fPolyEnd - fPolyStart) - fParagraphTextLength) / 2.0;
+ }
+ }
+ else if(XFormTextAdjust::AutoSize == maSdrFormTextAttribute.getFormTextAdjust())
+ {
+ // if scale, prepare scale factor between curve length and text length
+ if(0.0 != fParagraphTextLength)
+ {
+ fAutosizeScaleFactor = (fPolyEnd - fPolyStart) / fParagraphTextLength;
+ bAutosizeScale = true;
+ }
+ }
+ }
+
+ // handle text portions for this paragraph
+ for(auto a = rTextPortions.begin(); a != rTextPortions.end() && fPolyStart < fPolyEnd; ++a)
+ {
+ const impPathTextPortion* pCandidate = *a;
+ basegfx::B2DVector aFontScaling;
+
+ if(pCandidate && pCandidate->getTextLength())
+ {
+ const drawinglayer::attribute::FontAttribute aCandidateFontAttribute(
+ drawinglayer::primitive2d::getFontAttributeFromVclFont(
+ aFontScaling,
+ pCandidate->getFont(),
+ pCandidate->isRTL(),
+ false));
+
+ drawinglayer::primitive2d::TextLayouterDevice aTextLayouter;
+ aTextLayouter.setFont(pCandidate->getFont());
+ sal_Int32 nUsedTextLength(0);
+
+ while(nUsedTextLength < pCandidate->getTextLength() && fPolyStart < fPolyEnd)
+ {
+ sal_Int32 nNextGlyphLen(getNextGlyphLen(pCandidate, pCandidate->getTextStart() + nUsedTextLength, pCandidate->getLocale()));
+
+ // prepare portion length. Takes RTL sections into account.
+ double fPortionLength(pCandidate->getDisplayLength(nUsedTextLength, nNextGlyphLen));
+
+ if(bAutosizeScale)
+ {
+ // when autosize scaling, expand portion length
+ fPortionLength *= fAutosizeScaleFactor;
+ }
+
+ // create transformation
+ basegfx::B2DHomMatrix aNewTransformA, aNewTransformB, aNewShadowTransform;
+ basegfx::B2DPoint aStartPos(basegfx::utils::getPositionAbsolute(aPolygonCandidate, fPolyStart, fPolyLength));
+ basegfx::B2DPoint aEndPos(aStartPos);
+
+ // add font scaling
+ aNewTransformA.scale(aFontScaling.getX(), aFontScaling.getY());
+
+ // prepare scaling of text primitive
+ if(bAutosizeScale)
+ {
+ // when autosize scaling, expand text primitive scaling to it
+ aNewTransformA.scale(fAutosizeScaleFactor, fAutosizeScaleFactor);
+ }
+
+ // eventually create shadow primitives from aDecomposition and add to rDecomposition
+ const bool bShadow(XFormTextShadow::NONE != maSdrFormTextAttribute.getFormTextShadow());
+
+ if(bShadow)
+ {
+ if(XFormTextShadow::Normal == maSdrFormTextAttribute.getFormTextShadow())
+ {
+ aNewShadowTransform.translate(
+ maSdrFormTextAttribute.getFormTextShdwXVal(),
+ -maSdrFormTextAttribute.getFormTextShdwYVal());
+ }
+ else // XFormTextShadow::Slant
+ {
+ double fScaleValue(maSdrFormTextAttribute.getFormTextShdwYVal() / 100.0);
+ double fShearValue(-basegfx::deg2rad<10>(maSdrFormTextAttribute.getFormTextShdwXVal()));
+
+ aNewShadowTransform.scale(1.0, fScaleValue);
+ aNewShadowTransform.shearX(sin(fShearValue));
+ aNewShadowTransform.scale(1.0, cos(fShearValue));
+ }
+ }
+
+ switch(maSdrFormTextAttribute.getFormTextStyle())
+ {
+ case XFormTextStyle::Rotate :
+ {
+ aEndPos = basegfx::utils::getPositionAbsolute(aPolygonCandidate, fPolyStart + fPortionLength, fPolyLength);
+ const basegfx::B2DVector aDirection(aEndPos - aStartPos);
+ aNewTransformB.rotate(atan2(aDirection.getY(), aDirection.getX()));
+ aNewTransformB.translate(aStartPos.getX(), aStartPos.getY());
+
+ break;
+ }
+ case XFormTextStyle::Upright :
+ {
+ aNewTransformB.translate(aStartPos.getX() - (fPortionLength / 2.0), aStartPos.getY());
+
+ break;
+ }
+ case XFormTextStyle::SlantX :
+ {
+ aEndPos = basegfx::utils::getPositionAbsolute(aPolygonCandidate, fPolyStart + fPortionLength, fPolyLength);
+ const basegfx::B2DVector aDirection(aEndPos - aStartPos);
+ const double fShearValue(atan2(aDirection.getY(), aDirection.getX()));
+ const double fSin(sin(fShearValue));
+ const double fCos(cos(fShearValue));
+
+ aNewTransformB.shearX(-fSin);
+
+ // Scale may lead to objects without height since fCos == 0.0 is possible.
+ // Renderers need to handle that, it's not a forbidden value and does not
+ // need to be avoided
+ aNewTransformB.scale(1.0, fCos);
+ aNewTransformB.translate(aStartPos.getX() - (fPortionLength / 2.0), aStartPos.getY());
+
+ break;
+ }
+ case XFormTextStyle::SlantY :
+ {
+ aEndPos = basegfx::utils::getPositionAbsolute(aPolygonCandidate, fPolyStart + fPortionLength, fPolyLength);
+ const basegfx::B2DVector aDirection(aEndPos - aStartPos);
+ const double fShearValue(atan2(aDirection.getY(), aDirection.getX()));
+ const double fCos(cos(fShearValue));
+ const double fTan(tan(fShearValue));
+
+ // shear to 'stand' on the curve
+ aNewTransformB.shearY(fTan);
+
+ // scale in X to make as tight as needed. As with XFT_SLANT_X, this may
+ // lead to primitives without width which the renderers will handle
+ aNewTransformA.scale(fCos, 1.0);
+
+ aNewTransformB.translate(aStartPos.getX(), aStartPos.getY());
+
+ break;
+ }
+ default : break; // XFormTextStyle::NONE
+ }
+
+ // distance from path?
+ if(maSdrFormTextAttribute.getFormTextDistance())
+ {
+ if(aEndPos.equal(aStartPos))
+ {
+ aEndPos = basegfx::utils::getPositionAbsolute(aPolygonCandidate, fPolyStart + fPortionLength, fPolyLength);
+ }
+
+ // use back vector (aStartPos - aEndPos) here to get mirrored perpendicular as in old stuff
+ const basegfx::B2DVector aPerpendicular(
+ basegfx::getNormalizedPerpendicular(aStartPos - aEndPos) *
+ maSdrFormTextAttribute.getFormTextDistance());
+ aNewTransformB.translate(aPerpendicular.getX(), aPerpendicular.getY());
+ }
+
+ if(!pCandidate->getText().isEmpty() && nNextGlyphLen)
+ {
+ const sal_Int32 nPortionIndex(pCandidate->getPortionIndex(nUsedTextLength, nNextGlyphLen));
+ ::std::vector< double > aNewDXArray;
+
+ if(nNextGlyphLen > 1 && !pCandidate->getDoubleDXArray().empty())
+ {
+ // copy DXArray for portion
+ aNewDXArray.insert(
+ aNewDXArray.begin(),
+ pCandidate->getDoubleDXArray().begin() + nPortionIndex,
+ pCandidate->getDoubleDXArray().begin() + (nPortionIndex + nNextGlyphLen));
+
+ if(nPortionIndex > 0)
+ {
+ // adapt to portion start
+ double fDXOffset= *(pCandidate->getDoubleDXArray().begin() + (nPortionIndex - 1));
+ ::std::transform(
+ aNewDXArray.begin(), aNewDXArray.end(),
+ aNewDXArray.begin(), [fDXOffset](double x) { return x - fDXOffset; });
+ }
+
+ if(bAutosizeScale)
+ {
+ // when autosize scaling, adapt to DXArray, too
+ ::std::transform(
+ aNewDXArray.begin(), aNewDXArray.end(),
+ aNewDXArray.begin(), [fAutosizeScaleFactor](double x) { return x * fAutosizeScaleFactor; });
+ }
+ }
+
+ if(bShadow)
+ {
+ // shadow primitive creation
+ const Color aShadowColor(maSdrFormTextAttribute.getFormTextShdwColor());
+ const basegfx::BColor aRGBShadowColor(aShadowColor.getBColor());
+
+ mrShadowDecomposition.push_back(
+ new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
+ aNewTransformB * aNewShadowTransform * aNewTransformA,
+ pCandidate->getText(),
+ nPortionIndex,
+ nNextGlyphLen,
+ std::vector(aNewDXArray),
+ aCandidateFontAttribute,
+ pCandidate->getLocale(),
+ aRGBShadowColor) );
+ }
+
+ {
+ // primitive creation
+ const Color aColor(pCandidate->getFont().GetColor());
+ const basegfx::BColor aRGBColor(aColor.getBColor());
+
+ mrDecomposition.push_back(
+ new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
+ aNewTransformB * aNewTransformA,
+ pCandidate->getText(),
+ nPortionIndex,
+ nNextGlyphLen,
+ std::move(aNewDXArray),
+ aCandidateFontAttribute,
+ pCandidate->getLocale(),
+ aRGBColor) );
+ }
+ }
+
+ // consume from portion
+ nUsedTextLength += nNextGlyphLen;
+
+ // consume from polygon
+ fPolyStart += fPortionLength;
+ }
+ }
+ }
+ }
+ };
+} // end of anonymous namespace
+
+
+// primitive decomposition helpers
+
+namespace
+{
+ void impAddPolygonStrokePrimitives(
+ const basegfx::B2DPolyPolygonVector& rB2DPolyPolyVector,
+ const basegfx::B2DHomMatrix& rTransform,
+ const drawinglayer::attribute::LineAttribute& rLineAttribute,
+ const drawinglayer::attribute::StrokeAttribute& rStrokeAttribute,
+ drawinglayer::primitive2d::Primitive2DContainer& rTarget)
+ {
+ for(const auto& rB2DPolyPolygon : rB2DPolyPolyVector)
+ {
+ // prepare PolyPolygons
+ basegfx::B2DPolyPolygon aB2DPolyPolygon = rB2DPolyPolygon;
+ aB2DPolyPolygon.transform(rTransform);
+
+ for(auto const& rPolygon : std::as_const(aB2DPolyPolygon))
+ {
+ // create one primitive per polygon
+ rTarget.push_back(
+ new drawinglayer::primitive2d::PolygonStrokePrimitive2D(
+ rPolygon, rLineAttribute, rStrokeAttribute) );
+ }
+ }
+ }
+
+ drawinglayer::primitive2d::Primitive2DContainer impAddPathTextOutlines(
+ const drawinglayer::primitive2d::Primitive2DContainer& rSource,
+ const drawinglayer::attribute::SdrFormTextOutlineAttribute& rOutlineAttribute)
+ {
+ drawinglayer::primitive2d::Primitive2DContainer aNewPrimitives;
+
+ for(const drawinglayer::primitive2d::Primitive2DReference& a : rSource)
+ {
+ const drawinglayer::primitive2d::TextSimplePortionPrimitive2D* pTextCandidate = dynamic_cast< const drawinglayer::primitive2d::TextSimplePortionPrimitive2D* >(a.get());
+
+ if(pTextCandidate)
+ {
+ basegfx::B2DPolyPolygonVector aB2DPolyPolyVector;
+ basegfx::B2DHomMatrix aPolygonTransform;
+
+ // get text outlines and their object transformation
+ pTextCandidate->getTextOutlinesAndTransformation(aB2DPolyPolyVector, aPolygonTransform);
+
+ if(!aB2DPolyPolyVector.empty())
+ {
+ // create stroke primitives
+ drawinglayer::primitive2d::Primitive2DContainer aStrokePrimitives;
+ impAddPolygonStrokePrimitives(
+ aB2DPolyPolyVector,
+ aPolygonTransform,
+ rOutlineAttribute.getLineAttribute(),
+ rOutlineAttribute.getStrokeAttribute(),
+ aStrokePrimitives);
+ const sal_uInt32 nStrokeCount(aStrokePrimitives.size());
+
+ if(nStrokeCount)
+ {
+ if(rOutlineAttribute.getTransparence())
+ {
+ // create UnifiedTransparencePrimitive2D
+ aNewPrimitives.push_back(
+ new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
+ std::move(aStrokePrimitives),
+ static_cast<double>(rOutlineAttribute.getTransparence()) / 100.0) );
+ }
+ else
+ {
+ // add polygons to rDecomposition as polygonStrokePrimitives
+ aNewPrimitives.append( std::move(aStrokePrimitives) );
+ }
+ }
+ }
+ }
+ }
+
+ return aNewPrimitives;
+ }
+} // end of anonymous namespace
+
+
+// primitive decomposition
+
+void SdrTextObj::impDecomposePathTextPrimitive(
+ drawinglayer::primitive2d::Primitive2DContainer& rTarget,
+ const drawinglayer::primitive2d::SdrPathTextPrimitive2D& rSdrPathTextPrimitive,
+ const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
+{
+ drawinglayer::primitive2d::Primitive2DContainer aRetvalA;
+ drawinglayer::primitive2d::Primitive2DContainer aRetvalB;
+
+ // prepare outliner
+ SdrOutliner& rOutliner = ImpGetDrawOutliner();
+ rOutliner.SetUpdateLayout(true);
+ rOutliner.Clear();
+ rOutliner.SetPaperSize(Size(LONG_MAX,LONG_MAX));
+ rOutliner.SetText(rSdrPathTextPrimitive.getOutlinerParaObject());
+
+ // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
+ rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
+
+ // now break up to text portions
+ impTextBreakupHandler aConverter(rOutliner);
+ const ::std::vector< impPathTextPortion > rPathTextPortions = aConverter.decompositionPathTextPrimitive();
+
+ if(!rPathTextPortions.empty())
+ {
+ // get FormText and polygon values
+ const drawinglayer::attribute::SdrFormTextAttribute& rFormTextAttribute = rSdrPathTextPrimitive.getSdrFormTextAttribute();
+ const basegfx::B2DPolyPolygon& rPathPolyPolygon(rSdrPathTextPrimitive.getPathPolyPolygon());
+
+ // get loop count
+ sal_uInt32 nLoopCount(rPathPolyPolygon.count());
+
+ if(o3tl::make_unsigned(rOutliner.GetParagraphCount()) < nLoopCount)
+ {
+ nLoopCount = rOutliner.GetParagraphCount();
+ }
+
+ if(nLoopCount)
+ {
+ // prepare common decomposition stuff
+ drawinglayer::primitive2d::Primitive2DContainer aRegularDecomposition;
+ drawinglayer::primitive2d::Primitive2DContainer aShadowDecomposition;
+ impPolygonParagraphHandler aPolygonParagraphHandler(
+ rFormTextAttribute,
+ aRegularDecomposition,
+ aShadowDecomposition);
+ sal_uInt32 a;
+
+ for(a = 0; a < nLoopCount; a++)
+ {
+ // filter text portions for this paragraph
+ ::std::vector< const impPathTextPortion* > aParagraphTextPortions;
+
+ for(const auto & rCandidate : rPathTextPortions)
+ {
+ if(static_cast<sal_uInt32>(rCandidate.getParagraph()) == a)
+ {
+ aParagraphTextPortions.push_back(&rCandidate);
+ }
+ }
+
+ // handle data pair polygon/ParagraphTextPortions
+ if(!aParagraphTextPortions.empty())
+ {
+ aPolygonParagraphHandler.HandlePair(rPathPolyPolygon.getB2DPolygon(a), aParagraphTextPortions);
+ }
+ }
+
+ const sal_uInt32 nShadowCount(aShadowDecomposition.size());
+ const sal_uInt32 nRegularCount(aRegularDecomposition.size());
+
+ if(nShadowCount)
+ {
+ // add shadow primitives to decomposition
+
+ // if necessary, add shadow outlines
+ if(rFormTextAttribute.getFormTextOutline()
+ && !rFormTextAttribute.getShadowOutline().isDefault())
+ {
+ aRetvalA = aShadowDecomposition;
+ const drawinglayer::primitive2d::Primitive2DContainer aOutlines(
+ impAddPathTextOutlines(
+ aShadowDecomposition,
+ rFormTextAttribute.getShadowOutline()));
+
+ aRetvalA.append(aOutlines);
+ }
+ else
+ aRetvalA = std::move(aShadowDecomposition);
+ }
+
+ if(nRegularCount)
+ {
+ // add normal primitives to decomposition
+
+ // if necessary, add outlines
+ if(rFormTextAttribute.getFormTextOutline()
+ && !rFormTextAttribute.getOutline().isDefault())
+ {
+ aRetvalB = aRegularDecomposition;
+ const drawinglayer::primitive2d::Primitive2DContainer aOutlines(
+ impAddPathTextOutlines(
+ aRegularDecomposition,
+ rFormTextAttribute.getOutline()));
+
+ aRetvalB.append(aOutlines);
+ }
+ else
+ aRetvalB = std::move(aRegularDecomposition);
+ }
+ }
+ }
+
+ // clean up outliner
+ rOutliner.SetDrawPortionHdl(Link<DrawPortionInfo*,void>());
+ rOutliner.Clear();
+ rOutliner.setVisualizedPage(nullptr);
+
+ // concatenate all results
+ rTarget.append(std::move(aRetvalA));
+ rTarget.append(std::move(aRetvalB));
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdotxat.cxx b/svx/source/svdraw/svdotxat.cxx
new file mode 100644
index 000000000..97bdb5e59
--- /dev/null
+++ b/svx/source/svdraw/svdotxat.cxx
@@ -0,0 +1,457 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <comphelper/string.hxx>
+#include <o3tl/sorted_vector.hxx>
+#include <o3tl/string_view.hxx>
+#include <svl/style.hxx>
+#include <svx/svdotext.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdoutl.hxx>
+#include <svx/svdorect.hxx>
+#include <svx/svdocapt.hxx>
+#include <editeng/editdata.hxx>
+#include <svx/sdtfchim.hxx>
+
+
+#include <editeng/outlobj.hxx>
+#include <editeng/outliner.hxx>
+#include <editeng/editobj.hxx>
+
+namespace {
+// The style family which is appended to the style names is padded to this many characters.
+const short PADDING_LENGTH_FOR_STYLE_FAMILY = 5;
+// this character will be used to pad the style families when they are appended to the style names
+const char PADDING_CHARACTER_FOR_STYLE_FAMILY = ' ';
+}
+
+bool SdrTextObj::AdjustTextFrameWidthAndHeight( tools::Rectangle& rR, bool bHgt, bool bWdt ) const
+{
+ if (!mbTextFrame)
+ // Not a text frame. Bail out.
+ return false;
+
+ if (rR.IsEmpty())
+ // Empty rectangle.
+ return false;
+
+ bool bFitToSize = IsFitToSize();
+ if (bFitToSize)
+ return false;
+
+ bool bWdtGrow = bWdt && IsAutoGrowWidth();
+ bool bHgtGrow = bHgt && IsAutoGrowHeight();
+ if (!bWdtGrow && !bHgtGrow)
+ // Not supposed to auto-adjust width or height.
+ return false;
+
+ SdrTextAniKind eAniKind = GetTextAniKind();
+ SdrTextAniDirection eAniDir = GetTextAniDirection();
+
+ bool bScroll = eAniKind == SdrTextAniKind::Scroll || eAniKind == SdrTextAniKind::Alternate || eAniKind == SdrTextAniKind::Slide;
+ bool bHScroll = bScroll && (eAniDir == SdrTextAniDirection::Left || eAniDir == SdrTextAniDirection::Right);
+ bool bVScroll = bScroll && (eAniDir == SdrTextAniDirection::Up || eAniDir == SdrTextAniDirection::Down);
+
+ tools::Rectangle aOldRect = rR;
+ tools::Long nHgt = 0, nMinHgt = 0, nMaxHgt = 0;
+ tools::Long nWdt = 0, nMinWdt = 0, nMaxWdt = 0;
+
+ Size aNewSize = rR.GetSize();
+ aNewSize.AdjustWidth( -1 ); aNewSize.AdjustHeight( -1 );
+
+ Size aMaxSiz(100000, 100000);
+ Size aTmpSiz(getSdrModelFromSdrObject().GetMaxObjSize());
+
+ if (aTmpSiz.Width())
+ aMaxSiz.setWidth( aTmpSiz.Width() );
+ if (aTmpSiz.Height())
+ aMaxSiz.setHeight( aTmpSiz.Height() );
+
+ if (bWdtGrow)
+ {
+ nMinWdt = GetMinTextFrameWidth();
+ nMaxWdt = GetMaxTextFrameWidth();
+ if (nMaxWdt == 0 || nMaxWdt > aMaxSiz.Width())
+ nMaxWdt = aMaxSiz.Width();
+ if (nMinWdt <= 0)
+ nMinWdt = 1;
+
+ aNewSize.setWidth( nMaxWdt );
+ }
+
+ if (bHgtGrow)
+ {
+ nMinHgt = GetMinTextFrameHeight();
+ nMaxHgt = GetMaxTextFrameHeight();
+ if (nMaxHgt == 0 || nMaxHgt > aMaxSiz.Height())
+ nMaxHgt = aMaxSiz.Height();
+ if (nMinHgt <= 0)
+ nMinHgt = 1;
+
+ aNewSize.setHeight( nMaxHgt );
+ }
+
+ tools::Long nHDist = GetTextLeftDistance() + GetTextRightDistance();
+ tools::Long nVDist = GetTextUpperDistance() + GetTextLowerDistance();
+ aNewSize.AdjustWidth( -nHDist );
+ aNewSize.AdjustHeight( -nVDist );
+
+ if (aNewSize.Width() < 2)
+ aNewSize.setWidth( 2 );
+ if (aNewSize.Height() < 2)
+ aNewSize.setHeight( 2 );
+
+ if (!IsInEditMode())
+ {
+ if (bHScroll)
+ aNewSize.setWidth( 0x0FFFFFFF ); // don't break ticker text
+ if (bVScroll)
+ aNewSize.setHeight( 0x0FFFFFFF );
+ }
+
+ if (mpEditingOutliner)
+ {
+ mpEditingOutliner->SetMaxAutoPaperSize(aNewSize);
+ if (bWdtGrow)
+ {
+ Size aSiz2(mpEditingOutliner->CalcTextSize());
+ nWdt = aSiz2.Width() + 1; // a little tolerance
+ if (bHgtGrow)
+ nHgt = aSiz2.Height() + 1; // a little tolerance
+ }
+ else
+ {
+ nHgt = mpEditingOutliner->GetTextHeight() + 1; // a little tolerance
+ }
+ }
+ else
+ {
+ Outliner& rOutliner = ImpGetDrawOutliner();
+ rOutliner.SetPaperSize(aNewSize);
+ rOutliner.SetUpdateLayout(true);
+ // TODO: add the optimization with bPortionInfoChecked etc. here
+ OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject();
+ if (pOutlinerParaObject)
+ {
+ rOutliner.SetText(*pOutlinerParaObject);
+ rOutliner.SetFixedCellHeight(GetMergedItem(SDRATTR_TEXT_USEFIXEDCELLHEIGHT).GetValue());
+ }
+
+ if (bWdtGrow)
+ {
+ Size aSiz2(rOutliner.CalcTextSize());
+ nWdt = aSiz2.Width() + 1; // a little tolerance
+ if (bHgtGrow)
+ nHgt = aSiz2.Height() + 1; // a little tolerance
+ }
+ else
+ {
+ nHgt = rOutliner.GetTextHeight() + 1; // a little tolerance
+ }
+ rOutliner.Clear();
+ }
+
+ if (nWdt < nMinWdt)
+ nWdt = nMinWdt;
+ if (nWdt > nMaxWdt)
+ nWdt = nMaxWdt;
+ nWdt += nHDist;
+ if (nWdt < 1)
+ nWdt = 1; // nHDist may be negative
+ if (nHgt < nMinHgt)
+ nHgt = nMinHgt;
+ if (nHgt > nMaxHgt)
+ nHgt = nMaxHgt;
+ nHgt += nVDist;
+ if (nHgt < 1)
+ nHgt = 1; // nVDist may be negative
+ tools::Long nWdtGrow = nWdt - (rR.Right() - rR.Left());
+ tools::Long nHgtGrow = nHgt - (rR.Bottom() - rR.Top());
+
+ if (nWdtGrow == 0)
+ bWdtGrow = false;
+ if (nHgtGrow == 0)
+ bHgtGrow = false;
+
+ if (!bWdtGrow && !bHgtGrow)
+ return false;
+
+ if (bWdtGrow)
+ {
+ SdrTextHorzAdjust eHAdj = GetTextHorizontalAdjust();
+
+ if (eHAdj == SDRTEXTHORZADJUST_LEFT)
+ rR.AdjustRight(nWdtGrow );
+ else if (eHAdj == SDRTEXTHORZADJUST_RIGHT)
+ rR.AdjustLeft( -nWdtGrow );
+ else
+ {
+ tools::Long nWdtGrow2 = nWdtGrow / 2;
+ rR.AdjustLeft( -nWdtGrow2 );
+ rR.SetRight( rR.Left() + nWdt );
+ }
+ }
+
+ if (bHgtGrow)
+ {
+ SdrTextVertAdjust eVAdj = GetTextVerticalAdjust();
+
+ if (eVAdj == SDRTEXTVERTADJUST_TOP)
+ rR.AdjustBottom(nHgtGrow );
+ else if (eVAdj == SDRTEXTVERTADJUST_BOTTOM)
+ rR.AdjustTop( -nHgtGrow );
+ else
+ {
+ tools::Long nHgtGrow2 = nHgtGrow / 2;
+ rR.AdjustTop( -nHgtGrow2 );
+ rR.SetBottom( rR.Top() + nHgt );
+ }
+ }
+
+ if (maGeo.nRotationAngle)
+ {
+ // Object is rotated.
+ Point aD1(rR.TopLeft());
+ aD1 -= aOldRect.TopLeft();
+ Point aD2(aD1);
+ RotatePoint(aD2, Point(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
+ aD2 -= aD1;
+ rR.Move(aD2.X(), aD2.Y());
+ }
+
+ return true;
+}
+
+bool SdrTextObj::NbcAdjustTextFrameWidthAndHeight(bool bHgt, bool bWdt)
+{
+ bool bRet = AdjustTextFrameWidthAndHeight(maRect,bHgt,bWdt);
+ if (bRet)
+ {
+ SetBoundAndSnapRectsDirty();
+ if (auto pRectObj = dynamic_cast<SdrRectObj *>(this)) { // this is a hack
+ pRectObj->SetXPolyDirty();
+ }
+ if (auto pCaptionObj = dynamic_cast<SdrCaptionObj *>(this)) { // this is a hack
+ pCaptionObj->ImpRecalcTail();
+ }
+ }
+ return bRet;
+}
+
+bool SdrTextObj::AdjustTextFrameWidthAndHeight()
+{
+ tools::Rectangle aNewRect(maRect);
+ bool bRet=AdjustTextFrameWidthAndHeight(aNewRect);
+ if (bRet) {
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ maRect = aNewRect;
+ SetBoundAndSnapRectsDirty();
+ if (auto pRectObj = dynamic_cast<SdrRectObj *>(this)) { // this is a hack
+ pRectObj->SetXPolyDirty();
+ }
+ bool bScPostIt = false;
+ if (auto pCaptionObj = dynamic_cast<SdrCaptionObj *>(this)) { // this is a hack
+ pCaptionObj->ImpRecalcTail();
+ // tdf#114956, tdf#138549 use GetSpecialTextBoxShadow to recognize
+ // that this SdrCaption is for a ScPostit
+ bScPostIt = pCaptionObj->GetSpecialTextBoxShadow();
+ }
+
+ // to not slow down EditView visualization on Overlay (see
+ // TextEditOverlayObject) it is necessary to suppress the
+ // Invalidates for the deep repaint when the size of the
+ // TextFrame changed (AdjustTextFrameWidthAndHeight returned
+ // true). The ObjectChanges are valid, invalidate will be
+ // done on EndTextEdit anyways
+ const bool bSuppressChangeWhenEditOnOverlay(
+ IsInEditMode() &&
+ GetTextEditOutliner() &&
+ GetTextEditOutliner()->hasEditViewCallbacks());
+
+ if (!bSuppressChangeWhenEditOnOverlay || bScPostIt)
+ {
+ SetChanged();
+ BroadcastObjectChange();
+ }
+
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+ }
+ return bRet;
+}
+
+void SdrTextObj::ImpSetTextStyleSheetListeners()
+{
+ SfxStyleSheetBasePool* pStylePool(getSdrModelFromSdrObject().GetStyleSheetPool());
+ if (pStylePool==nullptr)
+ return;
+
+ std::vector<OUString> aStyleNames;
+ OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject();
+ if (pOutlinerParaObject!=nullptr)
+ {
+ // First, we collect all stylesheets contained in the ParaObject in
+ // the container aStyles. The Family is always appended to the name
+ // of the stylesheet.
+ const EditTextObject& rTextObj=pOutlinerParaObject->GetTextObject();
+ OUString aStyleName;
+ SfxStyleFamily eStyleFam;
+ sal_Int32 nParaCnt=rTextObj.GetParagraphCount();
+
+
+ for(sal_Int32 nParaNum(0); nParaNum < nParaCnt; nParaNum++)
+ {
+ rTextObj.GetStyleSheet(nParaNum, aStyleName, eStyleFam);
+
+ if (!aStyleName.isEmpty())
+ {
+ AppendFamilyToStyleName(aStyleName, eStyleFam);
+
+ bool bFnd(false);
+ sal_uInt32 nNum(aStyleNames.size());
+
+ while(!bFnd && nNum > 0)
+ {
+ // we don't want duplicate stylesheets
+ nNum--;
+ bFnd = aStyleName == aStyleNames[nNum];
+ }
+
+ if(!bFnd)
+ {
+ aStyleNames.push_back(aStyleName);
+ }
+ }
+ }
+ }
+
+ // now convert the strings in the vector from names to StyleSheet*
+ o3tl::sorted_vector<SfxStyleSheet*> aStyleSheets;
+ while (!aStyleNames.empty()) {
+ OUString aName = aStyleNames.back();
+ aStyleNames.pop_back();
+
+ SfxStyleFamily eFam = ReadFamilyFromStyleName(aName);
+ SfxStyleSheetBase* pStyleBase = pStylePool->Find(aName,eFam);
+ SfxStyleSheet* pStyle = dynamic_cast<SfxStyleSheet*>( pStyleBase );
+ if (pStyle!=nullptr && pStyle!=GetStyleSheet()) {
+ aStyleSheets.insert(pStyle);
+ }
+ }
+ // now remove all superfluous stylesheets
+ sal_uInt16 nNum=GetBroadcasterCount();
+ while (nNum>0) {
+ nNum--;
+ SfxBroadcaster* pBroadcast=GetBroadcasterJOE(nNum);
+ SfxStyleSheet* pStyle=dynamic_cast<SfxStyleSheet*>( pBroadcast );
+ if (pStyle!=nullptr && pStyle!=GetStyleSheet()) { // special case for stylesheet of the object
+ if (aStyleSheets.find(pStyle)==aStyleSheets.end()) {
+ EndListening(*pStyle);
+ }
+ }
+ }
+ // and finally, merge all stylesheets that are contained in aStyles with previous broadcasters
+ for(SfxStyleSheet* pStyle : aStyleSheets) {
+ // let StartListening see for itself if there's already a listener registered
+ StartListening(*pStyle, DuplicateHandling::Prevent);
+ }
+}
+
+/** iterates over the paragraphs of a given SdrObject and removes all
+ hard set character attributes with the which ids contained in the
+ given vector
+*/
+void SdrTextObj::RemoveOutlinerCharacterAttribs( const std::vector<sal_uInt16>& rCharWhichIds )
+{
+ sal_Int32 nText = getTextCount();
+
+ while( --nText >= 0 )
+ {
+ SdrText* pText = getText( nText );
+ OutlinerParaObject* pOutlinerParaObject = pText ? pText->GetOutlinerParaObject() : nullptr;
+
+ if(pOutlinerParaObject)
+ {
+ Outliner* pOutliner = nullptr;
+
+ if( mpEditingOutliner || (pText == getActiveText()) )
+ pOutliner = mpEditingOutliner;
+
+ if(!pOutliner)
+ {
+ pOutliner = &ImpGetDrawOutliner();
+ pOutliner->SetText(*pOutlinerParaObject);
+ }
+
+ ESelection aSelAll( 0, 0, EE_PARA_ALL, EE_TEXTPOS_ALL );
+ for( const auto& rWhichId : rCharWhichIds )
+ {
+ pOutliner->RemoveAttribs( aSelAll, false, rWhichId );
+ }
+
+ if(!mpEditingOutliner || (pText != getActiveText()) )
+ {
+ const sal_Int32 nParaCount = pOutliner->GetParagraphCount();
+ std::optional<OutlinerParaObject> pTemp = pOutliner->CreateParaObject(0, nParaCount);
+ pOutliner->Clear();
+ NbcSetOutlinerParaObjectForText(std::move(pTemp), pText);
+ }
+ }
+ }
+}
+
+bool SdrTextObj::HasText() const
+{
+ if (mpEditingOutliner)
+ return HasTextImpl(mpEditingOutliner);
+
+ OutlinerParaObject* pOPO = GetOutlinerParaObject();
+
+ bool bHasText = false;
+ if( pOPO )
+ {
+ const EditTextObject& rETO = pOPO->GetTextObject();
+ sal_Int32 nParaCount = rETO.GetParagraphCount();
+
+ if( nParaCount > 0 )
+ bHasText = (nParaCount > 1) || (!rETO.GetText( 0 ).isEmpty());
+ }
+
+ return bHasText;
+}
+
+void SdrTextObj::AppendFamilyToStyleName(OUString& styleName, SfxStyleFamily family)
+{
+ OUStringBuffer aFam;
+ aFam.append(static_cast<sal_Int32>(family));
+ comphelper::string::padToLength(aFam, PADDING_LENGTH_FOR_STYLE_FAMILY , PADDING_CHARACTER_FOR_STYLE_FAMILY);
+
+ styleName += "|" + aFam;
+}
+
+SfxStyleFamily SdrTextObj::ReadFamilyFromStyleName(const OUString& styleName)
+{
+ std::u16string_view familyString = styleName.subView(styleName.getLength() - PADDING_LENGTH_FOR_STYLE_FAMILY);
+ familyString = comphelper::string::stripEnd(familyString, PADDING_CHARACTER_FOR_STYLE_FAMILY);
+ sal_uInt16 nFam = static_cast<sal_uInt16>(o3tl::toInt32(familyString));
+ assert(nFam != 0);
+ return static_cast<SfxStyleFamily>(nFam);
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdotxdr.cxx b/svx/source/svdraw/svdotxdr.cxx
new file mode 100644
index 000000000..ad2f79933
--- /dev/null
+++ b/svx/source/svdraw/svdotxdr.cxx
@@ -0,0 +1,248 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <svx/svdotext.hxx>
+#include <svx/svdhdl.hxx>
+#include <svx/svddrag.hxx>
+#include <svx/svdview.hxx>
+#include <svx/svdorect.hxx>
+#include <svx/strings.hrc>
+#include <svx/svdoashp.hxx>
+#include <tools/bigint.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/ptrstyle.hxx>
+
+
+sal_uInt32 SdrTextObj::GetHdlCount() const
+{
+ return 8;
+}
+
+void SdrTextObj::AddToHdlList(SdrHdlList& rHdlList) const
+{
+ for(sal_uInt32 nHdlNum=0; nHdlNum<8; ++nHdlNum)
+ {
+ Point aPnt;
+ SdrHdlKind eKind = SdrHdlKind::UpperLeft;
+ switch (nHdlNum) {
+ case 0: aPnt=maRect.TopLeft(); eKind=SdrHdlKind::UpperLeft; break;
+ case 1: aPnt=maRect.TopCenter(); eKind=SdrHdlKind::Upper; break;
+ case 2: aPnt=maRect.TopRight(); eKind=SdrHdlKind::UpperRight; break;
+ case 3: aPnt=maRect.LeftCenter(); eKind=SdrHdlKind::Left ; break;
+ case 4: aPnt=maRect.RightCenter(); eKind=SdrHdlKind::Right; break;
+ case 5: aPnt=maRect.BottomLeft(); eKind=SdrHdlKind::LowerLeft; break;
+ case 6: aPnt=maRect.BottomCenter(); eKind=SdrHdlKind::Lower; break;
+ case 7: aPnt=maRect.BottomRight(); eKind=SdrHdlKind::LowerRight; break;
+ }
+ if (maGeo.nShearAngle) ShearPoint(aPnt,maRect.TopLeft(),maGeo.mfTanShearAngle);
+ if (maGeo.nRotationAngle) RotatePoint(aPnt,maRect.TopLeft(),maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle);
+ std::unique_ptr<SdrHdl> pH(new SdrHdl(aPnt,eKind));
+ pH->SetObj(const_cast<SdrTextObj*>(this));
+ pH->SetRotationAngle(maGeo.nRotationAngle);
+ rHdlList.AddHdl(std::move(pH));
+ }
+}
+
+
+bool SdrTextObj::hasSpecialDrag() const
+{
+ return true;
+}
+
+tools::Rectangle SdrTextObj::ImpDragCalcRect(const SdrDragStat& rDrag) const
+{
+ tools::Rectangle aTmpRect(maRect);
+ const SdrHdl* pHdl=rDrag.GetHdl();
+ SdrHdlKind eHdl=pHdl==nullptr ? SdrHdlKind::Move : pHdl->GetKind();
+ bool bEcke=(eHdl==SdrHdlKind::UpperLeft || eHdl==SdrHdlKind::UpperRight || eHdl==SdrHdlKind::LowerLeft || eHdl==SdrHdlKind::LowerRight);
+ bool bOrtho=rDrag.GetView()!=nullptr && rDrag.GetView()->IsOrtho();
+ bool bBigOrtho=bEcke && bOrtho && rDrag.GetView()->IsBigOrtho();
+ Point aPos(rDrag.GetNow());
+ // Unrotate:
+ if (maGeo.nRotationAngle) RotatePoint(aPos,aTmpRect.TopLeft(),-maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle);
+ // Unshear:
+ if (maGeo.nShearAngle) ShearPoint(aPos,aTmpRect.TopLeft(),-maGeo.mfTanShearAngle);
+
+ bool bLft=(eHdl==SdrHdlKind::UpperLeft || eHdl==SdrHdlKind::Left || eHdl==SdrHdlKind::LowerLeft);
+ bool bRgt=(eHdl==SdrHdlKind::UpperRight || eHdl==SdrHdlKind::Right || eHdl==SdrHdlKind::LowerRight);
+ bool bTop=(eHdl==SdrHdlKind::UpperRight || eHdl==SdrHdlKind::Upper || eHdl==SdrHdlKind::UpperLeft);
+ bool bBtm=(eHdl==SdrHdlKind::LowerRight || eHdl==SdrHdlKind::Lower || eHdl==SdrHdlKind::LowerLeft);
+ if (bLft) aTmpRect.SetLeft(aPos.X() );
+ if (bRgt) aTmpRect.SetRight(aPos.X() );
+ if (bTop) aTmpRect.SetTop(aPos.Y() );
+ if (bBtm) aTmpRect.SetBottom(aPos.Y() );
+ if (bOrtho) { // Ortho
+ tools::Long nWdt0=maRect.Right() -maRect.Left();
+ tools::Long nHgt0=maRect.Bottom()-maRect.Top();
+ tools::Long nXMul=aTmpRect.Right() -aTmpRect.Left();
+ tools::Long nYMul=aTmpRect.Bottom()-aTmpRect.Top();
+ tools::Long nXDiv=nWdt0;
+ tools::Long nYDiv=nHgt0;
+ bool bXNeg=(nXMul<0)!=(nXDiv<0);
+ bool bYNeg=(nYMul<0)!=(nYDiv<0);
+ nXMul=std::abs(nXMul);
+ nYMul=std::abs(nYMul);
+ nXDiv=std::abs(nXDiv);
+ nYDiv=std::abs(nYDiv);
+ Fraction aXFact(nXMul,nXDiv); // fractions for canceling
+ Fraction aYFact(nYMul,nYDiv); // and for comparing
+ nXMul=aXFact.GetNumerator();
+ nYMul=aYFact.GetNumerator();
+ nXDiv=aXFact.GetDenominator();
+ nYDiv=aYFact.GetDenominator();
+ if (bEcke) { // corner point handles
+ bool bUseX=(aXFact<aYFact) != bBigOrtho;
+ if (bUseX) {
+ tools::Long nNeed=tools::Long(BigInt(nHgt0)*BigInt(nXMul)/BigInt(nXDiv));
+ if (bYNeg) nNeed=-nNeed;
+ if (bTop) aTmpRect.SetTop(aTmpRect.Bottom()-nNeed );
+ if (bBtm) aTmpRect.SetBottom(aTmpRect.Top()+nNeed );
+ } else {
+ tools::Long nNeed=tools::Long(BigInt(nWdt0)*BigInt(nYMul)/BigInt(nYDiv));
+ if (bXNeg) nNeed=-nNeed;
+ if (bLft) aTmpRect.SetLeft(aTmpRect.Right()-nNeed );
+ if (bRgt) aTmpRect.SetRight(aTmpRect.Left()+nNeed );
+ }
+ } else { // apex handles
+ if ((bLft || bRgt) && nXDiv!=0) {
+ tools::Long nHgt0b=maRect.Bottom()-maRect.Top();
+ tools::Long nNeed=tools::Long(BigInt(nHgt0b)*BigInt(nXMul)/BigInt(nXDiv));
+ aTmpRect.AdjustTop( -((nNeed-nHgt0b)/2) );
+ aTmpRect.SetBottom(aTmpRect.Top()+nNeed );
+ }
+ if ((bTop || bBtm) && nYDiv!=0) {
+ tools::Long nWdt0b=maRect.Right()-maRect.Left();
+ tools::Long nNeed=tools::Long(BigInt(nWdt0b)*BigInt(nYMul)/BigInt(nYDiv));
+ aTmpRect.AdjustLeft( -((nNeed-nWdt0b)/2) );
+ aTmpRect.SetRight(aTmpRect.Left()+nNeed );
+ }
+ }
+ }
+ if (dynamic_cast<const SdrObjCustomShape*>(this) == nullptr) // not justifying when in CustomShapes, to be able to detect if a shape has to be mirrored
+ ImpJustifyRect(aTmpRect);
+ return aTmpRect;
+}
+
+
+// drag
+
+bool SdrTextObj::applySpecialDrag(SdrDragStat& rDrag)
+{
+ tools::Rectangle aNewRect(ImpDragCalcRect(rDrag));
+
+ if(aNewRect.TopLeft() != maRect.TopLeft() && (maGeo.nRotationAngle || maGeo.nShearAngle))
+ {
+ Point aNewPos(aNewRect.TopLeft());
+
+ if (maGeo.nShearAngle)
+ ShearPoint(aNewPos,maRect.TopLeft(),maGeo.mfTanShearAngle);
+
+ if (maGeo.nRotationAngle)
+ RotatePoint(aNewPos,maRect.TopLeft(),maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle);
+
+ aNewRect.SetPos(aNewPos);
+ }
+
+ if (aNewRect != maRect)
+ {
+ NbcSetLogicRect(aNewRect);
+ }
+
+ return true;
+}
+
+OUString SdrTextObj::getSpecialDragComment(const SdrDragStat& /*rDrag*/) const
+{
+ return ImpGetDescriptionStr(STR_DragRectResize);
+}
+
+
+// Create
+
+bool SdrTextObj::BegCreate(SdrDragStat& rStat)
+{
+ rStat.SetOrtho4Possible();
+ tools::Rectangle aRect1(rStat.GetStart(), rStat.GetNow());
+ aRect1.Justify();
+ rStat.SetActionRect(aRect1);
+ maRect = aRect1;
+ return true;
+}
+
+bool SdrTextObj::MovCreate(SdrDragStat& rStat)
+{
+ tools::Rectangle aRect1;
+ rStat.TakeCreateRect(aRect1);
+ ImpJustifyRect(aRect1);
+ rStat.SetActionRect(aRect1);
+ maRect = aRect1; // for ObjName
+ SetBoundRectDirty();
+ m_bSnapRectDirty=true;
+ if (auto pRectObj = dynamic_cast<SdrRectObj *>(this)) {
+ pRectObj->SetXPolyDirty();
+ }
+ return true;
+}
+
+bool SdrTextObj::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd)
+{
+ rStat.TakeCreateRect(maRect);
+ ImpJustifyRect(maRect);
+
+ AdaptTextMinSize();
+
+ SetBoundAndSnapRectsDirty();
+ if (auto pRectObj = dynamic_cast<SdrRectObj *>(this)) {
+ pRectObj->SetXPolyDirty();
+ }
+ return (eCmd==SdrCreateCmd::ForceEnd || rStat.GetPointCount()>=2);
+}
+
+void SdrTextObj::BrkCreate(SdrDragStat& /*rStat*/)
+{
+}
+
+bool SdrTextObj::BckCreate(SdrDragStat& /*rStat*/)
+{
+ return true;
+}
+
+basegfx::B2DPolyPolygon SdrTextObj::TakeCreatePoly(const SdrDragStat& rDrag) const
+{
+ tools::Rectangle aRect1;
+ rDrag.TakeCreateRect(aRect1);
+ aRect1.Justify();
+
+ basegfx::B2DPolyPolygon aRetval;
+ const basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(aRect1);
+ aRetval.append(basegfx::utils::createPolygonFromRect(aRange));
+ return aRetval;
+}
+
+PointerStyle SdrTextObj::GetCreatePointer() const
+{
+ if (IsTextFrame()) return PointerStyle::DrawText;
+ return PointerStyle::Cross;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdotxed.cxx b/svx/source/svdraw/svdotxed.cxx
new file mode 100644
index 000000000..96523d679
--- /dev/null
+++ b/svx/source/svdraw/svdotxed.cxx
@@ -0,0 +1,360 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <svx/svdotext.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdoutl.hxx>
+#include <editeng/editdata.hxx>
+#include <editeng/outliner.hxx>
+#include <editeng/outlobj.hxx>
+#include <editeng/editstat.hxx>
+#include <svl/itemset.hxx>
+#include <editeng/eeitem.hxx>
+#include <svx/sdtfchim.hxx>
+#include <textchain.hxx>
+
+
+bool SdrTextObj::HasTextEdit() const
+{
+ // linked text objects may be changed (no automatic reload)
+ return true;
+}
+
+bool SdrTextObj::BegTextEdit(SdrOutliner& rOutl)
+{
+ if (mpEditingOutliner!=nullptr) return false; // Textedit might already run in another View!
+ mpEditingOutliner=&rOutl;
+
+ mbInEditMode = true;
+
+ OutlinerMode nOutlinerMode = OutlinerMode::OutlineObject;
+ if ( !IsOutlText() )
+ nOutlinerMode = OutlinerMode::TextObject;
+ rOutl.Init( nOutlinerMode );
+ rOutl.SetRefDevice(getSdrModelFromSdrObject().GetRefDevice());
+
+ bool bFitToSize(IsFitToSize());
+ bool bContourFrame=IsContourTextFrame();
+ ImpSetTextEditParams();
+
+ if (!bContourFrame) {
+ EEControlBits nStat=rOutl.GetControlWord();
+ nStat|=EEControlBits::AUTOPAGESIZE;
+ if (bFitToSize || IsAutoFit())
+ nStat|=EEControlBits::STRETCHING;
+ else
+ nStat&=~EEControlBits::STRETCHING;
+ rOutl.SetControlWord(nStat);
+ }
+
+ // disable AUTOPAGESIZE if IsChainable (might be required for overflow check)
+ if ( IsChainable() ) {
+ EEControlBits nStat1=rOutl.GetControlWord();
+ nStat1 &=~EEControlBits::AUTOPAGESIZE;
+ rOutl.SetControlWord(nStat1);
+ }
+
+
+ OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject();
+ if(pOutlinerParaObject!=nullptr)
+ {
+ rOutl.SetText(*GetOutlinerParaObject());
+ rOutl.SetFixedCellHeight(GetMergedItem(SDRATTR_TEXT_USEFIXEDCELLHEIGHT).GetValue());
+ }
+
+ // if necessary, set frame attributes for the first (new) paragraph of the
+ // outliner
+ if( !HasTextImpl( &rOutl ) )
+ {
+ // Outliner has no text so we must set some
+ // empty text so the outliner initialise itself
+ rOutl.SetText( "", rOutl.GetParagraph( 0 ) );
+
+ if(GetStyleSheet())
+ rOutl.SetStyleSheet( 0, GetStyleSheet());
+
+ // When setting the "hard" attributes for first paragraph, the Parent
+ // pOutlAttr (i. e. the template) has to be removed temporarily. Else,
+ // at SetParaAttribs(), all attributes contained in the parent become
+ // attributed hard to the paragraph.
+ const SfxItemSet& rSet = GetObjectItemSet();
+ SfxItemSetFixed<EE_ITEMS_START, EE_ITEMS_END> aFilteredSet(*rSet.GetPool());
+ aFilteredSet.Put(rSet);
+ rOutl.SetParaAttribs(0, aFilteredSet);
+ }
+ if (bFitToSize)
+ {
+ tools::Rectangle aAnchorRect;
+ tools::Rectangle aTextRect;
+ TakeTextRect(rOutl, aTextRect, false,
+ &aAnchorRect);
+ Fraction aFitXCorrection(1,1);
+ ImpSetCharStretching(rOutl,aTextRect.GetSize(),aAnchorRect.GetSize(),aFitXCorrection);
+ }
+ else if (IsAutoFit())
+ {
+ ImpAutoFitText(rOutl);
+ }
+
+ if(pOutlinerParaObject)
+ {
+ if (maGeo.nRotationAngle || IsFontwork())
+ {
+ // only repaint here, no real objectchange
+ BroadcastObjectChange();
+ }
+ }
+
+ rOutl.UpdateFields();
+ rOutl.ClearModifyFlag();
+
+ return true;
+}
+
+void SdrTextObj::TakeTextEditArea(Size* pPaperMin, Size* pPaperMax, tools::Rectangle* pViewInit, tools::Rectangle* pViewMin) const
+{
+ bool bFitToSize(IsFitToSize());
+ Size aPaperMin,aPaperMax;
+ tools::Rectangle aViewInit;
+ TakeTextAnchorRect(aViewInit);
+ if (maGeo.nRotationAngle) {
+ Point aCenter(aViewInit.Center());
+ aCenter-=aViewInit.TopLeft();
+ Point aCenter0(aCenter);
+ RotatePoint(aCenter,Point(),maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle);
+ aCenter-=aCenter0;
+ aViewInit.Move(aCenter.X(),aCenter.Y());
+ }
+ Size aAnkSiz(aViewInit.GetSize());
+ aAnkSiz.AdjustWidth( -1 ); aAnkSiz.AdjustHeight( -1 ); // because GetSize() adds 1
+ Size aMaxSiz(1000000,1000000);
+ Size aTmpSiz(getSdrModelFromSdrObject().GetMaxObjSize());
+ if (aTmpSiz.Width()!=0) aMaxSiz.setWidth(aTmpSiz.Width() );
+ if (aTmpSiz.Height()!=0) aMaxSiz.setHeight(aTmpSiz.Height() );
+
+ // Done earlier since used in else tree below
+ SdrTextHorzAdjust eHAdj(GetTextHorizontalAdjust());
+ SdrTextVertAdjust eVAdj(GetTextVerticalAdjust());
+
+ if(IsTextFrame())
+ {
+ tools::Long nMinWdt=GetMinTextFrameWidth();
+ tools::Long nMinHgt=GetMinTextFrameHeight();
+ tools::Long nMaxWdt=GetMaxTextFrameWidth();
+ tools::Long nMaxHgt=GetMaxTextFrameHeight();
+ if (nMinWdt<1) nMinWdt=1;
+ if (nMinHgt<1) nMinHgt=1;
+ if (!bFitToSize) {
+ if (nMaxWdt==0 || nMaxWdt>aMaxSiz.Width()) nMaxWdt=aMaxSiz.Width();
+ if (nMaxHgt==0 || nMaxHgt>aMaxSiz.Height()) nMaxHgt=aMaxSiz.Height();
+
+ if (!IsAutoGrowWidth() )
+ {
+ nMinWdt = aAnkSiz.Width();
+ nMaxWdt = nMinWdt;
+ }
+
+ if (!IsAutoGrowHeight())
+ {
+ nMinHgt = aAnkSiz.Height();
+ nMaxHgt = nMinHgt;
+ }
+
+ SdrTextAniKind eAniKind=GetTextAniKind();
+ SdrTextAniDirection eAniDirection=GetTextAniDirection();
+
+ bool bInEditMode = IsInEditMode();
+
+ if (!bInEditMode && (eAniKind==SdrTextAniKind::Scroll || eAniKind==SdrTextAniKind::Alternate || eAniKind==SdrTextAniKind::Slide))
+ {
+ // ticker text uses an unlimited paper size
+ if (eAniDirection==SdrTextAniDirection::Left || eAniDirection==SdrTextAniDirection::Right) nMaxWdt=1000000;
+ if (eAniDirection==SdrTextAniDirection::Up || eAniDirection==SdrTextAniDirection::Down) nMaxHgt=1000000;
+ }
+
+ bool bChainedFrame = IsChainable();
+ // Might be required for overflow check working: do limit height to frame if box is chainable.
+ if (!bChainedFrame) {
+ // #i119885# Do not limit/force height to geometrical frame (vice versa for vertical writing)
+ if(IsVerticalWriting())
+ {
+ nMaxWdt = 1000000;
+ }
+ else
+ {
+ nMaxHgt = 1000000;
+ }
+ }
+
+ aPaperMax.setWidth(nMaxWdt );
+ aPaperMax.setHeight(nMaxHgt );
+ }
+ else
+ {
+ aPaperMax=aMaxSiz;
+ }
+ aPaperMin.setWidth(nMinWdt );
+ aPaperMin.setHeight(nMinHgt );
+ }
+ else
+ {
+ // aPaperMin needs to be set to object's size if full width is activated
+ // for hor or ver writing respectively
+ if((SDRTEXTHORZADJUST_BLOCK == eHAdj && !IsVerticalWriting())
+ || (SDRTEXTVERTADJUST_BLOCK == eVAdj && IsVerticalWriting()))
+ {
+ aPaperMin = aAnkSiz;
+ }
+
+ aPaperMax=aMaxSiz;
+ }
+
+ if (pViewMin!=nullptr) {
+ *pViewMin=aViewInit;
+
+ tools::Long nXFree=aAnkSiz.Width()-aPaperMin.Width();
+ if (eHAdj==SDRTEXTHORZADJUST_LEFT) pViewMin->AdjustRight( -nXFree );
+ else if (eHAdj==SDRTEXTHORZADJUST_RIGHT) pViewMin->AdjustLeft(nXFree );
+ else { pViewMin->AdjustLeft(nXFree/2 ); pViewMin->SetRight(pViewMin->Left()+aPaperMin.Width() ); }
+
+ tools::Long nYFree=aAnkSiz.Height()-aPaperMin.Height();
+ if (eVAdj==SDRTEXTVERTADJUST_TOP) pViewMin->AdjustBottom( -nYFree );
+ else if (eVAdj==SDRTEXTVERTADJUST_BOTTOM) pViewMin->AdjustTop(nYFree );
+ else { pViewMin->AdjustTop(nYFree/2 ); pViewMin->SetBottom(pViewMin->Top()+aPaperMin.Height() ); }
+ }
+
+ // PaperSize should grow automatically in most cases
+ if(IsVerticalWriting())
+ aPaperMin.setWidth( 0 );
+ else
+ aPaperMin.setHeight( 0 );
+
+ if(eHAdj!=SDRTEXTHORZADJUST_BLOCK || bFitToSize) {
+ aPaperMin.setWidth(0 );
+ }
+
+ // For complete vertical adjustment support, set paper min height to 0, here.
+ if(SDRTEXTVERTADJUST_BLOCK != eVAdj || bFitToSize)
+ {
+ aPaperMin.setHeight( 0 );
+ }
+
+ if (pPaperMin!=nullptr) *pPaperMin=aPaperMin;
+ if (pPaperMax!=nullptr) *pPaperMax=aPaperMax;
+ if (pViewInit!=nullptr) *pViewInit=aViewInit;
+}
+
+void SdrTextObj::EndTextEdit(SdrOutliner& rOutl)
+{
+ if(rOutl.IsModified())
+ {
+
+ // to make the gray field background vanish again
+ rOutl.UpdateFields();
+
+ std::optional<OutlinerParaObject> pNewText = rOutl.CreateParaObject( 0, rOutl.GetParagraphCount() );
+
+ // need to end edit mode early since SetOutlinerParaObject already
+ // uses GetCurrentBoundRect() which needs to take the text into account
+ // to work correct
+ mbInEditMode = false;
+
+ // We don't want broadcasting if we are merely trying to move to next box (this prevents infinite loops)
+ if (IsChainable() && GetTextChain()->GetSwitchingToNextBox(this)) {
+ GetTextChain()->SetSwitchingToNextBox(this, false);
+ if( getActiveText() )
+ {
+ getActiveText()->SetOutlinerParaObject( std::move(pNewText) );
+ }
+ } else { // If we are not doing in-chaining switching just set the ParaObject
+ SetOutlinerParaObject(std::move(pNewText));
+ }
+ }
+
+ /* Chaining-related code */
+ rOutl.ClearOverflowingParaNum();
+
+ mpEditingOutliner = nullptr;
+ rOutl.Clear();
+ EEControlBits nStat = rOutl.GetControlWord();
+ nStat &= ~EEControlBits::AUTOPAGESIZE;
+ rOutl.SetControlWord(nStat);
+
+ mbInEditMode = false;
+}
+
+EEAnchorMode SdrTextObj::GetOutlinerViewAnchorMode() const
+{
+ SdrTextHorzAdjust eH=GetTextHorizontalAdjust();
+ SdrTextVertAdjust eV=GetTextVerticalAdjust();
+ EEAnchorMode eRet=EEAnchorMode::TopLeft;
+ if (IsContourTextFrame()) return eRet;
+ if (eH==SDRTEXTHORZADJUST_LEFT) {
+ if (eV==SDRTEXTVERTADJUST_TOP) {
+ eRet=EEAnchorMode::TopLeft;
+ } else if (eV==SDRTEXTVERTADJUST_BOTTOM) {
+ eRet=EEAnchorMode::BottomLeft;
+ } else {
+ eRet=EEAnchorMode::VCenterLeft;
+ }
+ } else if (eH==SDRTEXTHORZADJUST_RIGHT) {
+ if (eV==SDRTEXTVERTADJUST_TOP) {
+ eRet=EEAnchorMode::TopRight;
+ } else if (eV==SDRTEXTVERTADJUST_BOTTOM) {
+ eRet=EEAnchorMode::BottomRight;
+ } else {
+ eRet=EEAnchorMode::VCenterRight;
+ }
+ } else {
+ if (eV==SDRTEXTVERTADJUST_TOP) {
+ eRet=EEAnchorMode::TopHCenter;
+ } else if (eV==SDRTEXTVERTADJUST_BOTTOM) {
+ eRet=EEAnchorMode::BottomHCenter;
+ } else {
+ eRet=EEAnchorMode::VCenterHCenter;
+ }
+ }
+ return eRet;
+}
+
+void SdrTextObj::ImpSetTextEditParams() const
+{
+ if (mpEditingOutliner==nullptr)
+ return;
+
+ bool bUpdBuf=mpEditingOutliner->SetUpdateLayout(false);
+ Size aPaperMin;
+ Size aPaperMax;
+ tools::Rectangle aEditArea;
+ TakeTextEditArea(&aPaperMin,&aPaperMax,&aEditArea,nullptr);
+ bool bContourFrame=IsContourTextFrame();
+ mpEditingOutliner->SetMinAutoPaperSize(aPaperMin);
+ mpEditingOutliner->SetMaxAutoPaperSize(aPaperMax);
+ mpEditingOutliner->SetPaperSize(Size());
+ mpEditingOutliner->SetTextColumns(GetTextColumnsNumber(), GetTextColumnsSpacing());
+ if (bContourFrame) {
+ tools::Rectangle aAnchorRect;
+ TakeTextAnchorRect(aAnchorRect);
+ ImpSetContourPolygon(*mpEditingOutliner,aAnchorRect, true);
+ }
+ if (bUpdBuf) mpEditingOutliner->SetUpdateLayout(true);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdotxfl.cxx b/svx/source/svdraw/svdotxfl.cxx
new file mode 100644
index 000000000..e2c7c7515
--- /dev/null
+++ b/svx/source/svdraw/svdotxfl.cxx
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/svdotext.hxx>
+
+bool SdrTextObj::CalcFieldValue(const SvxFieldItem& /*rField*/, sal_Int32 /*nPara*/, sal_uInt16 /*nPos*/,
+ bool /*bEdit*/, std::optional<Color>& /*rpTxtColor*/, std::optional<Color>& /*rpFldColor*/, OUString& /*rRet*/) const
+{
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdotxln.cxx b/svx/source/svdraw/svdotxln.cxx
new file mode 100644
index 000000000..19953dcc6
--- /dev/null
+++ b/svx/source/svdraw/svdotxln.cxx
@@ -0,0 +1,277 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <comphelper/processfactory.hxx>
+#include <osl/file.hxx>
+#include <osl/thread.h>
+#include <unotools/ucbstreamhelper.hxx>
+#include <ucbhelper/content.hxx>
+#include <unotools/datetime.hxx>
+#include <svx/svdotext.hxx>
+#include <svx/svdmodel.hxx>
+#include <editeng/editdata.hxx>
+#include <sfx2/lnkbase.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <tools/urlobj.hxx>
+#include <tools/debug.hxx>
+#include <tools/tenccvt.hxx>
+#include <memory>
+
+class ImpSdrObjTextLink: public ::sfx2::SvBaseLink
+{
+ SdrTextObj* pSdrObj;
+
+public:
+ explicit ImpSdrObjTextLink( SdrTextObj* pObj1 )
+ : ::sfx2::SvBaseLink( ::SfxLinkUpdateMode::ONCALL, SotClipboardFormatId::SIMPLE_FILE ),
+ pSdrObj( pObj1 )
+ {}
+
+ virtual void Closed() override;
+ virtual ::sfx2::SvBaseLink::UpdateResult DataChanged(
+ const OUString& rMimeType, const css::uno::Any & rValue ) override;
+};
+
+void ImpSdrObjTextLink::Closed()
+{
+ if (pSdrObj )
+ {
+ // set pLink of the object to NULL, because we are destroying the link instance now
+ ImpSdrObjTextLinkUserData* pData=pSdrObj->GetLinkUserData();
+ if (pData!=nullptr) pData->mpLink = nullptr;
+ pSdrObj->ReleaseTextLink();
+ }
+ SvBaseLink::Closed();
+}
+
+
+::sfx2::SvBaseLink::UpdateResult ImpSdrObjTextLink::DataChanged(
+ const OUString& /*rMimeType*/, const css::uno::Any & /*rValue */)
+{
+ bool bForceReload = false;
+ SdrModel* pModel(pSdrObj ? &pSdrObj->getSdrModelFromSdrObject() : nullptr);
+ sfx2::LinkManager* pLinkManager(pModel ? pModel->GetLinkManager() : nullptr);
+
+ if( pLinkManager )
+ {
+ ImpSdrObjTextLinkUserData* pData=pSdrObj->GetLinkUserData();
+ if( pData )
+ {
+ OUString aFile;
+ OUString aFilter;
+ sfx2::LinkManager::GetDisplayNames( this, nullptr,&aFile, nullptr, &aFilter );
+
+ if( pData->maFileName != aFile ||
+ pData->maFilterName != aFilter )
+ {
+ pData->maFileName = aFile;
+ pData->maFilterName = aFilter;
+ pSdrObj->SetChanged();
+ bForceReload = true;
+ }
+ }
+ }
+ if (pSdrObj )
+ pSdrObj->ReloadLinkedText( bForceReload );
+
+ return SUCCESS;
+}
+
+
+ImpSdrObjTextLinkUserData::ImpSdrObjTextLinkUserData():
+ SdrObjUserData(SdrInventor::Default,SDRUSERDATA_OBJTEXTLINK),
+ maFileDate0( DateTime::EMPTY ),
+ meCharSet(RTL_TEXTENCODING_DONTKNOW)
+{
+}
+
+ImpSdrObjTextLinkUserData::~ImpSdrObjTextLinkUserData()
+{
+}
+
+std::unique_ptr<SdrObjUserData> ImpSdrObjTextLinkUserData::Clone(SdrObject* ) const
+{
+ ImpSdrObjTextLinkUserData* pData = new ImpSdrObjTextLinkUserData;
+ pData->maFileName = maFileName;
+ pData->maFilterName = maFilterName;
+ pData->maFileDate0 = maFileDate0;
+ pData->meCharSet = meCharSet;
+ pData->mpLink = nullptr;
+ return std::unique_ptr<SdrObjUserData>(pData);
+}
+
+
+void SdrTextObj::SetTextLink(const OUString& rFileName, const OUString& rFilterName)
+{
+ rtl_TextEncoding eCharSet = osl_getThreadTextEncoding();
+
+ ImpSdrObjTextLinkUserData* pData=GetLinkUserData();
+ if (pData!=nullptr) {
+ ReleaseTextLink();
+ }
+ pData=new ImpSdrObjTextLinkUserData;
+ pData->maFileName = rFileName;
+ pData->maFilterName = rFilterName;
+ pData->meCharSet = eCharSet;
+ AppendUserData(std::unique_ptr<SdrObjUserData>(pData));
+ ImpRegisterLink();
+}
+
+void SdrTextObj::ReleaseTextLink()
+{
+ ImpDeregisterLink();
+ sal_uInt16 nCount=GetUserDataCount();
+ for (sal_uInt16 nNum=nCount; nNum>0;) {
+ nNum--;
+ SdrObjUserData* pData=GetUserData(nNum);
+ if (pData->GetInventor()==SdrInventor::Default && pData->GetId()==SDRUSERDATA_OBJTEXTLINK) {
+ DeleteUserData(nNum);
+ }
+ }
+}
+
+bool SdrTextObj::ReloadLinkedText( bool bForceLoad)
+{
+ ImpSdrObjTextLinkUserData* pData = GetLinkUserData();
+ bool bRet = true;
+
+ if( pData )
+ {
+ DateTime aFileDT( DateTime::EMPTY );
+ bool bExists = true;
+
+ try
+ {
+ INetURLObject aURL( pData->maFileName );
+ DBG_ASSERT( aURL.GetProtocol() != INetProtocol::NotValid, "invalid URL" );
+
+ ::ucbhelper::Content aCnt( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), css::uno::Reference< css::ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext() );
+ css::uno::Any aAny( aCnt.getPropertyValue("DateModified") );
+ css::util::DateTime aDateTime;
+
+ aAny >>= aDateTime;
+ ::utl::typeConvert( aDateTime, aFileDT );
+ }
+ catch( ... )
+ {
+ bExists = false;
+ }
+
+ if( bExists )
+ {
+ bool bLoad = false;
+ if( bForceLoad )
+ bLoad = true;
+ else
+ bLoad = ( aFileDT > pData->maFileDate0 );
+
+ if( bLoad )
+ {
+ bRet = LoadText( pData->maFileName, pData->meCharSet );
+ }
+
+ pData->maFileDate0 = aFileDT;
+ }
+ }
+
+ return bRet;
+}
+
+bool SdrTextObj::LoadText(const OUString& rFileName, rtl_TextEncoding eCharSet)
+{
+ INetURLObject aFileURL( rFileName );
+ bool bRet = false;
+
+ if( aFileURL.GetProtocol() == INetProtocol::NotValid )
+ {
+ OUString aFileURLStr;
+
+ if( osl::FileBase::getFileURLFromSystemPath( rFileName, aFileURLStr ) == osl::FileBase::E_None )
+ aFileURL = INetURLObject( aFileURLStr );
+ else
+ aFileURL.SetSmartURL( rFileName );
+ }
+
+ DBG_ASSERT( aFileURL.GetProtocol() != INetProtocol::NotValid, "invalid URL" );
+
+ std::unique_ptr<SvStream> pIStm(::utl::UcbStreamHelper::CreateStream( aFileURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), StreamMode::READ ));
+
+ if( pIStm )
+ {
+ pIStm->SetStreamCharSet(GetSOLoadTextEncoding(eCharSet));
+
+ char cRTF[5];
+ cRTF[4] = 0;
+ pIStm->ReadBytes(cRTF, 5);
+
+ bool bRTF = cRTF[0] == '{' && cRTF[1] == '\\' && cRTF[2] == 'r' && cRTF[3] == 't' && cRTF[4] == 'f';
+
+ pIStm->Seek(0);
+
+ if( !pIStm->GetError() )
+ {
+ SetText( *pIStm, aFileURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), bRTF ? EETextFormat::Rtf : EETextFormat::Text );
+ bRet = true;
+ }
+ }
+
+ return bRet;
+}
+
+ImpSdrObjTextLinkUserData* SdrTextObj::GetLinkUserData() const
+{
+ sal_uInt16 nCount=GetUserDataCount();
+ for (sal_uInt16 nNum=nCount; nNum>0;) {
+ nNum--;
+ SdrObjUserData * pData=GetUserData(nNum);
+ if (pData->GetInventor() == SdrInventor::Default
+ && pData->GetId() == SDRUSERDATA_OBJTEXTLINK)
+ {
+ return static_cast<ImpSdrObjTextLinkUserData *>(pData);
+ }
+ }
+ return nullptr;
+}
+
+void SdrTextObj::ImpRegisterLink()
+{
+ ImpSdrObjTextLinkUserData* pData=GetLinkUserData();
+ sfx2::LinkManager* pLinkManager(getSdrModelFromSdrObject().GetLinkManager());
+ if (pLinkManager!=nullptr && pData!=nullptr && pData->mpLink==nullptr) { // don't register twice
+ pData->mpLink = new ImpSdrObjTextLink(this);
+ pLinkManager->InsertFileLink(*pData->mpLink,sfx2::SvBaseLinkObjectType::ClientFile,pData->maFileName,
+ !pData->maFilterName.isEmpty() ?
+ &pData->maFilterName : nullptr);
+ }
+}
+
+void SdrTextObj::ImpDeregisterLink()
+{
+ ImpSdrObjTextLinkUserData* pData=GetLinkUserData();
+ sfx2::LinkManager* pLinkManager(getSdrModelFromSdrObject().GetLinkManager());
+ if (pLinkManager!=nullptr && pData!=nullptr && pData->mpLink!=nullptr) { // don't register twice
+ // when doing Remove, *pLink is deleted implicitly
+ pLinkManager->Remove( pData->mpLink.get() );
+ pData->mpLink=nullptr;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdotxtr.cxx b/svx/source/svdraw/svdotxtr.cxx
new file mode 100644
index 000000000..7da067468
--- /dev/null
+++ b/svx/source/svdraw/svdotxtr.cxx
@@ -0,0 +1,496 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <svx/svdotext.hxx>
+#include <svx/svdtrans.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdmodel.hxx>
+#include <sdr/properties/itemsettools.hxx>
+#include <svx/sdr/properties/properties.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <svl/itemset.hxx>
+#include <drawinglayer/processor2d/textaspolygonextractor2d.hxx>
+#include <svx/sdr/contact/viewcontact.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflclit.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/xlnclit.hxx>
+#include <svx/xlnwtit.hxx>
+#include <svx/sdshitm.hxx>
+
+using namespace com::sun::star;
+
+void SdrTextObj::NbcSetSnapRect(const tools::Rectangle& rRect)
+{
+ if (maGeo.nRotationAngle || maGeo.nShearAngle)
+ {
+ // Either the rotation or shear angle exists.
+ tools::Rectangle aSR0(GetSnapRect());
+ tools::Long nWdt0=aSR0.Right()-aSR0.Left();
+ tools::Long nHgt0=aSR0.Bottom()-aSR0.Top();
+ tools::Long nWdt1=rRect.Right()-rRect.Left();
+ tools::Long nHgt1=rRect.Bottom()-rRect.Top();
+ SdrTextObj::NbcResize(maSnapRect.TopLeft(),Fraction(nWdt1,nWdt0),Fraction(nHgt1,nHgt0));
+ SdrTextObj::NbcMove(Size(rRect.Left()-aSR0.Left(),rRect.Top()-aSR0.Top()));
+ }
+ else
+ {
+ // No rotation or shear.
+
+ maRect = rRect;
+ ImpJustifyRect(maRect);
+
+ AdaptTextMinSize();
+
+ ImpCheckShear();
+ SetBoundAndSnapRectsDirty();
+ }
+}
+
+const tools::Rectangle& SdrTextObj::GetLogicRect() const
+{
+ return maRect;
+}
+
+void SdrTextObj::NbcSetLogicRect(const tools::Rectangle& rRect)
+{
+ maRect = rRect;
+ ImpJustifyRect(maRect);
+
+ AdaptTextMinSize();
+
+ SetBoundAndSnapRectsDirty();
+}
+
+Degree100 SdrTextObj::GetRotateAngle() const
+{
+ return maGeo.nRotationAngle;
+}
+
+Degree100 SdrTextObj::GetShearAngle(bool /*bVertical*/) const
+{
+ return maGeo.nShearAngle;
+}
+
+void SdrTextObj::NbcMove(const Size& rSiz)
+{
+ maRect.Move(rSiz);
+ m_aOutRect.Move(rSiz);
+ maSnapRect.Move(rSiz);
+ SetBoundAndSnapRectsDirty(true);
+}
+
+void SdrTextObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
+{
+ bool bNotSheared=maGeo.nShearAngle==0_deg100;
+ bool bRotate90=bNotSheared && maGeo.nRotationAngle.get() % 9000 ==0;
+ bool bXMirr=(xFact.GetNumerator()<0) != (xFact.GetDenominator()<0);
+ bool bYMirr=(yFact.GetNumerator()<0) != (yFact.GetDenominator()<0);
+ if (bXMirr || bYMirr) {
+ Point aRef1(GetSnapRect().Center());
+ if (bXMirr) {
+ Point aRef2(aRef1);
+ aRef2.AdjustY( 1 );
+ NbcMirrorGluePoints(aRef1,aRef2);
+ }
+ if (bYMirr) {
+ Point aRef2(aRef1);
+ aRef2.AdjustX( 1 );
+ NbcMirrorGluePoints(aRef1,aRef2);
+ }
+ }
+
+ if (maGeo.nRotationAngle==0_deg100 && maGeo.nShearAngle==0_deg100) {
+ ResizeRect(maRect,rRef,xFact,yFact);
+ if (bYMirr) {
+ maRect.Justify();
+ maRect.Move(maRect.Right()-maRect.Left(),maRect.Bottom()-maRect.Top());
+ maGeo.nRotationAngle=18000_deg100;
+ maGeo.RecalcSinCos();
+ }
+ }
+ else
+ {
+ tools::Polygon aPol(Rect2Poly(maRect,maGeo));
+
+ for(sal_uInt16 a(0); a < aPol.GetSize(); a++)
+ {
+ ResizePoint(aPol[a], rRef, xFact, yFact);
+ }
+
+ if(bXMirr != bYMirr)
+ {
+ // turn polygon and move it a little
+ tools::Polygon aPol0(aPol);
+
+ aPol[0] = aPol0[1];
+ aPol[1] = aPol0[0];
+ aPol[2] = aPol0[3];
+ aPol[3] = aPol0[2];
+ aPol[4] = aPol0[1];
+ }
+
+ Poly2Rect(aPol, maRect, maGeo);
+ }
+
+ if (bRotate90) {
+ bool bRota90=maGeo.nRotationAngle.get() % 9000 ==0;
+ if (!bRota90) { // there's seems to be a rounding error occurring: correct it
+ Degree100 a=NormAngle36000(maGeo.nRotationAngle);
+ if (a<4500_deg100) a=0_deg100;
+ else if (a<13500_deg100) a=9000_deg100;
+ else if (a<22500_deg100) a=18000_deg100;
+ else if (a<31500_deg100) a=27000_deg100;
+ else a=0_deg100;
+ maGeo.nRotationAngle=a;
+ maGeo.RecalcSinCos();
+ }
+ if (bNotSheared!=(maGeo.nShearAngle==0_deg100)) { // correct a rounding error occurring with Shear
+ maGeo.nShearAngle=0_deg100;
+ maGeo.RecalcTan();
+ }
+ }
+
+ ImpJustifyRect(maRect);
+
+ AdaptTextMinSize();
+
+ if(mbTextFrame && !getSdrModelFromSdrObject().IsPasteResize())
+ {
+ NbcAdjustTextFrameWidthAndHeight();
+ }
+
+ ImpCheckShear();
+ SetBoundAndSnapRectsDirty();
+}
+
+void SdrTextObj::NbcRotate(const Point& rRef, Degree100 nAngle, double sn, double cs)
+{
+ SetGlueReallyAbsolute(true);
+ tools::Long dx=maRect.Right()-maRect.Left();
+ tools::Long dy=maRect.Bottom()-maRect.Top();
+ Point aP(maRect.TopLeft());
+ RotatePoint(aP,rRef,sn,cs);
+ maRect.SetLeft(aP.X() );
+ maRect.SetTop(aP.Y() );
+ maRect.SetRight(maRect.Left()+dx );
+ maRect.SetBottom(maRect.Top()+dy );
+ if (maGeo.nRotationAngle==0_deg100) {
+ maGeo.nRotationAngle=NormAngle36000(nAngle);
+ maGeo.mfSinRotationAngle=sn;
+ maGeo.mfCosRotationAngle=cs;
+ } else {
+ maGeo.nRotationAngle=NormAngle36000(maGeo.nRotationAngle+nAngle);
+ maGeo.RecalcSinCos();
+ }
+ SetBoundAndSnapRectsDirty();
+ NbcRotateGluePoints(rRef,nAngle,sn,cs);
+ SetGlueReallyAbsolute(false);
+}
+
+void SdrTextObj::NbcShear(const Point& rRef, Degree100 /*nAngle*/, double tn, bool bVShear)
+{
+ SetGlueReallyAbsolute(true);
+
+ // when this is a SdrPathObj, aRect may be uninitialized
+ tools::Polygon aPol(Rect2Poly(maRect.IsEmpty() ? GetSnapRect() : maRect, maGeo));
+
+ sal_uInt16 nPointCount=aPol.GetSize();
+ for (sal_uInt16 i=0; i<nPointCount; i++) {
+ ShearPoint(aPol[i],rRef,tn,bVShear);
+ }
+ Poly2Rect(aPol,maRect,maGeo);
+ ImpJustifyRect(maRect);
+ if (mbTextFrame) {
+ NbcAdjustTextFrameWidthAndHeight();
+ }
+ ImpCheckShear();
+ SetBoundAndSnapRectsDirty();
+ NbcShearGluePoints(rRef,tn,bVShear);
+ SetGlueReallyAbsolute(false);
+}
+
+void SdrTextObj::NbcMirror(const Point& rRef1, const Point& rRef2)
+{
+ SetGlueReallyAbsolute(true);
+ bool bNotSheared=maGeo.nShearAngle==0_deg100;
+ bool bRotate90 = false;
+ if (bNotSheared &&
+ (rRef1.X()==rRef2.X() || rRef1.Y()==rRef2.Y() ||
+ std::abs(rRef1.X()-rRef2.X())==std::abs(rRef1.Y()-rRef2.Y()))) {
+ bRotate90=maGeo.nRotationAngle.get() % 9000 ==0;
+ }
+ tools::Polygon aPol(Rect2Poly(maRect,maGeo));
+ sal_uInt16 i;
+ sal_uInt16 nPointCount=aPol.GetSize();
+ for (i=0; i<nPointCount; i++) {
+ MirrorPoint(aPol[i],rRef1,rRef2);
+ }
+ // turn polygon and move it a little
+ tools::Polygon aPol0(aPol);
+ aPol[0]=aPol0[1];
+ aPol[1]=aPol0[0];
+ aPol[2]=aPol0[3];
+ aPol[3]=aPol0[2];
+ aPol[4]=aPol0[1];
+ Poly2Rect(aPol,maRect,maGeo);
+
+ if (bRotate90) {
+ bool bRota90=maGeo.nRotationAngle.get() % 9000 ==0;
+ if (bRotate90 && !bRota90) { // there's seems to be a rounding error occurring: correct it
+ Degree100 a=NormAngle36000(maGeo.nRotationAngle);
+ if (a<4500_deg100) a=0_deg100;
+ else if (a<13500_deg100) a=9000_deg100;
+ else if (a<22500_deg100) a=18000_deg100;
+ else if (a<31500_deg100) a=27000_deg100;
+ else a=0_deg100;
+ maGeo.nRotationAngle=a;
+ maGeo.RecalcSinCos();
+ }
+ }
+ if (bNotSheared!=(maGeo.nShearAngle==0_deg100)) { // correct a rounding error occurring with Shear
+ maGeo.nShearAngle=0_deg100;
+ maGeo.RecalcTan();
+ }
+
+ ImpJustifyRect(maRect);
+ if (mbTextFrame) {
+ NbcAdjustTextFrameWidthAndHeight();
+ }
+ ImpCheckShear();
+ SetBoundAndSnapRectsDirty();
+ NbcMirrorGluePoints(rRef1,rRef2);
+ SetGlueReallyAbsolute(false);
+}
+
+
+SdrObjectUniquePtr SdrTextObj::ImpConvertContainedTextToSdrPathObjs(bool bToPoly) const
+{
+ SdrObjectUniquePtr pRetval;
+
+ if(!ImpCanConvTextToCurve())
+ {
+ // suppress HelpTexts from PresObj's
+ return nullptr;
+ }
+
+ // create an extractor with neutral ViewInformation
+ const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
+ drawinglayer::processor2d::TextAsPolygonExtractor2D aExtractor(aViewInformation2D);
+
+ // extract text as polygons
+ GetViewContact().getViewIndependentPrimitive2DContainer(aExtractor);
+
+ // get results
+ const drawinglayer::processor2d::TextAsPolygonDataNodeVector& rResult = aExtractor.getTarget();
+ const sal_uInt32 nResultCount(rResult.size());
+
+ if(nResultCount)
+ {
+ // prepare own target
+ SdrObjGroup* pGroup = new SdrObjGroup(getSdrModelFromSdrObject());
+ SdrObjList* pObjectList = pGroup->GetSubList();
+
+ // process results
+ for(sal_uInt32 a(0); a < nResultCount; a++)
+ {
+ const drawinglayer::processor2d::TextAsPolygonDataNode& rCandidate = rResult[a];
+ basegfx::B2DPolyPolygon aPolyPolygon(rCandidate.getB2DPolyPolygon());
+
+ if(aPolyPolygon.count())
+ {
+ // take care of wanted polygon type
+ if(bToPoly)
+ {
+ if(aPolyPolygon.areControlPointsUsed())
+ {
+ aPolyPolygon = basegfx::utils::adaptiveSubdivideByAngle(aPolyPolygon);
+ }
+ }
+ else
+ {
+ if(!aPolyPolygon.areControlPointsUsed())
+ {
+ aPolyPolygon = basegfx::utils::expandToCurve(aPolyPolygon);
+ }
+ }
+
+ // create ItemSet with object attributes
+ SfxItemSet aAttributeSet(GetObjectItemSet());
+ SdrPathObj* pPathObj = nullptr;
+
+ // always clear objectshadow; this is included in the extraction
+ aAttributeSet.Put(makeSdrShadowItem(false));
+
+ if(rCandidate.getIsFilled())
+ {
+ // set needed items
+ aAttributeSet.Put(XFillColorItem(OUString(), Color(rCandidate.getBColor())));
+ aAttributeSet.Put(XLineStyleItem(drawing::LineStyle_NONE));
+ aAttributeSet.Put(XFillStyleItem(drawing::FillStyle_SOLID));
+
+ // create filled SdrPathObj
+ pPathObj = new SdrPathObj(
+ getSdrModelFromSdrObject(),
+ SdrObjKind::PathFill,
+ aPolyPolygon);
+ }
+ else
+ {
+ // set needed items
+ aAttributeSet.Put(XLineColorItem(OUString(), Color(rCandidate.getBColor())));
+ aAttributeSet.Put(XLineStyleItem(drawing::LineStyle_SOLID));
+ aAttributeSet.Put(XLineWidthItem(0));
+ aAttributeSet.Put(XFillStyleItem(drawing::FillStyle_NONE));
+
+ // create line SdrPathObj
+ pPathObj = new SdrPathObj(
+ getSdrModelFromSdrObject(),
+ SdrObjKind::PathLine,
+ aPolyPolygon);
+ }
+
+ // copy basic information from original
+ pPathObj->ImpSetAnchorPos(GetAnchorPos());
+ pPathObj->NbcSetLayer(GetLayer());
+ pPathObj->NbcSetStyleSheet(GetStyleSheet(), true);
+
+ // apply prepared ItemSet and add to target
+ pPathObj->SetMergedItemSet(aAttributeSet);
+ pObjectList->InsertObject(pPathObj);
+ }
+ }
+
+ // postprocess; if no result and/or only one object, simplify
+ if(!pObjectList->GetObjCount())
+ {
+ // always use SdrObject::Free(...) for SdrObjects (!)
+ SdrObject* pTemp(pGroup);
+ SdrObject::Free(pTemp);
+ }
+ else if(1 == pObjectList->GetObjCount())
+ {
+ pRetval.reset(pObjectList->RemoveObject(0));
+
+ // always use SdrObject::Free(...) for SdrObjects (!)
+ SdrObject* pTemp(pGroup);
+ SdrObject::Free(pTemp);
+ }
+ else
+ {
+ pRetval.reset(pGroup);
+ }
+ }
+
+ return pRetval;
+}
+
+
+SdrObjectUniquePtr SdrTextObj::DoConvertToPolyObj(bool bBezier, bool bAddText) const
+{
+ if(bAddText)
+ {
+ return ImpConvertContainedTextToSdrPathObjs(!bBezier);
+ }
+
+ return nullptr;
+}
+
+bool SdrTextObj::ImpCanConvTextToCurve() const
+{
+ return !IsOutlText();
+}
+
+SdrPathObjUniquePtr SdrTextObj::ImpConvertMakeObj(const basegfx::B2DPolyPolygon& rPolyPolygon, bool bClosed, bool bBezier) const
+{
+ SdrObjKind ePathKind = bClosed ? SdrObjKind::PathFill : SdrObjKind::PathLine;
+ basegfx::B2DPolyPolygon aB2DPolyPolygon(rPolyPolygon);
+
+ // #i37011#
+ if(!bBezier)
+ {
+ aB2DPolyPolygon = basegfx::utils::adaptiveSubdivideByAngle(aB2DPolyPolygon);
+ ePathKind = bClosed ? SdrObjKind::Polygon : SdrObjKind::PolyLine;
+ }
+
+ SdrPathObjUniquePtr pPathObj(new SdrPathObj(
+ getSdrModelFromSdrObject(),
+ ePathKind,
+ aB2DPolyPolygon));
+
+ if(bBezier)
+ {
+ // create bezier curves
+ pPathObj->SetPathPoly(basegfx::utils::expandToCurve(pPathObj->GetPathPoly()));
+ }
+
+ pPathObj->ImpSetAnchorPos(m_aAnchor);
+ pPathObj->NbcSetLayer(GetLayer());
+ sdr::properties::ItemChangeBroadcaster aC(*pPathObj);
+ pPathObj->ClearMergedItem();
+ pPathObj->SetMergedItemSet(GetObjectItemSet());
+ pPathObj->GetProperties().BroadcastItemChange(aC);
+ pPathObj->NbcSetStyleSheet(GetStyleSheet(), true);
+
+ return pPathObj;
+}
+
+SdrObjectUniquePtr SdrTextObj::ImpConvertAddText(SdrObjectUniquePtr pObj, bool bBezier) const
+{
+ if(!ImpCanConvTextToCurve())
+ {
+ return pObj;
+ }
+
+ SdrObjectUniquePtr pText = ImpConvertContainedTextToSdrPathObjs(!bBezier);
+
+ if(!pText)
+ {
+ return pObj;
+ }
+
+ if(!pObj)
+ {
+ return pText;
+ }
+
+ if(pText->IsGroupObject())
+ {
+ // is already group object, add partial shape in front
+ SdrObjList* pOL=pText->GetSubList();
+ pOL->InsertObject(pObj.release(),0);
+
+ return pText;
+ }
+ else
+ {
+ // not yet a group, create one and add partial and new shapes
+ std::unique_ptr<SdrObjGroup, SdrObjectFreeOp> pGrp(new SdrObjGroup(getSdrModelFromSdrObject()));
+ SdrObjList* pOL=pGrp->GetSubList();
+ pOL->InsertObject(pObj.release());
+ pOL->InsertObject(pText.release());
+
+ return pGrp;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdouno.cxx b/svx/source/svdraw/svdouno.cxx
new file mode 100644
index 000000000..4f7066726
--- /dev/null
+++ b/svx/source/svdraw/svdouno.cxx
@@ -0,0 +1,502 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sdr/contact/viewcontactofunocontrol.hxx>
+#include <sdr/contact/viewobjectcontactofunocontrol.hxx>
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/util/XCloneable.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <comphelper/processfactory.hxx>
+#include <svx/svdouno.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <svx/svdview.hxx>
+#include <svx/svdorect.hxx>
+#include <svx/svdviter.hxx>
+#include <rtl/ref.hxx>
+#include <svx/sdrpagewindow.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/debug.hxx>
+#include <o3tl/sorted_vector.hxx>
+
+using namespace ::com::sun::star;
+using namespace sdr::contact;
+
+
+// Defines
+
+
+// Helper class SdrControlEventListenerImpl
+
+#include <com/sun/star/lang/XEventListener.hpp>
+
+#include <cppuhelper/implbase.hxx>
+
+
+class SdrControlEventListenerImpl : public ::cppu::WeakImplHelper< css::lang::XEventListener >
+{
+protected:
+ SdrUnoObj* pObj;
+
+public:
+ explicit SdrControlEventListenerImpl(SdrUnoObj* _pObj)
+ : pObj(_pObj)
+ {}
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+ void StopListening(const uno::Reference< lang::XComponent >& xComp);
+ void StartListening(const uno::Reference< lang::XComponent >& xComp);
+};
+
+// XEventListener
+void SAL_CALL SdrControlEventListenerImpl::disposing( const css::lang::EventObject& /*Source*/)
+{
+ if (pObj)
+ {
+ pObj->xUnoControlModel = nullptr;
+ }
+}
+
+void SdrControlEventListenerImpl::StopListening(const uno::Reference< lang::XComponent >& xComp)
+{
+ if (xComp.is())
+ xComp->removeEventListener(this);
+}
+
+void SdrControlEventListenerImpl::StartListening(const uno::Reference< lang::XComponent >& xComp)
+{
+ if (xComp.is())
+ xComp->addEventListener(this);
+}
+
+
+struct SdrUnoObjDataHolder
+{
+ mutable ::rtl::Reference< SdrControlEventListenerImpl >
+ pEventListener;
+};
+
+
+namespace
+{
+ void lcl_ensureControlVisibility( SdrView const * _pView, const SdrUnoObj* _pObject, bool _bVisible )
+ {
+ OSL_PRECOND( _pObject, "lcl_ensureControlVisibility: no object -> no survival!" );
+
+ SdrPageView* pPageView = _pView ? _pView->GetSdrPageView() : nullptr;
+ DBG_ASSERT( pPageView, "lcl_ensureControlVisibility: no view found!" );
+ if ( !pPageView )
+ return;
+
+ ViewContact& rUnoControlContact( _pObject->GetViewContact() );
+
+ for ( sal_uInt32 i = 0; i < pPageView->PageWindowCount(); ++i )
+ {
+ SdrPageWindow* pPageWindow = pPageView->GetPageWindow( i );
+ DBG_ASSERT( pPageWindow, "lcl_ensureControlVisibility: invalid PageViewWindow!" );
+ if ( !pPageWindow )
+ continue;
+
+ if ( !pPageWindow->HasObjectContact() )
+ continue;
+
+ ObjectContact& rPageViewContact( pPageWindow->GetObjectContact() );
+ const ViewObjectContact& rViewObjectContact( rUnoControlContact.GetViewObjectContact( rPageViewContact ) );
+ const ViewObjectContactOfUnoControl* pUnoControlContact = dynamic_cast< const ViewObjectContactOfUnoControl* >( &rViewObjectContact );
+ DBG_ASSERT( pUnoControlContact, "lcl_ensureControlVisibility: wrong ViewObjectContact type!" );
+ if ( !pUnoControlContact )
+ continue;
+
+ pUnoControlContact->ensureControlVisibility( _bVisible );
+ }
+ }
+}
+
+SdrUnoObj::SdrUnoObj(
+ SdrModel& rSdrModel,
+ const OUString& rModelName)
+: SdrRectObj(rSdrModel),
+ m_pImpl( new SdrUnoObjDataHolder )
+{
+ m_bIsUnoObj = true;
+
+ m_pImpl->pEventListener = new SdrControlEventListenerImpl(this);
+
+ // only an owner may create independently
+ if (!rModelName.isEmpty())
+ CreateUnoControlModel(rModelName);
+}
+
+SdrUnoObj::SdrUnoObj( SdrModel& rSdrModel, SdrUnoObj const & rSource)
+: SdrRectObj(rSdrModel, rSource),
+ m_pImpl( new SdrUnoObjDataHolder )
+{
+ m_bIsUnoObj = true;
+
+ m_pImpl->pEventListener = new SdrControlEventListenerImpl(this);
+
+ aUnoControlModelTypeName = rSource.aUnoControlModelTypeName;
+ aUnoControlTypeName = rSource.aUnoControlTypeName;
+
+ // copy the uno control model
+ const uno::Reference< awt::XControlModel > xSourceControlModel = rSource.GetUnoControlModel();
+ if ( xSourceControlModel.is() )
+ {
+ try
+ {
+ uno::Reference< util::XCloneable > xClone( xSourceControlModel, uno::UNO_QUERY_THROW );
+ xUnoControlModel.set( xClone->createClone(), uno::UNO_QUERY_THROW );
+ }
+ catch( const uno::Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+ // get service name of the control from the control model
+ uno::Reference< beans::XPropertySet > xSet(xUnoControlModel, uno::UNO_QUERY);
+ if (xSet.is())
+ {
+ uno::Any aValue( xSet->getPropertyValue("DefaultControl") );
+ OUString aStr;
+
+ if( aValue >>= aStr )
+ aUnoControlTypeName = aStr;
+ }
+
+ uno::Reference< lang::XComponent > xComp(xUnoControlModel, uno::UNO_QUERY);
+ if (xComp.is())
+ m_pImpl->pEventListener->StartListening(xComp);
+}
+
+SdrUnoObj::SdrUnoObj(
+ SdrModel& rSdrModel,
+ const OUString& rModelName,
+ const uno::Reference< lang::XMultiServiceFactory >& rxSFac)
+: SdrRectObj(rSdrModel),
+ m_pImpl( new SdrUnoObjDataHolder )
+{
+ m_bIsUnoObj = true;
+
+ m_pImpl->pEventListener = new SdrControlEventListenerImpl(this);
+
+ // only an owner may create independently
+ if (!rModelName.isEmpty())
+ CreateUnoControlModel(rModelName,rxSFac);
+}
+
+SdrUnoObj::~SdrUnoObj()
+{
+ try
+ {
+ // clean up the control model
+ uno::Reference< lang::XComponent > xComp(xUnoControlModel, uno::UNO_QUERY);
+ if (xComp.is())
+ {
+ // is the control model owned by its environment?
+ uno::Reference< container::XChild > xContent(xUnoControlModel, uno::UNO_QUERY);
+ if (xContent.is() && !xContent->getParent().is())
+ xComp->dispose();
+ else
+ m_pImpl->pEventListener->StopListening(xComp);
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "SdrUnoObj::~SdrUnoObj" );
+ }
+}
+
+void SdrUnoObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
+{
+ rInfo.bRotateFreeAllowed = false;
+ rInfo.bRotate90Allowed = false;
+ rInfo.bMirrorFreeAllowed = false;
+ rInfo.bMirror45Allowed = false;
+ rInfo.bMirror90Allowed = false;
+ rInfo.bTransparenceAllowed = false;
+ rInfo.bShearAllowed = false;
+ rInfo.bEdgeRadiusAllowed = false;
+ rInfo.bNoOrthoDesired = false;
+ rInfo.bCanConvToPath = false;
+ rInfo.bCanConvToPoly = false;
+ rInfo.bCanConvToPathLineToArea = false;
+ rInfo.bCanConvToPolyLineToArea = false;
+ rInfo.bCanConvToContour = false;
+}
+
+SdrObjKind SdrUnoObj::GetObjIdentifier() const
+{
+ return SdrObjKind::UNO;
+}
+
+void SdrUnoObj::SetContextWritingMode( const sal_Int16 _nContextWritingMode )
+{
+ try
+ {
+ uno::Reference< beans::XPropertySet > xModelProperties( GetUnoControlModel(), uno::UNO_QUERY_THROW );
+ xModelProperties->setPropertyValue( "ContextWritingMode", uno::Any( _nContextWritingMode ) );
+ }
+ catch( const uno::Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+OUString SdrUnoObj::TakeObjNameSingul() const
+{
+ OUString sName(SvxResId(STR_ObjNameSingulUno));
+
+ OUString aName(GetName());
+ if (!aName.isEmpty())
+ sName += " '" + aName + "'";
+
+ return sName;
+}
+
+OUString SdrUnoObj::TakeObjNamePlural() const
+{
+ return SvxResId(STR_ObjNamePluralUno);
+}
+
+SdrUnoObj* SdrUnoObj::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new SdrUnoObj(rTargetModel, *this);
+}
+
+void SdrUnoObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
+{
+ SdrRectObj::NbcResize(rRef,xFact,yFact);
+
+ if (maGeo.nShearAngle==0_deg100 && maGeo.nRotationAngle==0_deg100)
+ return;
+
+ // small correctors
+ if (maGeo.nRotationAngle>=9000_deg100 && maGeo.nRotationAngle<27000_deg100)
+ {
+ maRect.Move(maRect.Left()-maRect.Right(),maRect.Top()-maRect.Bottom());
+ }
+
+ maGeo.nRotationAngle = 0_deg100;
+ maGeo.nShearAngle = 0_deg100;
+ maGeo.mfSinRotationAngle = 0.0;
+ maGeo.mfCosRotationAngle = 1.0;
+ maGeo.mfTanShearAngle = 0.0;
+ SetBoundAndSnapRectsDirty();
+}
+
+
+bool SdrUnoObj::hasSpecialDrag() const
+{
+ // no special drag; we have no rounding rect and
+ // do want frame handles
+ return false;
+}
+
+void SdrUnoObj::NbcSetLayer( SdrLayerID _nLayer )
+{
+ if ( GetLayer() == _nLayer )
+ { // redundant call -> not interested in doing anything here
+ SdrRectObj::NbcSetLayer( _nLayer );
+ return;
+ }
+
+ // we need some special handling here in case we're moved from an invisible layer
+ // to a visible one, or vice versa
+ // (relative to a layer. Remember that the visibility of a layer is a view attribute
+ // - the same layer can be visible in one view, and invisible in another view, at the
+ // same time)
+
+ // collect all views in which our old layer is visible
+ o3tl::sorted_vector< SdrView* > aPreviouslyVisible;
+
+ {
+ SdrViewIter aIter( this );
+ for ( SdrView* pView = aIter.FirstView(); pView; pView = aIter.NextView() )
+ aPreviouslyVisible.insert( pView );
+ }
+
+ SdrRectObj::NbcSetLayer( _nLayer );
+
+ // collect all views in which our new layer is visible
+ o3tl::sorted_vector< SdrView* > aNewlyVisible;
+
+ {
+ SdrViewIter aIter( this );
+ for ( SdrView* pView = aIter.FirstView(); pView; pView = aIter.NextView() )
+ {
+ if ( aPreviouslyVisible.erase(pView) == 0 )
+ {
+ // in pView, we were visible _before_ the layer change, and are
+ // _not_ visible after the layer change
+ // => remember this view, as our visibility there changed
+ aNewlyVisible.insert( pView );
+ }
+ }
+ }
+
+ // now aPreviouslyVisible contains all views where we became invisible
+ for (const auto& rpView : aPreviouslyVisible)
+ {
+ lcl_ensureControlVisibility( rpView, this, false );
+ }
+
+ // and aNewlyVisible all views where we became visible
+ for (const auto& rpView : aNewlyVisible)
+ {
+ lcl_ensureControlVisibility( rpView, this, true );
+ }
+}
+
+void SdrUnoObj::CreateUnoControlModel(const OUString& rModelName)
+{
+ DBG_ASSERT(!xUnoControlModel.is(), "model already exists");
+
+ aUnoControlModelTypeName = rModelName;
+
+ uno::Reference< awt::XControlModel > xModel;
+ uno::Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
+ if (!aUnoControlModelTypeName.isEmpty() )
+ {
+ xModel.set(xContext->getServiceManager()->createInstanceWithContext(
+ aUnoControlModelTypeName, xContext), uno::UNO_QUERY);
+
+ if (xModel.is())
+ SetChanged();
+ }
+
+ SetUnoControlModel(xModel);
+}
+
+void SdrUnoObj::CreateUnoControlModel(const OUString& rModelName,
+ const uno::Reference< lang::XMultiServiceFactory >& rxSFac)
+{
+ DBG_ASSERT(!xUnoControlModel.is(), "model already exists");
+
+ aUnoControlModelTypeName = rModelName;
+
+ uno::Reference< awt::XControlModel > xModel;
+ if (!aUnoControlModelTypeName.isEmpty() && rxSFac.is() )
+ {
+ xModel.set(rxSFac->createInstance(aUnoControlModelTypeName), uno::UNO_QUERY);
+
+ if (xModel.is())
+ SetChanged();
+ }
+
+ SetUnoControlModel(xModel);
+}
+
+void SdrUnoObj::SetUnoControlModel( const uno::Reference< awt::XControlModel >& xModel)
+{
+ if (xUnoControlModel.is())
+ {
+ uno::Reference< lang::XComponent > xComp(xUnoControlModel, uno::UNO_QUERY);
+ if (xComp.is())
+ m_pImpl->pEventListener->StopListening(xComp);
+ }
+
+ xUnoControlModel = xModel;
+
+ // control model has to contain service name of the control
+ if (xUnoControlModel.is())
+ {
+ uno::Reference< beans::XPropertySet > xSet(xUnoControlModel, uno::UNO_QUERY);
+ if (xSet.is())
+ {
+ uno::Any aValue( xSet->getPropertyValue("DefaultControl") );
+ OUString aStr;
+ if( aValue >>= aStr )
+ aUnoControlTypeName = aStr;
+ }
+
+ uno::Reference< lang::XComponent > xComp(xUnoControlModel, uno::UNO_QUERY);
+ if (xComp.is())
+ m_pImpl->pEventListener->StartListening(xComp);
+ }
+
+ // invalidate all ViewObject contacts
+ ViewContactOfUnoControl* pVC = nullptr;
+ if ( impl_getViewContact( pVC ) )
+ {
+ // flushViewObjectContacts() removes all existing VOCs for the local DrawHierarchy. This
+ // is always allowed since they will be re-created on demand (and with the changed model)
+ GetViewContact().flushViewObjectContacts();
+ }
+}
+
+
+uno::Reference< awt::XControl > SdrUnoObj::GetUnoControl(const SdrView& _rView, const OutputDevice& _rOut) const
+{
+ uno::Reference< awt::XControl > xControl;
+
+ SdrPageView* pPageView = _rView.GetSdrPageView();
+ OSL_ENSURE( pPageView && getSdrPageFromSdrObject() == pPageView->GetPage(), "SdrUnoObj::GetUnoControl: This object is not displayed in that particular view!" );
+ if ( !pPageView || getSdrPageFromSdrObject() != pPageView->GetPage() )
+ return nullptr;
+
+ SdrPageWindow* pPageWindow = pPageView->FindPageWindow( _rOut );
+ OSL_ENSURE( pPageWindow, "SdrUnoObj::GetUnoControl: did not find my SdrPageWindow!" );
+ if ( !pPageWindow )
+ return nullptr;
+
+ ViewObjectContact& rViewObjectContact( GetViewContact().GetViewObjectContact( pPageWindow->GetObjectContact() ) );
+ ViewObjectContactOfUnoControl* pUnoContact = dynamic_cast< ViewObjectContactOfUnoControl* >( &rViewObjectContact );
+ OSL_ENSURE( pUnoContact, "SdrUnoObj::GetUnoControl: wrong contact type!" );
+ if ( pUnoContact )
+ xControl = pUnoContact->getControl();
+
+ return xControl;
+}
+
+
+uno::Reference< awt::XControl > SdrUnoObj::GetTemporaryControlForWindow(
+ const vcl::Window& _rWindow, uno::Reference< awt::XControlContainer >& _inout_ControlContainer ) const
+{
+ uno::Reference< awt::XControl > xControl;
+
+ ViewContactOfUnoControl* pVC = nullptr;
+ if ( impl_getViewContact( pVC ) )
+ xControl = pVC->getTemporaryControlForWindow( _rWindow, _inout_ControlContainer );
+
+ return xControl;
+}
+
+
+bool SdrUnoObj::impl_getViewContact( ViewContactOfUnoControl*& _out_rpContact ) const
+{
+ ViewContact& rViewContact( GetViewContact() );
+ _out_rpContact = dynamic_cast< ViewContactOfUnoControl* >( &rViewContact );
+ DBG_ASSERT( _out_rpContact, "SdrUnoObj::impl_getViewContact: could not find my ViewContact!" );
+ return ( _out_rpContact != nullptr );
+}
+
+
+std::unique_ptr<sdr::contact::ViewContact> SdrUnoObj::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::ViewContactOfUnoControl>( *this );
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdoutl.cxx b/svx/source/svdraw/svdoutl.cxx
new file mode 100644
index 000000000..3737b1fdf
--- /dev/null
+++ b/svx/source/svdraw/svdoutl.cxx
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/svdoutl.hxx>
+#include <editeng/outliner.hxx>
+#include <svx/svdotext.hxx>
+#include <editeng/editstat.hxx>
+#include <svl/itempool.hxx>
+#include <editeng/editview.hxx>
+
+
+SdrOutliner::SdrOutliner( SfxItemPool* pItemPool, OutlinerMode nMode )
+: Outliner( pItemPool, nMode ),
+ mpVisualizedPage(nullptr)
+{
+}
+
+
+SdrOutliner::~SdrOutliner()
+{
+}
+
+
+void SdrOutliner::SetTextObj( const SdrTextObj* pObj )
+{
+ if( pObj && pObj != mxWeakTextObj.get() )
+ {
+ SetUpdateLayout(false);
+ OutlinerMode nOutlinerMode2 = OutlinerMode::OutlineObject;
+ if ( !pObj->IsOutlText() )
+ nOutlinerMode2 = OutlinerMode::TextObject;
+ Init( nOutlinerMode2 );
+
+ SetGlobalCharStretching();
+
+ EEControlBits nStat = GetControlWord();
+ nStat &= ~EEControlBits( EEControlBits::STRETCHING | EEControlBits::AUTOPAGESIZE );
+ SetControlWord(nStat);
+
+ Size aMaxSize( 100000,100000 );
+ SetMinAutoPaperSize( Size() );
+ SetMaxAutoPaperSize( aMaxSize );
+ SetPaperSize( aMaxSize );
+ SetTextColumns(pObj->GetTextColumnsNumber(), pObj->GetTextColumnsSpacing());
+ ClearPolygon();
+ }
+
+ mxWeakTextObj.reset( const_cast< SdrTextObj* >(pObj) );
+}
+
+void SdrOutliner::SetTextObjNoInit( const SdrTextObj* pObj )
+{
+ mxWeakTextObj.reset( const_cast< SdrTextObj* >(pObj) );
+}
+
+OUString SdrOutliner::CalcFieldValue(const SvxFieldItem& rField, sal_Int32 nPara, sal_Int32 nPos,
+ std::optional<Color>& rpTxtColor, std::optional<Color>& rpFldColor)
+{
+ bool bOk = false;
+ OUString aRet;
+
+ if(mxWeakTextObj.is())
+ bOk = mxWeakTextObj->CalcFieldValue(rField, nPara, nPos, false, rpTxtColor, rpFldColor, aRet);
+
+ if (!bOk)
+ aRet = Outliner::CalcFieldValue(rField, nPara, nPos, rpTxtColor, rpFldColor);
+
+ return aRet;
+}
+
+const SdrTextObj* SdrOutliner::GetTextObj() const
+{
+ return mxWeakTextObj.get();
+}
+
+bool SdrOutliner::hasEditViewCallbacks() const
+{
+ for (size_t a(0); a < GetViewCount(); a++)
+ {
+ OutlinerView* pOutlinerView = GetView(a);
+
+ if (pOutlinerView && pOutlinerView->GetEditView().getEditViewCallbacks())
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdoutlinercache.cxx b/svx/source/svdraw/svdoutlinercache.cxx
new file mode 100644
index 000000000..0fc6fc973
--- /dev/null
+++ b/svx/source/svdraw/svdoutlinercache.cxx
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svdoutlinercache.hxx>
+#include <svx/svdoutl.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdetc.hxx>
+
+SdrOutlinerCache::SdrOutlinerCache( SdrModel* pModel )
+: mpModel( pModel )
+{
+}
+
+std::unique_ptr<SdrOutliner> SdrOutlinerCache::createOutliner( OutlinerMode nOutlinerMode )
+{
+ std::unique_ptr<SdrOutliner> pOutliner;
+
+ if( (OutlinerMode::OutlineObject == nOutlinerMode) && !maModeOutline.empty() )
+ {
+ pOutliner = std::move(maModeOutline.back());
+ maModeOutline.pop_back();
+ }
+ else if( (OutlinerMode::TextObject == nOutlinerMode) && !maModeText.empty() )
+ {
+ pOutliner = std::move(maModeText.back());
+ maModeText.pop_back();
+ }
+ else
+ {
+ pOutliner = SdrMakeOutliner(nOutlinerMode, *mpModel);
+ Outliner& aDrawOutliner = mpModel->GetDrawOutliner();
+ pOutliner->SetCalcFieldValueHdl( aDrawOutliner.GetCalcFieldValueHdl() );
+ maActiveOutliners.insert(pOutliner.get());
+ }
+
+ return pOutliner;
+}
+
+SdrOutlinerCache::~SdrOutlinerCache()
+{
+}
+
+void SdrOutlinerCache::disposeOutliner( std::unique_ptr<SdrOutliner> pOutliner )
+{
+ if( !pOutliner )
+ return;
+
+ OutlinerMode nOutlMode = pOutliner->GetOutlinerMode();
+
+ if( OutlinerMode::OutlineObject == nOutlMode )
+ {
+ pOutliner->Clear();
+ pOutliner->SetVertical( false );
+
+ // Deregister on outliner, might be reused from outliner cache
+ pOutliner->SetNotifyHdl( Link<EENotify&,void>() );
+ maModeOutline.emplace_back(std::move(pOutliner));
+ }
+ else if( OutlinerMode::TextObject == nOutlMode )
+ {
+ pOutliner->Clear();
+ pOutliner->SetVertical( false );
+
+ // Deregister on outliner, might be reused from outliner cache
+ pOutliner->SetNotifyHdl( Link<EENotify&,void>() );
+ maModeText.emplace_back(std::move(pOutliner));
+ }
+ else
+ {
+ maActiveOutliners.erase(pOutliner.get());
+ }
+}
+
+std::vector< SdrOutliner* > SdrOutlinerCache::GetActiveOutliners() const
+{
+ return std::vector< SdrOutliner* >(maActiveOutliners.begin(), maActiveOutliners.end());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdovirt.cxx b/svx/source/svdraw/svdovirt.cxx
new file mode 100644
index 000000000..372756cce
--- /dev/null
+++ b/svx/source/svdraw/svdovirt.cxx
@@ -0,0 +1,567 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <svx/svdovirt.hxx>
+#include <svx/svdhdl.hxx>
+#include <svx/sdr/contact/viewcontactofvirtobj.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svddrgv.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+
+sdr::properties::BaseProperties& SdrVirtObj::GetProperties() const
+{
+ return rRefObj.GetProperties();
+}
+
+
+// #i27224#
+std::unique_ptr<sdr::contact::ViewContact> SdrVirtObj::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::ViewContactOfVirtObj>(*this);
+}
+
+SdrVirtObj::SdrVirtObj(
+ SdrModel& rSdrModel,
+ SdrObject& rNewObj)
+: SdrObject(rSdrModel),
+ rRefObj(rNewObj)
+{
+ m_bVirtObj=true; // this is only a virtual object
+ rRefObj.AddReference(*this);
+ m_bClosedObj=rRefObj.IsClosedObj();
+}
+
+SdrVirtObj::SdrVirtObj(
+ SdrModel& rSdrModel, SdrVirtObj const & rSource)
+: SdrObject(rSdrModel, rSource),
+ rRefObj(rSource.rRefObj)
+{
+ m_bVirtObj=true; // this is only a virtual object
+ m_bClosedObj=rRefObj.IsClosedObj();
+
+ rRefObj.AddReference(*this);
+
+ aSnapRect = rSource.aSnapRect;
+ m_aAnchor = rSource.m_aAnchor;
+}
+
+SdrVirtObj::~SdrVirtObj()
+{
+ rRefObj.DelReference(*this);
+}
+
+const SdrObject& SdrVirtObj::GetReferencedObj() const
+{
+ return rRefObj;
+}
+
+SdrObject& SdrVirtObj::ReferencedObj()
+{
+ return rRefObj;
+}
+
+void SdrVirtObj::Notify(SfxBroadcaster& /*rBC*/, const SfxHint& /*rHint*/)
+{
+ m_bClosedObj=rRefObj.IsClosedObj();
+ SetBoundAndSnapRectsDirty(); // TODO: Optimize this.
+
+ // Only a repaint here, rRefObj may have changed and broadcasts
+ ActionChanged();
+}
+
+void SdrVirtObj::NbcSetAnchorPos(const Point& rAnchorPos)
+{
+ m_aAnchor=rAnchorPos;
+}
+
+void SdrVirtObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
+{
+ rRefObj.TakeObjInfo(rInfo);
+}
+
+SdrInventor SdrVirtObj::GetObjInventor() const
+{
+ return rRefObj.GetObjInventor();
+}
+
+SdrObjKind SdrVirtObj::GetObjIdentifier() const
+{
+ return rRefObj.GetObjIdentifier();
+}
+
+SdrObjList* SdrVirtObj::GetSubList() const
+{
+ return rRefObj.GetSubList();
+}
+
+void SdrVirtObj::SetName(const OUString& rStr, const bool bSetChanged)
+{
+ return rRefObj.SetName(rStr, bSetChanged);
+}
+
+const OUString & SdrVirtObj::GetName() const
+{
+ return rRefObj.GetName();
+}
+
+void SdrVirtObj::SetTitle(const OUString& rStr)
+{
+ return rRefObj.SetTitle(rStr);
+}
+
+OUString SdrVirtObj::GetTitle() const
+{
+ return rRefObj.GetTitle();
+}
+
+void SdrVirtObj::SetDescription(const OUString& rStr)
+{
+ return rRefObj.SetDescription(rStr);
+}
+
+OUString SdrVirtObj::GetDescription() const
+{
+ return rRefObj.GetDescription();
+}
+
+const tools::Rectangle& SdrVirtObj::GetCurrentBoundRect() const
+{
+ m_aOutRect = rRefObj.GetCurrentBoundRect(); // TODO: Optimize this.
+ m_aOutRect += m_aAnchor;
+ return m_aOutRect;
+}
+
+const tools::Rectangle& SdrVirtObj::GetLastBoundRect() const
+{
+ m_aOutRect = rRefObj.GetLastBoundRect(); // TODO: Optimize this.
+ m_aOutRect += m_aAnchor;
+ return m_aOutRect;
+}
+
+void SdrVirtObj::RecalcBoundRect()
+{
+ m_aOutRect=rRefObj.GetCurrentBoundRect();
+ m_aOutRect+=m_aAnchor;
+}
+
+SdrVirtObj* SdrVirtObj::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new SdrVirtObj(rTargetModel, *this);
+ // TTTT not sure if the above works - how could SdrObjFactory::MakeNewObject
+ // create an object with correct rRefObj (?) OTOH VirtObj probably needs not
+ // to be cloned ever - only used in Writer for multiple instances e.g. Header/Footer
+ // return new SdrVirtObj(
+ // getSdrModelFromSdrObject(),
+ // rRefObj); // only a further reference
+}
+
+OUString SdrVirtObj::TakeObjNameSingul() const
+{
+ OUString sName = "[" + rRefObj.TakeObjNameSingul() + "]";
+
+ OUString aName(GetName());
+ if (!aName.isEmpty())
+ sName += " '" + aName + "'";
+
+ return sName;
+}
+
+OUString SdrVirtObj::TakeObjNamePlural() const
+{
+ return "[" + rRefObj.TakeObjNamePlural() + "]";
+}
+
+bool SdrVirtObj::HasLimitedRotation() const
+{
+ // RotGrfFlyFrame: If true, this SdrObject supports only limited rotation
+ return rRefObj.HasLimitedRotation();
+}
+
+basegfx::B2DPolyPolygon SdrVirtObj::TakeXorPoly() const
+{
+ basegfx::B2DPolyPolygon aPolyPolygon(rRefObj.TakeXorPoly());
+
+ if(m_aAnchor.X() || m_aAnchor.Y())
+ {
+ aPolyPolygon.transform(basegfx::utils::createTranslateB2DHomMatrix(m_aAnchor.X(), m_aAnchor.Y()));
+ }
+
+ return aPolyPolygon;
+}
+
+
+sal_uInt32 SdrVirtObj::GetHdlCount() const
+{
+ return rRefObj.GetHdlCount();
+}
+
+void SdrVirtObj::AddToHdlList(SdrHdlList& rHdlList) const
+{
+ SdrHdlList tempList(nullptr);
+ rRefObj.AddToHdlList(tempList);
+ for (size_t i=0; i<tempList.GetHdlCount(); ++i)
+ {
+ SdrHdl* pHdl = tempList.GetHdl(i);
+ Point aP(pHdl->GetPos()+m_aAnchor);
+ pHdl->SetPos(aP);
+ }
+ tempList.MoveTo(rHdlList);
+}
+
+void SdrVirtObj::AddToPlusHdlList(SdrHdlList& rHdlList, SdrHdl& rHdl) const
+{
+ SdrHdlList tempList(nullptr);
+ rRefObj.AddToPlusHdlList(tempList, rHdl);
+ for (size_t i=0; i<tempList.GetHdlCount(); ++i)
+ {
+ SdrHdl* pHdl = tempList.GetHdl(i);
+ Point aP(pHdl->GetPos()+m_aAnchor);
+ pHdl->SetPos(aP);
+ }
+ tempList.MoveTo(rHdlList);
+}
+
+bool SdrVirtObj::hasSpecialDrag() const
+{
+ return rRefObj.hasSpecialDrag();
+}
+
+bool SdrVirtObj::supportsFullDrag() const
+{
+ return false;
+}
+
+SdrObjectUniquePtr SdrVirtObj::getFullDragClone() const
+{
+ SdrObject& rReferencedObject = const_cast<SdrVirtObj*>(this)->ReferencedObj();
+ return SdrObjectUniquePtr(new SdrGrafObj(
+ getSdrModelFromSdrObject(),
+ SdrDragView::GetObjGraphic(rReferencedObject),
+ GetLogicRect()));
+}
+
+bool SdrVirtObj::beginSpecialDrag(SdrDragStat& rDrag) const
+{
+ return rRefObj.beginSpecialDrag(rDrag);
+}
+
+bool SdrVirtObj::applySpecialDrag(SdrDragStat& rDrag)
+{
+ return rRefObj.applySpecialDrag(rDrag);
+}
+
+basegfx::B2DPolyPolygon SdrVirtObj::getSpecialDragPoly(const SdrDragStat& rDrag) const
+{
+ return rRefObj.getSpecialDragPoly(rDrag);
+ // TODO: we don't handle offsets yet!
+}
+
+OUString SdrVirtObj::getSpecialDragComment(const SdrDragStat& rDrag) const
+{
+ return rRefObj.getSpecialDragComment(rDrag);
+}
+
+
+bool SdrVirtObj::BegCreate(SdrDragStat& rStat)
+{
+ return rRefObj.BegCreate(rStat);
+}
+
+bool SdrVirtObj::MovCreate(SdrDragStat& rStat)
+{
+ return rRefObj.MovCreate(rStat);
+}
+
+bool SdrVirtObj::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd)
+{
+ return rRefObj.EndCreate(rStat,eCmd);
+}
+
+bool SdrVirtObj::BckCreate(SdrDragStat& rStat)
+{
+ return rRefObj.BckCreate(rStat);
+}
+
+void SdrVirtObj::BrkCreate(SdrDragStat& rStat)
+{
+ rRefObj.BrkCreate(rStat);
+}
+
+basegfx::B2DPolyPolygon SdrVirtObj::TakeCreatePoly(const SdrDragStat& rDrag) const
+{
+ return rRefObj.TakeCreatePoly(rDrag);
+ // TODO: we don't handle offsets yet!
+}
+
+
+void SdrVirtObj::NbcMove(const Size& rSiz)
+{
+ m_aAnchor.Move(rSiz);
+ SetBoundAndSnapRectsDirty();
+}
+
+void SdrVirtObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
+{
+ rRefObj.NbcResize(rRef-m_aAnchor,xFact,yFact);
+ SetBoundAndSnapRectsDirty();
+}
+
+void SdrVirtObj::NbcRotate(const Point& rRef, Degree100 nAngle, double sn, double cs)
+{
+ rRefObj.NbcRotate(rRef-m_aAnchor,nAngle,sn,cs);
+ SetBoundAndSnapRectsDirty();
+}
+
+void SdrVirtObj::NbcMirror(const Point& rRef1, const Point& rRef2)
+{
+ rRefObj.NbcMirror(rRef1-m_aAnchor,rRef2-m_aAnchor);
+ SetBoundAndSnapRectsDirty();
+}
+
+void SdrVirtObj::NbcShear(const Point& rRef, Degree100 nAngle, double tn, bool bVShear)
+{
+ rRefObj.NbcShear(rRef-m_aAnchor,nAngle,tn,bVShear);
+ SetBoundAndSnapRectsDirty();
+}
+
+
+void SdrVirtObj::Move(const Size& rSiz)
+{
+ if (!rSiz.IsEmpty()) {
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ NbcMove(rSiz);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::MoveOnly,aBoundRect0);
+ }
+}
+
+void SdrVirtObj::Resize(const Point& rRef, const Fraction& xFact, const Fraction& yFact, bool bUnsetRelative)
+{
+ if (xFact.GetNumerator()!=xFact.GetDenominator() || yFact.GetNumerator()!=yFact.GetDenominator()) {
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ rRefObj.Resize(rRef-m_aAnchor,xFact,yFact, bUnsetRelative);
+ SetBoundAndSnapRectsDirty();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+ }
+}
+
+void SdrVirtObj::Rotate(const Point& rRef, Degree100 nAngle, double sn, double cs)
+{
+ if (nAngle) {
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ rRefObj.Rotate(rRef-m_aAnchor,nAngle,sn,cs);
+ SetBoundAndSnapRectsDirty();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+ }
+}
+
+void SdrVirtObj::Mirror(const Point& rRef1, const Point& rRef2)
+{
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ rRefObj.Mirror(rRef1-m_aAnchor,rRef2-m_aAnchor);
+ SetBoundAndSnapRectsDirty();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+}
+
+void SdrVirtObj::Shear(const Point& rRef, Degree100 nAngle, double tn, bool bVShear)
+{
+ if (nAngle) {
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ rRefObj.Shear(rRef-m_aAnchor,nAngle,tn,bVShear);
+ SetBoundAndSnapRectsDirty();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+ }
+}
+
+
+void SdrVirtObj::RecalcSnapRect()
+{
+ aSnapRect=rRefObj.GetSnapRect();
+ aSnapRect+=m_aAnchor;
+}
+
+const tools::Rectangle& SdrVirtObj::GetSnapRect() const
+{
+ const_cast<SdrVirtObj*>(this)->aSnapRect=rRefObj.GetSnapRect();
+ const_cast<SdrVirtObj*>(this)->aSnapRect+=m_aAnchor;
+ return aSnapRect;
+}
+
+void SdrVirtObj::SetSnapRect(const tools::Rectangle& rRect)
+{
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ tools::Rectangle aR(rRect);
+ aR-=m_aAnchor;
+ rRefObj.SetSnapRect(aR);
+ SetBoundAndSnapRectsDirty();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+}
+
+void SdrVirtObj::NbcSetSnapRect(const tools::Rectangle& rRect)
+{
+ tools::Rectangle aR(rRect);
+ aR-=m_aAnchor;
+ SetBoundAndSnapRectsDirty();
+ rRefObj.NbcSetSnapRect(aR);
+}
+
+
+const tools::Rectangle& SdrVirtObj::GetLogicRect() const
+{
+ const_cast<SdrVirtObj*>(this)->aSnapRect=rRefObj.GetLogicRect(); // An abuse of aSnapRect!
+ const_cast<SdrVirtObj*>(this)->aSnapRect+=m_aAnchor; // If there's trouble, we need another Rectangle Member (or a Heap).
+ return aSnapRect;
+}
+
+void SdrVirtObj::SetLogicRect(const tools::Rectangle& rRect)
+{
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ tools::Rectangle aR(rRect);
+ aR-=m_aAnchor;
+ rRefObj.SetLogicRect(aR);
+ SetBoundAndSnapRectsDirty();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+}
+
+void SdrVirtObj::NbcSetLogicRect(const tools::Rectangle& rRect)
+{
+ tools::Rectangle aR(rRect);
+ aR-=m_aAnchor;
+ SetBoundAndSnapRectsDirty();
+ rRefObj.NbcSetLogicRect(aR);
+}
+
+
+Degree100 SdrVirtObj::GetRotateAngle() const
+{
+ return rRefObj.GetRotateAngle();
+}
+
+Degree100 SdrVirtObj::GetShearAngle(bool bVertical) const
+{
+ return rRefObj.GetShearAngle(bVertical);
+}
+
+
+sal_uInt32 SdrVirtObj::GetSnapPointCount() const
+{
+ return rRefObj.GetSnapPointCount();
+}
+
+Point SdrVirtObj::GetSnapPoint(sal_uInt32 i) const
+{
+ Point aP(rRefObj.GetSnapPoint(i));
+ aP+=m_aAnchor;
+ return aP;
+}
+
+bool SdrVirtObj::IsPolyObj() const
+{
+ return rRefObj.IsPolyObj();
+}
+
+sal_uInt32 SdrVirtObj::GetPointCount() const
+{
+ return rRefObj.GetPointCount();
+}
+
+Point SdrVirtObj::GetPoint(sal_uInt32 i) const
+{
+ return rRefObj.GetPoint(i) + m_aAnchor;
+}
+
+void SdrVirtObj::NbcSetPoint(const Point& rPnt, sal_uInt32 i)
+{
+ Point aP(rPnt);
+ aP-=m_aAnchor;
+ rRefObj.SetPoint(aP,i);
+ SetBoundAndSnapRectsDirty();
+}
+
+
+std::unique_ptr<SdrObjGeoData> SdrVirtObj::NewGeoData() const
+{
+ return rRefObj.NewGeoData();
+}
+
+void SdrVirtObj::SaveGeoData(SdrObjGeoData& rGeo) const
+{
+ rRefObj.SaveGeoData(rGeo);
+}
+
+void SdrVirtObj::RestoreGeoData(const SdrObjGeoData& rGeo)
+{
+ rRefObj.RestoreGeoData(rGeo);
+ SetBoundAndSnapRectsDirty();
+}
+
+
+std::unique_ptr<SdrObjGeoData> SdrVirtObj::GetGeoData() const
+{
+ return rRefObj.GetGeoData();
+}
+
+void SdrVirtObj::SetGeoData(const SdrObjGeoData& rGeo)
+{
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ rRefObj.SetGeoData(rGeo);
+ SetBoundAndSnapRectsDirty();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+}
+
+
+void SdrVirtObj::NbcReformatText()
+{
+ rRefObj.NbcReformatText();
+}
+
+bool SdrVirtObj::HasMacro() const
+{
+ return rRefObj.HasMacro();
+}
+
+SdrObject* SdrVirtObj::CheckMacroHit(const SdrObjMacroHitRec& rRec) const
+{
+ return rRefObj.CheckMacroHit(rRec); // TODO: positioning offset
+}
+
+PointerStyle SdrVirtObj::GetMacroPointer(const SdrObjMacroHitRec& rRec) const
+{
+ return rRefObj.GetMacroPointer(rRec); // TODO: positioning offset
+}
+
+void SdrVirtObj::PaintMacro(OutputDevice& rOut, const tools::Rectangle& rDirtyRect, const SdrObjMacroHitRec& rRec) const
+{
+ rRefObj.PaintMacro(rOut,rDirtyRect,rRec); // TODO: positioning offset
+}
+
+bool SdrVirtObj::DoMacro(const SdrObjMacroHitRec& rRec)
+{
+ return rRefObj.DoMacro(rRec); // TODO: positioning offset
+}
+
+Point SdrVirtObj::GetOffset() const
+{
+ // #i73248# default offset of SdrVirtObj is aAnchor
+ return m_aAnchor;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdpage.cxx b/svx/source/svdraw/svdpage.cxx
new file mode 100644
index 000000000..b7aa8777e
--- /dev/null
+++ b/svx/source/svdraw/svdpage.cxx
@@ -0,0 +1,1895 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <cassert>
+#include <set>
+#include <unordered_set>
+
+#include <svx/svdpage.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <string.h>
+
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <svtools/colorcfg.hxx>
+#include <svx/svdetc.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svdoedge.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svditer.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdlayer.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdundo.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/fmdpage.hxx>
+
+#include <sdr/contact/viewcontactofsdrpage.hxx>
+#include <svx/sdr/contact/viewobjectcontact.hxx>
+#include <svx/sdr/contact/displayinfo.hxx>
+#include <algorithm>
+#include <svl/hint.hxx>
+#include <rtl/strbuf.hxx>
+#include <libxml/xmlwriter.h>
+
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+
+using namespace ::com::sun::star;
+
+//////////////////////////////////////////////////////////////////////////////
+
+SdrObjList::SdrObjList()
+: mbObjOrdNumsDirty(false),
+ mbRectsDirty(false),
+ mbIsNavigationOrderDirty(false)
+{
+}
+
+void SdrObjList::impClearSdrObjList(bool bBroadcast)
+{
+ SdrModel* pSdrModelFromRemovedSdrObject(nullptr);
+
+ while(!maList.empty())
+ {
+ // remove last object from list
+ SdrObject* pObj(maList.back());
+ RemoveObjectFromContainer(maList.size()-1);
+
+ // flushViewObjectContacts() is done since SdrObject::Free is not guaranteed
+ // to delete the object and thus refresh visualisations
+ pObj->GetViewContact().flushViewObjectContacts();
+
+ if(bBroadcast)
+ {
+ if(nullptr == pSdrModelFromRemovedSdrObject)
+ {
+ pSdrModelFromRemovedSdrObject = &pObj->getSdrModelFromSdrObject();
+ }
+
+ // sent remove hint (after removal, see RemoveObject())
+ // TTTT SdrPage not needed, can be accessed using SdrObject
+ SdrHint aHint(SdrHintKind::ObjectRemoved, *pObj, getSdrPageFromSdrObjList());
+ pObj->getSdrModelFromSdrObject().Broadcast(aHint);
+ }
+
+ // delete the object itself
+ SdrObject::Free( pObj );
+ }
+
+ if(bBroadcast && nullptr != pSdrModelFromRemovedSdrObject)
+ {
+ pSdrModelFromRemovedSdrObject->SetChanged();
+ }
+}
+
+void SdrObjList::ClearSdrObjList()
+{
+ // clear SdrObjects with broadcasting
+ impClearSdrObjList(true);
+}
+
+SdrObjList::~SdrObjList()
+{
+ // clear SdrObjects without broadcasting
+ impClearSdrObjList(false);
+}
+
+SdrPage* SdrObjList::getSdrPageFromSdrObjList() const
+{
+ // default is no page and returns zero
+ return nullptr;
+}
+
+SdrObject* SdrObjList::getSdrObjectFromSdrObjList() const
+{
+ // default is no SdrObject (SdrObjGroup)
+ return nullptr;
+}
+
+void SdrObjList::CopyObjects(const SdrObjList& rSrcList)
+{
+ // clear SdrObjects with broadcasting
+ ClearSdrObjList();
+
+ mbObjOrdNumsDirty = false;
+ mbRectsDirty = false;
+ size_t nCloneErrCnt(0);
+ const size_t nCount(rSrcList.GetObjCount());
+
+ if(nullptr == getSdrObjectFromSdrObjList() && nullptr == getSdrPageFromSdrObjList())
+ {
+ OSL_ENSURE(false, "SdrObjList which is not part of SdrPage or SdrObject (!)");
+ return;
+ }
+
+ SdrModel& rTargetSdrModel(nullptr == getSdrObjectFromSdrObjList()
+ ? getSdrPageFromSdrObjList()->getSdrModelFromSdrPage()
+ : getSdrObjectFromSdrObjList()->getSdrModelFromSdrObject());
+
+ for (size_t no(0); no < nCount; ++no)
+ {
+ SdrObject* pSO(rSrcList.GetObj(no));
+ SdrObject* pDO(pSO->CloneSdrObject(rTargetSdrModel));
+
+ if(nullptr != pDO)
+ {
+ NbcInsertObject(pDO, SAL_MAX_SIZE);
+ }
+ else
+ {
+ nCloneErrCnt++;
+ }
+ }
+
+ // and now for the Connectors
+ // The new objects would be shown in the rSrcList
+ // and then the object connections are made.
+ // Similar implementation are setup as the following:
+ // void SdrObjList::CopyObjects(const SdrObjList& rSrcList)
+ // SdrModel* SdrExchangeView::CreateMarkedObjModel() const
+ // BOOL SdrExchangeView::Paste(const SdrModel& rMod,...)
+ // void SdrEditView::CopyMarked()
+ if (nCloneErrCnt==0) {
+ for (size_t no=0; no<nCount; ++no) {
+ const SdrObject* pSrcOb=rSrcList.GetObj(no);
+ const SdrEdgeObj* pSrcEdge=dynamic_cast<const SdrEdgeObj*>( pSrcOb );
+ if (pSrcEdge!=nullptr) {
+ SdrObject* pSrcNode1=pSrcEdge->GetConnectedNode(true);
+ SdrObject* pSrcNode2=pSrcEdge->GetConnectedNode(false);
+ if (pSrcNode1!=nullptr && pSrcNode1->getParentSdrObjListFromSdrObject()!=pSrcEdge->getParentSdrObjListFromSdrObject()) pSrcNode1=nullptr; // can't do this
+ if (pSrcNode2!=nullptr && pSrcNode2->getParentSdrObjListFromSdrObject()!=pSrcEdge->getParentSdrObjListFromSdrObject()) pSrcNode2=nullptr; // across all lists (yet)
+ if (pSrcNode1!=nullptr || pSrcNode2!=nullptr) {
+ SdrObject* pEdgeObjTmp=GetObj(no);
+ SdrEdgeObj* pDstEdge=dynamic_cast<SdrEdgeObj*>( pEdgeObjTmp );
+ if (pDstEdge!=nullptr) {
+ if (pSrcNode1!=nullptr) {
+ sal_uInt32 nDstNode1=pSrcNode1->GetOrdNum();
+ SdrObject* pDstNode1=GetObj(nDstNode1);
+ if (pDstNode1!=nullptr) { // else we get an error!
+ pDstEdge->ConnectToNode(true,pDstNode1);
+ } else {
+ OSL_FAIL("SdrObjList::operator=(): pDstNode1==NULL!");
+ }
+ }
+ if (pSrcNode2!=nullptr) {
+ sal_uInt32 nDstNode2=pSrcNode2->GetOrdNum();
+ SdrObject* pDstNode2=GetObj(nDstNode2);
+ if (pDstNode2!=nullptr) { // else the node was probably not selected
+ pDstEdge->ConnectToNode(false,pDstNode2);
+ } else {
+ OSL_FAIL("SdrObjList::operator=(): pDstNode2==NULL!");
+ }
+ }
+ } else {
+ OSL_FAIL("SdrObjList::operator=(): pDstEdge==NULL!");
+ }
+ }
+ }
+ }
+ } else {
+#ifdef DBG_UTIL
+ OStringBuffer aStr("SdrObjList::operator=(): Error when cloning ");
+
+ if(nCloneErrCnt == 1)
+ {
+ aStr.append("a drawing object.");
+ }
+ else
+ {
+ aStr.append(static_cast<sal_Int32>(nCloneErrCnt));
+ aStr.append(" drawing objects.");
+ }
+
+ aStr.append(" Not copying connectors.");
+
+ OSL_FAIL(aStr.getStr());
+#endif
+ }
+}
+
+void SdrObjList::RecalcObjOrdNums()
+{
+ const size_t nCount = GetObjCount();
+ for (size_t no=0; no<nCount; ++no) {
+ SdrObject* pObj=GetObj(no);
+ pObj->SetOrdNum(no);
+ }
+ mbObjOrdNumsDirty=false;
+}
+
+void SdrObjList::RecalcRects()
+{
+ maSdrObjListOutRect=tools::Rectangle();
+ maSdrObjListSnapRect=maSdrObjListOutRect;
+ const size_t nCount = GetObjCount();
+ for (size_t i=0; i<nCount; ++i) {
+ SdrObject* pObj=GetObj(i);
+ if (i==0) {
+ maSdrObjListOutRect=pObj->GetCurrentBoundRect();
+ maSdrObjListSnapRect=pObj->GetSnapRect();
+ } else {
+ maSdrObjListOutRect.Union(pObj->GetCurrentBoundRect());
+ maSdrObjListSnapRect.Union(pObj->GetSnapRect());
+ }
+ }
+}
+
+void SdrObjList::SetSdrObjListRectsDirty()
+{
+ mbRectsDirty=true;
+ SdrObject* pParentSdrObject(getSdrObjectFromSdrObjList());
+
+ if(nullptr != pParentSdrObject)
+ {
+ pParentSdrObject->SetBoundAndSnapRectsDirty();
+ }
+}
+
+void SdrObjList::impChildInserted(SdrObject const & rChild)
+{
+ sdr::contact::ViewContact* pParent = rChild.GetViewContact().GetParentContact();
+
+ if(pParent)
+ {
+ pParent->ActionChildInserted(rChild.GetViewContact());
+ }
+}
+
+void SdrObjList::NbcInsertObject(SdrObject* pObj, size_t nPos)
+{
+ DBG_ASSERT(pObj!=nullptr,"SdrObjList::NbcInsertObject(NULL)");
+ if (pObj==nullptr)
+ return;
+
+ DBG_ASSERT(!pObj->IsInserted(),"The object already has the status Inserted.");
+ const size_t nCount = GetObjCount();
+ if (nPos>nCount) nPos=nCount;
+ InsertObjectIntoContainer(*pObj,nPos);
+
+ if (nPos<nCount) mbObjOrdNumsDirty=true;
+ pObj->SetOrdNum(nPos);
+ pObj->setParentOfSdrObject(this);
+
+ // Inform the parent about change to allow invalidations at
+ // evtl. existing parent visualisations
+ impChildInserted(*pObj);
+
+ if (!mbRectsDirty) {
+ mbRectsDirty = true;
+ }
+ pObj->InsertedStateChange(); // calls the UserCall (among others)
+}
+
+void SdrObjList::InsertObjectThenMakeNameUnique(SdrObject* pObj)
+{
+ std::unordered_set<rtl::OUString> aNameSet;
+ InsertObjectThenMakeNameUnique(pObj, aNameSet);
+}
+
+void SdrObjList::InsertObjectThenMakeNameUnique(SdrObject* pObj, std::unordered_set<OUString>& rNameSet, size_t nPos)
+{
+ InsertObject(pObj, nPos);
+ if (pObj->GetName().isEmpty())
+ return;
+
+ pObj->MakeNameUnique(rNameSet);
+ SdrObjList* pSdrObjList = pObj->GetSubList(); // group
+ if (pSdrObjList)
+ {
+ SdrObject* pListObj;
+ SdrObjListIter aIter(pSdrObjList, SdrIterMode::DeepWithGroups);
+ while (aIter.IsMore())
+ {
+ pListObj = aIter.Next();
+ pListObj->MakeNameUnique(rNameSet);
+ }
+ }
+}
+
+void SdrObjList::InsertObject(SdrObject* pObj, size_t nPos)
+{
+ DBG_ASSERT(pObj!=nullptr,"SdrObjList::InsertObject(NULL)");
+
+ if(!pObj)
+ return;
+
+ // if anchor is used, reset it before grouping
+ if(getSdrObjectFromSdrObjList())
+ {
+ const Point& rAnchorPos = pObj->GetAnchorPos();
+ if(rAnchorPos.X() || rAnchorPos.Y())
+ pObj->NbcSetAnchorPos(Point());
+ }
+
+ // do insert to new group
+ NbcInsertObject(pObj, nPos);
+
+ // In case the object is inserted into a group and doesn't overlap with
+ // the group's other members, it needs an own repaint.
+ SdrObject* pParentSdrObject(getSdrObjectFromSdrObjList());
+
+ if(pParentSdrObject)
+ {
+ // only repaint here
+ pParentSdrObject->ActionChanged();
+ }
+
+ // TODO: We need a different broadcast here!
+ // Repaint from object number ... (heads-up: GroupObj)
+ if(pObj->getSdrPageFromSdrObject() && !pObj->getSdrModelFromSdrObject().isLocked())
+ {
+ SdrHint aHint(SdrHintKind::ObjectInserted, *pObj);
+ pObj->getSdrModelFromSdrObject().Broadcast(aHint);
+ }
+
+ pObj->getSdrModelFromSdrObject().SetChanged();
+}
+
+SdrObject* SdrObjList::NbcRemoveObject(size_t nObjNum)
+{
+ if (nObjNum >= maList.size())
+ {
+ OSL_ASSERT(nObjNum<maList.size());
+ return nullptr;
+ }
+
+ const size_t nCount = GetObjCount();
+ SdrObject* pObj=maList[nObjNum];
+ RemoveObjectFromContainer(nObjNum);
+
+ DBG_ASSERT(pObj!=nullptr,"Could not find object to remove.");
+ if (pObj!=nullptr)
+ {
+ // flushViewObjectContacts() clears the VOC's and those invalidate
+ pObj->GetViewContact().flushViewObjectContacts();
+
+ DBG_ASSERT(pObj->IsInserted(),"The object does not have the status Inserted.");
+
+ // tdf#121022 Do first remove from SdrObjList - InsertedStateChange
+ // relies now on IsInserted which uses getParentSdrObjListFromSdrObject
+ pObj->setParentOfSdrObject(nullptr);
+
+ // calls UserCall, among other
+ pObj->InsertedStateChange();
+
+ if (!mbObjOrdNumsDirty)
+ {
+ // optimizing for the case that the last object has to be removed
+ if (nObjNum+1!=nCount) {
+ mbObjOrdNumsDirty=true;
+ }
+ }
+ SetSdrObjListRectsDirty();
+ }
+ return pObj;
+}
+
+SdrObject* SdrObjList::RemoveObject(size_t nObjNum)
+{
+ if (nObjNum >= maList.size())
+ {
+ OSL_ASSERT(nObjNum<maList.size());
+ return nullptr;
+ }
+
+ const size_t nCount = GetObjCount();
+ SdrObject* pObj=maList[nObjNum];
+ RemoveObjectFromContainer(nObjNum);
+
+ DBG_ASSERT(pObj!=nullptr,"Object to remove not found.");
+ if(pObj)
+ {
+ // flushViewObjectContacts() clears the VOC's and those invalidate
+ pObj->GetViewContact().flushViewObjectContacts();
+ DBG_ASSERT(pObj->IsInserted(),"The object does not have the status Inserted.");
+
+ // TODO: We need a different broadcast here.
+ if (pObj->getSdrPageFromSdrObject()!=nullptr)
+ {
+ SdrHint aHint(SdrHintKind::ObjectRemoved, *pObj);
+ pObj->getSdrModelFromSdrObject().Broadcast(aHint);
+ }
+
+ pObj->getSdrModelFromSdrObject().SetChanged();
+
+ // tdf#121022 Do first remove from SdrObjList - InsertedStateChange
+ // relies now on IsInserted which uses getParentSdrObjListFromSdrObject
+ pObj->setParentOfSdrObject(nullptr);
+
+ // calls, among other things, the UserCall
+ pObj->InsertedStateChange();
+
+ if (!mbObjOrdNumsDirty)
+ {
+ // optimization for the case that the last object is removed
+ if (nObjNum+1!=nCount) {
+ mbObjOrdNumsDirty=true;
+ }
+ }
+
+ SetSdrObjListRectsDirty();
+ SdrObject* pParentSdrObject(getSdrObjectFromSdrObjList());
+
+ if(pParentSdrObject && !GetObjCount())
+ {
+ // empty group created; it needs to be repainted since it's
+ // visualization changes
+ pParentSdrObject->ActionChanged();
+ }
+ }
+ return pObj;
+}
+
+SdrObject* SdrObjList::ReplaceObject(SdrObject* pNewObj, size_t nObjNum)
+{
+ if (nObjNum >= maList.size())
+ {
+ OSL_ASSERT(nObjNum<maList.size());
+ return nullptr;
+ }
+ if (pNewObj == nullptr)
+ {
+ OSL_ASSERT(pNewObj!=nullptr);
+ return nullptr;
+ }
+
+ SdrObject* pObj=maList[nObjNum];
+ DBG_ASSERT(pObj!=nullptr,"SdrObjList::ReplaceObject: Could not find object to remove.");
+ if (pObj!=nullptr) {
+ DBG_ASSERT(pObj->IsInserted(),"SdrObjList::ReplaceObject: the object does not have status Inserted.");
+
+ // TODO: We need a different broadcast here.
+ if (pObj->getSdrPageFromSdrObject()!=nullptr)
+ {
+ SdrHint aHint(SdrHintKind::ObjectRemoved, *pObj);
+ pObj->getSdrModelFromSdrObject().Broadcast(aHint);
+ }
+
+ // Change parent and replace in SdrObjList
+ pObj->setParentOfSdrObject(nullptr);
+ ReplaceObjectInContainer(*pNewObj,nObjNum);
+
+ // tdf#121022 InsertedStateChange uses the parent
+ // to detect if pObj is inserted or not, so have to call
+ // it *after* changing these settings, else an obviously wrong
+ // 'SdrUserCallType::Inserted' would be sent
+ pObj->InsertedStateChange();
+
+ // flushViewObjectContacts() clears the VOC's and those
+ // trigger the evtl. needed invalidate(s)
+ pObj->GetViewContact().flushViewObjectContacts();
+
+ // Setup data at new SdrObject - it already *is* inserted to
+ // the SdrObjList due to 'ReplaceObjectInContainer' above
+ pNewObj->SetOrdNum(nObjNum);
+ pNewObj->setParentOfSdrObject(this);
+
+ // Inform the parent about change to allow invalidations at
+ // evtl. existing parent visualisations, but also react on
+ // newly inserted SdrObjects (as e.g. GraphCtrlUserCall does)
+ impChildInserted(*pNewObj);
+
+ pNewObj->InsertedStateChange();
+
+ // TODO: We need a different broadcast here.
+ if (pNewObj->getSdrPageFromSdrObject()!=nullptr) {
+ SdrHint aHint(SdrHintKind::ObjectInserted, *pNewObj);
+ pNewObj->getSdrModelFromSdrObject().Broadcast(aHint);
+ }
+
+ pNewObj->getSdrModelFromSdrObject().SetChanged();
+
+ SetSdrObjListRectsDirty();
+ }
+ return pObj;
+}
+
+SdrObject* SdrObjList::SetObjectOrdNum(size_t nOldObjNum, size_t nNewObjNum)
+{
+ if (nOldObjNum >= maList.size() || nNewObjNum >= maList.size())
+ {
+ OSL_ASSERT(nOldObjNum<maList.size());
+ OSL_ASSERT(nNewObjNum<maList.size());
+ return nullptr;
+ }
+
+ SdrObject* pObj=maList[nOldObjNum];
+ if (nOldObjNum==nNewObjNum) return pObj;
+ DBG_ASSERT(pObj!=nullptr,"SdrObjList::SetObjectOrdNum: Object not found.");
+ if (pObj!=nullptr) {
+ DBG_ASSERT(pObj->IsInserted(),"SdrObjList::SetObjectOrdNum: the object does not have status Inserted.");
+ RemoveObjectFromContainer(nOldObjNum);
+ InsertObjectIntoContainer(*pObj,nNewObjNum);
+
+ // No need to delete visualisation data since same object
+ // gets inserted again. Also a single ActionChanged is enough
+ pObj->ActionChanged();
+
+ pObj->SetOrdNum(nNewObjNum);
+ mbObjOrdNumsDirty=true;
+
+ // TODO: We need a different broadcast here.
+ if (pObj->getSdrPageFromSdrObject()!=nullptr)
+ pObj->getSdrModelFromSdrObject().Broadcast(SdrHint(SdrHintKind::ObjectChange, *pObj));
+ pObj->getSdrModelFromSdrObject().SetChanged();
+ }
+ return pObj;
+}
+
+void SdrObjList::SetExistingObjectOrdNum(SdrObject* pObj, size_t nNewObjNum)
+{
+ assert(std::find(maList.begin(), maList.end(), pObj) != maList.end() && "This method requires that the child object already be inserted");
+ assert(pObj->IsInserted() && "SdrObjList::SetObjectOrdNum: the object does not have status Inserted.");
+
+ // I am deliberately bypassing getOrdNum() because I don't want to unnecessarily
+ // trigger RecalcObjOrdNums()
+ const sal_uInt32 nOldOrdNum = pObj->m_nOrdNum;
+ if (!mbObjOrdNumsDirty && nOldOrdNum == nNewObjNum)
+ return;
+
+ // Update the navigation positions.
+ if (HasObjectNavigationOrder())
+ {
+ tools::WeakReference<SdrObject> aReference (pObj);
+ auto iObject = ::std::find(
+ mxNavigationOrder->begin(),
+ mxNavigationOrder->end(),
+ aReference);
+ mxNavigationOrder->erase(iObject);
+ mbIsNavigationOrderDirty = true;
+ // The new object does not have a user defined position so append it
+ // to the list.
+ pObj->SetNavigationPosition(mxNavigationOrder->size());
+ mxNavigationOrder->push_back(pObj);
+ }
+ if (nOldOrdNum < maList.size() && maList[nOldOrdNum] == pObj)
+ maList.erase(maList.begin()+nOldOrdNum);
+ else
+ {
+ auto it = std::find(maList.begin(), maList.end(), pObj);
+ maList.erase(it);
+ }
+ // Insert object into object list. Because the insert() method requires
+ // a valid iterator as insertion position, we have to use push_back() to
+ // insert at the end of the list.
+ if (nNewObjNum >= maList.size())
+ maList.push_back(pObj);
+ else
+ maList.insert(maList.begin()+nNewObjNum, pObj);
+
+ mbObjOrdNumsDirty=true;
+
+ // No need to delete visualisation data since same object
+ // gets inserted again. Also a single ActionChanged is enough
+ pObj->ActionChanged();
+
+ pObj->SetOrdNum(nNewObjNum);
+ mbObjOrdNumsDirty=true;
+
+ // TODO: We need a different broadcast here.
+ if (pObj->getSdrPageFromSdrObject()!=nullptr)
+ pObj->getSdrModelFromSdrObject().Broadcast(SdrHint(SdrHintKind::ObjectChange, *pObj));
+ pObj->getSdrModelFromSdrObject().SetChanged();
+}
+
+void SdrObjList::sort( std::vector<sal_Int32>& sortOrder)
+{
+ // no negative indexes and indexes larger than maList size are allowed
+ auto it = std::find_if( sortOrder.begin(), sortOrder.end(), [this](const sal_Int32& rIt)
+ { return ( rIt < 0 || o3tl::make_unsigned(rIt) >= maList.size() ); } );
+ if ( it != sortOrder.end())
+ throw css::lang::IllegalArgumentException("negative index of shape", nullptr, 1);
+
+ // no duplicates
+ std::vector<bool> aNoDuplicates(sortOrder.size(), false);
+ for (size_t i = 0; i < sortOrder.size(); ++i )
+ {
+ size_t idx = static_cast<size_t>( sortOrder[i] );
+
+ if ( aNoDuplicates[idx] )
+ throw css::lang::IllegalArgumentException("duplicate index of shape", nullptr, 2);
+
+ aNoDuplicates[idx] = true;
+ }
+
+ // example sortOrder [2 0 1]
+ // example maList [T T S T T] ( T T = shape with textbox, S = just a shape )
+ // (shapes at positions 0 and 2 have a textbox)
+
+ std::deque<SdrObject*> aNewList(maList.size());
+ std::set<sal_Int32> aShapesWithTextbox;
+ std::vector<sal_Int32> aIncrements;
+ std::vector<sal_Int32> aDuplicates;
+
+ if ( maList.size() > 1)
+ {
+ for (size_t i = 1; i< maList.size(); ++i)
+ {
+ // if this shape is a textbox, then look at its left neighbour
+ // (shape this textbox is in)
+ // and insert the number of textboxes to the left of it
+ if (maList[i]->IsTextBox())
+ aShapesWithTextbox.insert( i - 1 - aShapesWithTextbox.size() );
+ }
+ // example aShapesWithTextbox [0 2]
+ }
+
+ if (aShapesWithTextbox.size() != maList.size() - sortOrder.size())
+ {
+ throw lang::IllegalArgumentException("mismatch of no. of shapes", nullptr, 0);
+ }
+
+ for (size_t i = 0; i< sortOrder.size(); ++i)
+ {
+
+ if (aShapesWithTextbox.count(sortOrder[i]) > 0)
+ aDuplicates.push_back(sortOrder[i]);
+
+ aDuplicates.push_back(sortOrder[i]);
+
+ // example aDuplicates [2 2 0 0 1]
+ }
+ assert(aDuplicates.size() == maList.size());
+
+ aIncrements.push_back(0);
+ for (size_t i = 1; i< sortOrder.size(); ++i)
+ {
+ if (aShapesWithTextbox.count(i - 1))
+ aIncrements.push_back(aIncrements[i-1] + 1 );
+ else
+ aIncrements.push_back(aIncrements[i-1]);
+
+ // example aIncrements [0 1 1]
+ }
+ assert(aIncrements.size() == sortOrder.size());
+
+ std::vector<sal_Int32> aNewSortOrder(maList.size());
+ sal_Int32 nPrev = -1;
+ for (size_t i = 0; i< aDuplicates.size(); ++i)
+ {
+ if (nPrev != aDuplicates[i])
+ aNewSortOrder[i] = aDuplicates[i] + aIncrements[aDuplicates[i]];
+ else
+ aNewSortOrder[i] = aNewSortOrder[i-1] + 1;
+
+ nPrev = aDuplicates[i];
+
+ // example aNewSortOrder [3 4 0 1 2]
+ }
+ assert(aNewSortOrder.size() == maList.size());
+
+#ifndef NDEBUG
+ {
+ std::vector<sal_Int32> tmp(aNewSortOrder);
+ std::sort(tmp.begin(), tmp.end());
+ for (size_t i = 0; i < tmp.size(); ++i)
+ {
+ assert(size_t(tmp[i]) == i);
+ }
+ }
+#endif
+
+ SdrModel & rModel(getSdrPageFromSdrObjList()->getSdrModelFromSdrPage());
+ bool const isUndo(rModel.IsUndoEnabled());
+ if (isUndo)
+ {
+ rModel.AddUndo(SdrUndoFactory::CreateUndoSort(*getSdrPageFromSdrObjList(), sortOrder));
+ }
+
+ for (size_t i = 0; i < aNewSortOrder.size(); ++i)
+ {
+ aNewList[i] = maList[ aNewSortOrder[i] ];
+ aNewList[i]->SetOrdNum(i);
+ }
+
+ std::swap(aNewList, maList);
+}
+
+const tools::Rectangle& SdrObjList::GetAllObjSnapRect() const
+{
+ if (mbRectsDirty) {
+ const_cast<SdrObjList*>(this)->RecalcRects();
+ const_cast<SdrObjList*>(this)->mbRectsDirty=false;
+ }
+ return maSdrObjListSnapRect;
+}
+
+const tools::Rectangle& SdrObjList::GetAllObjBoundRect() const
+{
+ // #i106183# for deep group hierarchies like in chart2, the invalidates
+ // through the hierarchy are not correct; use a 2nd hint for the needed
+ // recalculation. Future versions will have no bool flag at all, but
+ // just maSdrObjListOutRect in empty state to represent an invalid state, thus
+ // it's a step in the right direction.
+ if (mbRectsDirty || maSdrObjListOutRect.IsEmpty())
+ {
+ const_cast<SdrObjList*>(this)->RecalcRects();
+ const_cast<SdrObjList*>(this)->mbRectsDirty=false;
+ }
+ return maSdrObjListOutRect;
+}
+
+void SdrObjList::NbcReformatAllTextObjects()
+{
+ size_t nCount=GetObjCount();
+ size_t nNum=0;
+
+ while (nNum<nCount)
+ {
+ SdrObject* pObj = GetObj(nNum);
+
+ pObj->NbcReformatText();
+ nCount=GetObjCount(); // ReformatText may delete an object
+ nNum++;
+ }
+
+}
+
+void SdrObjList::ReformatAllTextObjects()
+{
+ NbcReformatAllTextObjects();
+}
+
+/** steps over all available objects and reformats all
+ edge objects that are connected to other objects so that
+ they may reposition themselves.
+*/
+void SdrObjList::ReformatAllEdgeObjects()
+{
+ // #i120437# go over whole hierarchy, not only over object level null (seen from grouping)
+ SdrObjListIter aIter(this, SdrIterMode::DeepNoGroups);
+
+ while(aIter.IsMore())
+ {
+ SdrObject* pObj = aIter.Next();
+ if (pObj->GetObjIdentifier() != SdrObjKind::Edge)
+ continue;
+
+ SdrEdgeObj* pSdrEdgeObj = static_cast< SdrEdgeObj* >(pObj);
+ pSdrEdgeObj->Reformat();
+ }
+}
+
+void SdrObjList::BurnInStyleSheetAttributes()
+{
+ for(size_t a = 0; a < GetObjCount(); ++a)
+ {
+ GetObj(a)->BurnInStyleSheetAttributes();
+ }
+}
+
+size_t SdrObjList::GetObjCount() const
+{
+ return maList.size();
+}
+
+
+SdrObject* SdrObjList::GetObj(size_t nNum) const
+{
+ if (nNum < maList.size())
+ return maList[nNum];
+
+ return nullptr;
+}
+
+
+bool SdrObjList::IsReadOnly() const
+{
+ bool bRet(false);
+ SdrObject* pParentSdrObject(getSdrObjectFromSdrObjList());
+
+ if(nullptr != pParentSdrObject)
+ {
+ SdrPage* pSdrPage(pParentSdrObject->getSdrPageFromSdrObject());
+
+ if(nullptr != pSdrPage)
+ {
+ bRet = pSdrPage->IsReadOnly();
+ }
+ }
+
+ return bRet;
+}
+
+void SdrObjList::FlattenGroups()
+{
+ const size_t nObj = GetObjCount();
+ for( size_t i = nObj; i>0; )
+ UnGroupObj(--i);
+}
+
+void SdrObjList::UnGroupObj( size_t nObjNum )
+{
+ // if the given object is no group, this method is a noop
+ SdrObject* pUngroupObj = GetObj( nObjNum );
+ if( pUngroupObj )
+ {
+ SdrObjList* pSrcLst = pUngroupObj->GetSubList();
+ if(pSrcLst)
+ if(auto pUngroupGroup = dynamic_cast<SdrObjGroup*>( pUngroupObj))
+ {
+ // ungroup recursively (has to be head recursion,
+ // otherwise our indices will get trashed when doing it in
+ // the loop)
+ pSrcLst->FlattenGroups();
+
+ // the position at which we insert the members of rUngroupGroup
+ size_t nInsertPos( pUngroupGroup->GetOrdNum() );
+
+ const size_t nCount = pSrcLst->GetObjCount();
+ for( size_t i=0; i<nCount; ++i )
+ {
+ SdrObject* pObj = pSrcLst->RemoveObject(0);
+ InsertObject(pObj, nInsertPos);
+ ++nInsertPos;
+ }
+
+ RemoveObject(nInsertPos);
+ }
+ }
+#ifdef DBG_UTIL
+ else
+ OSL_FAIL("SdrObjList::UnGroupObj: object index invalid");
+#endif
+}
+
+bool SdrObjList::HasObjectNavigationOrder() const { return bool(mxNavigationOrder); }
+
+void SdrObjList::SetObjectNavigationPosition (
+ SdrObject& rObject,
+ const sal_uInt32 nNewPosition)
+{
+ // When the navigation order container has not yet been created then
+ // create one now. It is initialized with the z-order taken from
+ // maList.
+ if (!mxNavigationOrder)
+ {
+ mxNavigationOrder.emplace(maList.begin(), maList.end());
+ }
+ OSL_ASSERT(bool(mxNavigationOrder));
+ OSL_ASSERT( mxNavigationOrder->size() == maList.size());
+
+ tools::WeakReference<SdrObject> aReference (&rObject);
+
+ // Look up the object whose navigation position is to be changed.
+ auto iObject = ::std::find(
+ mxNavigationOrder->begin(),
+ mxNavigationOrder->end(),
+ aReference);
+ if (iObject == mxNavigationOrder->end())
+ {
+ // The given object is not a member of the navigation order.
+ return;
+ }
+
+ // Move the object to its new position.
+ const sal_uInt32 nOldPosition = ::std::distance(mxNavigationOrder->begin(), iObject);
+ if (nOldPosition == nNewPosition)
+ return;
+
+ mxNavigationOrder->erase(iObject);
+ sal_uInt32 nInsertPosition (nNewPosition);
+ // Adapt insertion position for the just erased object.
+ if (nNewPosition >= nOldPosition)
+ nInsertPosition -= 1;
+ if (nInsertPosition >= mxNavigationOrder->size())
+ mxNavigationOrder->push_back(aReference);
+ else
+ mxNavigationOrder->insert(mxNavigationOrder->begin()+nInsertPosition, aReference);
+
+ mbIsNavigationOrderDirty = true;
+
+ // The navigation order is written out to file so mark the model as modified.
+ rObject.getSdrModelFromSdrObject().SetChanged();
+}
+
+
+SdrObject* SdrObjList::GetObjectForNavigationPosition (const sal_uInt32 nNavigationPosition) const
+{
+ if (HasObjectNavigationOrder())
+ {
+ // There is a user defined navigation order. Make sure the object
+ // index is correct and look up the object in mxNavigationOrder.
+ if (nNavigationPosition >= mxNavigationOrder->size())
+ {
+ OSL_ASSERT(nNavigationPosition < mxNavigationOrder->size());
+ }
+ else
+ return (*mxNavigationOrder)[nNavigationPosition].get();
+ }
+ else
+ {
+ // There is no user defined navigation order. Use the z-order
+ // instead.
+ if (nNavigationPosition >= maList.size())
+ {
+ OSL_ASSERT(nNavigationPosition < maList.size());
+ }
+ else
+ return maList[nNavigationPosition];
+ }
+ return nullptr;
+}
+
+
+void SdrObjList::ClearObjectNavigationOrder()
+{
+ mxNavigationOrder.reset();
+ mbIsNavigationOrderDirty = true;
+}
+
+
+bool SdrObjList::RecalcNavigationPositions()
+{
+ if (mbIsNavigationOrderDirty)
+ {
+ if (mxNavigationOrder)
+ {
+ mbIsNavigationOrderDirty = false;
+
+ sal_uInt32 nIndex (0);
+ for (auto& rpObject : *mxNavigationOrder)
+ {
+ rpObject->SetNavigationPosition(nIndex);
+ ++nIndex;
+ }
+ }
+ }
+
+ return bool(mxNavigationOrder);
+}
+
+
+void SdrObjList::SetNavigationOrder (const uno::Reference<container::XIndexAccess>& rxOrder)
+{
+ if (rxOrder.is())
+ {
+ const sal_Int32 nCount = rxOrder->getCount();
+ if (static_cast<sal_uInt32>(nCount) != maList.size())
+ return;
+
+ if (!mxNavigationOrder)
+ mxNavigationOrder = std::vector<tools::WeakReference<SdrObject>>(nCount);
+
+ for (sal_Int32 nIndex=0; nIndex<nCount; ++nIndex)
+ {
+ uno::Reference<uno::XInterface> xShape (rxOrder->getByIndex(nIndex), uno::UNO_QUERY);
+ SdrObject* pObject = SdrObject::getSdrObjectFromXShape(xShape);
+ if (pObject == nullptr)
+ break;
+ (*mxNavigationOrder)[nIndex] = pObject;
+ }
+
+ mbIsNavigationOrderDirty = true;
+ }
+ else
+ {
+ ClearObjectNavigationOrder();
+ }
+}
+
+
+void SdrObjList::InsertObjectIntoContainer (
+ SdrObject& rObject,
+ const sal_uInt32 nInsertPosition)
+{
+ OSL_ASSERT(nInsertPosition<=maList.size());
+
+ // Update the navigation positions.
+ if (HasObjectNavigationOrder())
+ {
+ // The new object does not have a user defined position so append it
+ // to the list.
+ rObject.SetNavigationPosition(mxNavigationOrder->size());
+ mxNavigationOrder->push_back(&rObject);
+ }
+
+ // Insert object into object list. Because the insert() method requires
+ // a valid iterator as insertion position, we have to use push_back() to
+ // insert at the end of the list.
+ if (nInsertPosition >= maList.size())
+ maList.push_back(&rObject);
+ else
+ maList.insert(maList.begin()+nInsertPosition, &rObject);
+ mbObjOrdNumsDirty=true;
+}
+
+
+void SdrObjList::ReplaceObjectInContainer (
+ SdrObject& rNewObject,
+ const sal_uInt32 nObjectPosition)
+{
+ if (nObjectPosition >= maList.size())
+ {
+ OSL_ASSERT(nObjectPosition<maList.size());
+ return;
+ }
+
+ // Update the navigation positions.
+ if (HasObjectNavigationOrder())
+ {
+ // A user defined position of the object that is to be replaced is
+ // not transferred to the new object so erase the former and append
+ // the later object from/to the navigation order.
+ OSL_ASSERT(nObjectPosition < maList.size());
+ tools::WeakReference<SdrObject> aReference (maList[nObjectPosition]);
+ auto iObject = ::std::find(
+ mxNavigationOrder->begin(),
+ mxNavigationOrder->end(),
+ aReference);
+ if (iObject != mxNavigationOrder->end())
+ mxNavigationOrder->erase(iObject);
+
+ mxNavigationOrder->push_back(&rNewObject);
+
+ mbIsNavigationOrderDirty = true;
+ }
+
+ maList[nObjectPosition] = &rNewObject;
+ mbObjOrdNumsDirty=true;
+}
+
+
+void SdrObjList::RemoveObjectFromContainer (
+ const sal_uInt32 nObjectPosition)
+{
+ if (nObjectPosition >= maList.size())
+ {
+ OSL_ASSERT(nObjectPosition<maList.size());
+ return;
+ }
+
+ // Update the navigation positions.
+ if (HasObjectNavigationOrder())
+ {
+ tools::WeakReference<SdrObject> aReference (maList[nObjectPosition]);
+ auto iObject = ::std::find(
+ mxNavigationOrder->begin(),
+ mxNavigationOrder->end(),
+ aReference);
+ if (iObject != mxNavigationOrder->end())
+ mxNavigationOrder->erase(iObject);
+ mbIsNavigationOrderDirty = true;
+ }
+
+ maList.erase(maList.begin()+nObjectPosition);
+ mbObjOrdNumsDirty=true;
+}
+
+void SdrObjList::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SdrObjList"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("symbol"), "%s", BAD_CAST(typeid(*this).name()));
+
+ size_t nObjCount = GetObjCount();
+ for (size_t i = 0; i < nObjCount; ++i)
+ {
+ if (const SdrObject* pObject = GetObj(i))
+ pObject->dumpAsXml(pWriter);
+ }
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+
+void SdrPageGridFrameList::Clear()
+{
+ sal_uInt16 nCount=GetCount();
+ for (sal_uInt16 i=0; i<nCount; i++) {
+ delete GetObject(i);
+ }
+ aList.clear();
+}
+
+
+// PageUser section
+
+void SdrPage::AddPageUser(sdr::PageUser& rNewUser)
+{
+ maPageUsers.push_back(&rNewUser);
+}
+
+void SdrPage::RemovePageUser(sdr::PageUser& rOldUser)
+{
+ const sdr::PageUserVector::iterator aFindResult = ::std::find(maPageUsers.begin(), maPageUsers.end(), &rOldUser);
+ if(aFindResult != maPageUsers.end())
+ {
+ maPageUsers.erase(aFindResult);
+ }
+}
+
+
+// DrawContact section
+
+std::unique_ptr<sdr::contact::ViewContact> SdrPage::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::ViewContactOfSdrPage>(*this);
+}
+
+const sdr::contact::ViewContact& SdrPage::GetViewContact() const
+{
+ if (!mpViewContact)
+ const_cast<SdrPage*>(this)->mpViewContact =
+ const_cast<SdrPage*>(this)->CreateObjectSpecificViewContact();
+
+ return *mpViewContact;
+}
+
+sdr::contact::ViewContact& SdrPage::GetViewContact()
+{
+ if (!mpViewContact)
+ mpViewContact = CreateObjectSpecificViewContact();
+
+ return *mpViewContact;
+}
+
+void SdrPageProperties::ImpRemoveStyleSheet()
+{
+ if(mpStyleSheet)
+ {
+ EndListening(*mpStyleSheet);
+ maProperties.SetParent(nullptr);
+ mpStyleSheet = nullptr;
+ }
+}
+
+void SdrPageProperties::ImpAddStyleSheet(SfxStyleSheet& rNewStyleSheet)
+{
+ if(mpStyleSheet != &rNewStyleSheet)
+ {
+ ImpRemoveStyleSheet();
+ mpStyleSheet = &rNewStyleSheet;
+ StartListening(rNewStyleSheet);
+ maProperties.SetParent(&rNewStyleSheet.GetItemSet());
+ }
+}
+
+static void ImpPageChange(SdrPage& rSdrPage)
+{
+ rSdrPage.ActionChanged();
+ rSdrPage.getSdrModelFromSdrPage().SetChanged();
+ SdrHint aHint(SdrHintKind::PageOrderChange, &rSdrPage);
+ rSdrPage.getSdrModelFromSdrPage().Broadcast(aHint);
+}
+
+SdrPageProperties::SdrPageProperties(SdrPage& rSdrPage)
+: mpSdrPage(&rSdrPage),
+ mpStyleSheet(nullptr),
+ maProperties(
+ mpSdrPage->getSdrModelFromSdrPage().GetItemPool(),
+ svl::Items<XATTR_FILL_FIRST, XATTR_FILL_LAST>)
+{
+ if(!rSdrPage.IsMasterPage())
+ {
+ maProperties.Put(XFillStyleItem(drawing::FillStyle_NONE));
+ }
+}
+
+SdrPageProperties::~SdrPageProperties()
+{
+ ImpRemoveStyleSheet();
+}
+
+void SdrPageProperties::Notify(SfxBroadcaster& /*rBC*/, const SfxHint& rHint)
+{
+ switch(rHint.GetId())
+ {
+ case SfxHintId::DataChanged :
+ {
+ // notify change, broadcast
+ ImpPageChange(*mpSdrPage);
+ break;
+ }
+ case SfxHintId::Dying :
+ {
+ // Style needs to be forgotten
+ ImpRemoveStyleSheet();
+ break;
+ }
+ default: break;
+ }
+}
+
+bool SdrPageProperties::isUsedByModel() const
+{
+ assert(mpSdrPage);
+ return mpSdrPage->IsInserted();
+}
+
+
+void SdrPageProperties::PutItemSet(const SfxItemSet& rSet)
+{
+ OSL_ENSURE(!mpSdrPage->IsMasterPage(), "Item set at MasterPage Attributes (!)");
+ maProperties.Put(rSet);
+ ImpPageChange(*mpSdrPage);
+}
+
+void SdrPageProperties::PutItem(const SfxPoolItem& rItem)
+{
+ OSL_ENSURE(!mpSdrPage->IsMasterPage(), "Item set at MasterPage Attributes (!)");
+ maProperties.Put(rItem);
+ ImpPageChange(*mpSdrPage);
+}
+
+void SdrPageProperties::ClearItem(const sal_uInt16 nWhich)
+{
+ maProperties.ClearItem(nWhich);
+ ImpPageChange(*mpSdrPage);
+}
+
+void SdrPageProperties::SetStyleSheet(SfxStyleSheet* pStyleSheet)
+{
+ if(pStyleSheet)
+ {
+ ImpAddStyleSheet(*pStyleSheet);
+ }
+ else
+ {
+ ImpRemoveStyleSheet();
+ }
+
+ ImpPageChange(*mpSdrPage);
+}
+
+void SdrPageProperties::SetTheme(std::unique_ptr<svx::Theme> pTheme)
+{
+ mpTheme = std::move(pTheme);
+
+ if (mpTheme && mpSdrPage->IsMasterPage())
+ {
+ SdrModel& rModel = mpSdrPage->getSdrModelFromSdrPage();
+ sal_uInt16 nPageCount = rModel.GetPageCount();
+ for (sal_uInt16 nPage = 0; nPage < nPageCount; ++nPage)
+ {
+ SdrPage* pPage = rModel.GetPage(nPage);
+ if (!pPage->TRG_HasMasterPage() || &pPage->TRG_GetMasterPage() != mpSdrPage)
+ {
+ continue;
+ }
+
+ mpTheme->UpdateSdrPage(pPage);
+ }
+ }
+}
+
+svx::Theme* SdrPageProperties::GetTheme() { return mpTheme.get(); }
+
+void SdrPageProperties::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SdrPageProperties"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+
+ if (mpTheme)
+ {
+ mpTheme->dumpAsXml(pWriter);
+ }
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+SdrPage::SdrPage(SdrModel& rModel, bool bMasterPage)
+: mrSdrModelFromSdrPage(rModel),
+ mnWidth(10),
+ mnHeight(10),
+ mnBorderLeft(0),
+ mnBorderUpper(0),
+ mnBorderRight(0),
+ mnBorderLower(0),
+ mpLayerAdmin(new SdrLayerAdmin(&rModel.GetLayerAdmin())),
+ nPageNum(0),
+ mbMaster(bMasterPage),
+ mbInserted(false),
+ mbObjectsNotPersistent(false),
+ mbPageBorderOnlyLeftRight(false)
+{
+ mpSdrPageProperties.reset(new SdrPageProperties(*this));
+}
+
+SdrPage::~SdrPage()
+{
+ if( mxUnoPage.is() ) try
+ {
+ uno::Reference< lang::XComponent > xPageComponent( mxUnoPage, uno::UNO_QUERY_THROW );
+ mxUnoPage.clear();
+ xPageComponent->dispose();
+ }
+ catch( const uno::Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ // tell all the registered PageUsers that the page is in destruction
+ // This causes some (all?) PageUsers to remove themselves from the list
+ // of page users. Therefore we have to use a copy of the list for the
+ // iteration.
+ sdr::PageUserVector aListCopy (maPageUsers.begin(), maPageUsers.end());
+ for(sdr::PageUser* pPageUser : aListCopy)
+ {
+ DBG_ASSERT(pPageUser, "SdrPage::~SdrPage: corrupt PageUser list (!)");
+ pPageUser->PageInDestruction(*this);
+ }
+
+ // Clear the vector. This means that user do not need to call RemovePageUser()
+ // when they get called from PageInDestruction().
+ maPageUsers.clear();
+
+ mpLayerAdmin.reset();
+
+ TRG_ClearMasterPage();
+
+ mpViewContact.reset();
+ mpSdrPageProperties.reset();
+}
+
+void SdrPage::lateInit(const SdrPage& rSrcPage)
+{
+ assert(!mpViewContact);
+ assert(!mxUnoPage.is());
+
+ // copy all the local parameters to make this instance
+ // a valid copy of source page before copying and inserting
+ // the contained objects
+ mbMaster = rSrcPage.mbMaster;
+ mbPageBorderOnlyLeftRight = rSrcPage.mbPageBorderOnlyLeftRight;
+ mnWidth = rSrcPage.mnWidth;
+ mnHeight = rSrcPage.mnHeight;
+ mnBorderLeft = rSrcPage.mnBorderLeft;
+ mnBorderUpper = rSrcPage.mnBorderUpper;
+ mnBorderRight = rSrcPage.mnBorderRight;
+ mnBorderLower = rSrcPage.mnBorderLower;
+ mbBackgroundFullSize = rSrcPage.mbBackgroundFullSize;
+ nPageNum = rSrcPage.nPageNum;
+
+ if(rSrcPage.TRG_HasMasterPage())
+ {
+ TRG_SetMasterPage(rSrcPage.TRG_GetMasterPage());
+ TRG_SetMasterPageVisibleLayers(rSrcPage.TRG_GetMasterPageVisibleLayers());
+ }
+ else
+ {
+ TRG_ClearMasterPage();
+ }
+
+ mbObjectsNotPersistent = rSrcPage.mbObjectsNotPersistent;
+
+ {
+ mpSdrPageProperties.reset(new SdrPageProperties(*this));
+
+ if(!IsMasterPage())
+ {
+ mpSdrPageProperties->PutItemSet(rSrcPage.getSdrPageProperties().GetItemSet());
+ }
+
+ mpSdrPageProperties->SetStyleSheet(rSrcPage.getSdrPageProperties().GetStyleSheet());
+ }
+
+ // Now copy the contained objects
+ if(0 != rSrcPage.GetObjCount())
+ {
+ CopyObjects(rSrcPage);
+ }
+}
+
+rtl::Reference<SdrPage> SdrPage::CloneSdrPage(SdrModel& rTargetModel) const
+{
+ rtl::Reference<SdrPage> pClonedPage(new SdrPage(rTargetModel));
+ pClonedPage->lateInit(*this);
+ return pClonedPage;
+}
+
+void SdrPage::SetSize(const Size& aSiz)
+{
+ bool bChanged(false);
+
+ if(aSiz.Width() != mnWidth)
+ {
+ mnWidth = aSiz.Width();
+ bChanged = true;
+ }
+
+ if(aSiz.Height() != mnHeight)
+ {
+ mnHeight = aSiz.Height();
+ bChanged = true;
+ }
+
+ if(bChanged)
+ {
+ SetChanged();
+ }
+}
+
+Size SdrPage::GetSize() const
+{
+ return Size(mnWidth,mnHeight);
+}
+
+tools::Long SdrPage::GetWidth() const
+{
+ return mnWidth;
+}
+
+void SdrPage::SetOrientation(Orientation eOri)
+{
+ // square: handle like portrait format
+ Size aSiz(GetSize());
+ if (aSiz.Width()!=aSiz.Height()) {
+ if ((eOri==Orientation::Portrait) == (aSiz.Width()>aSiz.Height())) {
+ // coverity[swapped_arguments : FALSE] - this is in the correct order
+ SetSize(Size(aSiz.Height(),aSiz.Width()));
+ }
+ }
+}
+
+Orientation SdrPage::GetOrientation() const
+{
+ // square: handle like portrait format
+ Orientation eRet=Orientation::Portrait;
+ Size aSiz(GetSize());
+ if (aSiz.Width()>aSiz.Height()) eRet=Orientation::Landscape;
+ return eRet;
+}
+
+tools::Long SdrPage::GetHeight() const
+{
+ return mnHeight;
+}
+
+void SdrPage::SetBorder(sal_Int32 nLft, sal_Int32 nUpp, sal_Int32 nRgt, sal_Int32 nLwr)
+{
+ bool bChanged(false);
+
+ if(mnBorderLeft != nLft)
+ {
+ mnBorderLeft = nLft;
+ bChanged = true;
+ }
+
+ if(mnBorderUpper != nUpp)
+ {
+ mnBorderUpper = nUpp;
+ bChanged = true;
+ }
+
+ if(mnBorderRight != nRgt)
+ {
+ mnBorderRight = nRgt;
+ bChanged = true;
+ }
+
+ if(mnBorderLower != nLwr)
+ {
+ mnBorderLower = nLwr;
+ bChanged = true;
+ }
+
+ if(bChanged)
+ {
+ SetChanged();
+ }
+}
+
+void SdrPage::SetLeftBorder(sal_Int32 nBorder)
+{
+ if(mnBorderLeft != nBorder)
+ {
+ mnBorderLeft = nBorder;
+ SetChanged();
+ }
+}
+
+void SdrPage::SetUpperBorder(sal_Int32 nBorder)
+{
+ if(mnBorderUpper != nBorder)
+ {
+ mnBorderUpper = nBorder;
+ SetChanged();
+ }
+}
+
+void SdrPage::SetRightBorder(sal_Int32 nBorder)
+{
+ if(mnBorderRight != nBorder)
+ {
+ mnBorderRight=nBorder;
+ SetChanged();
+ }
+}
+
+void SdrPage::SetLowerBorder(sal_Int32 nBorder)
+{
+ if(mnBorderLower != nBorder)
+ {
+ mnBorderLower=nBorder;
+ SetChanged();
+ }
+}
+
+sal_Int32 SdrPage::GetLeftBorder() const
+{
+ return mnBorderLeft;
+}
+
+sal_Int32 SdrPage::GetUpperBorder() const
+{
+ return mnBorderUpper;
+}
+
+sal_Int32 SdrPage::GetRightBorder() const
+{
+ return mnBorderRight;
+}
+
+sal_Int32 SdrPage::GetLowerBorder() const
+{
+ return mnBorderLower;
+}
+
+void SdrPage::SetBackgroundFullSize(bool const bIn)
+{
+ if (bIn != mbBackgroundFullSize)
+ {
+ mbBackgroundFullSize = bIn;
+ SetChanged();
+ }
+}
+
+bool SdrPage::IsBackgroundFullSize() const
+{
+ return mbBackgroundFullSize;
+}
+
+// #i68775# React on PageNum changes (from Model in most cases)
+void SdrPage::SetPageNum(sal_uInt16 nNew)
+{
+ if(nNew != nPageNum)
+ {
+ // change
+ nPageNum = nNew;
+
+ // notify visualisations, also notifies e.g. buffered MasterPages
+ ActionChanged();
+ }
+}
+
+sal_uInt16 SdrPage::GetPageNum() const
+{
+ if (!mbInserted)
+ return 0;
+
+ if (mbMaster) {
+ if (getSdrModelFromSdrPage().IsMPgNumsDirty())
+ getSdrModelFromSdrPage().RecalcPageNums(true);
+ } else {
+ if (getSdrModelFromSdrPage().IsPagNumsDirty())
+ getSdrModelFromSdrPage().RecalcPageNums(false);
+ }
+ return nPageNum;
+}
+
+void SdrPage::SetChanged()
+{
+ // For test purposes, use the new ViewContact for change
+ // notification now.
+ ActionChanged();
+ getSdrModelFromSdrPage().SetChanged();
+}
+
+SdrPage* SdrPage::getSdrPageFromSdrObjList() const
+{
+ return const_cast< SdrPage* >(this);
+}
+
+// MasterPage interface
+
+void SdrPage::TRG_SetMasterPage(SdrPage& rNew)
+{
+ if(mpMasterPageDescriptor && &(mpMasterPageDescriptor->GetUsedPage()) == &rNew)
+ return;
+
+ if(mpMasterPageDescriptor)
+ TRG_ClearMasterPage();
+
+ mpMasterPageDescriptor.reset(new sdr::MasterPageDescriptor(*this, rNew));
+ GetViewContact().ActionChanged();
+}
+
+void SdrPage::TRG_ClearMasterPage()
+{
+ if(mpMasterPageDescriptor)
+ {
+ SetChanged();
+
+ // the flushViewObjectContacts() will do needed invalidates by deleting the involved VOCs
+ mpMasterPageDescriptor->GetUsedPage().GetViewContact().flushViewObjectContacts();
+
+ mpMasterPageDescriptor.reset();
+ }
+}
+
+SdrPage& SdrPage::TRG_GetMasterPage() const
+{
+ DBG_ASSERT(mpMasterPageDescriptor != nullptr, "TRG_GetMasterPage(): No MasterPage available. Use TRG_HasMasterPage() before access (!)");
+ return mpMasterPageDescriptor->GetUsedPage();
+}
+
+const SdrLayerIDSet& SdrPage::TRG_GetMasterPageVisibleLayers() const
+{
+ DBG_ASSERT(mpMasterPageDescriptor != nullptr, "TRG_GetMasterPageVisibleLayers(): No MasterPage available. Use TRG_HasMasterPage() before access (!)");
+ return mpMasterPageDescriptor->GetVisibleLayers();
+}
+
+void SdrPage::TRG_SetMasterPageVisibleLayers(const SdrLayerIDSet& rNew)
+{
+ DBG_ASSERT(mpMasterPageDescriptor != nullptr, "TRG_SetMasterPageVisibleLayers(): No MasterPage available. Use TRG_HasMasterPage() before access (!)");
+ mpMasterPageDescriptor->SetVisibleLayers(rNew);
+}
+
+sdr::contact::ViewContact& SdrPage::TRG_GetMasterPageDescriptorViewContact() const
+{
+ DBG_ASSERT(mpMasterPageDescriptor != nullptr, "TRG_GetMasterPageDescriptorViewContact(): No MasterPage available. Use TRG_HasMasterPage() before access (!)");
+ return mpMasterPageDescriptor->GetViewContact();
+}
+
+// used from SdrModel::RemoveMasterPage
+void SdrPage::TRG_ImpMasterPageRemoved(const SdrPage& rRemovedPage)
+{
+ if(TRG_HasMasterPage())
+ {
+ if(&TRG_GetMasterPage() == &rRemovedPage)
+ {
+ TRG_ClearMasterPage();
+ }
+ }
+}
+
+void SdrPage::MakePageObjectsNamesUnique()
+{
+ std::unordered_set<OUString> aNameSet;
+ for (size_t no(0); no < GetObjCount(); ++no)
+ {
+ SdrObject* pObj(GetObj(no));
+ if(nullptr != pObj)
+ {
+ if (!pObj->GetName().isEmpty())
+ {
+ pObj->MakeNameUnique(aNameSet);
+ SdrObjList* pSdrObjList = pObj->GetSubList(); // group
+ if (pSdrObjList)
+ {
+ SdrObject* pListObj;
+ SdrObjListIter aIter(pSdrObjList, SdrIterMode::DeepWithGroups);
+ while (aIter.IsMore())
+ {
+ pListObj = aIter.Next();
+ pListObj->MakeNameUnique(aNameSet);
+ }
+ }
+ }
+ }
+ }
+}
+
+const SdrPageGridFrameList* SdrPage::GetGridFrameList(const SdrPageView* /*pPV*/, const tools::Rectangle* /*pRect*/) const
+{
+ return nullptr;
+}
+
+const SdrLayerAdmin& SdrPage::GetLayerAdmin() const
+{
+ return *mpLayerAdmin;
+}
+
+SdrLayerAdmin& SdrPage::GetLayerAdmin()
+{
+ return *mpLayerAdmin;
+}
+
+OUString SdrPage::GetLayoutName() const
+{
+ return OUString();
+}
+
+void SdrPage::SetInserted( bool bIns )
+{
+ if( mbInserted == bIns )
+ return;
+
+ mbInserted = bIns;
+
+ // #i120437# go over whole hierarchy, not only over object level null (seen from grouping)
+ SdrObjListIter aIter(this, SdrIterMode::DeepNoGroups);
+
+ while ( aIter.IsMore() )
+ {
+ SdrObject* pObj = aIter.Next();
+ if ( auto pOleObj = dynamic_cast<SdrOle2Obj* >(pObj) )
+ {
+ if( mbInserted )
+ pOleObj->Connect();
+ else
+ pOleObj->Disconnect();
+ }
+ }
+}
+
+void SdrPage::SetUnoPage(uno::Reference<drawing::XDrawPage> const& xNewPage)
+{
+ mxUnoPage = xNewPage;
+}
+
+uno::Reference< uno::XInterface > const & SdrPage::getUnoPage()
+{
+ if( !mxUnoPage.is() )
+ {
+ // create one
+ mxUnoPage = createUnoPage();
+ }
+
+ return mxUnoPage;
+}
+
+uno::Reference< uno::XInterface > SdrPage::createUnoPage()
+{
+ css::uno::Reference< css::uno::XInterface > xInt =
+ static_cast<cppu::OWeakObject*>( new SvxFmDrawPage( this ) );
+ return xInt;
+}
+
+SfxStyleSheet* SdrPage::GetTextStyleSheetForObject( SdrObject* pObj ) const
+{
+ return pObj->GetStyleSheet();
+}
+
+/** returns an averaged background color of this page */
+// #i75566# GetBackgroundColor -> GetPageBackgroundColor and bScreenDisplay hint value
+Color SdrPage::GetPageBackgroundColor( SdrPageView const * pView, bool bScreenDisplay ) const
+{
+ Color aColor;
+
+ if(bScreenDisplay && (!pView || pView->GetApplicationDocumentColor() == COL_AUTO))
+ {
+ svtools::ColorConfig aColorConfig;
+ aColor = aColorConfig.GetColorValue( svtools::DOCCOLOR ).nColor;
+ }
+ else
+ {
+ aColor = pView->GetApplicationDocumentColor();
+ }
+
+ const SfxItemSet* pBackgroundFill = &getSdrPageProperties().GetItemSet();
+
+ if(!IsMasterPage() && TRG_HasMasterPage())
+ {
+ if(drawing::FillStyle_NONE == pBackgroundFill->Get(XATTR_FILLSTYLE).GetValue())
+ {
+ pBackgroundFill = &TRG_GetMasterPage().getSdrPageProperties().GetItemSet();
+ }
+ }
+
+ GetDraftFillColor(*pBackgroundFill, aColor);
+
+ return aColor;
+}
+
+/** *deprecated, use GetBackgroundColor with SdrPageView */
+Color SdrPage::GetPageBackgroundColor() const
+// #i75566# GetBackgroundColor -> GetPageBackgroundColor
+{
+ return GetPageBackgroundColor( nullptr );
+}
+
+/** this method returns true if the object from the ViewObjectContact should
+ be visible on this page while rendering.
+ bEdit selects if visibility test is for an editing view or a final render,
+ like printing.
+*/
+bool SdrPage::checkVisibility(
+ const sdr::contact::ViewObjectContact& /*rOriginal*/,
+ const sdr::contact::DisplayInfo& /*rDisplayInfo*/,
+ bool /*bEdit*/)
+{
+ // this will be handled in the application if needed
+ return true;
+}
+
+void SdrPage::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SdrPage"));
+ SdrObjList::dumpAsXml(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("width"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("value"), "%s",
+ BAD_CAST(OString::number(mnWidth).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("height"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("value"), "%s",
+ BAD_CAST(OString::number(mnHeight).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ if (mpSdrPageProperties)
+ {
+ mpSdrPageProperties->dumpAsXml(pWriter);
+ }
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+// DrawContact support: Methods for handling Page changes
+void SdrPage::ActionChanged()
+{
+ // Do necessary ViewContact actions
+ GetViewContact().ActionChanged();
+
+ // #i48535# also handle MasterPage change
+ if(TRG_HasMasterPage())
+ {
+ TRG_GetMasterPageDescriptorViewContact().ActionChanged();
+ }
+}
+
+SdrPageProperties& SdrPage::getSdrPageProperties()
+{
+ return *mpSdrPageProperties;
+}
+
+const SdrPageProperties& SdrPage::getSdrPageProperties() const
+{
+ return *mpSdrPageProperties;
+}
+
+const SdrPageProperties* SdrPage::getCorrectSdrPageProperties() const
+{
+ if(mpMasterPageDescriptor)
+ {
+ return mpMasterPageDescriptor->getCorrectSdrPageProperties();
+ }
+ else
+ {
+ return &getSdrPageProperties();
+ }
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdpagv.cxx b/svx/source/svdraw/svdpagv.cxx
new file mode 100644
index 000000000..60cd8cfe8
--- /dev/null
+++ b/svx/source/svdraw/svdpagv.cxx
@@ -0,0 +1,894 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/svdpagv.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdview.hxx>
+
+#include <svx/svdobj.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svdtypes.hxx>
+
+#include <svx/sdr/contact/viewobjectcontactredirector.hxx>
+
+#include <algorithm>
+
+#include <svx/sdrpagewindow.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <comphelper/lok.hxx>
+#include <comphelper/scopeguard.hxx>
+#include <basegfx/range/b2irectangle.hxx>
+#include <osl/diagnose.h>
+
+using namespace ::com::sun::star;
+
+// interface to SdrPageWindow
+
+SdrPageWindow* SdrPageView::FindPageWindow(const SdrPaintWindow& rPaintWindow) const
+{
+ for(auto & a : maPageWindows)
+ {
+ if(&(a->GetPaintWindow()) == &rPaintWindow)
+ {
+ return a.get();
+ }
+ }
+
+ return nullptr;
+}
+
+const SdrPageWindow* SdrPageView::FindPatchedPageWindow( const OutputDevice& _rOutDev ) const
+{
+ for ( auto const & pPageWindow : maPageWindows )
+ {
+ const SdrPaintWindow& rPaintWindow( pPageWindow->GetOriginalPaintWindow() ? *pPageWindow->GetOriginalPaintWindow() : pPageWindow->GetPaintWindow() );
+ if ( &rPaintWindow.GetOutputDevice() == &_rOutDev )
+ {
+ return pPageWindow.get();
+ }
+ }
+
+ return nullptr;
+}
+
+SdrPageWindow* SdrPageView::FindPageWindow(const OutputDevice& rOutDev) const
+{
+ for ( auto const & pPageWindow : maPageWindows )
+ {
+ if(&(pPageWindow->GetPaintWindow().GetOutputDevice()) == &rOutDev)
+ {
+ return pPageWindow.get();
+ }
+ }
+
+ return nullptr;
+}
+
+SdrPageWindow* SdrPageView::GetPageWindow(sal_uInt32 nIndex) const
+{
+ return maPageWindows[nIndex].get();
+}
+
+SdrPageView::SdrPageView(SdrPage* pPage1, SdrView& rNewView)
+: mrView(rNewView),
+ // col_auto color lets the view takes the default SvxColorConfig entry
+ maDocumentColor( COL_AUTO ),
+ maBackgroundColor( COL_AUTO ), // #i48367# also react on autocolor
+ mpPreparedPageWindow(nullptr) // #i72752#
+{
+ mpPage = pPage1;
+
+ if(mpPage)
+ {
+ aPgOrg.setX(mpPage->GetLeftBorder() );
+ aPgOrg.setY(mpPage->GetUpperBorder() );
+ }
+ // For example, in the case of charts, there is a LayerAdmin, but it has no valid values. Therefore
+ // a solution like pLayerAdmin->getVisibleLayersODF(aLayerVisi) is not possible. So use the
+ // generic SetAll() for now.
+ aLayerVisi.SetAll();
+ aLayerPrn.SetAll();
+
+ mbHasMarked = false;
+ mbVisible = false;
+ pCurrentList = nullptr;
+ pCurrentGroup = nullptr;
+ SetCurrentGroupAndList(nullptr, mpPage);
+
+ for(sal_uInt32 a(0); a < rNewView.PaintWindowCount(); a++)
+ {
+ AddPaintWindowToPageView(*rNewView.GetPaintWindow(a));
+ }
+}
+
+SdrPageView::~SdrPageView()
+{
+}
+
+void SdrPageView::AddPaintWindowToPageView(SdrPaintWindow& rPaintWindow)
+{
+ if(!FindPageWindow(rPaintWindow))
+ {
+ maPageWindows.emplace_back(new SdrPageWindow(*this, rPaintWindow));
+ }
+}
+
+void SdrPageView::RemovePaintWindowFromPageView(SdrPaintWindow& rPaintWindow)
+{
+ auto it = std::find_if(maPageWindows.begin(), maPageWindows.end(),
+ [&rPaintWindow](const std::unique_ptr<SdrPageWindow>& rpWindow) {
+ return &(rpWindow->GetPaintWindow()) == &rPaintWindow;
+ });
+ if (it != maPageWindows.end())
+ maPageWindows.erase(it);
+}
+
+css::uno::Reference< css::awt::XControlContainer > SdrPageView::GetControlContainer( const OutputDevice& _rDevice ) const
+{
+ css::uno::Reference< css::awt::XControlContainer > xReturn;
+ const SdrPageWindow* pCandidate = FindPatchedPageWindow( _rDevice );
+
+ if ( pCandidate )
+ xReturn = pCandidate->GetControlContainer();
+
+ return xReturn;
+}
+
+void SdrPageView::ModelHasChanged()
+{
+ if (GetCurrentGroup()!=nullptr) CheckCurrentGroup();
+}
+
+bool SdrPageView::IsReadOnly() const
+{
+ return (nullptr == GetPage() || GetView().GetModel()->IsReadOnly() || GetPage()->IsReadOnly() || GetObjList()->IsReadOnly());
+}
+
+void SdrPageView::Show()
+{
+ if(!IsVisible())
+ {
+ mbVisible = true;
+
+ for(sal_uInt32 a(0); a < GetView().PaintWindowCount(); a++)
+ {
+ AddPaintWindowToPageView(*GetView().GetPaintWindow(a));
+ }
+ }
+}
+
+void SdrPageView::Hide()
+{
+ if(IsVisible())
+ {
+ if (!comphelper::LibreOfficeKit::isActive())
+ {
+ InvalidateAllWin();
+ }
+ mbVisible = false;
+ maPageWindows.clear();
+ }
+}
+
+tools::Rectangle SdrPageView::GetPageRect() const
+{
+ if (GetPage()==nullptr) return tools::Rectangle();
+ return tools::Rectangle(Point(),Size(GetPage()->GetWidth()+1,GetPage()->GetHeight()+1));
+}
+
+void SdrPageView::InvalidateAllWin()
+{
+ if(IsVisible() && GetPage())
+ {
+ tools::Rectangle aRect(Point(0,0),Size(GetPage()->GetWidth()+1,GetPage()->GetHeight()+1));
+ aRect.Union(GetPage()->GetAllObjBoundRect());
+ GetView().InvalidateAllWin(aRect);
+ }
+}
+
+
+void SdrPageView::PrePaint()
+{
+ const sal_uInt32 nCount(PageWindowCount());
+
+ for(sal_uInt32 a(0); a < nCount; a++)
+ {
+ SdrPageWindow* pCandidate = GetPageWindow(a);
+
+ if(pCandidate)
+ {
+ pCandidate->PrePaint();
+ }
+ }
+}
+
+void SdrPageView::CompleteRedraw(
+ SdrPaintWindow& rPaintWindow, const vcl::Region& rReg, sdr::contact::ViewObjectContactRedirector* pRedirector )
+{
+ if(!GetPage())
+ return;
+
+ SdrPageWindow* pPageWindow = FindPageWindow(rPaintWindow);
+ std::unique_ptr<SdrPageWindow> pTempPageWindow;
+
+ if(!pPageWindow)
+ {
+ // create temp PageWindow
+ pTempPageWindow.reset(new SdrPageWindow(*this, rPaintWindow));
+ pPageWindow = pTempPageWindow.get();
+ }
+
+ // do the redraw
+ pPageWindow->PrepareRedraw(rReg);
+ pPageWindow->RedrawAll(pRedirector);
+}
+
+
+// #i74769# use SdrPaintWindow directly
+
+void SdrPageView::setPreparedPageWindow(SdrPageWindow* pKnownTarget)
+{
+ // #i72752# remember prepared SdrPageWindow
+ mpPreparedPageWindow = pKnownTarget;
+}
+
+void SdrPageView::DrawLayer(SdrLayerID nID, OutputDevice* pGivenTarget,
+ sdr::contact::ViewObjectContactRedirector* pRedirector,
+ const tools::Rectangle& rRect, basegfx::B2IRectangle const*const pPageFrame)
+{
+ if(!GetPage())
+ return;
+
+ if(pGivenTarget)
+ {
+ SdrPageWindow* pKnownTarget = FindPageWindow(*pGivenTarget);
+
+ if(pKnownTarget)
+ {
+ // paint known target
+ pKnownTarget->RedrawLayer(&nID, pRedirector, pPageFrame);
+ }
+ else
+ {
+ // #i72752# DrawLayer() uses an OutputDevice different from BeginDrawLayer. This happens
+ // e.g. when SW paints a single text line in text edit mode. Try to use it
+ SdrPageWindow* pPreparedTarget = mpPreparedPageWindow;
+
+ if(pPreparedTarget)
+ {
+ // if we have a prepared target, do not use a new SdrPageWindow since this
+ // works but is expensive. Just use a temporary PaintWindow
+ SdrPaintWindow aTemporaryPaintWindow(mrView, *pGivenTarget);
+
+ // Copy existing paint region to use the same as prepared in BeginDrawLayer
+ SdrPaintWindow& rExistingPaintWindow = pPreparedTarget->GetPaintWindow();
+ const vcl::Region& rExistingRegion = rExistingPaintWindow.GetRedrawRegion();
+ bool bUseRect(false);
+ if (!rRect.IsEmpty())
+ {
+ vcl::Region r(rExistingRegion);
+ r.Intersect(rRect);
+ // fdo#74435: FIXME: visibility check broken if empty
+ if (!r.IsEmpty())
+ bUseRect = true;
+ }
+ if (!bUseRect)
+ aTemporaryPaintWindow.SetRedrawRegion(rExistingRegion);
+ else
+ aTemporaryPaintWindow.SetRedrawRegion(vcl::Region(rRect));
+
+ // patch the ExistingPageWindow
+ auto pPreviousWindow = pPreparedTarget->patchPaintWindow(aTemporaryPaintWindow);
+ // unpatch window when leaving the scope
+ const ::comphelper::ScopeGuard aGuard(
+ [&pPreviousWindow, &pPreparedTarget]() { pPreparedTarget->unpatchPaintWindow(pPreviousWindow); } );
+ // redraw the layer
+ pPreparedTarget->RedrawLayer(&nID, pRedirector, pPageFrame);
+ }
+ else
+ {
+ OSL_FAIL("SdrPageView::DrawLayer: Creating temporary SdrPageWindow (ObjectContact), this should never be needed (!)");
+
+ // None of the known OutputDevices is the target of this paint, use
+ // a temporary SdrPageWindow for this Redraw.
+ SdrPaintWindow aTemporaryPaintWindow(mrView, *pGivenTarget);
+ SdrPageWindow aTemporaryPageWindow(*this, aTemporaryPaintWindow);
+
+ // #i72752#
+ // Copy existing paint region if other PageWindows exist, this was created by
+ // PrepareRedraw() from BeginDrawLayer(). Needs to be used e.g. when suddenly SW
+ // paints into an unknown device other than the view was created for (e.g. VirtualDevice)
+ if(PageWindowCount())
+ {
+ SdrPageWindow* pExistingPageWindow = GetPageWindow(0);
+ SdrPaintWindow& rExistingPaintWindow = pExistingPageWindow->GetPaintWindow();
+ const vcl::Region& rExistingRegion = rExistingPaintWindow.GetRedrawRegion();
+ aTemporaryPaintWindow.SetRedrawRegion(rExistingRegion);
+ }
+
+ aTemporaryPageWindow.RedrawLayer(&nID, pRedirector, nullptr);
+ }
+ }
+ }
+ else
+ {
+ // paint in all known windows
+ for(sal_uInt32 a(0); a < PageWindowCount(); a++)
+ {
+ SdrPageWindow* pTarget = GetPageWindow(a);
+ pTarget->RedrawLayer(&nID, pRedirector, nullptr);
+ }
+ }
+}
+
+void SdrPageView::SetDesignMode( bool _bDesignMode ) const
+{
+ for ( sal_uInt32 i = 0; i < PageWindowCount(); ++i )
+ {
+ const SdrPageWindow& rPageViewWindow = *GetPageWindow(i);
+ rPageViewWindow.SetDesignMode( _bDesignMode );
+ }
+}
+
+
+void SdrPageView::DrawPageViewGrid(OutputDevice& rOut, const tools::Rectangle& rRect, Color aColor)
+{
+ if (GetPage()==nullptr)
+ return;
+
+ tools::Long nx1=GetView().maGridBig.Width();
+ tools::Long nx2=GetView().maGridFin.Width();
+ tools::Long ny1=GetView().maGridBig.Height();
+ tools::Long ny2=GetView().maGridFin.Height();
+
+ if (nx1==0) nx1=nx2;
+ if (nx2==0) nx2=nx1;
+ if (ny1==0) ny1=ny2;
+ if (ny2==0) ny2=ny1;
+ if (nx1==0) { nx1=ny1; nx2=ny2; }
+ if (ny1==0) { ny1=nx1; ny2=nx2; }
+ if (nx1<0) nx1=-nx1;
+ if (nx2<0) nx2=-nx2;
+ if (ny1<0) ny1=-ny1;
+ if (ny2<0) ny2=-ny2;
+
+ if (nx1==0)
+ return;
+
+ // no more global output size, use window size instead to decide grid sizes
+ tools::Long nScreenWdt = rOut.GetOutputSizePixel().Width();
+
+ tools::Long nMinDotPix=2;
+ tools::Long nMinLinPix=4;
+
+ if (nScreenWdt>=1600)
+ {
+ nMinDotPix=4;
+ nMinLinPix=8;
+ }
+ else if (nScreenWdt>=1024)
+ {
+ nMinDotPix=3;
+ nMinLinPix=6;
+ }
+ else
+ { // e. g. 640x480
+ nMinDotPix=2;
+ nMinLinPix=4;
+ }
+ Size aMinDotDist(rOut.PixelToLogic(Size(nMinDotPix,nMinDotPix)));
+ Size aMinLinDist(rOut.PixelToLogic(Size(nMinLinPix,nMinLinPix)));
+ bool bHoriSolid=nx2<aMinDotDist.Width();
+ bool bVertSolid=ny2<aMinDotDist.Height();
+ // enlarge line offset (minimum 4 pixels)
+ // enlarge by: *2 *5 *10 *20 *50 *100 ...
+ int nTgl=0;
+ tools::Long nVal0=nx1;
+ while (nx1<aMinLinDist.Width())
+ {
+ tools::Long a=nx1;
+
+ if (nTgl==0) nx1*=2;
+ if (nTgl==1) nx1=nVal0*5; // => nx1*=2.5
+ if (nTgl==2) nx1*=2;
+
+ nVal0=a;
+ nTgl++; if (nTgl>=3) nTgl=0;
+ }
+ nTgl=0;
+ nVal0=ny1;
+ while (ny1<aMinLinDist.Height())
+ {
+ tools::Long a=ny1;
+
+ if (nTgl==0) ny1*=2;
+ if (nTgl==1) ny1=nVal0*5; // => ny1*=2.5
+ if (nTgl==2) ny1*=2;
+
+ nVal0=a;
+ nTgl++;
+
+ if (nTgl>=3) nTgl=0;
+ }
+
+ bool bHoriFine=nx2<nx1;
+ bool bVertFine=ny2<ny1;
+ bool bHoriLines=bHoriSolid || bHoriFine || !bVertFine;
+ bool bVertLines=bVertSolid || bVertFine;
+
+ Color aOriginalLineColor( rOut.GetLineColor() );
+ rOut.SetLineColor( aColor );
+
+ bool bMap0=rOut.IsMapModeEnabled();
+
+ tools::Long nWrX=0;
+ tools::Long nWrY=0;
+ Point aOrg(aPgOrg);
+ tools::Long x1 = 0;
+ tools::Long x2 = 0;
+ if (GetPage()->GetWidth() < 0) // ScDrawPage of RTL sheet
+ {
+ x1 = GetPage()->GetWidth() + GetPage()->GetLeftBorder() + 1;
+ x2 = - GetPage()->GetRightBorder() - 1;
+ }
+ else
+ {
+ x1 = GetPage()->GetLeftBorder() + 1;
+ x2 = GetPage()->GetWidth() - GetPage()->GetRightBorder() - 1;
+ }
+ tools::Long y1 = GetPage()->GetUpperBorder() + 1;
+ tools::Long y2 = GetPage()->GetHeight() - GetPage()->GetLowerBorder() - 1;
+ const SdrPageGridFrameList* pFrames=GetPage()->GetGridFrameList(this,nullptr);
+
+ sal_uInt16 nGridPaintCnt=1;
+ if (pFrames!=nullptr) nGridPaintCnt=pFrames->GetCount();
+ for (sal_uInt16 nGridPaintNum=0; nGridPaintNum<nGridPaintCnt; nGridPaintNum++) {
+ if (pFrames!=nullptr) {
+ const SdrPageGridFrame& rGF=(*pFrames)[nGridPaintNum];
+ nWrX=rGF.GetPaperRect().Left();
+ nWrY=rGF.GetPaperRect().Top();
+ x1=rGF.GetUserArea().Left();
+ x2=rGF.GetUserArea().Right();
+ y1=rGF.GetUserArea().Top();
+ y2=rGF.GetUserArea().Bottom();
+ aOrg=rGF.GetUserArea().TopLeft();
+ aOrg-=rGF.GetPaperRect().TopLeft();
+ }
+ if (!rRect.IsEmpty()) {
+ Size a1PixSiz(rOut.PixelToLogic(Size(1,1)));
+ tools::Long nX1Pix=a1PixSiz.Width(); // add 1 pixel of tolerance
+ tools::Long nY1Pix=a1PixSiz.Height();
+ if (x1<rRect.Left() -nX1Pix) x1=rRect.Left() -nX1Pix;
+ if (x2>rRect.Right() +nX1Pix) x2=rRect.Right() +nX1Pix;
+ if (y1<rRect.Top() -nY1Pix) y1=rRect.Top() -nY1Pix;
+ if (y2>rRect.Bottom()+nY1Pix) y2=rRect.Bottom()+nY1Pix;
+ }
+
+ tools::Long xBigOrg=aOrg.X()+nWrX;
+ while (xBigOrg>=x1) xBigOrg-=nx1;
+ while (xBigOrg<x1) xBigOrg+=nx1;
+ tools::Long xFinOrg=xBigOrg;
+ while (xFinOrg>=x1) xFinOrg-=nx2;
+ while (xFinOrg<x1) xFinOrg+=nx2;
+
+ tools::Long yBigOrg=aOrg.Y()+nWrY;
+ while (yBigOrg>=y1) yBigOrg-=ny1;
+ while (yBigOrg<y1) yBigOrg+=ny1;
+ tools::Long yFinOrg=yBigOrg;
+ while (yFinOrg>=y1) yFinOrg-=ny2;
+ while (yFinOrg<y1) yFinOrg+=ny2;
+
+ if( x1 <= x2 && y1 <= y2 )
+ {
+ if( bHoriLines )
+ {
+ DrawGridFlags nGridFlags = ( bHoriSolid ? DrawGridFlags::HorzLines : DrawGridFlags::Dots );
+ sal_uInt16 nSteps = sal_uInt16(nx1 / nx2);
+ sal_uInt32 nRestPerStepMul1000 = nSteps ? ( ((nx1 * 1000)/ nSteps) - (nx2 * 1000) ) : 0;
+ sal_uInt32 nStepOffset = 0;
+ sal_uInt16 nPointOffset = 0;
+
+ for(sal_uInt16 a=0;a<nSteps;a++)
+ {
+ // draw
+ rOut.DrawGrid(
+ tools::Rectangle( xFinOrg + (a * nx2) + nPointOffset, yBigOrg, x2, y2 ),
+ Size( nx1, ny1 ), nGridFlags );
+
+ // do a step
+ nStepOffset += nRestPerStepMul1000;
+ while(nStepOffset >= 1000)
+ {
+ nStepOffset -= 1000;
+ nPointOffset++;
+ }
+ }
+ }
+
+ if( bVertLines )
+ {
+ DrawGridFlags nGridFlags = ( bVertSolid ? DrawGridFlags::VertLines : DrawGridFlags::Dots );
+ sal_uInt16 nSteps = sal_uInt16(ny1 / ny2);
+ sal_uInt32 nRestPerStepMul1000 = nSteps ? ( ((ny1 * 1000L)/ nSteps) - (ny2 * 1000L) ) : 0;
+ sal_uInt32 nStepOffset = 0;
+ sal_uInt16 nPointOffset = 0;
+
+ for(sal_uInt16 a=0;a<nSteps;a++)
+ {
+ // draw
+ rOut.DrawGrid(
+ tools::Rectangle( xBigOrg, yFinOrg + (a * ny2) + nPointOffset, x2, y2 ),
+ Size( nx1, ny1 ), nGridFlags );
+
+ // do a step
+ nStepOffset += nRestPerStepMul1000;
+ while(nStepOffset >= 1000)
+ {
+ nStepOffset -= 1000;
+ nPointOffset++;
+ }
+ }
+ }
+ }
+ }
+
+ rOut.EnableMapMode(bMap0);
+ rOut.SetLineColor(aOriginalLineColor);
+}
+
+void SdrPageView::AdjHdl()
+{
+ GetView().AdjustMarkHdl();
+}
+
+void SdrPageView::SetLayer(const OUString& rName, SdrLayerIDSet& rBS, bool bJa)
+{
+ if(!GetPage())
+ return;
+
+ SdrLayerID nID = GetPage()->GetLayerAdmin().GetLayerID(rName);
+
+ if(SDRLAYER_NOTFOUND != nID)
+ rBS.Set(nID, bJa);
+}
+
+bool SdrPageView::IsLayer(const OUString& rName, const SdrLayerIDSet& rBS) const
+{
+ if(!GetPage())
+ return false;
+
+ bool bRet(false);
+
+ if (!rName.isEmpty())
+ {
+ SdrLayerID nId = GetPage()->GetLayerAdmin().GetLayerID(rName);
+
+ if(SDRLAYER_NOTFOUND != nId)
+ {
+ bRet = rBS.IsSet(nId);
+ }
+ }
+
+ return bRet;
+}
+
+bool SdrPageView::IsObjMarkable(SdrObject const * pObj) const
+{
+ if (!pObj)
+ return false;
+ if (pObj->IsMarkProtect())
+ return false; // excluded from selection?
+ if (!pObj->IsVisible())
+ return false; // only visible are selectable
+ if (!pObj->IsInserted())
+ return false; // Obj deleted?
+ if (auto pObjGroup = dynamic_cast<const SdrObjGroup*>(pObj))
+ {
+ // If object is a Group object, visibility may depend on
+ // multiple layers. If one object is markable, Group is markable.
+ SdrObjList* pObjList = pObjGroup->GetSubList();
+
+ if (pObjList && pObjList->GetObjCount())
+ {
+ for (size_t a = 0; a < pObjList->GetObjCount(); ++a)
+ {
+ SdrObject* pCandidate = pObjList->GetObj(a);
+ // call recursively
+ if (IsObjMarkable(pCandidate))
+ return true;
+ }
+ return false;
+ }
+ else
+ {
+ // #i43302#
+ // Allow empty groups to be selected to be able to delete them
+ return true;
+ }
+ }
+ if (!pObj->Is3DObj() && pObj->getSdrPageFromSdrObject() != GetPage())
+ {
+ // Obj suddenly in different Page
+ return false;
+ }
+
+ // the layer has to be visible and must not be locked
+ SdrLayerID nL = pObj->GetLayer();
+ if (!aLayerVisi.IsSet(nL))
+ return false;
+ if (aLayerLock.IsSet(nL))
+ return false;
+ return true;
+}
+
+void SdrPageView::SetPageOrigin(const Point& rOrg)
+{
+ if (rOrg!=aPgOrg) {
+ aPgOrg=rOrg;
+ if (GetView().IsGridVisible()) {
+ InvalidateAllWin();
+ }
+ }
+}
+
+void SdrPageView::ImpInvalidateHelpLineArea(sal_uInt16 nNum) const
+{
+ if (!(GetView().IsHlplVisible() && nNum<aHelpLines.GetCount())) return;
+
+ const SdrHelpLine& rHL=aHelpLines[nNum];
+
+ for(sal_uInt32 a(0); a < GetView().PaintWindowCount(); a++)
+ {
+ SdrPaintWindow* pCandidate = GetView().GetPaintWindow(a);
+
+ if(pCandidate->OutputToWindow())
+ {
+ OutputDevice& rOutDev = pCandidate->GetOutputDevice();
+ tools::Rectangle aR(rHL.GetBoundRect(rOutDev));
+ Size aSiz(rOutDev.PixelToLogic(Size(1,1)));
+ aR.AdjustLeft( -(aSiz.Width()) );
+ aR.AdjustRight(aSiz.Width() );
+ aR.AdjustTop( -(aSiz.Height()) );
+ aR.AdjustBottom(aSiz.Height() );
+ const_cast<SdrView&>(GetView()).InvalidateOneWin(rOutDev, aR);
+ }
+ }
+}
+
+void SdrPageView::SetHelpLines(const SdrHelpLineList& rHLL)
+{
+ aHelpLines=rHLL;
+ InvalidateAllWin();
+}
+
+void SdrPageView::SetHelpLine(sal_uInt16 nNum, const SdrHelpLine& rNewHelpLine)
+{
+ if (nNum >= aHelpLines.GetCount() || aHelpLines[nNum] == rNewHelpLine)
+ return;
+
+ bool bNeedRedraw = true;
+ if (aHelpLines[nNum].GetKind()==rNewHelpLine.GetKind()) {
+ switch (rNewHelpLine.GetKind()) {
+ case SdrHelpLineKind::Vertical : if (aHelpLines[nNum].GetPos().X()==rNewHelpLine.GetPos().X()) bNeedRedraw = false; break;
+ case SdrHelpLineKind::Horizontal: if (aHelpLines[nNum].GetPos().Y()==rNewHelpLine.GetPos().Y()) bNeedRedraw = false; break;
+ default: break;
+ } // switch
+ }
+ if (bNeedRedraw) ImpInvalidateHelpLineArea(nNum);
+ aHelpLines[nNum]=rNewHelpLine;
+ if (bNeedRedraw) ImpInvalidateHelpLineArea(nNum);
+}
+
+void SdrPageView::DeleteHelpLine(sal_uInt16 nNum)
+{
+ if (nNum<aHelpLines.GetCount()) {
+ ImpInvalidateHelpLineArea(nNum);
+ aHelpLines.Delete(nNum);
+ }
+}
+
+void SdrPageView::InsertHelpLine(const SdrHelpLine& rHL)
+{
+ sal_uInt16 nNum = aHelpLines.GetCount();
+ aHelpLines.Insert(rHL,nNum);
+ if (GetView().IsHlplVisible())
+ ImpInvalidateHelpLineArea(nNum);
+}
+
+// set current group and list
+void SdrPageView::SetCurrentGroupAndList(SdrObject* pNewGroup, SdrObjList* pNewList)
+{
+ if(pCurrentGroup != pNewGroup)
+ {
+ pCurrentGroup = pNewGroup;
+ }
+ if(pCurrentList != pNewList)
+ {
+ pCurrentList = pNewList;
+ }
+}
+
+bool SdrPageView::EnterGroup(SdrObject* pObj)
+{
+ if(!pObj || !pObj->IsGroupObject())
+ return false;
+
+ // Don't allow enter Diagrams
+ if(nullptr != pObj && pObj->isDiagram())
+ return false;
+
+ const bool bGlueInvalidate(GetView().ImpIsGlueVisible());
+
+ if (bGlueInvalidate)
+ {
+ GetView().GlueInvalidate();
+ }
+
+ // deselect all
+ GetView().UnmarkAll();
+
+ // set current group and list
+ SdrObjList* pNewObjList = pObj->GetSubList();
+ SetCurrentGroupAndList(pObj, pNewObjList);
+
+ // select contained object if only one object is contained,
+ // else select nothing and let the user decide what to do next
+ if(pNewObjList && pNewObjList->GetObjCount() == 1)
+ {
+ SdrObject* pFirstObject = pNewObjList->GetObj(0);
+
+ if(GetView().GetSdrPageView())
+ {
+ GetView().MarkObj(pFirstObject, GetView().GetSdrPageView());
+ }
+ }
+
+ // build new handles
+ GetView().AdjustMarkHdl();
+
+ // invalidate only when view wants to visualize group entering
+ InvalidateAllWin();
+
+ if (bGlueInvalidate)
+ {
+ GetView().GlueInvalidate();
+ }
+
+ return true;
+}
+
+void SdrPageView::LeaveOneGroup()
+{
+ SdrObject* pLastGroup = GetCurrentGroup();
+ if (!pLastGroup)
+ return;
+
+ bool bGlueInvalidate = GetView().ImpIsGlueVisible();
+
+ if(bGlueInvalidate)
+ GetView().GlueInvalidate();
+
+ SdrObject* pParentGroup = pLastGroup->getParentSdrObjectFromSdrObject();
+ SdrObjList* pParentList = GetPage();
+
+ if(pParentGroup)
+ pParentList = pParentGroup->GetSubList();
+
+ // deselect everything
+ GetView().UnmarkAll();
+
+ // allocations, pCurrentGroup and pCurrentList need to be set
+ SetCurrentGroupAndList(pParentGroup, pParentList);
+
+ // select the group we just left
+ if (GetView().GetSdrPageView())
+ GetView().MarkObj(pLastGroup, GetView().GetSdrPageView());
+
+ GetView().AdjustMarkHdl();
+
+ // invalidate only if view wants to visualize group entering
+ InvalidateAllWin();
+
+ if(bGlueInvalidate)
+ GetView().GlueInvalidate();
+}
+
+void SdrPageView::LeaveAllGroup()
+{
+ SdrObject* pLastGroup = GetCurrentGroup();
+ if (!pLastGroup)
+ return;
+
+ bool bGlueInvalidate = GetView().ImpIsGlueVisible();
+
+ if(bGlueInvalidate)
+ GetView().GlueInvalidate();
+
+ // deselect everything
+ GetView().UnmarkAll();
+
+ // allocations, pCurrentGroup and pCurrentList always need to be set
+ SetCurrentGroupAndList(nullptr, GetPage());
+
+ // find and select uppermost group
+ while (pLastGroup->getParentSdrObjectFromSdrObject())
+ pLastGroup = pLastGroup->getParentSdrObjectFromSdrObject();
+
+ if (GetView().GetSdrPageView())
+ GetView().MarkObj(pLastGroup, GetView().GetSdrPageView());
+
+ GetView().AdjustMarkHdl();
+
+ // invalidate only when view wants to visualize group entering
+ InvalidateAllWin();
+
+ if(bGlueInvalidate)
+ GetView().GlueInvalidate();
+}
+
+sal_uInt16 SdrPageView::GetEnteredLevel() const
+{
+ sal_uInt16 nCount=0;
+ SdrObject* pGrp=GetCurrentGroup();
+ while (pGrp!=nullptr) {
+ nCount++;
+ pGrp=pGrp->getParentSdrObjectFromSdrObject();
+ }
+ return nCount;
+}
+
+void SdrPageView::CheckCurrentGroup()
+{
+ SdrObject* pGrp(GetCurrentGroup());
+
+ while(nullptr != pGrp &&
+ (!pGrp->IsInserted() || nullptr == pGrp->getParentSdrObjListFromSdrObject() || nullptr == pGrp->getSdrPageFromSdrObject()))
+ {
+ // anything outside of the borders?
+ pGrp = pGrp->getParentSdrObjectFromSdrObject();
+ }
+
+ if(pGrp != GetCurrentGroup())
+ {
+ if(nullptr != pGrp)
+ {
+ EnterGroup(pGrp);
+ }
+ else
+ {
+ LeaveAllGroup();
+ }
+ }
+}
+
+// Set background color for svx at SdrPageViews
+void SdrPageView::SetApplicationBackgroundColor(Color aBackgroundColor)
+{
+ maBackgroundColor = aBackgroundColor;
+}
+
+
+// Set document color for svx at SdrPageViews
+void SdrPageView::SetApplicationDocumentColor(Color aDocumentColor)
+{
+ maDocumentColor = aDocumentColor;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdpdf.cxx b/svx/source/svdraw/svdpdf.cxx
new file mode 100644
index 000000000..7beba145a
--- /dev/null
+++ b/svx/source/svdraw/svdpdf.cxx
@@ -0,0 +1,1052 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svdpdf.hxx>
+
+#include <tools/UnitConversion.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/vectorgraphicdata.hxx>
+
+#include <math.h>
+#include <editeng/eeitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/shdditem.hxx>
+#include <svx/xlnclit.hxx>
+#include <svx/xlncapit.hxx>
+#include <svx/xlnwtit.hxx>
+#include <svx/xflclit.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/wrlmitem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/colritem.hxx>
+#include <vcl/metric.hxx>
+#include <editeng/charscaleitem.hxx>
+#include <svx/sdtditm.hxx>
+#include <svx/sdtagitm.hxx>
+#include <svx/sdtfsitm.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdotext.hxx>
+#include <svx/svdorect.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/svdetc.hxx>
+#include <svl/itemset.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <tools/helpers.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <svx/xlinjoit.hxx>
+#include <svx/xlndsit.hxx>
+#include <basegfx/polygon/b2dpolygonclipper.hxx>
+#include <svx/xbtmpit.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflbmtit.hxx>
+#include <svx/xflbstit.hxx>
+#include <svx/xlineit0.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <svx/svditer.hxx>
+#include <svx/svdogrp.hxx>
+#include <vcl/dibtools.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+using namespace com::sun::star;
+
+ImpSdrPdfImport::ImpSdrPdfImport(SdrModel& rModel, SdrLayerID nLay, const tools::Rectangle& rRect,
+ Graphic const& rGraphic)
+ : mpVD(VclPtr<VirtualDevice>::Create())
+ , maScaleRect(rRect)
+ , mnMapScalingOfs(0)
+ , mpModel(&rModel)
+ , mnLayer(nLay)
+ , mnLineWidth(0)
+ , maDash(css::drawing::DashStyle_RECT, 0, 0, 0, 0, 0)
+ , mbMov(false)
+ , mbSize(false)
+ , maOfs(0, 0)
+ , mfScaleX(1.0)
+ , mfScaleY(1.0)
+ , maScaleX(1.0)
+ , maScaleY(1.0)
+ , mbFntDirty(true)
+ , mbLastObjWasPolyWithoutLine(false)
+ , mbNoLine(false)
+ , mbNoFill(false)
+ , mnPageCount(0)
+ , mdPageHeightPts(0)
+ , mpPDFium(vcl::pdf::PDFiumLibrary::get())
+{
+ mpVD->EnableOutput(false);
+ mpVD->SetLineColor();
+ mpVD->SetFillColor();
+ maOldLineColor.SetRed(mpVD->GetLineColor().GetRed() + 1);
+ mpLineAttr = std::make_unique<SfxItemSetFixed<XATTR_LINE_FIRST, XATTR_LINE_LAST>>(
+ rModel.GetItemPool());
+ mpFillAttr = std::make_unique<SfxItemSetFixed<XATTR_FILL_FIRST, XATTR_FILL_LAST>>(
+ rModel.GetItemPool());
+ mpTextAttr
+ = std::make_unique<SfxItemSetFixed<EE_ITEMS_START, EE_ITEMS_END>>(rModel.GetItemPool());
+
+ checkClip();
+
+ // Load the buffer using pdfium.
+ auto const& rVectorGraphicData = rGraphic.getVectorGraphicData();
+ auto* pData = rVectorGraphicData->getBinaryDataContainer().getData();
+ sal_Int32 nSize = rVectorGraphicData->getBinaryDataContainer().getSize();
+ mpPdfDocument = mpPDFium ? mpPDFium->openDocument(pData, nSize, OString()) : nullptr;
+ if (!mpPdfDocument)
+ return;
+
+ mnPageCount = mpPdfDocument->getPageCount();
+}
+
+ImpSdrPdfImport::~ImpSdrPdfImport() = default;
+
+void ImpSdrPdfImport::DoObjects(SvdProgressInfo* pProgrInfo, sal_uInt32* pActionsToReport,
+ int nPageIndex)
+{
+ const int nPageCount = mpPdfDocument->getPageCount();
+ if (!(nPageCount > 0 && nPageIndex >= 0 && nPageIndex < nPageCount))
+ return;
+
+ // Render next page.
+ auto pPdfPage = mpPdfDocument->openPage(nPageIndex);
+ if (!pPdfPage)
+ return;
+
+ basegfx::B2DSize dPageSize = mpPdfDocument->getPageSize(nPageIndex);
+
+ const double dPageWidth = dPageSize.getX();
+ const double dPageHeight = dPageSize.getY();
+
+ SetupPageScale(dPageWidth, dPageHeight);
+
+ // Load the page text to extract it when we get text elements.
+ auto pTextPage = pPdfPage->getTextPage();
+
+ const int nPageObjectCount = pPdfPage->getObjectCount();
+ if (pProgrInfo)
+ pProgrInfo->SetActionCount(nPageObjectCount);
+
+ for (int nPageObjectIndex = 0; nPageObjectIndex < nPageObjectCount; ++nPageObjectIndex)
+ {
+ auto pPageObject = pPdfPage->getObject(nPageObjectIndex);
+ ImportPdfObject(pPageObject, pTextPage, nPageObjectIndex);
+ if (pProgrInfo && pActionsToReport)
+ {
+ (*pActionsToReport)++;
+
+ if (*pActionsToReport >= 16)
+ {
+ if (!pProgrInfo->ReportActions(*pActionsToReport))
+ break;
+
+ *pActionsToReport = 0;
+ }
+ }
+ }
+}
+
+void ImpSdrPdfImport::SetupPageScale(const double dPageWidth, const double dPageHeight)
+{
+ mfScaleX = mfScaleY = 1.0;
+
+ // Store the page dimensions in Points.
+ mdPageHeightPts = dPageHeight;
+
+ Size aPageSize(convertPointToMm100(dPageWidth), convertPointToMm100(dPageHeight));
+
+ if (aPageSize.Width() && aPageSize.Height() && (!maScaleRect.IsEmpty()))
+ {
+ maOfs = maScaleRect.TopLeft();
+
+ if (aPageSize.Width() != (maScaleRect.GetWidth() - 1))
+ {
+ mfScaleX = static_cast<double>(maScaleRect.GetWidth() - 1)
+ / static_cast<double>(aPageSize.Width());
+ }
+
+ if (aPageSize.Height() != (maScaleRect.GetHeight() - 1))
+ {
+ mfScaleY = static_cast<double>(maScaleRect.GetHeight() - 1)
+ / static_cast<double>(aPageSize.Height());
+ }
+ }
+
+ mbMov = maOfs.X() != 0 || maOfs.Y() != 0;
+ mbSize = false;
+ maScaleX = Fraction(1, 1);
+ maScaleY = Fraction(1, 1);
+
+ if (aPageSize.Width() != (maScaleRect.GetWidth() - 1))
+ {
+ maScaleX = Fraction(maScaleRect.GetWidth() - 1, aPageSize.Width());
+ mbSize = true;
+ }
+
+ if (aPageSize.Height() != (maScaleRect.GetHeight() - 1))
+ {
+ maScaleY = Fraction(maScaleRect.GetHeight() - 1, aPageSize.Height());
+ mbSize = true;
+ }
+}
+
+size_t ImpSdrPdfImport::DoImport(SdrObjList& rOL, size_t nInsPos, int nPageNumber,
+ SvdProgressInfo* pProgrInfo)
+{
+ sal_uInt32 nActionsToReport(0);
+
+ // execute
+ DoObjects(pProgrInfo, &nActionsToReport, nPageNumber);
+
+ if (pProgrInfo)
+ {
+ pProgrInfo->ReportActions(nActionsToReport);
+ nActionsToReport = 0;
+ }
+
+ // MapMode scaling
+ MapScaling();
+
+ // To calculate the progress meter, we use GetActionSize()*3.
+ // However, maTmpList has a lower entry count limit than GetActionSize(),
+ // so the actions that were assumed were too much have to be re-added.
+ // nActionsToReport = (rMtf.GetActionSize() - maTmpList.size()) * 2;
+
+ // announce all currently unannounced rescales
+ if (pProgrInfo)
+ {
+ pProgrInfo->ReportRescales(nActionsToReport);
+ pProgrInfo->SetInsertCount(maTmpList.size());
+ }
+
+ nActionsToReport = 0;
+
+ // insert all objects cached in aTmpList now into rOL from nInsPos
+ nInsPos = std::min(nInsPos, rOL.GetObjCount());
+
+ for (SdrObject* pObj : maTmpList)
+ {
+ rOL.NbcInsertObject(pObj, nInsPos);
+ nInsPos++;
+
+ if (pProgrInfo)
+ {
+ nActionsToReport++;
+
+ if (nActionsToReport >= 32) // update all 32 actions
+ {
+ pProgrInfo->ReportInserts(nActionsToReport);
+ nActionsToReport = 0;
+ }
+ }
+ }
+
+ // report all remaining inserts for the last time
+ if (pProgrInfo)
+ {
+ pProgrInfo->ReportInserts(nActionsToReport);
+ }
+
+ return maTmpList.size();
+}
+
+void ImpSdrPdfImport::SetAttributes(SdrObject* pObj, bool bForceTextAttr)
+{
+ mbNoLine = false;
+ mbNoFill = false;
+ bool bLine(!bForceTextAttr);
+ bool bFill(!pObj || (pObj->IsClosedObj() && !bForceTextAttr));
+ bool bText(bForceTextAttr || (pObj && pObj->GetOutlinerParaObject()));
+
+ if (bLine)
+ {
+ if (mnLineWidth)
+ {
+ mpLineAttr->Put(XLineWidthItem(mnLineWidth));
+ }
+ else
+ {
+ mpLineAttr->Put(XLineWidthItem(0));
+ }
+
+ maOldLineColor = mpVD->GetLineColor();
+
+ if (mpVD->IsLineColor())
+ {
+ mpLineAttr->Put(XLineStyleItem(drawing::LineStyle_SOLID)); //TODO support dashed lines.
+ mpLineAttr->Put(XLineColorItem(OUString(), mpVD->GetLineColor()));
+ }
+ else
+ {
+ mpLineAttr->Put(XLineStyleItem(drawing::LineStyle_NONE));
+ }
+
+ mpLineAttr->Put(XLineJointItem(css::drawing::LineJoint_NONE));
+
+ // Add LineCap support
+ mpLineAttr->Put(XLineCapItem(gaLineCap));
+
+ if (((maDash.GetDots() && maDash.GetDotLen())
+ || (maDash.GetDashes() && maDash.GetDashLen()))
+ && maDash.GetDistance())
+ {
+ mpLineAttr->Put(XLineDashItem(OUString(), maDash));
+ }
+ else
+ {
+ mpLineAttr->Put(XLineDashItem(OUString(), XDash(css::drawing::DashStyle_RECT)));
+ }
+ }
+ else
+ {
+ mbNoLine = true;
+ }
+
+ if (bFill)
+ {
+ if (mpVD->IsFillColor())
+ {
+ mpFillAttr->Put(XFillStyleItem(drawing::FillStyle_SOLID));
+ mpFillAttr->Put(XFillColorItem(OUString(), mpVD->GetFillColor()));
+ }
+ else
+ {
+ mpFillAttr->Put(XFillStyleItem(drawing::FillStyle_NONE));
+ }
+ }
+ else
+ {
+ mbNoFill = true;
+ }
+
+ if (bText && mbFntDirty)
+ {
+ vcl::Font aFnt(mpVD->GetFont());
+ const sal_uInt32 nHeight(FRound(aFnt.GetFontSize().Height() * mfScaleY));
+
+ mpTextAttr->Put(SvxFontItem(aFnt.GetFamilyType(), aFnt.GetFamilyName(), aFnt.GetStyleName(),
+ aFnt.GetPitch(), aFnt.GetCharSet(), EE_CHAR_FONTINFO));
+ mpTextAttr->Put(SvxFontItem(aFnt.GetFamilyType(), aFnt.GetFamilyName(), aFnt.GetStyleName(),
+ aFnt.GetPitch(), aFnt.GetCharSet(), EE_CHAR_FONTINFO_CJK));
+ mpTextAttr->Put(SvxFontItem(aFnt.GetFamilyType(), aFnt.GetFamilyName(), aFnt.GetStyleName(),
+ aFnt.GetPitch(), aFnt.GetCharSet(), EE_CHAR_FONTINFO_CTL));
+ mpTextAttr->Put(SvxPostureItem(aFnt.GetItalic(), EE_CHAR_ITALIC));
+ mpTextAttr->Put(SvxWeightItem(aFnt.GetWeight(), EE_CHAR_WEIGHT));
+ mpTextAttr->Put(SvxFontHeightItem(nHeight, 100, EE_CHAR_FONTHEIGHT));
+ mpTextAttr->Put(SvxFontHeightItem(nHeight, 100, EE_CHAR_FONTHEIGHT_CJK));
+ mpTextAttr->Put(SvxFontHeightItem(nHeight, 100, EE_CHAR_FONTHEIGHT_CTL));
+ mpTextAttr->Put(SvxCharScaleWidthItem(100, EE_CHAR_FONTWIDTH));
+ mpTextAttr->Put(SvxUnderlineItem(aFnt.GetUnderline(), EE_CHAR_UNDERLINE));
+ mpTextAttr->Put(SvxOverlineItem(aFnt.GetOverline(), EE_CHAR_OVERLINE));
+ mpTextAttr->Put(SvxCrossedOutItem(aFnt.GetStrikeout(), EE_CHAR_STRIKEOUT));
+ mpTextAttr->Put(SvxShadowedItem(aFnt.IsShadow(), EE_CHAR_SHADOW));
+
+ // #i118485# Setting this item leads to problems (written #i118498# for this)
+ // mpTextAttr->Put(SvxAutoKernItem(aFnt.IsKerning(), EE_CHAR_KERNING));
+
+ mpTextAttr->Put(SvxWordLineModeItem(aFnt.IsWordLineMode(), EE_CHAR_WLM));
+ mpTextAttr->Put(SvxContourItem(aFnt.IsOutline(), EE_CHAR_OUTLINE));
+ mpTextAttr->Put(SvxColorItem(mpVD->GetTextColor(), EE_CHAR_COLOR));
+ //... svxfont textitem svditext
+ mbFntDirty = false;
+ }
+
+ if (!pObj)
+ return;
+
+ pObj->SetLayer(mnLayer);
+
+ if (bLine)
+ {
+ pObj->SetMergedItemSet(*mpLineAttr);
+ }
+
+ if (bFill)
+ {
+ pObj->SetMergedItemSet(*mpFillAttr);
+ }
+
+ if (bText)
+ {
+ pObj->SetMergedItemSet(*mpTextAttr);
+ pObj->SetMergedItem(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_LEFT));
+ }
+}
+
+void ImpSdrPdfImport::InsertObj(SdrObject* pObj, bool bScale)
+{
+ if (bScale && !maScaleRect.IsEmpty())
+ {
+ if (mbSize)
+ {
+ pObj->NbcResize(Point(), maScaleX, maScaleY);
+ }
+
+ if (mbMov)
+ {
+ pObj->NbcMove(Size(maOfs.X(), maOfs.Y()));
+ }
+ }
+
+ if (isClip())
+ {
+ const basegfx::B2DPolyPolygon aPoly(pObj->TakeXorPoly());
+ const basegfx::B2DRange aOldRange(aPoly.getB2DRange());
+ const SdrLayerID aOldLayer(pObj->GetLayer());
+ const SfxItemSet aOldItemSet(pObj->GetMergedItemSet());
+ const SdrGrafObj* pSdrGrafObj = dynamic_cast<SdrGrafObj*>(pObj);
+ const SdrTextObj* pSdrTextObj = dynamic_cast<SdrTextObj*>(pObj);
+
+ if (pSdrTextObj && pSdrTextObj->HasText())
+ {
+ // all text objects are created from ImportText and have no line or fill attributes, so
+ // it is okay to concentrate on the text itself
+ while (true)
+ {
+ const basegfx::B2DPolyPolygon aTextContour(pSdrTextObj->TakeContour());
+ const basegfx::B2DRange aTextRange(aTextContour.getB2DRange());
+ const basegfx::B2DRange aClipRange(maClip.getB2DRange());
+
+ // no overlap -> completely outside
+ if (!aClipRange.overlaps(aTextRange))
+ {
+ SdrObject::Free(pObj);
+ break;
+ }
+
+ // when the clip is a rectangle fast check for inside is possible
+ if (basegfx::utils::isRectangle(maClip) && aClipRange.isInside(aTextRange))
+ {
+ // completely inside ClipRect
+ break;
+ }
+
+ // here text needs to be clipped; to do so, convert to SdrObjects with polygons
+ // and add these recursively. Delete original object, do not add in this run
+ SdrObjectUniquePtr pConverted = pSdrTextObj->ConvertToPolyObj(true, true);
+ SdrObject::Free(pObj);
+
+ if (pConverted)
+ {
+ // recursively add created conversion; per definition this shall not
+ // contain further SdrTextObjs. Visit only non-group objects
+ SdrObjListIter aIter(*pConverted, SdrIterMode::DeepNoGroups);
+
+ // work with clones; the created conversion may contain group objects
+ // and when working with the original objects the loop itself could
+ // break and the cleanup later would be pretty complicated (only delete group
+ // objects, are these empty, ...?)
+ while (aIter.IsMore())
+ {
+ SdrObject* pCandidate = aIter.Next();
+ OSL_ENSURE(pCandidate && dynamic_cast<SdrObjGroup*>(pCandidate) == nullptr,
+ "SdrObjListIter with SdrIterMode::DeepNoGroups error (!)");
+ SdrObject* pNewClone(
+ pCandidate->CloneSdrObject(pCandidate->getSdrModelFromSdrObject()));
+
+ if (pNewClone)
+ {
+ InsertObj(pNewClone, false);
+ }
+ else
+ {
+ OSL_ENSURE(false, "SdrObject::Clone() failed (!)");
+ }
+ }
+ }
+
+ break;
+ }
+ }
+ else
+ {
+ BitmapEx aBitmapEx;
+
+ if (pSdrGrafObj)
+ {
+ aBitmapEx = pSdrGrafObj->GetGraphic().GetBitmapEx();
+ }
+
+ SdrObject::Free(pObj);
+
+ if (!aOldRange.isEmpty())
+ {
+ // clip against ClipRegion
+ const basegfx::B2DPolyPolygon aNewPoly(basegfx::utils::clipPolyPolygonOnPolyPolygon(
+ aPoly, maClip, true, !aPoly.isClosed()));
+ const basegfx::B2DRange aNewRange(aNewPoly.getB2DRange());
+
+ if (!aNewRange.isEmpty())
+ {
+ pObj = new SdrPathObj(
+ *mpModel, aNewPoly.isClosed() ? SdrObjKind::Polygon : SdrObjKind::PolyLine,
+ aNewPoly);
+
+ pObj->SetLayer(aOldLayer);
+ pObj->SetMergedItemSet(aOldItemSet);
+
+ if (!aBitmapEx.IsEmpty())
+ {
+ // aNewRange is inside of aOldRange and defines which part of aBitmapEx is used
+ const double fScaleX(aBitmapEx.GetSizePixel().Width()
+ / (aOldRange.getWidth() ? aOldRange.getWidth() : 1.0));
+ const double fScaleY(
+ aBitmapEx.GetSizePixel().Height()
+ / (aOldRange.getHeight() ? aOldRange.getHeight() : 1.0));
+ basegfx::B2DRange aPixel(aNewRange);
+ basegfx::B2DHomMatrix aTrans;
+
+ aTrans.translate(-aOldRange.getMinX(), -aOldRange.getMinY());
+ aTrans.scale(fScaleX, fScaleY);
+ aPixel.transform(aTrans);
+
+ const Size aOrigSizePixel(aBitmapEx.GetSizePixel());
+ const Point aClipTopLeft(
+ basegfx::fround(floor(std::max(0.0, aPixel.getMinX()))),
+ basegfx::fround(floor(std::max(0.0, aPixel.getMinY()))));
+ const Size aClipSize(
+ basegfx::fround(ceil(std::min(
+ static_cast<double>(aOrigSizePixel.Width()), aPixel.getWidth()))),
+ basegfx::fround(
+ ceil(std::min(static_cast<double>(aOrigSizePixel.Height()),
+ aPixel.getHeight()))));
+ const BitmapEx aClippedBitmap(aBitmapEx, aClipTopLeft, aClipSize);
+
+ pObj->SetMergedItem(XFillStyleItem(drawing::FillStyle_BITMAP));
+ pObj->SetMergedItem(XFillBitmapItem(OUString(), Graphic(aClippedBitmap)));
+ pObj->SetMergedItem(XFillBmpTileItem(false));
+ pObj->SetMergedItem(XFillBmpStretchItem(true));
+ }
+ }
+ }
+ }
+ }
+
+ if (!pObj)
+ return;
+
+ // #i111954# check object for visibility
+ // used are SdrPathObj, SdrRectObj, SdrCircObj, SdrGrafObj
+ bool bVisible(false);
+
+ if (pObj->HasLineStyle())
+ {
+ bVisible = true;
+ }
+
+ if (!bVisible && pObj->HasFillStyle())
+ {
+ bVisible = true;
+ }
+
+ if (!bVisible)
+ {
+ SdrTextObj* pTextObj = dynamic_cast<SdrTextObj*>(pObj);
+
+ if (pTextObj && pTextObj->HasText())
+ {
+ bVisible = true;
+ }
+ }
+
+ if (!bVisible)
+ {
+ SdrGrafObj* pGrafObj = dynamic_cast<SdrGrafObj*>(pObj);
+
+ if (pGrafObj)
+ {
+ // this may be refined to check if the graphic really is visible. It
+ // is here to ensure that graphic objects without fill, line and text
+ // get created
+ bVisible = true;
+ }
+ }
+
+ if (!bVisible)
+ {
+ SdrObject::Free(pObj);
+ }
+ else
+ {
+ maTmpList.push_back(pObj);
+
+ if (dynamic_cast<SdrPathObj*>(pObj))
+ {
+ const bool bClosed(pObj->IsClosedObj());
+
+ mbLastObjWasPolyWithoutLine = mbNoLine && bClosed;
+ }
+ else
+ {
+ mbLastObjWasPolyWithoutLine = false;
+ }
+ }
+}
+
+bool ImpSdrPdfImport::CheckLastPolyLineAndFillMerge(const basegfx::B2DPolyPolygon& rPolyPolygon)
+{
+ // #i73407# reformulation to use new B2DPolygon classes
+ if (mbLastObjWasPolyWithoutLine)
+ {
+ SdrObject* pTmpObj = !maTmpList.empty() ? maTmpList[maTmpList.size() - 1] : nullptr;
+ SdrPathObj* pLastPoly = dynamic_cast<SdrPathObj*>(pTmpObj);
+
+ if (pLastPoly)
+ {
+ if (pLastPoly->GetPathPoly() == rPolyPolygon)
+ {
+ SetAttributes(nullptr);
+
+ if (!mbNoLine && mbNoFill)
+ {
+ pLastPoly->SetMergedItemSet(*mpLineAttr);
+
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+void ImpSdrPdfImport::checkClip()
+{
+ if (mpVD->IsClipRegion())
+ {
+ maClip = mpVD->GetClipRegion().GetAsB2DPolyPolygon();
+
+ if (isClip())
+ {
+ const basegfx::B2DHomMatrix aTransform(basegfx::utils::createScaleTranslateB2DHomMatrix(
+ mfScaleX, mfScaleY, maOfs.X(), maOfs.Y()));
+
+ maClip.transform(aTransform);
+ }
+ }
+}
+
+bool ImpSdrPdfImport::isClip() const { return !maClip.getB2DRange().isEmpty(); }
+void ImpSdrPdfImport::ImportPdfObject(
+ std::unique_ptr<vcl::pdf::PDFiumPageObject> const& pPageObject,
+ std::unique_ptr<vcl::pdf::PDFiumTextPage> const& pTextPage, int nPageObjectIndex)
+{
+ if (!pPageObject)
+ return;
+
+ const vcl::pdf::PDFPageObjectType ePageObjectType = pPageObject->getType();
+ switch (ePageObjectType)
+ {
+ case vcl::pdf::PDFPageObjectType::Text:
+ ImportText(pPageObject, pTextPage, nPageObjectIndex);
+ break;
+ case vcl::pdf::PDFPageObjectType::Path:
+ ImportPath(pPageObject, nPageObjectIndex);
+ break;
+ case vcl::pdf::PDFPageObjectType::Image:
+ ImportImage(pPageObject, nPageObjectIndex);
+ break;
+ case vcl::pdf::PDFPageObjectType::Shading:
+ SAL_WARN("sd.filter", "Got page object SHADING: " << nPageObjectIndex);
+ break;
+ case vcl::pdf::PDFPageObjectType::Form:
+ ImportForm(pPageObject, pTextPage, nPageObjectIndex);
+ break;
+ default:
+ SAL_WARN("sd.filter", "Unknown PDF page object #" << nPageObjectIndex << " of type: "
+ << static_cast<int>(ePageObjectType));
+ break;
+ }
+}
+
+void ImpSdrPdfImport::ImportForm(std::unique_ptr<vcl::pdf::PDFiumPageObject> const& pPageObject,
+ std::unique_ptr<vcl::pdf::PDFiumTextPage> const& pTextPage,
+ int /*nPageObjectIndex*/)
+{
+ // Get the form matrix to perform correct translation/scaling of the form sub-objects.
+ const basegfx::B2DHomMatrix aOldMatrix = maCurrentMatrix;
+
+ maCurrentMatrix = pPageObject->getMatrix();
+
+ const int nCount = pPageObject->getFormObjectCount();
+ for (int nIndex = 0; nIndex < nCount; ++nIndex)
+ {
+ auto pFormObject = pPageObject->getFormObject(nIndex);
+
+ ImportPdfObject(pFormObject, pTextPage, -1);
+ }
+
+ // Restore the old one.
+ maCurrentMatrix = aOldMatrix;
+}
+
+void ImpSdrPdfImport::ImportText(std::unique_ptr<vcl::pdf::PDFiumPageObject> const& pPageObject,
+ std::unique_ptr<vcl::pdf::PDFiumTextPage> const& pTextPage,
+ int /*nPageObjectIndex*/)
+{
+ basegfx::B2DRectangle aTextRect = pPageObject->getBounds();
+ basegfx::B2DHomMatrix aMatrix = pPageObject->getMatrix();
+
+ basegfx::B2DHomMatrix aTextMatrix(maCurrentMatrix);
+
+ aTextRect *= aTextMatrix;
+ const tools::Rectangle aRect = PointsToLogic(aTextRect.getMinX(), aTextRect.getMaxX(),
+ aTextRect.getMinY(), aTextRect.getMaxY());
+
+ OUString sText = pPageObject->getText(pTextPage);
+
+ const double dFontSize = pPageObject->getFontSize();
+ double dFontSizeH = fabs(std::hypot(aMatrix.a(), aMatrix.c()) * dFontSize);
+ double dFontSizeV = fabs(std::hypot(aMatrix.b(), aMatrix.d()) * dFontSize);
+
+ dFontSizeH = convertPointToMm100(dFontSizeH);
+ dFontSizeV = convertPointToMm100(dFontSizeV);
+
+ const Size aFontSize(dFontSizeH, dFontSizeV);
+ vcl::Font aFnt = mpVD->GetFont();
+ if (aFontSize != aFnt.GetFontSize())
+ {
+ aFnt.SetFontSize(aFontSize);
+ mpVD->SetFont(aFnt);
+ mbFntDirty = true;
+ }
+
+ OUString sFontName = pPageObject->getFontName();
+ if (!sFontName.isEmpty() && sFontName != aFnt.GetFamilyName())
+ {
+ aFnt.SetFamilyName(sFontName);
+ mpVD->SetFont(aFnt);
+ mbFntDirty = true;
+ }
+
+ Color aTextColor(COL_TRANSPARENT);
+ bool bFill = false;
+ bool bUse = true;
+ switch (pPageObject->getTextRenderMode())
+ {
+ case vcl::pdf::PDFTextRenderMode::Fill:
+ case vcl::pdf::PDFTextRenderMode::FillClip:
+ case vcl::pdf::PDFTextRenderMode::FillStroke:
+ case vcl::pdf::PDFTextRenderMode::FillStrokeClip:
+ bFill = true;
+ break;
+ case vcl::pdf::PDFTextRenderMode::Stroke:
+ case vcl::pdf::PDFTextRenderMode::StrokeClip:
+ case vcl::pdf::PDFTextRenderMode::Unknown:
+ break;
+ case vcl::pdf::PDFTextRenderMode::Invisible:
+ case vcl::pdf::PDFTextRenderMode::Clip:
+ bUse = false;
+ break;
+ }
+ if (bUse)
+ {
+ Color aColor = bFill ? pPageObject->getFillColor() : pPageObject->getStrokeColor();
+ if (aColor != COL_TRANSPARENT)
+ aTextColor = aColor.GetRGBColor();
+ }
+
+ if (aTextColor != mpVD->GetTextColor())
+ {
+ mpVD->SetTextColor(aTextColor);
+ mbFntDirty = true;
+ }
+
+ InsertTextObject(aRect.TopLeft(), aRect.GetSize(), sText);
+}
+
+void ImpSdrPdfImport::InsertTextObject(const Point& rPos, const Size& rSize, const OUString& rStr)
+{
+ // calc text box size, add 5% to make it fit safely
+
+ FontMetric aFontMetric(mpVD->GetFontMetric());
+ vcl::Font aFont(mpVD->GetFont());
+ TextAlign eAlignment(aFont.GetAlignment());
+
+ // sal_Int32 nTextWidth = static_cast<sal_Int32>(mpVD->GetTextWidth(rStr) * mfScaleX);
+ sal_Int32 nTextHeight = static_cast<sal_Int32>(mpVD->GetTextHeight() * mfScaleY);
+
+ Point aPosition(FRound(rPos.X() * mfScaleX + maOfs.X()),
+ FRound(rPos.Y() * mfScaleY + maOfs.Y()));
+ Size aSize(FRound(rSize.Width() * mfScaleX), FRound(rSize.Height() * mfScaleY));
+
+ if (eAlignment == ALIGN_BASELINE)
+ aPosition.AdjustY(-FRound(aFontMetric.GetAscent() * mfScaleY));
+ else if (eAlignment == ALIGN_BOTTOM)
+ aPosition.AdjustY(-nTextHeight);
+
+ tools::Rectangle aTextRect(aPosition, aSize);
+ SdrRectObj* pText = new SdrRectObj(*mpModel, SdrObjKind::Text, aTextRect);
+
+ pText->SetMergedItem(makeSdrTextUpperDistItem(0));
+ pText->SetMergedItem(makeSdrTextLowerDistItem(0));
+ pText->SetMergedItem(makeSdrTextRightDistItem(0));
+ pText->SetMergedItem(makeSdrTextLeftDistItem(0));
+
+ if (aFont.GetAverageFontWidth())
+ {
+ pText->ClearMergedItem(SDRATTR_TEXT_AUTOGROWWIDTH);
+ pText->SetMergedItem(makeSdrTextAutoGrowHeightItem(false));
+ // don't let the margins eat the space needed for the text
+ pText->SetMergedItem(SdrTextFitToSizeTypeItem(drawing::TextFitToSizeType_ALLLINES));
+ }
+ else
+ {
+ pText->SetMergedItem(makeSdrTextAutoGrowWidthItem(true));
+ }
+
+ pText->SetLayer(mnLayer);
+ pText->NbcSetText(rStr);
+ SetAttributes(pText, true);
+ pText->SetSnapRect(aTextRect);
+
+ if (!aFont.IsTransparent())
+ {
+ SfxItemSetFixed<XATTR_FILL_FIRST, XATTR_FILL_LAST> aAttr(*mpFillAttr->GetPool());
+ aAttr.Put(XFillStyleItem(drawing::FillStyle_SOLID));
+ aAttr.Put(XFillColorItem(OUString(), aFont.GetFillColor()));
+ pText->SetMergedItemSet(aAttr);
+ }
+ Degree100 nAngle = to<Degree100>(aFont.GetOrientation());
+ if (nAngle)
+ pText->SdrAttrObj::NbcRotate(aPosition, nAngle);
+ InsertObj(pText, false);
+}
+
+void ImpSdrPdfImport::MapScaling()
+{
+ const size_t nCount(maTmpList.size());
+ const MapMode& rMap = mpVD->GetMapMode();
+ Point aMapOrg(rMap.GetOrigin());
+ bool bMov2(aMapOrg.X() != 0 || aMapOrg.Y() != 0);
+
+ if (bMov2)
+ {
+ for (size_t i = mnMapScalingOfs; i < nCount; i++)
+ {
+ SdrObject* pObj = maTmpList[i];
+
+ pObj->NbcMove(Size(aMapOrg.X(), aMapOrg.Y()));
+ }
+ }
+
+ mnMapScalingOfs = nCount;
+}
+
+void ImpSdrPdfImport::ImportImage(std::unique_ptr<vcl::pdf::PDFiumPageObject> const& pPageObject,
+ int /*nPageObjectIndex*/)
+{
+ std::unique_ptr<vcl::pdf::PDFiumBitmap> bitmap = pPageObject->getImageBitmap();
+ if (!bitmap)
+ {
+ SAL_WARN("sd.filter", "Failed to get IMAGE");
+ return;
+ }
+
+ const vcl::pdf::PDFBitmapType format = bitmap->getFormat();
+ if (format == vcl::pdf::PDFBitmapType::Unknown)
+ {
+ SAL_WARN("sd.filter", "Failed to get IMAGE format");
+ return;
+ }
+
+ const unsigned char* pBuf = bitmap->getBuffer();
+ const int nWidth = bitmap->getWidth();
+ const int nHeight = bitmap->getHeight();
+ const int nStride = bitmap->getStride();
+ BitmapEx aBitmap(Size(nWidth, nHeight), vcl::PixelFormat::N24_BPP);
+
+ switch (format)
+ {
+ case vcl::pdf::PDFBitmapType::BGR:
+ ReadRawDIB(aBitmap, pBuf, ScanlineFormat::N24BitTcBgr, nHeight, nStride);
+ break;
+ case vcl::pdf::PDFBitmapType::BGRx:
+ ReadRawDIB(aBitmap, pBuf, ScanlineFormat::N32BitTcRgba, nHeight, nStride);
+ break;
+ case vcl::pdf::PDFBitmapType::BGRA:
+ ReadRawDIB(aBitmap, pBuf, ScanlineFormat::N32BitTcBgra, nHeight, nStride);
+ break;
+ default:
+ SAL_WARN("sd.filter", "Got IMAGE width: " << nWidth << ", height: " << nHeight
+ << ", stride: " << nStride
+ << ", format: " << static_cast<int>(format));
+ break;
+ }
+
+ basegfx::B2DRectangle aBounds = pPageObject->getBounds();
+ float left = aBounds.getMinX();
+ // Upside down.
+ float bottom = aBounds.getMinY();
+ float right = aBounds.getMaxX();
+ // Upside down.
+ float top = aBounds.getMaxY();
+ tools::Rectangle aRect = PointsToLogic(left, right, top, bottom);
+ aRect.AdjustRight(1);
+ aRect.AdjustBottom(1);
+
+ SdrGrafObj* pGraf = new SdrGrafObj(*mpModel, Graphic(aBitmap), aRect);
+
+ // This action is not creating line and fill, set directly, do not use SetAttributes(..)
+ pGraf->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE));
+ pGraf->SetMergedItem(XFillStyleItem(drawing::FillStyle_NONE));
+ InsertObj(pGraf);
+}
+
+void ImpSdrPdfImport::ImportPath(std::unique_ptr<vcl::pdf::PDFiumPageObject> const& pPageObject,
+ int /*nPageObjectIndex*/)
+{
+ auto aPathMatrix = pPageObject->getMatrix();
+
+ aPathMatrix *= maCurrentMatrix;
+
+ basegfx::B2DPolyPolygon aPolyPoly;
+ basegfx::B2DPolygon aPoly;
+ std::vector<basegfx::B2DPoint> aBezier;
+
+ const int nSegments = pPageObject->getPathSegmentCount();
+ for (int nSegmentIndex = 0; nSegmentIndex < nSegments; ++nSegmentIndex)
+ {
+ auto pPathSegment = pPageObject->getPathSegment(nSegmentIndex);
+ if (pPathSegment != nullptr)
+ {
+ basegfx::B2DPoint aB2DPoint = pPathSegment->getPoint();
+ aB2DPoint *= aPathMatrix;
+
+ const bool bClose = pPathSegment->isClosed();
+ if (bClose)
+ aPoly.setClosed(bClose); // TODO: Review
+
+ Point aPoint = PointsToLogic(aB2DPoint.getX(), aB2DPoint.getY());
+ aB2DPoint.setX(aPoint.X());
+ aB2DPoint.setY(aPoint.Y());
+
+ const vcl::pdf::PDFSegmentType eSegmentType = pPathSegment->getType();
+ switch (eSegmentType)
+ {
+ case vcl::pdf::PDFSegmentType::Lineto:
+ aPoly.append(aB2DPoint);
+ break;
+
+ case vcl::pdf::PDFSegmentType::Bezierto:
+ aBezier.emplace_back(aB2DPoint.getX(), aB2DPoint.getY());
+ if (aBezier.size() == 3)
+ {
+ aPoly.appendBezierSegment(aBezier[0], aBezier[1], aBezier[2]);
+ aBezier.clear();
+ }
+ break;
+
+ case vcl::pdf::PDFSegmentType::Moveto:
+ // New Poly.
+ if (aPoly.count() > 0)
+ {
+ aPolyPoly.append(aPoly, 1);
+ aPoly.clear();
+ }
+
+ aPoly.append(aB2DPoint);
+ break;
+
+ case vcl::pdf::PDFSegmentType::Unknown:
+ default:
+ SAL_WARN("sd.filter", "Unknown path segment type in PDF: "
+ << static_cast<int>(eSegmentType));
+ break;
+ }
+ }
+ }
+
+ if (aBezier.size() == 3)
+ {
+ aPoly.appendBezierSegment(aBezier[0], aBezier[1], aBezier[2]);
+ aBezier.clear();
+ }
+
+ if (aPoly.count() > 0)
+ {
+ aPolyPoly.append(aPoly, 1);
+ aPoly.clear();
+ }
+
+ const basegfx::B2DHomMatrix aTransform(
+ basegfx::utils::createScaleTranslateB2DHomMatrix(mfScaleX, mfScaleY, maOfs.X(), maOfs.Y()));
+ aPolyPoly.transform(aTransform);
+
+ float fWidth = pPageObject->getStrokeWidth();
+ const double dWidth = 0.5 * fabs(std::hypot(aPathMatrix.a(), aPathMatrix.c()) * fWidth);
+ mnLineWidth = convertPointToMm100(dWidth);
+
+ vcl::pdf::PDFFillMode nFillMode = vcl::pdf::PDFFillMode::Alternate;
+ bool bStroke = true; // Assume we have to draw, unless told otherwise.
+ if (pPageObject->getDrawMode(nFillMode, bStroke))
+ {
+ if (nFillMode == vcl::pdf::PDFFillMode::Alternate)
+ mpVD->SetDrawMode(DrawModeFlags::Default);
+ else if (nFillMode == vcl::pdf::PDFFillMode::Winding)
+ mpVD->SetDrawMode(DrawModeFlags::Default);
+ else
+ mpVD->SetDrawMode(DrawModeFlags::NoFill);
+ }
+
+ mpVD->SetFillColor(pPageObject->getFillColor());
+
+ if (bStroke)
+ {
+ mpVD->SetLineColor(pPageObject->getStrokeColor());
+ }
+ else
+ mpVD->SetLineColor(COL_TRANSPARENT);
+
+ if (!mbLastObjWasPolyWithoutLine || !CheckLastPolyLineAndFillMerge(aPolyPoly))
+ {
+ SdrPathObj* pPath = new SdrPathObj(*mpModel, SdrObjKind::Polygon, aPolyPoly);
+ SetAttributes(pPath);
+ InsertObj(pPath, false);
+ }
+}
+
+Point ImpSdrPdfImport::PointsToLogic(double x, double y) const
+{
+ y = correctVertOrigin(y);
+
+ Point aPos(convertPointToMm100(x), convertPointToMm100(y));
+ return aPos;
+}
+
+tools::Rectangle ImpSdrPdfImport::PointsToLogic(double left, double right, double top,
+ double bottom) const
+{
+ top = correctVertOrigin(top);
+ bottom = correctVertOrigin(bottom);
+
+ Point aPos(convertPointToMm100(left), convertPointToMm100(top));
+ Size aSize(convertPointToMm100(right - left), convertPointToMm100(bottom - top));
+
+ return tools::Rectangle(aPos, aSize);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdpntv.cxx b/svx/source/svdraw/svdpntv.cxx
new file mode 100644
index 000000000..f403ddd57
--- /dev/null
+++ b/svx/source/svdraw/svdpntv.cxx
@@ -0,0 +1,1206 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <svx/svdpntv.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/window.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <svx/svdmodel.hxx>
+
+#include <svx/svdpage.hxx>
+#include <svx/svdpagv.hxx>
+#include <svl/hint.hxx>
+
+#include <svx/svdview.hxx>
+#include <svx/svdglue.hxx>
+#include <svx/svdobj.hxx>
+#include <sxlayitm.hxx>
+#include <svl/itemiter.hxx>
+#include <editeng/eeitem.hxx>
+#include <svl/whiter.hxx>
+#include <svl/style.hxx>
+#include <svx/sdrpagewindow.hxx>
+#include <vcl/svapp.hxx>
+#include <svx/sdr/contact/objectcontact.hxx>
+#include <svx/sdr/animation/objectanimator.hxx>
+#include <drawinglayer/primitive2d/metafileprimitive2d.hxx>
+#include <drawinglayer/converters.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <comphelper/lok.hxx>
+#include <svx/svdviter.hxx>
+#include <svtools/optionsdrawinglayer.hxx>
+#include <osl/diagnose.h>
+
+using namespace ::com::sun::star;
+
+// interface to SdrPaintWindow
+
+SdrPaintWindow* SdrPaintView::FindPaintWindow(const OutputDevice& rOut) const
+{
+ // back to loop - there is more to test than a std::find_if and a lambda can do
+ for(auto& candidate : maPaintWindows)
+ {
+ if(&(candidate->GetOutputDevice()) == &rOut)
+ {
+ return candidate.get();
+ }
+
+ // check for patched to allow finding in that state, too
+ if(nullptr != candidate->getPatched() && &(candidate->getPatched()->GetOutputDevice()) == &rOut)
+ {
+ return candidate->getPatched();
+ }
+ }
+
+ return nullptr;
+}
+
+SdrPaintWindow* SdrPaintView::GetPaintWindow(sal_uInt32 nIndex) const
+{
+ return maPaintWindows[nIndex].get();
+}
+
+void SdrPaintView::DeletePaintWindow(const SdrPaintWindow& rOld)
+{
+ auto aFindResult = ::std::find_if(maPaintWindows.begin(), maPaintWindows.end(),
+ [&](const std::unique_ptr<SdrPaintWindow>& p) { return p.get() == &rOld; });
+
+ if(aFindResult != maPaintWindows.end())
+ {
+ maPaintWindows.erase(aFindResult);
+ }
+}
+
+OutputDevice* SdrPaintView::GetFirstOutputDevice() const
+{
+ if(PaintWindowCount())
+ {
+ return &(GetPaintWindow(0)->GetOutputDevice());
+ }
+
+ return nullptr;
+}
+
+
+SvxViewChangedHint::SvxViewChangedHint()
+{
+}
+
+
+BitmapEx convertMetafileToBitmapEx(
+ const GDIMetaFile& rMtf,
+ const basegfx::B2DRange& rTargetRange,
+ const sal_uInt32 nMaximumQuadraticPixels)
+{
+ BitmapEx aBitmapEx;
+
+ if(rMtf.GetActionSize())
+ {
+ const drawinglayer::primitive2d::Primitive2DReference aMtf(
+ new drawinglayer::primitive2d::MetafilePrimitive2D(
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ rTargetRange.getRange(),
+ rTargetRange.getMinimum()),
+ rMtf));
+ aBitmapEx = drawinglayer::convertPrimitive2DContainerToBitmapEx(
+ drawinglayer::primitive2d::Primitive2DContainer { aMtf },
+ rTargetRange,
+ nMaximumQuadraticPixels);
+ }
+
+ return aBitmapEx;
+}
+
+SdrPaintView::SdrPaintView(SdrModel& rSdrModel, OutputDevice* pOut)
+ : mrSdrModelFromSdrView(rSdrModel)
+ , mpModel(&rSdrModel)
+ , mpActualOutDev(nullptr)
+ , mpDragWin(nullptr)
+ , mpDefaultStyleSheet(nullptr)
+ , maDefaultAttr(rSdrModel.GetItemPool())
+ , maComeBackIdle( "svx::SdrPaintView aComeBackIdle" )
+ , meAnimationMode(SdrAnimationMode::Animate)
+ , mnHitTolPix(2)
+ , mnMinMovPix(3)
+ , mnHitTolLog(0)
+ , mnMinMovLog(0)
+ , mbPageVisible(true)
+ , mbPageShadowVisible(true)
+ , mbPageBorderVisible(true)
+ , mbBordVisible(true)
+ , mbGridVisible(true)
+ , mbGridFront(false)
+ , mbHlplVisible(true)
+ , mbHlplFront(true)
+ , mbGlueVisible(false)
+ , mbGlueVisible2(false)
+ , mbGlueVisible3(false)
+ , mbGlueVisible4(false)
+ , mbSomeObjChgdFlag(false)
+ , mbSwapAsynchron(false)
+ , mbPrintPreview(false)
+ , mbAnimationPause(false)
+ , mbBufferedOutputAllowed(false)
+ , mbBufferedOverlayAllowed(false)
+ , mbPagePaintingAllowed(true)
+ , mbPreviewRenderer(false)
+ , mbHideOle(false)
+ , mbHideChart(false)
+ , mbHideDraw(false)
+ , mbHideFormControl(false)
+ , maGridColor(COL_BLACK)
+{
+ maComeBackIdle.SetPriority(TaskPriority::REPAINT);
+ maComeBackIdle.SetInvokeHandler(LINK(this,SdrPaintView,ImpComeBackHdl));
+
+ if (mpModel)
+ SetDefaultStyleSheet(mpModel->GetDefaultStyleSheet(), true);
+
+ if (pOut)
+ AddWindowToPaintView(pOut, nullptr);
+
+ maColorConfig.AddListener(this);
+ onChangeColorConfig();
+}
+
+SdrPaintView::~SdrPaintView()
+{
+ if (mpDefaultStyleSheet)
+ EndListening(*mpDefaultStyleSheet);
+
+ maColorConfig.RemoveListener(this);
+ ClearPageView();
+
+ // delete existing SdrPaintWindows
+ maPaintWindows.clear();
+}
+
+
+void SdrPaintView::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
+{
+ //If the stylesheet has been destroyed
+ if (&rBC == mpDefaultStyleSheet)
+ {
+ if (rHint.GetId() == SfxHintId::Dying)
+ mpDefaultStyleSheet = nullptr;
+ return;
+ }
+
+ if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint)
+ return;
+ const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint);
+ SdrHintKind eKind = pSdrHint->GetKind();
+ if (eKind==SdrHintKind::ObjectChange || eKind==SdrHintKind::ObjectInserted || eKind==SdrHintKind::ObjectRemoved)
+ {
+ bool bObjChg = !mbSomeObjChgdFlag; // if true, evaluate for ComeBack timer
+ if (bObjChg)
+ {
+ mbSomeObjChgdFlag=true;
+ maComeBackIdle.Start();
+ }
+ }
+
+ if (eKind==SdrHintKind::PageOrderChange)
+ {
+ const SdrPage* pPg=pSdrHint->GetPage();
+ if (pPg && !pPg->IsInserted())
+ {
+ if(mpPageView && mpPageView->GetPage() == pPg)
+ {
+ HideSdrPage();
+ }
+ }
+ }
+}
+
+void SdrPaintView::ConfigurationChanged( ::utl::ConfigurationBroadcaster* , ConfigurationHints )
+{
+ onChangeColorConfig();
+ InvalidateAllWin();
+}
+
+IMPL_LINK_NOARG(SdrPaintView, ImpComeBackHdl, Timer *, void)
+{
+ if (mbSomeObjChgdFlag) {
+ mbSomeObjChgdFlag=false;
+ ModelHasChanged();
+ }
+}
+
+void SdrPaintView::FlushComeBackTimer() const
+{
+ if (mbSomeObjChgdFlag) {
+ // casting to nonconst
+ const_cast<SdrPaintView*>(this)->ImpComeBackHdl(&const_cast<SdrPaintView*>(this)->maComeBackIdle);
+ const_cast<SdrPaintView*>(this)->maComeBackIdle.Stop();
+ }
+}
+
+void SdrPaintView::ModelHasChanged()
+{
+ // broadcast to all PageViews
+ if(mpPageView && !mpPageView->GetPage()->IsInserted())
+ {
+ HideSdrPage();
+ }
+
+ // test mpPageView here again, HideSdrPage() may have invalidated it.
+ if(mpPageView)
+ {
+ mpPageView->ModelHasChanged();
+ }
+}
+
+
+bool SdrPaintView::IsAction() const
+{
+ return false;
+}
+
+void SdrPaintView::MovAction(const Point&)
+{
+}
+
+void SdrPaintView::EndAction()
+{
+}
+
+void SdrPaintView::BckAction()
+{
+}
+
+void SdrPaintView::BrkAction()
+{
+}
+
+void SdrPaintView::TakeActionRect(tools::Rectangle&) const
+{
+}
+
+
+// info about TextEdit. Default is false.
+bool SdrPaintView::IsTextEdit() const
+{
+ return false;
+}
+
+sal_uInt16 SdrPaintView::ImpGetMinMovLogic(short nMinMov, const OutputDevice* pOut) const
+{
+ if (nMinMov>=0) return sal_uInt16(nMinMov);
+ if (pOut==nullptr)
+ {
+ pOut = GetFirstOutputDevice();
+ }
+ if (pOut!=nullptr) {
+ return short(-pOut->PixelToLogic(Size(nMinMov,0)).Width());
+ } else {
+ return 0;
+ }
+}
+
+sal_uInt16 SdrPaintView::ImpGetHitTolLogic(short nHitTol, const OutputDevice* pOut) const
+{
+ if (nHitTol>=0) return sal_uInt16(nHitTol);
+ if (pOut==nullptr)
+ {
+ pOut = GetFirstOutputDevice();
+ }
+ if (pOut!=nullptr) {
+ return short(-pOut->PixelToLogic(Size(nHitTol,0)).Width());
+ } else {
+ return 0;
+ }
+}
+
+void SdrPaintView::TheresNewMapMode()
+{
+ if (mpActualOutDev) {
+ mnHitTolLog=static_cast<sal_uInt16>(mpActualOutDev->PixelToLogic(Size(mnHitTolPix,0)).Width());
+ mnMinMovLog=static_cast<sal_uInt16>(mpActualOutDev->PixelToLogic(Size(mnMinMovPix,0)).Width());
+ }
+}
+
+void SdrPaintView::SetActualWin(const OutputDevice* pWin)
+{
+ mpActualOutDev = const_cast<OutputDevice *>(pWin);
+ TheresNewMapMode();
+}
+
+
+void SdrPaintView::ClearPageView()
+{
+ BrkAction();
+
+ if(mpPageView)
+ {
+ InvalidateAllWin();
+ mpPageView.reset();
+ }
+}
+
+SdrPageView* SdrPaintView::ShowSdrPage(SdrPage* pPage)
+{
+ if(pPage && (!mpPageView || mpPageView->GetPage() != pPage))
+ {
+ if(mpPageView)
+ {
+ InvalidateAllWin();
+ mpPageView.reset();
+ }
+
+ if (SdrView *pView = dynamic_cast<SdrView*>(this))
+ {
+ mpPageView.reset(new SdrPageView(pPage, *pView));
+ mpPageView->Show();
+ }
+ }
+
+ return mpPageView.get();
+}
+
+void SdrPaintView::HideSdrPage()
+{
+ if(mpPageView)
+ {
+ mpPageView->Hide();
+ mpPageView.reset();
+ }
+}
+
+void SdrPaintView::AddWindowToPaintView(OutputDevice* pNewWin, vcl::Window *pWindow)
+{
+ DBG_ASSERT(pNewWin, "SdrPaintView::AddWindowToPaintView: No OutputDevice(!)");
+ SdrPaintWindow* pNewPaintWindow = new SdrPaintWindow(*this, *pNewWin, pWindow);
+ maPaintWindows.emplace_back(pNewPaintWindow);
+
+ if(mpPageView)
+ {
+ mpPageView->AddPaintWindowToPageView(*pNewPaintWindow);
+ }
+}
+
+void SdrPaintView::DeleteWindowFromPaintView(OutputDevice* pOldWin)
+{
+ assert(pOldWin && "SdrPaintView::DeleteWindowFromPaintView: No OutputDevice(!)");
+ SdrPaintWindow* pCandidate = FindPaintWindow(*pOldWin);
+
+ if(pCandidate)
+ {
+ if(mpPageView)
+ {
+ mpPageView->RemovePaintWindowFromPageView(*pCandidate);
+ }
+
+ DeletePaintWindow(*pCandidate);
+ }
+}
+
+void SdrPaintView::SetLayerVisible(const OUString& rName, bool bShow)
+{
+ if(mpPageView)
+ {
+ mpPageView->SetLayerVisible(rName, bShow);
+ }
+
+ InvalidateAllWin();
+}
+
+bool SdrPaintView::IsLayerVisible(const OUString& rName) const
+{
+ if(mpPageView)
+ {
+ return mpPageView->IsLayerVisible(rName);
+ }
+
+ return false;
+}
+
+void SdrPaintView::SetLayerLocked(const OUString& rName, bool bLock)
+{
+ if(mpPageView)
+ {
+ mpPageView->SetLayerLocked(rName,bLock);
+ }
+}
+
+bool SdrPaintView::IsLayerLocked(const OUString& rName) const
+{
+ if(mpPageView)
+ {
+ return mpPageView->IsLayerLocked(rName);
+ }
+
+ return false;
+}
+
+void SdrPaintView::SetLayerPrintable(const OUString& rName, bool bPrn)
+{
+ if(mpPageView)
+ {
+ mpPageView->SetLayerPrintable(rName,bPrn);
+ }
+}
+
+bool SdrPaintView::IsLayerPrintable(const OUString& rName) const
+{
+ if(mpPageView)
+ {
+ return mpPageView->IsLayerPrintable(rName);
+ }
+
+ return false;
+}
+
+void SdrPaintView::PrePaint()
+{
+ if(mpPageView)
+ {
+ mpPageView->PrePaint();
+ }
+}
+
+
+// #define SVX_REPAINT_TIMER_TEST
+
+void SdrPaintView::CompleteRedraw(OutputDevice* pOut, const vcl::Region& rReg, sdr::contact::ViewObjectContactRedirector* pRedirector)
+{
+#ifdef SVX_REPAINT_TIMER_TEST
+#define REMEMBERED_TIMES_COUNT (10)
+ static bool bDoTimerTest(false);
+ static bool bTimesInited(false);
+ static sal_uInt32 nRepeatCount(10);
+ static double fLastTimes[REMEMBERED_TIMES_COUNT];
+ const sal_uInt64 nStartTime(tools::Time::GetSystemTicks());
+ sal_uInt32 count(1);
+ sal_uInt32 a;
+
+ if(bDoTimerTest)
+ {
+ count = nRepeatCount;
+ }
+
+ for(a = 0; a < count; a++)
+ {
+#endif // SVX_REPAINT_TIMER_TEST
+
+ // #i74769# check if pOut is a win and has a ClipRegion. If Yes, the Region
+ // rReg may be made more granular (fine) with using it. Normally, rReg
+ // does come from Window::Paint() anyways and thus is based on a single
+ // rectangle which was derived from exactly that repaint region
+ vcl::Region aOptimizedRepaintRegion(rReg);
+
+ if(pOut && OUTDEV_WINDOW == pOut->GetOutDevType())
+ {
+ vcl::Window* pWindow = pOut->GetOwnerWindow();
+
+ if(pWindow->IsInPaint())
+ {
+ if(!pWindow->GetPaintRegion().IsEmpty())
+ {
+ aOptimizedRepaintRegion.Intersect(pWindow->GetPaintRegion());
+ }
+ }
+ }
+
+ SdrPaintWindow* pPaintWindow = BeginCompleteRedraw(pOut);
+ OSL_ENSURE(pPaintWindow, "SdrPaintView::CompleteRedraw: No OutDev (!)");
+
+ DoCompleteRedraw(*pPaintWindow, aOptimizedRepaintRegion, pRedirector);
+ EndCompleteRedraw(*pPaintWindow, true);
+
+#ifdef SVX_REPAINT_TIMER_TEST
+ }
+
+ if(bDoTimerTest)
+ {
+ const sal_uInt64 nStopTime(tools::Time::GetSystemTicks());
+ const sal_uInt64 nNeededTime(nStopTime - nStartTime);
+ const double fTimePerPaint((double)nNeededTime / (double)nRepeatCount);
+
+ if(!bTimesInited)
+ {
+ for(a = 0; a < REMEMBERED_TIMES_COUNT; a++)
+ {
+ fLastTimes[a] = fTimePerPaint;
+ }
+
+ bTimesInited = true;
+ }
+ else
+ {
+ for(a = 1; a < REMEMBERED_TIMES_COUNT; a++)
+ {
+ fLastTimes[a - 1] = fLastTimes[a];
+ }
+
+ fLastTimes[REMEMBERED_TIMES_COUNT - 1] = fTimePerPaint;
+ }
+
+ double fAddedTimes(0.0);
+
+ for(a = 0; a < REMEMBERED_TIMES_COUNT; a++)
+ {
+ fAddedTimes += fLastTimes[a];
+ }
+
+ const double fAverageTimePerPaint(fAddedTimes / (double)REMEMBERED_TIMES_COUNT);
+
+ fprintf(stderr, "-----------(start result)----------\n");
+ fprintf(stderr, "StartTime : %" SAL_PRIuUINT64 ", StopTime: %" SAL_PRIuUINT64 ", NeededTime: %" SAL_PRIuUINT64 ", TimePerPaint: %f\n", nStartTime, nStopTime, nNeededTime, fTimePerPaint);
+ fprintf(stderr, "Remembered times: ");
+
+ for(a = 0; a < REMEMBERED_TIMES_COUNT; a++)
+ {
+ fprintf(stderr, "%d: %f ", a, fLastTimes[a]);
+ }
+
+ fprintf(stderr, "\n");
+ fprintf(stderr, "AverageTimePerPaint: %f\n", fAverageTimePerPaint);
+ fprintf(stderr, "-----------(stop result)----------\n");
+ }
+#endif // SVX_REPAINT_TIMER_TEST
+}
+
+
+// #i72889#
+
+SdrPaintWindow* SdrPaintView::BeginCompleteRedraw(OutputDevice* pOut)
+{
+ OSL_ENSURE(pOut, "SdrPaintView::BeginCompleteRedraw: No OutDev (!)");
+ SdrPaintWindow* pPaintWindow = FindPaintWindow(*pOut);
+
+ if(pPaintWindow)
+ {
+ // draw preprocessing, only for known devices
+ // prepare PreRendering
+ pPaintWindow->PreparePreRenderDevice();
+ }
+ else
+ {
+ // None of the known OutputDevices is the target of this paint, use
+ // a temporary SdrPaintWindow for this Redraw.
+ pPaintWindow = new SdrPaintWindow(*this, *pOut);
+ pPaintWindow->setTemporaryTarget(true);
+ }
+
+ return pPaintWindow;
+}
+
+void SdrPaintView::DoCompleteRedraw(SdrPaintWindow& rPaintWindow, const vcl::Region& rReg, sdr::contact::ViewObjectContactRedirector* pRedirector)
+{
+ // redraw all PageViews with the target. This may expand the RedrawRegion
+ // at the PaintWindow, plus taking care of FormLayer expansion
+ if(mpPageView)
+ {
+ mpPageView->CompleteRedraw(rPaintWindow, rReg, pRedirector);
+ }
+}
+
+void SdrPaintView::EndCompleteRedraw(SdrPaintWindow& rPaintWindow, bool bPaintFormLayer)
+{
+ std::unique_ptr<SdrPaintWindow> pPaintWindow;
+ if (comphelper::LibreOfficeKit::isActive() && rPaintWindow.getTemporaryTarget())
+ {
+ // Tiled rendering, we must paint the TextEdit to the output device.
+ pPaintWindow.reset(&rPaintWindow);
+ pPaintWindow->setTemporaryTarget(false);
+ }
+
+ if(rPaintWindow.getTemporaryTarget())
+ {
+ // get rid of temp target again
+ delete &rPaintWindow;
+ }
+ else
+ {
+ // draw postprocessing, only for known devices
+ // it is necessary to always paint FormLayer
+ if(bPaintFormLayer)
+ {
+ ImpFormLayerDrawing(rPaintWindow);
+ }
+
+ // look for active TextEdit. As long as this cannot be painted to a VDev,
+ // it cannot get part of buffering. In that case, output evtl. prerender
+ // early and paint text edit to window.
+ if(IsTextEdit() && GetSdrPageView())
+ {
+ static_cast< SdrView* >(this)->TextEditDrawing(rPaintWindow);
+ }
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ // Look for active text edits in other views showing the same page,
+ // and show them as well.
+ if (SdrPageView* pPageView = GetSdrPageView())
+ {
+ SdrViewIter aIter(pPageView->GetPage());
+ for (SdrView* pView = aIter.FirstView(); pView; pView = aIter.NextView())
+ {
+ if (pView == this)
+ continue;
+
+ if (pView->IsTextEdit() && pView->GetSdrPageView())
+ {
+ pView->TextEditDrawing(rPaintWindow);
+ }
+ }
+ }
+ }
+
+ // draw Overlay, also to PreRender device if exists
+ rPaintWindow.DrawOverlay(rPaintWindow.GetRedrawRegion());
+
+ // output PreRendering
+ rPaintWindow.OutputPreRenderDevice(rPaintWindow.GetRedrawRegion());
+ }
+}
+
+
+SdrPaintWindow* SdrPaintView::BeginDrawLayers(OutputDevice* pOut, const vcl::Region& rReg, bool bDisableIntersect)
+{
+ // #i74769# use BeginCompleteRedraw() as common base
+ SdrPaintWindow* pPaintWindow = BeginCompleteRedraw(pOut);
+ OSL_ENSURE(pPaintWindow, "SdrPaintView::BeginDrawLayers: No SdrPaintWindow (!)");
+
+ if(mpPageView)
+ {
+ SdrPageWindow* pKnownTarget = mpPageView->FindPageWindow(*pPaintWindow);
+
+ if(pKnownTarget)
+ {
+ vcl::Region aOptimizedRepaintRegion = OptimizeDrawLayersRegion( pOut, rReg, bDisableIntersect );
+
+ // prepare redraw
+ pKnownTarget->PrepareRedraw(aOptimizedRepaintRegion);
+
+ // remember prepared SdrPageWindow
+ mpPageView->setPreparedPageWindow(pKnownTarget);
+ }
+ }
+
+ return pPaintWindow;
+}
+
+void SdrPaintView::EndDrawLayers(SdrPaintWindow& rPaintWindow, bool bPaintFormLayer)
+{
+ // #i74769# use EndCompleteRedraw() as common base
+ EndCompleteRedraw(rPaintWindow, bPaintFormLayer);
+
+ if(mpPageView)
+ {
+ // forget prepared SdrPageWindow
+ mpPageView->setPreparedPageWindow(nullptr);
+ }
+}
+
+void SdrPaintView::UpdateDrawLayersRegion(const OutputDevice* pOut, const vcl::Region& rReg)
+{
+ SdrPaintWindow* pPaintWindow = FindPaintWindow(*pOut);
+ OSL_ENSURE(pPaintWindow, "SdrPaintView::UpdateDrawLayersRegion: No SdrPaintWindow (!)");
+
+ if(mpPageView)
+ {
+ SdrPageWindow* pKnownTarget = mpPageView->FindPageWindow(*pPaintWindow);
+
+ if(pKnownTarget)
+ {
+ vcl::Region aOptimizedRepaintRegion = OptimizeDrawLayersRegion( pOut, rReg, false/*bDisableIntersect*/ );
+ pKnownTarget->GetPaintWindow().SetRedrawRegion(aOptimizedRepaintRegion);
+ mpPageView->setPreparedPageWindow(pKnownTarget); // already set actually
+ }
+ }
+}
+
+vcl::Region SdrPaintView::OptimizeDrawLayersRegion(const OutputDevice* pOut, const vcl::Region& rReg, bool bDisableIntersect)
+{
+ // #i74769# check if pOut is a win and has a ClipRegion. If Yes, the Region
+ // rReg may be made more granular (fine) with using it. Normally, rReg
+ // does come from Window::Paint() anyways and thus is based on a single
+ // rectangle which was derived from exactly that repaint region
+ vcl::Region aOptimizedRepaintRegion(rReg);
+
+ // #i76114# Intersecting the region with the Window's paint region is disabled
+ // for print preview in Calc, because the intersection can be empty (if the paint
+ // region is outside of the table area of the page), and then no clip region
+ // would be set.
+ if(pOut && OUTDEV_WINDOW == pOut->GetOutDevType() && !bDisableIntersect)
+ {
+ vcl::Window* pWindow = pOut->GetOwnerWindow();
+
+ if(pWindow->IsInPaint())
+ {
+ if(!pWindow->GetPaintRegion().IsEmpty())
+ {
+ aOptimizedRepaintRegion.Intersect(pWindow->GetPaintRegion());
+ }
+ }
+ }
+ return aOptimizedRepaintRegion;
+}
+
+
+void SdrPaintView::ImpFormLayerDrawing( SdrPaintWindow& rPaintWindow )
+{
+ if(!mpPageView)
+ return;
+
+ SdrPageWindow* pKnownTarget = mpPageView->FindPageWindow(rPaintWindow);
+
+ if(pKnownTarget)
+ {
+ const SdrModel& rModel = *(GetModel());
+ const SdrLayerAdmin& rLayerAdmin = rModel.GetLayerAdmin();
+ const SdrLayerID nControlLayerId = rLayerAdmin.GetLayerID(rLayerAdmin.GetControlLayerName());
+
+ // BUFFERED use GetTargetOutputDevice() now, it may be targeted to VDevs, too
+ // need to set PreparedPageWindow to make DrawLayer use the correct ObjectContact
+ mpPageView->setPreparedPageWindow(pKnownTarget);
+ mpPageView->DrawLayer(nControlLayerId, &rPaintWindow.GetTargetOutputDevice());
+ mpPageView->setPreparedPageWindow(nullptr);
+ }
+}
+
+
+bool SdrPaintView::KeyInput(const KeyEvent& /*rKEvt*/, vcl::Window* /*pWin*/)
+{
+ return false;
+}
+
+void SdrPaintView::GlueInvalidate() const
+{
+ // Do not invalidate GluePoints in Online
+ // They are handled on front-end
+ if (comphelper::LibreOfficeKit::isActive())
+ return;
+
+ const sal_uInt32 nWindowCount(PaintWindowCount());
+
+ for(sal_uInt32 nWinNum(0); nWinNum < nWindowCount; nWinNum++)
+ {
+ SdrPaintWindow* pPaintWindow = GetPaintWindow(nWinNum);
+
+ if(pPaintWindow->OutputToWindow())
+ {
+ OutputDevice& rOutDev = pPaintWindow->GetOutputDevice();
+
+ if(mpPageView)
+ {
+ const SdrObjList* pOL=mpPageView->GetObjList();
+ const size_t nObjCount = pOL->GetObjCount();
+ for (size_t nObjNum=0; nObjNum<nObjCount; ++nObjNum) {
+ const SdrObject* pObj=pOL->GetObj(nObjNum);
+ const SdrGluePointList* pGPL=pObj->GetGluePointList();
+ if (pGPL!=nullptr && pGPL->GetCount()!=0) {
+ pGPL->Invalidate(*rOutDev.GetOwnerWindow(), pObj);
+ }
+ }
+ }
+ }
+ }
+}
+
+void SdrPaintView::InvalidateAllWin()
+{
+ const sal_uInt32 nWindowCount(PaintWindowCount());
+
+ for(sal_uInt32 a(0); a < nWindowCount; a++)
+ {
+ SdrPaintWindow* pPaintWindow = GetPaintWindow(a);
+
+ if(pPaintWindow->OutputToWindow())
+ {
+ InvalidateOneWin(pPaintWindow->GetOutputDevice());
+ }
+ }
+}
+
+void SdrPaintView::InvalidateAllWin(const tools::Rectangle& rRect)
+{
+ const sal_uInt32 nWindowCount(PaintWindowCount());
+
+ for(sal_uInt32 a(0); a < nWindowCount; a++)
+ {
+ SdrPaintWindow* pPaintWindow = GetPaintWindow(a);
+
+ if(pPaintWindow->OutputToWindow())
+ {
+ OutputDevice& rOutDev = pPaintWindow->GetOutputDevice();
+ tools::Rectangle aRect(rRect);
+
+ Point aOrg(rOutDev.GetMapMode().GetOrigin());
+ aOrg.setX(-aOrg.X() ); aOrg.setY(-aOrg.Y() );
+ tools::Rectangle aOutRect(aOrg, rOutDev.GetOutputSize());
+
+ // In case of tiled rendering we want to get all invalidations, so visual area is not interesting.
+ if (aRect.Overlaps(aOutRect) || comphelper::LibreOfficeKit::isActive())
+ {
+ InvalidateOneWin(rOutDev, aRect);
+ }
+ }
+ }
+}
+
+void SdrPaintView::InvalidateOneWin(OutputDevice& rDevice)
+{
+ // do not erase background, that causes flicker (!)
+ rDevice.GetOwnerWindow()->Invalidate(InvalidateFlags::NoErase);
+}
+
+void SdrPaintView::InvalidateOneWin(OutputDevice& rDevice, const tools::Rectangle& rRect)
+{
+ // do not erase background, that causes flicker (!)
+ rDevice.GetOwnerWindow()->Invalidate(rRect, InvalidateFlags::NoErase);
+}
+
+void SdrPaintView::LeaveOneGroup()
+{
+ if(mpPageView)
+ {
+ mpPageView->LeaveOneGroup();
+ }
+}
+
+void SdrPaintView::LeaveAllGroup()
+{
+ if(mpPageView)
+ {
+ mpPageView->LeaveAllGroup();
+ }
+}
+
+bool SdrPaintView::IsGroupEntered() const
+{
+ if(mpPageView)
+ {
+ return (mpPageView->GetEnteredLevel() != 0);
+ }
+
+ return false;
+}
+
+void SdrPaintView::SetNotPersistDefaultAttr(const SfxItemSet& rAttr)
+{
+ // bReplaceAll has no effect here at all.
+ bool bMeasure= dynamic_cast<const SdrView*>(this) != nullptr && static_cast<SdrView*>(this)->IsMeasureTool();
+
+ if (const SdrLayerIdItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_LAYERID))
+ {
+ SdrLayerID nLayerId = pPoolItem->GetValue();
+ const SdrLayer* pLayer=mpModel->GetLayerAdmin().GetLayerPerID(nLayerId);
+ if (pLayer!=nullptr) {
+ if (bMeasure) maMeasureLayer=pLayer->GetName();
+ else maActualLayer=pLayer->GetName();
+ }
+ }
+ if (const SdrLayerNameItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_LAYERNAME))
+ {
+ if (bMeasure) maMeasureLayer = pPoolItem->GetValue();
+ else maActualLayer = pPoolItem->GetValue();
+ }
+}
+
+void SdrPaintView::MergeNotPersistDefaultAttr(SfxItemSet& rAttr) const
+{
+ // bOnlyHardAttr has no effect here at all.
+ bool bMeasure= dynamic_cast<const SdrView*>(this) != nullptr && static_cast<const SdrView*>(this)->IsMeasureTool();
+ const OUString& aNam = bMeasure ? maMeasureLayer : maActualLayer;
+ rAttr.Put(SdrLayerNameItem(aNam));
+ SdrLayerID nLayer=mpModel->GetLayerAdmin().GetLayerID(aNam);
+ if (nLayer!=SDRLAYER_NOTFOUND) {
+ rAttr.Put(SdrLayerIdItem(nLayer));
+ }
+}
+
+void SdrPaintView::SetDefaultAttr(const SfxItemSet& rAttr, bool bReplaceAll)
+{
+#ifdef DBG_UTIL
+ {
+ bool bHasEEFeatureItems=false;
+ SfxItemIter aIter(rAttr);
+ for (const SfxPoolItem* pItem = aIter.GetCurItem(); !bHasEEFeatureItems && pItem;
+ pItem = aIter.NextItem())
+ {
+ if (!IsInvalidItem(pItem)) {
+ sal_uInt16 nW=pItem->Which();
+ if (nW>=EE_FEATURE_START && nW<=EE_FEATURE_END) bHasEEFeatureItems=true;
+ }
+ }
+
+ if(bHasEEFeatureItems)
+ {
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Info, VclButtonsType::Ok,
+ "SdrPaintView::SetDefaultAttr(): Setting EE_FEATURE items at the SdrView does not make sense! It only leads to overhead and unreadable documents."));
+ xInfoBox->run();
+ }
+ }
+#endif
+ if (bReplaceAll) maDefaultAttr.Set(rAttr);
+ else maDefaultAttr.Put(rAttr,false); // if FALSE, regard InvalidItems as "holes," not as Default
+ SetNotPersistDefaultAttr(rAttr);
+}
+
+void SdrPaintView::SetDefaultStyleSheet(SfxStyleSheet* pStyleSheet, bool bDontRemoveHardAttr)
+{
+ if (mpDefaultStyleSheet)
+ EndListening(*mpDefaultStyleSheet);
+ mpDefaultStyleSheet=pStyleSheet;
+ if (mpDefaultStyleSheet)
+ StartListening(*mpDefaultStyleSheet);
+
+ if (pStyleSheet!=nullptr && !bDontRemoveHardAttr) {
+ SfxWhichIter aIter(pStyleSheet->GetItemSet());
+ sal_uInt16 nWhich=aIter.FirstWhich();
+ while (nWhich!=0) {
+ if (aIter.GetItemState()==SfxItemState::SET) {
+ maDefaultAttr.ClearItem(nWhich);
+ }
+ nWhich=aIter.NextWhich();
+ }
+ }
+}
+
+void SdrPaintView::GetAttributes(SfxItemSet& rTargetSet, bool bOnlyHardAttr) const
+{
+ if(bOnlyHardAttr || !mpDefaultStyleSheet)
+ {
+ rTargetSet.Put(maDefaultAttr, false);
+ }
+ else
+ {
+ // else merge with DefStyleSheet
+ rTargetSet.Put(mpDefaultStyleSheet->GetItemSet(), false);
+ rTargetSet.Put(maDefaultAttr, false);
+ }
+ MergeNotPersistDefaultAttr(rTargetSet);
+}
+
+void SdrPaintView::SetAttributes(const SfxItemSet& rSet, bool bReplaceAll)
+{
+ SetDefaultAttr(rSet,bReplaceAll);
+}
+
+SfxStyleSheet* SdrPaintView::GetStyleSheet() const
+{
+ return mpDefaultStyleSheet;
+}
+
+void SdrPaintView::SetStyleSheet(SfxStyleSheet* pStyleSheet, bool bDontRemoveHardAttr)
+{
+ SetDefaultStyleSheet(pStyleSheet,bDontRemoveHardAttr);
+}
+
+void SdrPaintView::MakeVisible(const tools::Rectangle& rRect, vcl::Window& rWin)
+{
+ // TODO: handle when the text cursor goes out of the chart area
+ // However this hack avoids that the cursor gets misplaced wrt the text.
+ if (comphelper::LibreOfficeKit::isActive() && rWin.IsChart())
+ {
+ return;
+ }
+
+ MapMode aMap(rWin.GetMapMode());
+ Size aActualSize(rWin.GetOutDev()->GetOutputSize());
+
+ if( aActualSize.IsEmpty() )
+ return;
+
+ Size aNewSize(rRect.GetSize());
+ bool bNewScale=false;
+ bool bNeedMoreX=aNewSize.Width()>aActualSize.Width();
+ bool bNeedMoreY=aNewSize.Height()>aActualSize.Height();
+ if (bNeedMoreX || bNeedMoreY)
+ {
+ bNewScale=true;
+ // set new MapMode (Size+Org) and invalidate everything
+ Fraction aXFact(aNewSize.Width(),aActualSize.Width());
+ Fraction aYFact(aNewSize.Height(),aActualSize.Height());
+ if (aYFact>aXFact) aXFact=aYFact;
+ aXFact*=aMap.GetScaleX();
+ aXFact.ReduceInaccurate(10); // to avoid runovers and BigInt mapping
+ aMap.SetScaleX(aXFact);
+ aMap.SetScaleY(aYFact);
+ rWin.SetMapMode(aMap);
+ aActualSize=rWin.GetOutDev()->GetOutputSize();
+ }
+ Point aOrg(aMap.GetOrigin());
+ tools::Long dx=0,dy=0;
+ tools::Long l=-aOrg.X();
+ tools::Long r=-aOrg.X()+aActualSize.Width()-1;
+ tools::Long o=-aOrg.Y();
+ tools::Long u=-aOrg.Y()+aActualSize.Height()-1;
+ if (l>rRect.Left()) dx=rRect.Left()-l;
+ else if (r<rRect.Right()) dx=rRect.Right()-r;
+ if (o>rRect.Top()) dy=rRect.Top()-o;
+ else if (u<rRect.Bottom()) dy=rRect.Bottom()-u;
+ aMap.SetOrigin(Point(aOrg.X()-dx,aOrg.Y()-dy));
+ if (!bNewScale) {
+ if (dx!=0 || dy!=0) {
+ rWin.Scroll(-dx,-dy);
+ rWin.SetMapMode(aMap);
+ rWin.PaintImmediately();
+ }
+ } else {
+ rWin.SetMapMode(aMap);
+ InvalidateOneWin(*rWin.GetOutDev());
+ }
+}
+
+void SdrPaintView::DoConnect(SdrOle2Obj* /*pOleObj*/)
+{
+}
+
+void SdrPaintView::SetAnimationEnabled( bool bEnable )
+{
+ SetAnimationMode( bEnable ? SdrAnimationMode::Animate : SdrAnimationMode::Disable );
+}
+
+void SdrPaintView::SetAnimationPause( bool bSet )
+{
+ if(mbAnimationPause == bSet)
+ return;
+
+ mbAnimationPause = bSet;
+
+ if(!mpPageView)
+ return;
+
+ for(sal_uInt32 b(0); b < mpPageView->PageWindowCount(); b++)
+ {
+ SdrPageWindow& rPageWindow = *(mpPageView->GetPageWindow(b));
+ sdr::contact::ObjectContact& rObjectContact = rPageWindow.GetObjectContact();
+ sdr::animation::primitiveAnimator& rAnimator = rObjectContact.getPrimitiveAnimator();
+
+ if(rAnimator.IsPaused() != bSet)
+ {
+ rAnimator.SetPaused(bSet);
+ }
+ }
+}
+
+void SdrPaintView::SetAnimationMode( const SdrAnimationMode eMode )
+{
+ meAnimationMode = eMode;
+}
+
+void SdrPaintView::VisAreaChanged(const OutputDevice* pOut)
+{
+ if(!mpPageView)
+ return;
+
+ if (pOut)
+ {
+ SdrPageWindow* pWindow = mpPageView->FindPageWindow(*const_cast<OutputDevice*>(pOut));
+
+ if(pWindow)
+ {
+ VisAreaChanged();
+ }
+ }
+ else
+ {
+ VisAreaChanged();
+ }
+}
+
+void SdrPaintView::VisAreaChanged()
+{
+ // notify SfxListener
+ Broadcast(SvxViewChangedHint());
+}
+
+
+void SdrPaintView::onChangeColorConfig()
+{
+ maGridColor = maColorConfig.GetColorValue( svtools::DRAWGRID ).nColor;
+}
+
+
+// Set background color for svx at SdrPageViews
+void SdrPaintView::SetApplicationBackgroundColor(Color aBackgroundColor)
+{
+ if(mpPageView)
+ {
+ mpPageView->SetApplicationBackgroundColor(aBackgroundColor);
+ }
+}
+
+// Set document color for svx at SdrPageViews
+void SdrPaintView::SetApplicationDocumentColor(Color aDocumentColor)
+{
+ if(mpPageView)
+ {
+ mpPageView->SetApplicationDocumentColor(aDocumentColor);
+ }
+}
+
+bool SdrPaintView::IsBufferedOutputAllowed() const
+{
+ return (mbBufferedOutputAllowed && SvtOptionsDrawinglayer::IsPaintBuffer());
+}
+
+void SdrPaintView::SetBufferedOutputAllowed(bool bNew)
+{
+ if(bNew != mbBufferedOutputAllowed)
+ {
+ mbBufferedOutputAllowed = bNew;
+ }
+}
+
+bool SdrPaintView::IsBufferedOverlayAllowed() const
+{
+ return (mbBufferedOverlayAllowed && SvtOptionsDrawinglayer::IsOverlayBuffer());
+}
+
+void SdrPaintView::SetBufferedOverlayAllowed(bool bNew)
+{
+ if(bNew != mbBufferedOverlayAllowed)
+ {
+ mbBufferedOverlayAllowed = bNew;
+ }
+}
+
+
+void SdrPaintView::SetPagePaintingAllowed(bool bNew)
+{
+ if(bNew != mbPagePaintingAllowed)
+ {
+ mbPagePaintingAllowed = bNew;
+ }
+}
+
+// #i38135# Sets the timer for Object animations and restarts.
+void SdrPaintView::SetAnimationTimer(sal_uInt32 nTime)
+{
+ if(mpPageView)
+ {
+ // first, reset all timers at all windows to 0L
+ for(sal_uInt32 a(0); a < mpPageView->PageWindowCount(); a++)
+ {
+ SdrPageWindow& rPageWindow = *mpPageView->GetPageWindow(a);
+ sdr::contact::ObjectContact& rObjectContact = rPageWindow.GetObjectContact();
+ sdr::animation::primitiveAnimator& rAnimator = rObjectContact.getPrimitiveAnimator();
+ rAnimator.SetTime(nTime);
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdpoev.cxx b/svx/source/svdraw/svdpoev.cxx
new file mode 100644
index 000000000..bcf9168a4
--- /dev/null
+++ b/svx/source/svdraw/svdpoev.cxx
@@ -0,0 +1,652 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <svx/svdpoev.hxx>
+#include <math.h>
+#include <svx/svdpagv.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/svdundo.hxx>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include <svx/svdtrans.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <tools/debug.hxx>
+#include <tools/helpers.hxx>
+
+#include <svx/polypolygoneditor.hxx>
+
+using namespace sdr;
+
+
+void SdrPolyEditView::ImpResetPolyPossibilityFlags()
+{
+ eMarkedPointsSmooth=SdrPathSmoothKind::DontCare;
+ eMarkedSegmentsKind=SdrPathSegmentKind::DontCare;
+ bSetMarkedPointsSmoothPossible=false;
+ bSetMarkedSegmentsKindPossible=false;
+}
+
+SdrPolyEditView::SdrPolyEditView(
+ SdrModel& rSdrModel,
+ OutputDevice* pOut)
+: SdrEditView(rSdrModel, pOut)
+{
+ ImpResetPolyPossibilityFlags();
+}
+
+SdrPolyEditView::~SdrPolyEditView()
+{
+}
+
+void SdrPolyEditView::ImpCheckPolyPossibilities()
+{
+ ImpResetPolyPossibilityFlags();
+ const size_t nMarkCount(GetMarkedObjectCount());
+
+ if(!nMarkCount || ImpIsFrameHandles())
+ return;
+
+ bool b1stSmooth(true);
+ bool b1stSegm(true);
+ bool bCurve(false);
+ bool bSmoothFuz(false);
+ bool bSegmFuz(false);
+ basegfx::B2VectorContinuity eSmooth = basegfx::B2VectorContinuity::NONE;
+
+ for(size_t nMarkNum = 0; nMarkNum < nMarkCount; ++nMarkNum)
+ {
+ SdrMark* pM = GetSdrMarkByIndex(nMarkNum);
+ CheckPolyPossibilitiesHelper( pM, b1stSmooth, b1stSegm, bCurve, bSmoothFuz, bSegmFuz, eSmooth );
+ }
+}
+
+void SdrPolyEditView::CheckPolyPossibilitiesHelper( SdrMark* pM, bool& b1stSmooth, bool& b1stSegm, bool& bCurve, bool& bSmoothFuz, bool& bSegmFuz, basegfx::B2VectorContinuity& eSmooth )
+{
+ SdrObject* pObj = pM->GetMarkedSdrObj();
+ SdrPathObj* pPath = dynamic_cast<SdrPathObj*>( pObj );
+
+ if (!pPath)
+ return;
+
+ SdrUShortCont& rPts = pM->GetMarkedPoints();
+ if (rPts.empty())
+ return;
+
+ const bool bClosed(pPath->IsClosed());
+ bSetMarkedPointsSmoothPossible = true;
+
+ if (bClosed)
+ {
+ bSetMarkedSegmentsKindPossible = true;
+ }
+
+ for (const auto& rPt : rPts)
+ {
+ sal_uInt32 nNum(rPt);
+ sal_uInt32 nPolyNum, nPntNum;
+
+ if(PolyPolygonEditor::GetRelativePolyPoint(pPath->GetPathPoly(), nNum, nPolyNum, nPntNum))
+ {
+ const basegfx::B2DPolygon aLocalPolygon(pPath->GetPathPoly().getB2DPolygon(nPolyNum));
+ bool bCanSegment(bClosed || nPntNum < aLocalPolygon.count() - 1);
+
+ if(!bSetMarkedSegmentsKindPossible && bCanSegment)
+ {
+ bSetMarkedSegmentsKindPossible = true;
+ }
+
+ if(!bSmoothFuz)
+ {
+ if (b1stSmooth)
+ {
+ b1stSmooth = false;
+ eSmooth = basegfx::utils::getContinuityInPoint(aLocalPolygon, nPntNum);
+ }
+ else
+ {
+ bSmoothFuz = (eSmooth != basegfx::utils::getContinuityInPoint(aLocalPolygon, nPntNum));
+ }
+ }
+
+ if(!bSegmFuz && bCanSegment)
+ {
+ bool bCrv(aLocalPolygon.isNextControlPointUsed(nPntNum));
+
+ if(b1stSegm)
+ {
+ b1stSegm = false;
+ bCurve = bCrv;
+ }
+ else
+ {
+ bSegmFuz = (bCrv != bCurve);
+ }
+ }
+ }
+ }
+
+ if(!b1stSmooth && !bSmoothFuz)
+ {
+ if(basegfx::B2VectorContinuity::NONE == eSmooth)
+ {
+ eMarkedPointsSmooth = SdrPathSmoothKind::Angular;
+ }
+
+ if(basegfx::B2VectorContinuity::C1 == eSmooth)
+ {
+ eMarkedPointsSmooth = SdrPathSmoothKind::Asymmetric;
+ }
+
+ if(basegfx::B2VectorContinuity::C2 == eSmooth)
+ {
+ eMarkedPointsSmooth = SdrPathSmoothKind::Symmetric;
+ }
+ }
+
+ if(!b1stSegm && !bSegmFuz)
+ {
+ eMarkedSegmentsKind = bCurve ? SdrPathSegmentKind::Curve : SdrPathSegmentKind::Line;
+ }
+}
+
+void SdrPolyEditView::SetMarkedPointsSmooth(SdrPathSmoothKind eKind)
+{
+ basegfx::B2VectorContinuity eFlags;
+
+ if(SdrPathSmoothKind::Angular == eKind)
+ {
+ eFlags = basegfx::B2VectorContinuity::NONE;
+ }
+ else if(SdrPathSmoothKind::Asymmetric == eKind)
+ {
+ eFlags = basegfx::B2VectorContinuity::C1;
+ }
+ else if(SdrPathSmoothKind::Symmetric == eKind)
+ {
+ eFlags = basegfx::B2VectorContinuity::C2;
+ }
+ else
+ {
+ return;
+ }
+
+ if(!HasMarkedPoints())
+ return;
+
+ SortMarkedObjects();
+
+ const bool bUndo = IsUndoEnabled();
+ if( bUndo )
+ BegUndo(SvxResId(STR_EditSetPointsSmooth), GetDescriptionOfMarkedPoints());
+ const size_t nMarkCount(GetMarkedObjectCount());
+
+ for(size_t nMarkNum(nMarkCount); nMarkNum > 0;)
+ {
+ --nMarkNum;
+ SdrMark* pM = GetSdrMarkByIndex(nMarkNum);
+ SdrPathObj* pPath = dynamic_cast< SdrPathObj* >( pM->GetMarkedSdrObj() );
+ if (!pPath)
+ continue;
+
+ SdrUShortCont& rPts = pM->GetMarkedPoints();
+ PolyPolygonEditor aEditor(pPath->GetPathPoly());
+ if (aEditor.SetPointsSmooth(eFlags, rPts))
+ {
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pPath));
+ pPath->SetPathPoly(aEditor.GetPolyPolygon());
+ }
+ }
+
+ if( bUndo )
+ EndUndo();
+}
+
+void SdrPolyEditView::SetMarkedSegmentsKind(SdrPathSegmentKind eKind)
+{
+ if(!HasMarkedPoints())
+ return;
+
+ SortMarkedObjects();
+
+ const bool bUndo = IsUndoEnabled();
+ if( bUndo )
+ BegUndo(SvxResId(STR_EditSetSegmentsKind), GetDescriptionOfMarkedPoints());
+ const size_t nMarkCount(GetMarkedObjectCount());
+
+ for(size_t nMarkNum=nMarkCount; nMarkNum > 0;)
+ {
+ --nMarkNum;
+ SdrMark* pM = GetSdrMarkByIndex(nMarkNum);
+ SdrPathObj* pPath = dynamic_cast< SdrPathObj* >( pM->GetMarkedSdrObj() );
+ if (!pPath)
+ continue;
+ SdrUShortCont& rPts = pM->GetMarkedPoints();
+ PolyPolygonEditor aEditor( pPath->GetPathPoly());
+ if (aEditor.SetSegmentsKind(eKind, rPts))
+ {
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pPath));
+ pPath->SetPathPoly(aEditor.GetPolyPolygon());
+ }
+ }
+
+ if( bUndo )
+ EndUndo();
+}
+
+bool SdrPolyEditView::IsSetMarkedPointsSmoothPossible() const
+{
+ ForcePossibilities();
+ return bSetMarkedPointsSmoothPossible;
+}
+
+SdrPathSmoothKind SdrPolyEditView::GetMarkedPointsSmooth() const
+{
+ ForcePossibilities();
+ return eMarkedPointsSmooth;
+}
+
+bool SdrPolyEditView::IsSetMarkedSegmentsKindPossible() const
+{
+ ForcePossibilities();
+ return bSetMarkedSegmentsKindPossible;
+}
+
+SdrPathSegmentKind SdrPolyEditView::GetMarkedSegmentsKind() const
+{
+ ForcePossibilities();
+ return eMarkedSegmentsKind;
+}
+
+bool SdrPolyEditView::IsDeleteMarkedPointsPossible() const
+{
+ return HasMarkedPoints();
+}
+
+void SdrPolyEditView::DeleteMarkedPoints()
+{
+ if (!HasMarkedPoints())
+ return;
+
+ BrkAction();
+ SortMarkedObjects();
+ const size_t nMarkCount=GetMarkedObjectCount();
+
+ const bool bUndo = IsUndoEnabled();
+ if( bUndo )
+ {
+ // Description
+ BegUndo(SvxResId(STR_EditDelete),GetDescriptionOfMarkedPoints(),SdrRepeatFunc::Delete);
+ }
+
+ for (size_t nMarkNum=nMarkCount; nMarkNum>0;)
+ {
+ --nMarkNum;
+ SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
+ SdrPathObj* pPath = dynamic_cast< SdrPathObj* >( pM->GetMarkedSdrObj() );
+ if (!pPath)
+ continue;
+
+ SdrUShortCont& rPts = pM->GetMarkedPoints();
+ PolyPolygonEditor aEditor( pPath->GetPathPoly());
+ if (aEditor.DeletePoints(rPts))
+ {
+ if( aEditor.GetPolyPolygon().count() )
+ {
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pPath ));
+ pPath->SetPathPoly( aEditor.GetPolyPolygon() );
+ }
+ else
+ {
+ if( bUndo )
+ AddUndo( GetModel()->GetSdrUndoFactory().CreateUndoDeleteObject(*pPath ) );
+ pM->GetPageView()->GetObjList()->RemoveObject(pPath->GetOrdNum());
+ if( !bUndo )
+ {
+ SdrObject* pObj = pPath;
+ SdrObject::Free(pObj);
+ }
+ }
+ }
+ }
+
+ if( bUndo )
+ EndUndo();
+ UnmarkAllPoints();
+ MarkListHasChanged();
+}
+
+void SdrPolyEditView::RipUpAtMarkedPoints()
+{
+ if(!HasMarkedPoints())
+ return;
+
+ SortMarkedObjects();
+ const size_t nMarkCount(GetMarkedObjectCount());
+
+ const bool bUndo = IsUndoEnabled();
+ if( bUndo )
+ BegUndo(SvxResId(STR_EditRipUp), GetDescriptionOfMarkedPoints());
+
+ for(size_t nMarkNum = nMarkCount; nMarkNum > 0;)
+ {
+ --nMarkNum;
+ SdrMark* pM = GetSdrMarkByIndex(nMarkNum);
+ SdrPathObj* pObj = dynamic_cast<SdrPathObj*>( pM->GetMarkedSdrObj() );
+ if (!pObj)
+ continue;
+
+ SdrUShortCont& rPts = pM->GetMarkedPoints();
+
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pObj));
+ bool bCorrectionFlag(false);
+ sal_uInt32 nMax(pObj->GetHdlCount());
+
+ for(SdrUShortCont::const_reverse_iterator it = rPts.rbegin(); it != rPts.rend(); ++it)
+ {
+ sal_uInt32 nNewPt0Idx(0);
+ SdrObject* pNewObj = pObj->RipPoint(*it, nNewPt0Idx);
+
+ if(pNewObj)
+ {
+ pM->GetPageView()->GetObjList()->InsertObject(pNewObj, pObj->GetOrdNum() + 1);
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pNewObj));
+ MarkObj(pNewObj, pM->GetPageView(), false, true);
+ }
+
+ if(nNewPt0Idx)
+ {
+ // correction necessary?
+ DBG_ASSERT(!bCorrectionFlag,"Multiple index corrections at SdrPolyEditView::RipUp().");
+ if(!bCorrectionFlag)
+ {
+ bCorrectionFlag = true;
+
+ SdrUShortCont aReplaceSet;
+ for(const auto& rPt : rPts)
+ {
+ sal_uInt32 nPntNum(rPt);
+ nPntNum += nNewPt0Idx;
+
+ if(nPntNum >= nMax)
+ {
+ nPntNum -= nMax;
+ }
+
+ aReplaceSet.insert( static_cast<sal_uInt16>(nPntNum) );
+ }
+ rPts.swap(aReplaceSet);
+
+ it = rPts.rbegin();
+ }
+ }
+ }
+ }
+
+ UnmarkAllPoints();
+ if( bUndo )
+ EndUndo();
+ MarkListHasChanged();
+}
+
+bool SdrPolyEditView::IsRipUpAtMarkedPointsPossible() const
+{
+ bool bRetval(false);
+ const size_t nMarkCount(GetMarkedObjectCount());
+
+ for(size_t a = 0; a < nMarkCount; ++a)
+ {
+ const SdrMark* pMark = GetSdrMarkByIndex(a);
+ const SdrPathObj* pMarkedPathObject = dynamic_cast< const SdrPathObj* >(pMark->GetMarkedSdrObj());
+
+ if (!pMarkedPathObject)
+ continue;
+
+ const SdrUShortCont& rSelectedPoints = pMark->GetMarkedPoints();
+ if (rSelectedPoints.empty())
+ continue;
+
+ const basegfx::B2DPolyPolygon& rPathPolyPolygon = pMarkedPathObject->GetPathPoly();
+
+ if(1 == rPathPolyPolygon.count())
+ {
+ // #i76617# Do not yet use basegfx::B2DPolygon since curve definitions
+ // are different and methods need to be changed thoroughly with interaction rework
+ const tools::Polygon aPathPolygon(rPathPolyPolygon.getB2DPolygon(0));
+ const sal_uInt16 nPointCount(aPathPolygon.GetSize());
+
+ if(nPointCount >= 3)
+ {
+ bRetval = pMarkedPathObject->IsClosedObj() // #i76617#
+ || std::any_of(rSelectedPoints.begin(), rSelectedPoints.end(),
+ [nPointCount](const sal_uInt16 nMarkedPointNum) {
+ return nMarkedPointNum > 0 && nMarkedPointNum < nPointCount - 1;
+ });
+ }
+ }
+ }
+
+ return bRetval;
+}
+
+bool SdrPolyEditView::IsOpenCloseMarkedObjectsPossible() const
+{
+ bool bRetval(false);
+ const size_t nMarkCount(GetMarkedObjectCount());
+
+ for(size_t a = 0; a < nMarkCount; ++a)
+ {
+ const SdrMark* pMark = GetSdrMarkByIndex(a);
+ const SdrPathObj* pMarkedPathObject = dynamic_cast< const SdrPathObj* >(pMark->GetMarkedSdrObj());
+
+ if(pMarkedPathObject)
+ {
+ // #i76617# Do not yet use basegfx::B2DPolygon since curve definitions
+ // are different and methods need to be changed thoroughly with interaction rework
+ const tools::PolyPolygon aPathPolyPolygon(pMarkedPathObject->GetPathPoly());
+ const sal_uInt16 nPolygonCount(aPathPolyPolygon.Count());
+
+ for(sal_uInt16 b(0); !bRetval && b < nPolygonCount; b++)
+ {
+ const tools::Polygon& rPathPolygon = aPathPolyPolygon[b];
+ const sal_uInt16 nPointCount(rPathPolygon.GetSize());
+
+ bRetval = (nPointCount >= 3);
+ }
+ }
+ }
+
+ return bRetval;
+}
+
+SdrObjClosedKind SdrPolyEditView::GetMarkedObjectsClosedState() const
+{
+ bool bOpen(false);
+ bool bClosed(false);
+ const size_t nMarkCount(GetMarkedObjectCount());
+
+ for(size_t a = 0; !(bOpen && bClosed) && a < nMarkCount; ++a)
+ {
+ const SdrMark* pMark = GetSdrMarkByIndex(a);
+ const SdrPathObj* pMarkedPathObject = dynamic_cast< const SdrPathObj* >(pMark->GetMarkedSdrObj());
+
+ if(pMarkedPathObject)
+ {
+ if(pMarkedPathObject->IsClosedObj())
+ {
+ bClosed = true;
+ }
+ else
+ {
+ bOpen = true;
+ }
+ }
+ }
+
+ if(bOpen && bClosed)
+ {
+ return SdrObjClosedKind::DontCare;
+ }
+ else if(bOpen)
+ {
+ return SdrObjClosedKind::Open;
+ }
+ else
+ {
+ return SdrObjClosedKind::Closed;
+ }
+}
+
+void SdrPolyEditView::ImpTransformMarkedPoints(PPolyTrFunc pTrFunc, const void* p1, const void* p2, const void* p3, const void* p4)
+{
+ const bool bUndo = IsUndoEnabled();
+
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nm=0; nm<nMarkCount; ++nm)
+ {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ SdrPathObj* pPath=dynamic_cast<SdrPathObj*>( pObj );
+ if (!pPath)
+ continue;
+
+ const SdrUShortCont& rPts = pM->GetMarkedPoints();
+ if (rPts.empty())
+ continue;
+
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pObj));
+
+ basegfx::B2DPolyPolygon aXPP(pPath->GetPathPoly());
+
+ for (const auto& rPt : rPts)
+ {
+ sal_uInt32 nPt = rPt;
+ sal_uInt32 nPolyNum, nPointNum;
+
+ if(PolyPolygonEditor::GetRelativePolyPoint(aXPP, nPt, nPolyNum, nPointNum))
+ {
+ //#i83671# used nLocalPointNum (which was the polygon point count)
+ // instead of the point index (nPointNum). This of course led
+ // to a wrong point access to the B2DPolygon.
+ basegfx::B2DPolygon aNewXP(aXPP.getB2DPolygon(nPolyNum));
+ Point aPos, aC1, aC2;
+ bool bC1(false);
+ bool bC2(false);
+
+ const basegfx::B2DPoint aB2DPos(aNewXP.getB2DPoint(nPointNum));
+ aPos = Point(FRound(aB2DPos.getX()), FRound(aB2DPos.getY()));
+
+ if(aNewXP.isPrevControlPointUsed(nPointNum))
+ {
+ const basegfx::B2DPoint aB2DC1(aNewXP.getPrevControlPoint(nPointNum));
+ aC1 = Point(FRound(aB2DC1.getX()), FRound(aB2DC1.getY()));
+ bC1 = true;
+ }
+
+ if(aNewXP.isNextControlPointUsed(nPointNum))
+ {
+ const basegfx::B2DPoint aB2DC2(aNewXP.getNextControlPoint(nPointNum));
+ aC2 = Point(FRound(aB2DC2.getX()), FRound(aB2DC2.getY()));
+ bC2 = true;
+ }
+
+ (*pTrFunc)(aPos,&aC1,&aC2,p1,p2,p3,p4);
+ aNewXP.setB2DPoint(nPointNum, basegfx::B2DPoint(aPos.X(), aPos.Y()));
+
+ if (bC1)
+ {
+ aNewXP.setPrevControlPoint(nPointNum, basegfx::B2DPoint(aC1.X(), aC1.Y()));
+ }
+
+ if (bC2)
+ {
+ aNewXP.setNextControlPoint(nPointNum, basegfx::B2DPoint(aC2.X(), aC2.Y()));
+ }
+
+ aXPP.setB2DPolygon(nPolyNum, aNewXP);
+ }
+ }
+
+ pPath->SetPathPoly(aXPP);
+ }
+}
+
+
+static void ImpMove(Point& rPt, Point* pC1, Point* pC2, const void* p1, const void* /*p2*/, const void* /*p3*/, const void* /*p4*/)
+{
+ rPt.Move(*static_cast<const Size*>(p1));
+ if (pC1!=nullptr) pC1->Move(*static_cast<const Size*>(p1));
+ if (pC2!=nullptr) pC2->Move(*static_cast<const Size*>(p1));
+}
+
+void SdrPolyEditView::MoveMarkedPoints(const Size& rSiz)
+{
+ ForceUndirtyMrkPnt();
+ OUString aStr(SvxResId(STR_EditMove));
+ BegUndo(aStr,GetDescriptionOfMarkedPoints(),SdrRepeatFunc::Move);
+ ImpTransformMarkedPoints(ImpMove,&rSiz);
+ EndUndo();
+ AdjustMarkHdl();
+}
+
+static void ImpResize(Point& rPt, Point* pC1, Point* pC2, const void* p1, const void* p2, const void* p3, const void* /*p4*/)
+{
+ ResizePoint(rPt,*static_cast<const Point*>(p1),*static_cast<const Fraction*>(p2),*static_cast<const Fraction*>(p3));
+ if (pC1!=nullptr) ResizePoint(*pC1,*static_cast<const Point*>(p1),*static_cast<const Fraction*>(p2),*static_cast<const Fraction*>(p3));
+ if (pC2!=nullptr) ResizePoint(*pC2,*static_cast<const Point*>(p1),*static_cast<const Fraction*>(p2),*static_cast<const Fraction*>(p3));
+}
+
+void SdrPolyEditView::ResizeMarkedPoints(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
+{
+ ForceUndirtyMrkPnt();
+ OUString aStr(SvxResId(STR_EditResize));
+ BegUndo(aStr,GetDescriptionOfMarkedPoints(),SdrRepeatFunc::Resize);
+ ImpTransformMarkedPoints(ImpResize,&rRef,&xFact,&yFact);
+ EndUndo();
+ AdjustMarkHdl();
+}
+
+static void ImpRotate(Point& rPt, Point* pC1, Point* pC2, const void* p1, const void* /*p2*/, const void* p3, const void* p4)
+{
+ RotatePoint(rPt,*static_cast<const Point*>(p1),*static_cast<const double*>(p3),*static_cast<const double*>(p4));
+ if (pC1!=nullptr) RotatePoint(*pC1,*static_cast<const Point*>(p1),*static_cast<const double*>(p3),*static_cast<const double*>(p4));
+ if (pC2!=nullptr) RotatePoint(*pC2,*static_cast<const Point*>(p1),*static_cast<const double*>(p3),*static_cast<const double*>(p4));
+}
+
+void SdrPolyEditView::RotateMarkedPoints(const Point& rRef, Degree100 nAngle)
+{
+ ForceUndirtyMrkPnt();
+ OUString aStr(SvxResId(STR_EditResize));
+ BegUndo(aStr,GetDescriptionOfMarkedPoints(),SdrRepeatFunc::Rotate);
+ double nSin = sin(toRadians(nAngle));
+ double nCos = cos(toRadians(nAngle));
+ ImpTransformMarkedPoints(ImpRotate,&rRef,&nAngle,&nSin,&nCos);
+ EndUndo();
+ AdjustMarkHdl();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdsnpv.cxx b/svx/source/svdraw/svdsnpv.cxx
new file mode 100644
index 000000000..bcef8b3c3
--- /dev/null
+++ b/svx/source/svdraw/svdsnpv.cxx
@@ -0,0 +1,633 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <svx/svdsnpv.hxx>
+#include <math.h>
+
+#include <svx/svdobj.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svditer.hxx>
+#include <svx/sdr/overlay/overlayobjectlist.hxx>
+#include <sdr/overlay/overlaycrosshair.hxx>
+#include <sdr/overlay/overlayhelpline.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <tools/debug.hxx>
+#include <vcl/ptrstyle.hxx>
+
+
+class ImplPageOriginOverlay
+{
+ // The OverlayObjects
+ sdr::overlay::OverlayObjectList maObjects;
+
+ // The current position in logical coordinates
+ basegfx::B2DPoint maPosition;
+
+public:
+ ImplPageOriginOverlay(const SdrPaintView& rView, const basegfx::B2DPoint& rStartPos);
+
+ // The OverlayObjects are cleared using the destructor of OverlayObjectList.
+ // That destructor calls clear() at the list which removes all objects from the
+ // OverlayManager and deletes them.
+
+ void SetPosition(const basegfx::B2DPoint& rNewPosition);
+};
+
+ImplPageOriginOverlay::ImplPageOriginOverlay(const SdrPaintView& rView, const basegfx::B2DPoint& rStartPos)
+: maPosition(rStartPos)
+{
+ for(sal_uInt32 a(0); a < rView.PaintWindowCount(); a++)
+ {
+ SdrPaintWindow* pCandidate = rView.GetPaintWindow(a);
+ const rtl::Reference< sdr::overlay::OverlayManager >& xTargetOverlay = pCandidate->GetOverlayManager();
+
+ if (xTargetOverlay.is())
+ {
+ std::unique_ptr<sdr::overlay::OverlayCrosshairStriped> aNew(new sdr::overlay::OverlayCrosshairStriped(
+ maPosition));
+ xTargetOverlay->add(*aNew);
+ maObjects.append(std::move(aNew));
+ }
+ }
+}
+
+void ImplPageOriginOverlay::SetPosition(const basegfx::B2DPoint& rNewPosition)
+{
+ if(rNewPosition == maPosition)
+ return;
+
+ // apply to OverlayObjects
+ for(sal_uInt32 a(0); a < maObjects.count(); a++)
+ {
+ sdr::overlay::OverlayCrosshairStriped* pCandidate =
+ static_cast< sdr::overlay::OverlayCrosshairStriped* >(&maObjects.getOverlayObject(a));
+
+ if(pCandidate)
+ {
+ pCandidate->setBasePosition(rNewPosition);
+ }
+ }
+
+ // remember new position
+ maPosition = rNewPosition;
+}
+
+
+class ImplHelpLineOverlay
+{
+ // The OverlayObjects
+ sdr::overlay::OverlayObjectList maObjects;
+
+ // The current position in logical coordinates
+ basegfx::B2DPoint maPosition;
+
+ // HelpLine specific stuff
+ SdrPageView* mpPageView;
+ sal_uInt16 mnHelpLineNumber;
+ SdrHelpLineKind meHelpLineKind;
+
+public:
+ ImplHelpLineOverlay(const SdrPaintView& rView, const basegfx::B2DPoint& rStartPos,
+ SdrPageView* pPageView, sal_uInt16 nHelpLineNumber, SdrHelpLineKind eKind);
+
+ // The OverlayObjects are cleared using the destructor of OverlayObjectList.
+ // That destructor calls clear() at the list which removes all objects from the
+ // OverlayManager and deletes them.
+
+ void SetPosition(const basegfx::B2DPoint& rNewPosition);
+
+ // access to HelpLine specific stuff
+ SdrPageView* GetPageView() const { return mpPageView; }
+ sal_uInt16 GetHelpLineNumber() const { return mnHelpLineNumber; }
+ SdrHelpLineKind GetHelpLineKind() const { return meHelpLineKind; }
+};
+
+ImplHelpLineOverlay::ImplHelpLineOverlay(
+ const SdrPaintView& rView, const basegfx::B2DPoint& rStartPos,
+ SdrPageView* pPageView, sal_uInt16 nHelpLineNumber, SdrHelpLineKind eKind)
+: maPosition(rStartPos),
+ mpPageView(pPageView),
+ mnHelpLineNumber(nHelpLineNumber),
+ meHelpLineKind(eKind)
+{
+ for(sal_uInt32 a(0); a < rView.PaintWindowCount(); a++)
+ {
+ SdrPaintWindow* pCandidate = rView.GetPaintWindow(a);
+ const rtl::Reference< sdr::overlay::OverlayManager >& xTargetOverlay = pCandidate->GetOverlayManager();
+
+ if (xTargetOverlay.is())
+ {
+ std::unique_ptr<sdr::overlay::OverlayHelplineStriped> aNew(new sdr::overlay::OverlayHelplineStriped(
+ maPosition, meHelpLineKind));
+ xTargetOverlay->add(*aNew);
+ maObjects.append(std::move(aNew));
+ }
+ }
+}
+
+void ImplHelpLineOverlay::SetPosition(const basegfx::B2DPoint& rNewPosition)
+{
+ if(rNewPosition == maPosition)
+ return;
+
+ // apply to OverlayObjects
+ for(sal_uInt32 a(0); a < maObjects.count(); a++)
+ {
+ sdr::overlay::OverlayHelplineStriped* pCandidate =
+ static_cast< sdr::overlay::OverlayHelplineStriped* >(&maObjects.getOverlayObject(a));
+
+ if(pCandidate)
+ {
+ pCandidate->setBasePosition(rNewPosition);
+ }
+ }
+
+ // remember new position
+ maPosition = rNewPosition;
+}
+
+SdrSnapView::SdrSnapView(
+ SdrModel& rSdrModel,
+ OutputDevice* pOut)
+: SdrPaintView(rSdrModel, pOut)
+ ,mpPageOriginOverlay(nullptr)
+ ,mpHelpLineOverlay(nullptr)
+ ,nMagnSizPix(4)
+ ,nSnapAngle(1500)
+ ,nEliminatePolyPointLimitAngle(0)
+ ,eCrookMode(SdrCrookMode::Rotate)
+ ,bSnapEnab(true)
+ ,bGridSnap(true)
+ ,bBordSnap(true)
+ ,bHlplSnap(true)
+ ,bOFrmSnap(true)
+ ,bOPntSnap(false)
+ ,bOConSnap(true)
+ ,bMoveSnapOnlyTopLeft(false)
+ ,bOrtho(false)
+ ,bBigOrtho(true)
+ ,bAngleSnapEnab(false)
+ ,bMoveOnlyDragging(false)
+ ,bSlantButShear(false)
+ ,bCrookNoContortion(false)
+ ,bEliminatePolyPoints(false)
+{
+}
+
+SdrSnapView::~SdrSnapView()
+{
+ BrkSetPageOrg();
+ BrkDragHelpLine();
+}
+
+
+bool SdrSnapView::IsAction() const
+{
+ return IsSetPageOrg() || IsDragHelpLine() || SdrPaintView::IsAction();
+}
+
+void SdrSnapView::MovAction(const Point& rPnt)
+{
+ SdrPaintView::MovAction(rPnt);
+ if (IsSetPageOrg()) {
+ MovSetPageOrg(rPnt);
+ }
+ if (IsDragHelpLine()) {
+ MovDragHelpLine(rPnt);
+ }
+}
+
+void SdrSnapView::EndAction()
+{
+ if (IsSetPageOrg()) {
+ EndSetPageOrg();
+ }
+ if (IsDragHelpLine()) {
+ EndDragHelpLine();
+ }
+ SdrPaintView::EndAction();
+}
+
+void SdrSnapView::BckAction()
+{
+ BrkSetPageOrg();
+ BrkDragHelpLine();
+ SdrPaintView::BckAction();
+}
+
+void SdrSnapView::BrkAction()
+{
+ BrkSetPageOrg();
+ BrkDragHelpLine();
+ SdrPaintView::BrkAction();
+}
+
+void SdrSnapView::TakeActionRect(tools::Rectangle& rRect) const
+{
+ if (IsSetPageOrg() || IsDragHelpLine()) {
+ rRect=tools::Rectangle(maDragStat.GetNow(),maDragStat.GetNow());
+ } else {
+ SdrPaintView::TakeActionRect(rRect);
+ }
+}
+
+
+Point SdrSnapView::GetSnapPos(const Point& rPnt, const SdrPageView* pPV) const
+{
+ Point aPt(rPnt);
+ SnapPos(aPt,pPV);
+ return aPt;
+}
+
+#define NOT_SNAPPED 0x7FFFFFFF
+SdrSnap SdrSnapView::SnapPos(Point& rPnt, const SdrPageView* pPV) const
+{
+ if (!bSnapEnab) return SdrSnap::NOTSNAPPED;
+ tools::Long x=rPnt.X();
+ tools::Long y=rPnt.Y();
+ if (pPV==nullptr) {
+ pPV=GetSdrPageView();
+ if (pPV==nullptr) return SdrSnap::NOTSNAPPED;
+ }
+
+ tools::Long dx=NOT_SNAPPED;
+ tools::Long dy=NOT_SNAPPED;
+ tools::Long dx1,dy1;
+ tools::Long mx=aMagnSiz.Width();
+ tools::Long my=aMagnSiz.Height();
+ if (mbHlplVisible && bHlplSnap && !IsDragHelpLine())
+ {
+ const SdrHelpLineList& rHLL=pPV->GetHelpLines();
+ sal_uInt16 nCount=rHLL.GetCount();
+ for (sal_uInt16 i=nCount; i>0;) {
+ i--;
+ const SdrHelpLine& rHL=rHLL[i];
+ const Point& rPos=rHL.GetPos();
+ switch (rHL.GetKind()) {
+ case SdrHelpLineKind::Vertical: {
+ tools::Long a=x-rPos.X();
+ if (std::abs(a)<=mx) { dx1=-a; if (std::abs(dx1)<std::abs(dx)) dx=dx1; }
+ } break;
+ case SdrHelpLineKind::Horizontal: {
+ tools::Long b=y-rPos.Y();
+ if (std::abs(b)<=my) { dy1=-b; if (std::abs(dy1)<std::abs(dy)) dy=dy1; }
+ } break;
+ case SdrHelpLineKind::Point: {
+ tools::Long a=x-rPos.X();
+ tools::Long b=y-rPos.Y();
+ if (std::abs(a)<=mx && std::abs(b)<=my) {
+ dx1=-a; dy1=-b;
+ if (std::abs(dx1)<std::abs(dx) && std::abs(dy1)<std::abs(dy)) { dx=dx1; dy=dy1; }
+ }
+ } break;
+ } // switch
+ }
+ }
+ if (mbBordVisible && bBordSnap) {
+ SdrPage* pPage=pPV->GetPage();
+ tools::Long xs=pPage->GetWidth();
+ tools::Long ys=pPage->GetHeight();
+ tools::Long lft=pPage->GetLeftBorder();
+ tools::Long rgt=pPage->GetRightBorder();
+ tools::Long upp=pPage->GetUpperBorder();
+ tools::Long lwr=pPage->GetLowerBorder();
+ tools::Long a;
+ a=x- lft ; if (std::abs(a)<=mx) { dx1=-a; if (std::abs(dx1)<std::abs(dx)) dx=dx1; } // left margin
+ a=x-(xs-rgt); if (std::abs(a)<=mx) { dx1=-a; if (std::abs(dx1)<std::abs(dx)) dx=dx1; } // right margin
+ a=x ; if (std::abs(a)<=mx) { dx1=-a; if (std::abs(dx1)<std::abs(dx)) dx=dx1; } // left edge of paper
+ a=x- xs ; if (std::abs(a)<=mx) { dx1=-a; if (std::abs(dx1)<std::abs(dx)) dx=dx1; } // right edge of paper
+ a=y- upp ; if (std::abs(a)<=my) { dy1=-a; if (std::abs(dy1)<std::abs(dy)) dy=dy1; } // left margin
+ a=y-(ys-lwr); if (std::abs(a)<=my) { dy1=-a; if (std::abs(dy1)<std::abs(dy)) dy=dy1; } // right margin
+ a=y ; if (std::abs(a)<=my) { dy1=-a; if (std::abs(dy1)<std::abs(dy)) dy=dy1; } // left edge of paper
+ a=y- ys ; if (std::abs(a)<=my) { dy1=-a; if (std::abs(dy1)<std::abs(dy)) dy=dy1; } // right edge of paper
+ }
+ if (bOFrmSnap || bOPntSnap) {
+ sal_uInt32 nMaxPointSnapCount=200;
+ sal_uInt32 nMaxFrameSnapCount=200;
+
+ // go back to SdrIterMode::DeepNoGroups runthrough for snap to object comparisons
+ SdrObjListIter aIter(pPV->GetPage(),SdrIterMode::DeepNoGroups,true);
+
+ while (aIter.IsMore() && (nMaxPointSnapCount>0 || nMaxFrameSnapCount>0)) {
+ SdrObject* pO=aIter.Next();
+ tools::Rectangle aRect(pO->GetCurrentBoundRect());
+ aRect.AdjustLeft( -mx );
+ aRect.AdjustRight(mx );
+ aRect.AdjustTop( -my );
+ aRect.AdjustBottom(my );
+ if (aRect.Contains(rPnt)) {
+ if (bOPntSnap && nMaxPointSnapCount>0)
+ {
+ sal_uInt32 nCount(pO->GetSnapPointCount());
+ for (sal_uInt32 i(0); i < nCount && nMaxPointSnapCount > 0; i++)
+ {
+ Point aP(pO->GetSnapPoint(i));
+ dx1=x-aP.X();
+ dy1=y-aP.Y();
+ if (std::abs(dx1)<=mx && std::abs(dy1)<=my && std::abs(dx1)<std::abs(dx) && std::abs(dy1)<std::abs(dy)) {
+ dx=-dx1;
+ dy=-dy1;
+ }
+ nMaxPointSnapCount--;
+ }
+ }
+ if (bOFrmSnap && nMaxFrameSnapCount>0) {
+ tools::Rectangle aLog(pO->GetSnapRect());
+ tools::Rectangle aR1(aLog);
+ aR1.AdjustLeft( -mx );
+ aR1.AdjustRight(mx );
+ aR1.AdjustTop( -my );
+ aR1.AdjustBottom(my );
+ if (aR1.Contains(rPnt)) {
+ if (std::abs(x-aLog.Left ())<=mx) { dx1=-(x-aLog.Left ()); if (std::abs(dx1)<std::abs(dx)) dx=dx1; }
+ if (std::abs(x-aLog.Right ())<=mx) { dx1=-(x-aLog.Right ()); if (std::abs(dx1)<std::abs(dx)) dx=dx1; }
+ if (std::abs(y-aLog.Top ())<=my) { dy1=-(y-aLog.Top ()); if (std::abs(dy1)<std::abs(dy)) dy=dy1; }
+ if (std::abs(y-aLog.Bottom())<=my) { dy1=-(y-aLog.Bottom()); if (std::abs(dy1)<std::abs(dy)) dy=dy1; }
+ }
+ nMaxFrameSnapCount--;
+ }
+ }
+ }
+ }
+ if(bGridSnap)
+ {
+ double fSnapWidth(aSnapWdtX);
+ if(dx == NOT_SNAPPED && fSnapWidth != 0.0)
+ {
+ double fx = static_cast<double>(x);
+
+ // round instead of trunc
+ if(fx - static_cast<double>(pPV->GetPageOrigin().X()) >= 0.0)
+ fx += fSnapWidth / 2.0;
+ else
+ fx -= fSnapWidth / 2.0;
+
+ x = static_cast<tools::Long>((fx - static_cast<double>(pPV->GetPageOrigin().X())) / fSnapWidth);
+ x = static_cast<tools::Long>(static_cast<double>(x) * fSnapWidth + static_cast<double>(pPV->GetPageOrigin().X()));
+ dx = 0;
+ }
+ fSnapWidth = double(aSnapWdtY);
+ if(dy == NOT_SNAPPED && fSnapWidth)
+ {
+ double fy = static_cast<double>(y);
+
+ // round instead of trunc
+ if(fy - static_cast<double>(pPV->GetPageOrigin().Y()) >= 0.0)
+ fy += fSnapWidth / 2.0;
+ else
+ fy -= fSnapWidth / 2.0;
+
+ y = static_cast<tools::Long>((fy - static_cast<double>(pPV->GetPageOrigin().Y())) / fSnapWidth);
+ y = static_cast<tools::Long>(static_cast<double>(y) * fSnapWidth + static_cast<double>(pPV->GetPageOrigin().Y()));
+ dy = 0;
+ }
+ }
+ SdrSnap bRet=SdrSnap::NOTSNAPPED;
+ if (dx==NOT_SNAPPED) dx=0; else bRet|=SdrSnap::XSNAPPED;
+ if (dy==NOT_SNAPPED) dy=0; else bRet|=SdrSnap::YSNAPPED;
+ rPnt.setX(x+dx );
+ rPnt.setY(y+dy );
+ return bRet;
+}
+
+void SdrSnapView::CheckSnap(const Point& rPt, tools::Long& nBestXSnap, tools::Long& nBestYSnap, bool& bXSnapped, bool& bYSnapped) const
+{
+ Point aPt(rPt);
+ SdrSnap nRet=SnapPos(aPt,nullptr);
+ aPt-=rPt;
+ if (nRet & SdrSnap::XSNAPPED) {
+ if (bXSnapped) {
+ if (std::abs(aPt.X())<std::abs(nBestXSnap)) {
+ nBestXSnap=aPt.X();
+ }
+ } else {
+ nBestXSnap=aPt.X();
+ bXSnapped=true;
+ }
+ }
+ if (nRet & SdrSnap::YSNAPPED) {
+ if (bYSnapped) {
+ if (std::abs(aPt.Y())<std::abs(nBestYSnap)) {
+ nBestYSnap=aPt.Y();
+ }
+ } else {
+ nBestYSnap=aPt.Y();
+ bYSnapped=true;
+ }
+ }
+}
+
+
+void SdrSnapView::BegSetPageOrg(const Point& rPnt)
+{
+ BrkAction();
+
+ DBG_ASSERT(nullptr == mpPageOriginOverlay, "SdrSnapView::BegSetPageOrg: There exists an ImplPageOriginOverlay (!)");
+ basegfx::B2DPoint aStartPos(rPnt.X(), rPnt.Y());
+ mpPageOriginOverlay = new ImplPageOriginOverlay(*this, aStartPos);
+ maDragStat.Reset(GetSnapPos(rPnt,nullptr));
+}
+
+void SdrSnapView::MovSetPageOrg(const Point& rPnt)
+{
+ if(IsSetPageOrg())
+ {
+ maDragStat.NextMove(GetSnapPos(rPnt,nullptr));
+ DBG_ASSERT(mpPageOriginOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
+ basegfx::B2DPoint aNewPos(maDragStat.GetNow().X(), maDragStat.GetNow().Y());
+ mpPageOriginOverlay->SetPosition(aNewPos);
+ }
+}
+
+void SdrSnapView::EndSetPageOrg()
+{
+ if(!IsSetPageOrg())
+ return;
+
+ SdrPageView* pPV = GetSdrPageView();
+
+ if(pPV)
+ {
+ Point aPnt(maDragStat.GetNow());
+ pPV->SetPageOrigin(aPnt);
+ }
+
+ // cleanup
+ BrkSetPageOrg();
+}
+
+void SdrSnapView::BrkSetPageOrg()
+{
+ if(IsSetPageOrg())
+ {
+ DBG_ASSERT(mpPageOriginOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
+ delete mpPageOriginOverlay;
+ mpPageOriginOverlay = nullptr;
+ }
+}
+
+
+bool SdrSnapView::PickHelpLine(const Point& rPnt, short nTol, const OutputDevice& rOut, sal_uInt16& rnHelpLineNum, SdrPageView*& rpPV) const
+{
+ rpPV=nullptr;
+ nTol=ImpGetHitTolLogic(nTol,&rOut);
+ SdrPageView* pPV = GetSdrPageView();
+
+ if(pPV)
+ {
+ Point aPnt(rPnt);
+ sal_uInt16 nIndex=pPV->GetHelpLines().HitTest(aPnt,sal_uInt16(nTol),rOut);
+ if (nIndex!=SDRHELPLINE_NOTFOUND) {
+ rpPV=pPV;
+ rnHelpLineNum=nIndex;
+ return true;
+ }
+ }
+ return false;
+}
+
+// start HelpLine drag for new HelpLine
+bool SdrSnapView::BegDragHelpLine(sal_uInt16 nHelpLineNum, SdrPageView* pPV)
+{
+ bool bRet(false);
+
+ BrkAction();
+
+ if(pPV && nHelpLineNum < pPV->GetHelpLines().GetCount())
+ {
+ const SdrHelpLineList& rHelpLines = pPV->GetHelpLines();
+ const SdrHelpLine& rHelpLine = rHelpLines[nHelpLineNum];
+ Point aHelpLinePos = rHelpLine.GetPos();
+ basegfx::B2DPoint aStartPos(aHelpLinePos.X(), aHelpLinePos.Y());
+
+ DBG_ASSERT(nullptr == mpHelpLineOverlay, "SdrSnapView::BegDragHelpLine: There exists an ImplHelpLineOverlay (!)");
+ mpHelpLineOverlay = new ImplHelpLineOverlay(*this, aStartPos, pPV, nHelpLineNum, rHelpLine.GetKind());
+
+ maDragStat.Reset(GetSnapPos(aHelpLinePos, pPV));
+ maDragStat.SetMinMove(ImpGetMinMovLogic(-3, nullptr));
+
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+// start HelpLine drag with existing HelpLine
+void SdrSnapView::BegDragHelpLine(const Point& rPnt, SdrHelpLineKind eNewKind)
+{
+ BrkAction();
+
+ if(GetSdrPageView())
+ {
+ DBG_ASSERT(nullptr == mpHelpLineOverlay, "SdrSnapView::BegDragHelpLine: There exists an ImplHelpLineOverlay (!)");
+ basegfx::B2DPoint aStartPos(rPnt.X(), rPnt.Y());
+ mpHelpLineOverlay = new ImplHelpLineOverlay(*this, aStartPos, nullptr, 0, eNewKind);
+ maDragStat.Reset(GetSnapPos(rPnt, nullptr));
+ }
+}
+
+PointerStyle SdrSnapView::GetDraggedHelpLinePointer() const
+{
+ if(IsDragHelpLine())
+ {
+ switch(mpHelpLineOverlay->GetHelpLineKind())
+ {
+ case SdrHelpLineKind::Vertical : return PointerStyle::ESize;
+ case SdrHelpLineKind::Horizontal: return PointerStyle::SSize;
+ default : return PointerStyle::Move;
+ }
+ }
+
+ return PointerStyle::Move;
+}
+
+void SdrSnapView::MovDragHelpLine(const Point& rPnt)
+{
+ if(IsDragHelpLine() && maDragStat.CheckMinMoved(rPnt))
+ {
+ Point aPnt(GetSnapPos(rPnt, nullptr));
+
+ if(aPnt != maDragStat.GetNow())
+ {
+ maDragStat.NextMove(aPnt);
+ DBG_ASSERT(mpHelpLineOverlay, "SdrSnapView::MovDragHelpLine: no ImplHelpLineOverlay (!)");
+ basegfx::B2DPoint aNewPos(maDragStat.GetNow().X(), maDragStat.GetNow().Y());
+ mpHelpLineOverlay->SetPosition(aNewPos);
+ }
+ }
+}
+
+bool SdrSnapView::EndDragHelpLine()
+{
+ bool bRet(false);
+
+ if(IsDragHelpLine())
+ {
+ if(maDragStat.IsMinMoved())
+ {
+ SdrPageView* pPageView = mpHelpLineOverlay->GetPageView();
+
+ if(pPageView)
+ {
+ // moved existing one
+ Point aPnt(maDragStat.GetNow());
+ const SdrHelpLineList& rHelpLines = pPageView->GetHelpLines();
+ SdrHelpLine aChangedHelpLine = rHelpLines[mpHelpLineOverlay->GetHelpLineNumber()];
+ aChangedHelpLine.SetPos(aPnt);
+ pPageView->SetHelpLine(mpHelpLineOverlay->GetHelpLineNumber(), aChangedHelpLine);
+
+ bRet = true;
+ }
+ else
+ {
+ // create new one
+ pPageView = GetSdrPageView();
+
+ if(pPageView)
+ {
+ Point aPnt(maDragStat.GetNow());
+ SdrHelpLine aNewHelpLine(mpHelpLineOverlay->GetHelpLineKind(), aPnt);
+ pPageView->InsertHelpLine(aNewHelpLine);
+
+ bRet = true;
+ }
+ }
+ }
+
+ // cleanup
+ BrkDragHelpLine();
+ }
+
+ return bRet;
+}
+
+void SdrSnapView::BrkDragHelpLine()
+{
+ if(IsDragHelpLine())
+ {
+ DBG_ASSERT(mpHelpLineOverlay, "SdrSnapView::EndDragHelpLine: no ImplHelpLineOverlay (!)");
+ delete mpHelpLineOverlay;
+ mpHelpLineOverlay = nullptr;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdtext.cxx b/svx/source/svdraw/svdtext.cxx
new file mode 100644
index 000000000..72591ef67
--- /dev/null
+++ b/svx/source/svdraw/svdtext.cxx
@@ -0,0 +1,154 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/svdotext.hxx>
+#include <svx/svdetc.hxx>
+#include <editeng/outlobj.hxx>
+#include <svx/svdoutl.hxx>
+#include <svx/svdmodel.hxx>
+#include <svl/itemset.hxx>
+#include <osl/diagnose.h>
+#include <libxml/xmlwriter.h>
+#include <memory>
+
+SdrText::SdrText( SdrTextObj& rObject )
+: mrObject( rObject )
+, mbPortionInfoChecked( false )
+{
+ OSL_ENSURE(&mrObject, "SdrText created without SdrTextObj (!)");
+}
+
+SdrText::~SdrText()
+{
+ clearWeak();
+}
+
+void SdrText::CheckPortionInfo( const SdrOutliner& rOutliner )
+{
+ if(mbPortionInfoChecked)
+ return;
+
+ // #i102062# no action when the Outliner is the HitTestOutliner,
+ // this will remove WrongList info at the OPO
+ if(&rOutliner == &mrObject.getSdrModelFromSdrObject().GetHitTestOutliner())
+ return;
+
+ // TODO: optimization: we could create a BigTextObject
+ mbPortionInfoChecked=true;
+
+ if(mpOutlinerParaObject && rOutliner.ShouldCreateBigTextObject())
+ {
+ // #i102062# MemoryLeak closed
+ mpOutlinerParaObject = rOutliner.CreateParaObject();
+ }
+}
+
+void SdrText::ReformatText()
+{
+ mbPortionInfoChecked=false;
+ mpOutlinerParaObject->ClearPortionInfo();
+}
+
+const SfxItemSet& SdrText::GetItemSet() const
+{
+ return const_cast< SdrText* >(this)->GetObjectItemSet();
+}
+
+void SdrText::SetOutlinerParaObject( std::optional<OutlinerParaObject> pTextObject )
+{
+ // Update HitTestOutliner
+ const SdrTextObj* pTestObj(mrObject.getSdrModelFromSdrObject().GetHitTestOutliner().GetTextObj());
+
+ if(pTestObj)
+ if ( (!pTestObj->GetOutlinerParaObject() && !mpOutlinerParaObject)
+ || (pTestObj->GetOutlinerParaObject() && mpOutlinerParaObject && *pTestObj->GetOutlinerParaObject() == *mpOutlinerParaObject) )
+ {
+ mrObject.getSdrModelFromSdrObject().GetHitTestOutliner().SetTextObj(nullptr);
+ }
+
+ mpOutlinerParaObject = std::move(pTextObject);
+ mbPortionInfoChecked = false;
+}
+
+OutlinerParaObject* SdrText::GetOutlinerParaObject()
+{
+ return mpOutlinerParaObject ? &*mpOutlinerParaObject : nullptr;
+}
+
+const OutlinerParaObject* SdrText::GetOutlinerParaObject() const
+{
+ return mpOutlinerParaObject ? &*mpOutlinerParaObject : nullptr;
+}
+
+/** returns the current OutlinerParaObject and removes it from this instance */
+std::optional<OutlinerParaObject> SdrText::RemoveOutlinerParaObject()
+{
+ // Update HitTestOutliner
+ const SdrTextObj* pTestObj(mrObject.getSdrModelFromSdrObject().GetHitTestOutliner().GetTextObj());
+
+ if(pTestObj)
+ if ( (!pTestObj->GetOutlinerParaObject() && !mpOutlinerParaObject)
+ || (pTestObj->GetOutlinerParaObject() && mpOutlinerParaObject && *pTestObj->GetOutlinerParaObject() == *mpOutlinerParaObject) )
+ {
+ mrObject.getSdrModelFromSdrObject().GetHitTestOutliner().SetTextObj(nullptr);
+ }
+
+ std::optional<OutlinerParaObject> pOPO = std::move(mpOutlinerParaObject);
+ mbPortionInfoChecked = false;
+
+ return pOPO;
+}
+
+void SdrText::ForceOutlinerParaObject( OutlinerMode nOutlMode )
+{
+ if(mpOutlinerParaObject)
+ return;
+
+ std::unique_ptr<Outliner> pOutliner(
+ SdrMakeOutliner(
+ nOutlMode,
+ mrObject.getSdrModelFromSdrObject()));
+
+ if(pOutliner)
+ {
+ Outliner& aDrawOutliner(mrObject.getSdrModelFromSdrObject().GetDrawOutliner());
+ pOutliner->SetCalcFieldValueHdl( aDrawOutliner.GetCalcFieldValueHdl() );
+ pOutliner->SetStyleSheet( 0, GetStyleSheet());
+ SetOutlinerParaObject( pOutliner->CreateParaObject() );
+ }
+}
+
+const SfxItemSet& SdrText::GetObjectItemSet()
+{
+ return mrObject.GetObjectItemSet();
+}
+
+SfxStyleSheet* SdrText::GetStyleSheet() const
+{
+ return mrObject.GetStyleSheet();
+}
+
+void SdrText::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SdrText"));
+ mpOutlinerParaObject->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdtrans.cxx b/svx/source/svdraw/svdtrans.cxx
new file mode 100644
index 000000000..201e9f86d
--- /dev/null
+++ b/svx/source/svdraw/svdtrans.cxx
@@ -0,0 +1,867 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <svx/svdtrans.hxx>
+#include <math.h>
+#include <svx/xpoly.hxx>
+#include <rtl/ustrbuf.hxx>
+
+#include <vcl/virdev.hxx>
+#include <tools/bigint.hxx>
+#include <tools/UnitConversion.hxx>
+#include <unotools/syslocale.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <sal/log.hxx>
+
+void MoveXPoly(XPolygon& rPoly, const Size& S)
+{
+ rPoly.Move(S.Width(),S.Height());
+}
+
+void ResizeRect(tools::Rectangle& rRect, const Point& rRef, const Fraction& rxFact, const Fraction& ryFact)
+{
+ Fraction aXFact(rxFact);
+ Fraction aYFact(ryFact);
+
+ if (!aXFact.IsValid()) {
+ SAL_WARN( "svx.svdraw", "invalid fraction xFract, using Fraction(1,1)" );
+ aXFact = Fraction(1,1);
+ tools::Long nWdt = rRect.Right() - rRect.Left();
+ if (nWdt == 0) rRect.AdjustRight( 1 );
+ }
+ rRect.SetLeft( rRef.X() + FRound( (rRect.Left() - rRef.X()) * double(aXFact) ) );
+ rRect.SetRight( rRef.X() + FRound( (rRect.Right() - rRef.X()) * double(aXFact) ) );
+
+ if (!aYFact.IsValid()) {
+ SAL_WARN( "svx.svdraw", "invalid fraction yFract, using Fraction(1,1)" );
+ aYFact = Fraction(1,1);
+ tools::Long nHgt = rRect.Bottom() - rRect.Top();
+ if (nHgt == 0) rRect.AdjustBottom( 1 );
+ }
+ rRect.SetTop( rRef.Y() + FRound( (rRect.Top() - rRef.Y()) * double(aYFact) ) );
+ rRect.SetBottom( rRef.Y() + FRound( (rRect.Bottom() - rRef.Y()) * double(aYFact) ) );
+
+ rRect.Justify();
+}
+
+
+void ResizePoly(tools::Polygon& rPoly, const Point& rRef, const Fraction& xFact, const Fraction& yFact)
+{
+ sal_uInt16 nCount=rPoly.GetSize();
+ for (sal_uInt16 i=0; i<nCount; i++) {
+ ResizePoint(rPoly[i],rRef,xFact,yFact);
+ }
+}
+
+void ResizeXPoly(XPolygon& rPoly, const Point& rRef, const Fraction& xFact, const Fraction& yFact)
+{
+ sal_uInt16 nCount=rPoly.GetPointCount();
+ for (sal_uInt16 i=0; i<nCount; i++) {
+ ResizePoint(rPoly[i],rRef,xFact,yFact);
+ }
+}
+
+void RotatePoly(tools::Polygon& rPoly, const Point& rRef, double sn, double cs)
+{
+ sal_uInt16 nCount=rPoly.GetSize();
+ for (sal_uInt16 i=0; i<nCount; i++) {
+ RotatePoint(rPoly[i],rRef,sn,cs);
+ }
+}
+
+void RotateXPoly(XPolygon& rPoly, const Point& rRef, double sn, double cs)
+{
+ sal_uInt16 nCount=rPoly.GetPointCount();
+ for (sal_uInt16 i=0; i<nCount; i++) {
+ RotatePoint(rPoly[i],rRef,sn,cs);
+ }
+}
+
+void RotateXPoly(XPolyPolygon& rPoly, const Point& rRef, double sn, double cs)
+{
+ sal_uInt16 nCount=rPoly.Count();
+ for (sal_uInt16 i=0; i<nCount; i++) {
+ RotateXPoly(rPoly[i],rRef,sn,cs);
+ }
+}
+
+void MirrorPoint(Point& rPnt, const Point& rRef1, const Point& rRef2)
+{
+ tools::Long mx=rRef2.X()-rRef1.X();
+ tools::Long my=rRef2.Y()-rRef1.Y();
+ if (mx==0) { // vertical axis
+ tools::Long dx=rRef1.X()-rPnt.X();
+ rPnt.AdjustX(2*dx );
+ } else if (my==0) { // horizontal axis
+ tools::Long dy=rRef1.Y()-rPnt.Y();
+ rPnt.AdjustY(2*dy );
+ } else if (mx==my) { // diagonal axis '\'
+ tools::Long dx1=rPnt.X()-rRef1.X();
+ tools::Long dy1=rPnt.Y()-rRef1.Y();
+ rPnt.setX(rRef1.X()+dy1 );
+ rPnt.setY(rRef1.Y()+dx1 );
+ } else if (mx==-my) { // diagonal axis '/'
+ tools::Long dx1=rPnt.X()-rRef1.X();
+ tools::Long dy1=rPnt.Y()-rRef1.Y();
+ rPnt.setX(rRef1.X()-dy1 );
+ rPnt.setY(rRef1.Y()-dx1 );
+ } else { // arbitrary axis
+ // TODO: Optimize this! Raise perpendicular on the mirroring axis..?
+ Degree100 nRefAngle=GetAngle(rRef2-rRef1);
+ rPnt-=rRef1;
+ Degree100 nPntAngle=GetAngle(rPnt);
+ Degree100 nAngle=2_deg100*(nRefAngle-nPntAngle);
+ double a = toRadians(nAngle);
+ double nSin=sin(a);
+ double nCos=cos(a);
+ RotatePoint(rPnt,Point(),nSin,nCos);
+ rPnt+=rRef1;
+ }
+}
+
+void MirrorXPoly(XPolygon& rPoly, const Point& rRef1, const Point& rRef2)
+{
+ sal_uInt16 nCount=rPoly.GetPointCount();
+ for (sal_uInt16 i=0; i<nCount; i++) {
+ MirrorPoint(rPoly[i],rRef1,rRef2);
+ }
+}
+
+void ShearPoly(tools::Polygon& rPoly, const Point& rRef, double tn)
+{
+ sal_uInt16 nCount=rPoly.GetSize();
+ for (sal_uInt16 i=0; i<nCount; i++) {
+ ShearPoint(rPoly[i],rRef,tn);
+ }
+}
+
+void ShearXPoly(XPolygon& rPoly, const Point& rRef, double tn, bool bVShear)
+{
+ sal_uInt16 nCount=rPoly.GetPointCount();
+ for (sal_uInt16 i=0; i<nCount; i++) {
+ ShearPoint(rPoly[i],rRef,tn,bVShear);
+ }
+}
+
+double CrookRotateXPoint(Point& rPnt, Point* pC1, Point* pC2, const Point& rCenter,
+ const Point& rRad, double& rSin, double& rCos, bool bVert)
+{
+ bool bC1=pC1!=nullptr;
+ bool bC2=pC2!=nullptr;
+ tools::Long x0=rPnt.X();
+ tools::Long y0=rPnt.Y();
+ tools::Long cx=rCenter.X();
+ tools::Long cy=rCenter.Y();
+ double nAngle=GetCrookAngle(rPnt,rCenter,rRad,bVert);
+ double sn=sin(nAngle);
+ double cs=cos(nAngle);
+ RotatePoint(rPnt,rCenter,sn,cs);
+ if (bC1) {
+ if (bVert) {
+ // move into the direction of the center, as a basic position for the rotation
+ pC1->AdjustY( -y0 );
+ // resize, account for the distance from the center
+ pC1->setY(FRound(static_cast<double>(pC1->Y()) /rRad.X()*(cx-pC1->X())) );
+ pC1->AdjustY(cy );
+ } else {
+ // move into the direction of the center, as a basic position for the rotation
+ pC1->AdjustX( -x0 );
+ // resize, account for the distance from the center
+ tools::Long nPntRad=cy-pC1->Y();
+ double nFact=static_cast<double>(nPntRad)/static_cast<double>(rRad.Y());
+ pC1->setX(FRound(static_cast<double>(pC1->X())*nFact) );
+ pC1->AdjustX(cx );
+ }
+ RotatePoint(*pC1,rCenter,sn,cs);
+ }
+ if (bC2) {
+ if (bVert) {
+ // move into the direction of the center, as a basic position for the rotation
+ pC2->AdjustY( -y0 );
+ // resize, account for the distance from the center
+ pC2->setY(FRound(static_cast<double>(pC2->Y()) /rRad.X()*(rCenter.X()-pC2->X())) );
+ pC2->AdjustY(cy );
+ } else {
+ // move into the direction of the center, as a basic position for the rotation
+ pC2->AdjustX( -x0 );
+ // resize, account for the distance from the center
+ tools::Long nPntRad=rCenter.Y()-pC2->Y();
+ double nFact=static_cast<double>(nPntRad)/static_cast<double>(rRad.Y());
+ pC2->setX(FRound(static_cast<double>(pC2->X())*nFact) );
+ pC2->AdjustX(cx );
+ }
+ RotatePoint(*pC2,rCenter,sn,cs);
+ }
+ rSin=sn;
+ rCos=cs;
+ return nAngle;
+}
+
+double CrookSlantXPoint(Point& rPnt, Point* pC1, Point* pC2, const Point& rCenter,
+ const Point& rRad, double& rSin, double& rCos, bool bVert)
+{
+ bool bC1=pC1!=nullptr;
+ bool bC2=pC2!=nullptr;
+ tools::Long x0=rPnt.X();
+ tools::Long y0=rPnt.Y();
+ tools::Long dx1=0,dy1=0;
+ tools::Long dxC1=0,dyC1=0;
+ tools::Long dxC2=0,dyC2=0;
+ if (bVert) {
+ tools::Long nStart=rCenter.X()-rRad.X();
+ dx1=rPnt.X()-nStart;
+ rPnt.setX(nStart );
+ if (bC1) {
+ dxC1=pC1->X()-nStart;
+ pC1->setX(nStart );
+ }
+ if (bC2) {
+ dxC2=pC2->X()-nStart;
+ pC2->setX(nStart );
+ }
+ } else {
+ tools::Long nStart=rCenter.Y()-rRad.Y();
+ dy1=rPnt.Y()-nStart;
+ rPnt.setY(nStart );
+ if (bC1) {
+ dyC1=pC1->Y()-nStart;
+ pC1->setY(nStart );
+ }
+ if (bC2) {
+ dyC2=pC2->Y()-nStart;
+ pC2->setY(nStart );
+ }
+ }
+ double nAngle=GetCrookAngle(rPnt,rCenter,rRad,bVert);
+ double sn=sin(nAngle);
+ double cs=cos(nAngle);
+ RotatePoint(rPnt,rCenter,sn,cs);
+ if (bC1) { if (bVert) pC1->AdjustY( -(y0-rCenter.Y()) ); else pC1->AdjustX( -(x0-rCenter.X()) ); RotatePoint(*pC1,rCenter,sn,cs); }
+ if (bC2) { if (bVert) pC2->AdjustY( -(y0-rCenter.Y()) ); else pC2->AdjustX( -(x0-rCenter.X()) ); RotatePoint(*pC2,rCenter,sn,cs); }
+ if (bVert) {
+ rPnt.AdjustX(dx1 );
+ if (bC1) pC1->AdjustX(dxC1 );
+ if (bC2) pC2->AdjustX(dxC2 );
+ } else {
+ rPnt.AdjustY(dy1 );
+ if (bC1) pC1->AdjustY(dyC1 );
+ if (bC2) pC2->AdjustY(dyC2 );
+ }
+ rSin=sn;
+ rCos=cs;
+ return nAngle;
+}
+
+double CrookStretchXPoint(Point& rPnt, Point* pC1, Point* pC2, const Point& rCenter,
+ const Point& rRad, double& rSin, double& rCos, bool bVert,
+ const tools::Rectangle& rRefRect)
+{
+ tools::Long y0=rPnt.Y();
+ CrookSlantXPoint(rPnt,pC1,pC2,rCenter,rRad,rSin,rCos,bVert);
+ if (bVert) {
+ } else {
+ tools::Long nTop=rRefRect.Top();
+ tools::Long nBtm=rRefRect.Bottom();
+ tools::Long nHgt=nBtm-nTop;
+ tools::Long dy=rPnt.Y()-y0;
+ double a=static_cast<double>(y0-nTop)/nHgt;
+ a*=dy;
+ rPnt.setY(y0+FRound(a) );
+ }
+ return 0.0;
+}
+
+
+void CrookRotatePoly(XPolygon& rPoly, const Point& rCenter, const Point& rRad, bool bVert)
+{
+ double nSin,nCos;
+ sal_uInt16 nPointCnt=rPoly.GetPointCount();
+ sal_uInt16 i=0;
+ while (i<nPointCnt) {
+ Point* pPnt=&rPoly[i];
+ Point* pC1=nullptr;
+ Point* pC2=nullptr;
+ if (i+1<nPointCnt && rPoly.IsControl(i)) { // control point to the left
+ pC1=pPnt;
+ i++;
+ pPnt=&rPoly[i];
+ }
+ i++;
+ if (i<nPointCnt && rPoly.IsControl(i)) { // control point to the right
+ pC2=&rPoly[i];
+ i++;
+ }
+ CrookRotateXPoint(*pPnt,pC1,pC2,rCenter,rRad,nSin,nCos,bVert);
+ }
+}
+
+void CrookSlantPoly(XPolygon& rPoly, const Point& rCenter, const Point& rRad, bool bVert)
+{
+ double nSin,nCos;
+ sal_uInt16 nPointCnt=rPoly.GetPointCount();
+ sal_uInt16 i=0;
+ while (i<nPointCnt) {
+ Point* pPnt=&rPoly[i];
+ Point* pC1=nullptr;
+ Point* pC2=nullptr;
+ if (i+1<nPointCnt && rPoly.IsControl(i)) { // control point to the left
+ pC1=pPnt;
+ i++;
+ pPnt=&rPoly[i];
+ }
+ i++;
+ if (i<nPointCnt && rPoly.IsControl(i)) { // control point to the right
+ pC2=&rPoly[i];
+ i++;
+ }
+ CrookSlantXPoint(*pPnt,pC1,pC2,rCenter,rRad,nSin,nCos,bVert);
+ }
+}
+
+void CrookStretchPoly(XPolygon& rPoly, const Point& rCenter, const Point& rRad, bool bVert, const tools::Rectangle& rRefRect)
+{
+ double nSin,nCos;
+ sal_uInt16 nPointCnt=rPoly.GetPointCount();
+ sal_uInt16 i=0;
+ while (i<nPointCnt) {
+ Point* pPnt=&rPoly[i];
+ Point* pC1=nullptr;
+ Point* pC2=nullptr;
+ if (i+1<nPointCnt && rPoly.IsControl(i)) { // control point to the left
+ pC1=pPnt;
+ i++;
+ pPnt=&rPoly[i];
+ }
+ i++;
+ if (i<nPointCnt && rPoly.IsControl(i)) { // control point to the right
+ pC2=&rPoly[i];
+ i++;
+ }
+ CrookStretchXPoint(*pPnt,pC1,pC2,rCenter,rRad,nSin,nCos,bVert,rRefRect);
+ }
+}
+
+
+void CrookRotatePoly(XPolyPolygon& rPoly, const Point& rCenter, const Point& rRad, bool bVert)
+{
+ sal_uInt16 nPolyCount=rPoly.Count();
+ for (sal_uInt16 nPolyNum=0; nPolyNum<nPolyCount; nPolyNum++) {
+ CrookRotatePoly(rPoly[nPolyNum],rCenter,rRad,bVert);
+ }
+}
+
+void CrookSlantPoly(XPolyPolygon& rPoly, const Point& rCenter, const Point& rRad, bool bVert)
+{
+ sal_uInt16 nPolyCount=rPoly.Count();
+ for (sal_uInt16 nPolyNum=0; nPolyNum<nPolyCount; nPolyNum++) {
+ CrookSlantPoly(rPoly[nPolyNum],rCenter,rRad,bVert);
+ }
+}
+
+void CrookStretchPoly(XPolyPolygon& rPoly, const Point& rCenter, const Point& rRad, bool bVert, const tools::Rectangle& rRefRect)
+{
+ sal_uInt16 nPolyCount=rPoly.Count();
+ for (sal_uInt16 nPolyNum=0; nPolyNum<nPolyCount; nPolyNum++) {
+ CrookStretchPoly(rPoly[nPolyNum],rCenter,rRad,bVert,rRefRect);
+ }
+}
+
+
+Degree100 GetAngle(const Point& rPnt)
+{
+ Degree100 a;
+ if (rPnt.Y()==0) {
+ if (rPnt.X()<0) a=-18000_deg100;
+ } else if (rPnt.X()==0) {
+ if (rPnt.Y()>0) a=-9000_deg100;
+ else a=9000_deg100;
+ } else {
+ a = Degree100(FRound(basegfx::rad2deg<100>(atan2(static_cast<double>(-rPnt.Y()), static_cast<double>(rPnt.X())))));
+ }
+ return a;
+}
+
+Degree100 NormAngle18000(Degree100 a)
+{
+ while (a<-18000_deg100) a+=36000_deg100;
+ while (a>=18000_deg100) a-=36000_deg100;
+ return a;
+}
+
+Degree100 NormAngle36000(Degree100 a)
+{
+ a %= 36000_deg100;
+ if (a < 0_deg100)
+ a += 36000_deg100;
+ return a;
+}
+
+sal_uInt16 GetAngleSector(Degree100 nAngle) { return (NormAngle36000(nAngle) / 9000_deg100).get(); }
+
+tools::Long GetLen(const Point& rPnt)
+{
+ tools::Long x=std::abs(rPnt.X());
+ tools::Long y=std::abs(rPnt.Y());
+ if (x+y<0x8000) { // because 7FFF * 7FFF * 2 = 7FFE0002
+ x*=x;
+ y*=y;
+ x+=y;
+ x=FRound(sqrt(static_cast<double>(x)));
+ return x;
+ } else {
+ double nx=x;
+ double ny=y;
+ nx*=nx;
+ ny*=ny;
+ nx+=ny;
+ nx=sqrt(nx);
+ if (nx>0x7FFFFFFF) {
+ return 0x7FFFFFFF; // we can't go any further, for fear of an overrun!
+ } else {
+ return FRound(nx);
+ }
+ }
+}
+
+
+void GeoStat::RecalcSinCos()
+{
+ if (nRotationAngle==0_deg100) {
+ mfSinRotationAngle=0.0;
+ mfCosRotationAngle=1.0;
+ } else {
+ double a = toRadians(nRotationAngle);
+ mfSinRotationAngle=sin(a);
+ mfCosRotationAngle=cos(a);
+ }
+}
+
+void GeoStat::RecalcTan()
+{
+ if (nShearAngle==0_deg100) {
+ mfTanShearAngle=0.0;
+ } else {
+ double a = toRadians(nShearAngle);
+ mfTanShearAngle=tan(a);
+ }
+}
+
+
+tools::Polygon Rect2Poly(const tools::Rectangle& rRect, const GeoStat& rGeo)
+{
+ tools::Polygon aPol(5);
+ aPol[0]=rRect.TopLeft();
+ aPol[1]=rRect.TopRight();
+ aPol[2]=rRect.BottomRight();
+ aPol[3]=rRect.BottomLeft();
+ aPol[4]=rRect.TopLeft();
+ if (rGeo.nShearAngle) ShearPoly(aPol,rRect.TopLeft(),rGeo.mfTanShearAngle);
+ if (rGeo.nRotationAngle) RotatePoly(aPol,rRect.TopLeft(),rGeo.mfSinRotationAngle,rGeo.mfCosRotationAngle);
+ return aPol;
+}
+
+void Poly2Rect(const tools::Polygon& rPol, tools::Rectangle& rRect, GeoStat& rGeo)
+{
+ rGeo.nRotationAngle=GetAngle(rPol[1]-rPol[0]);
+ rGeo.nRotationAngle=NormAngle36000(rGeo.nRotationAngle);
+ // rotation successful
+ rGeo.RecalcSinCos();
+
+ Point aPt1(rPol[1]-rPol[0]);
+ if (rGeo.nRotationAngle) RotatePoint(aPt1,Point(0,0),-rGeo.mfSinRotationAngle,rGeo.mfCosRotationAngle); // -Sin to reverse rotation
+ tools::Long nWdt=aPt1.X();
+
+ Point aPt0(rPol[0]);
+ Point aPt3(rPol[3]-rPol[0]);
+ if (rGeo.nRotationAngle) RotatePoint(aPt3,Point(0,0),-rGeo.mfSinRotationAngle,rGeo.mfCosRotationAngle); // -Sin to reverse rotation
+ tools::Long nHgt=aPt3.Y();
+
+
+ Degree100 nShW=GetAngle(aPt3);
+ nShW-=27000_deg100; // ShearWink is measured against a vertical line
+ nShW=-nShW; // negating, because '+' is shearing clock-wise
+
+ bool bMirr=aPt3.Y()<0;
+ if (bMirr) { // "exchange of points" when mirroring
+ nHgt=-nHgt;
+ nShW+=18000_deg100;
+ aPt0=rPol[3];
+ }
+ nShW=NormAngle18000(nShW);
+ if (nShW<-9000_deg100 || nShW>9000_deg100) {
+ nShW=NormAngle18000(nShW+18000_deg100);
+ }
+ if (nShW<-SDRMAXSHEAR) nShW=-SDRMAXSHEAR; // limit ShearWinkel (shear angle) to +/- 89.00 deg
+ if (nShW>SDRMAXSHEAR) nShW=SDRMAXSHEAR;
+ rGeo.nShearAngle=nShW;
+ rGeo.RecalcTan();
+ Point aRU(aPt0);
+ aRU.AdjustX(nWdt );
+ aRU.AdjustY(nHgt );
+ rRect=tools::Rectangle(aPt0,aRU);
+}
+
+
+void OrthoDistance8(const Point& rPt0, Point& rPt, bool bBigOrtho)
+{
+ tools::Long dx=rPt.X()-rPt0.X();
+ tools::Long dy=rPt.Y()-rPt0.Y();
+ tools::Long dxa=std::abs(dx);
+ tools::Long dya=std::abs(dy);
+ if (dx==0 || dy==0 || dxa==dya) return;
+ if (dxa>=dya*2) { rPt.setY(rPt0.Y() ); return; }
+ if (dya>=dxa*2) { rPt.setX(rPt0.X() ); return; }
+ if ((dxa<dya) != bBigOrtho) {
+ rPt.setY(rPt0.Y()+(dxa* (dy>=0 ? 1 : -1) ) );
+ } else {
+ rPt.setX(rPt0.X()+(dya* (dx>=0 ? 1 : -1) ) );
+ }
+}
+
+void OrthoDistance4(const Point& rPt0, Point& rPt, bool bBigOrtho)
+{
+ tools::Long dx=rPt.X()-rPt0.X();
+ tools::Long dy=rPt.Y()-rPt0.Y();
+ tools::Long dxa=std::abs(dx);
+ tools::Long dya=std::abs(dy);
+ if ((dxa<dya) != bBigOrtho) {
+ rPt.setY(rPt0.Y()+(dxa* (dy>=0 ? 1 : -1) ) );
+ } else {
+ rPt.setX(rPt0.X()+(dya* (dx>=0 ? 1 : -1) ) );
+ }
+}
+
+
+tools::Long BigMulDiv(tools::Long nVal, tools::Long nMul, tools::Long nDiv)
+{
+ if (!nDiv)
+ return 0x7fffffff;
+ return BigInt::Scale(nVal, nMul, nDiv);
+}
+
+static FrPair toPair(o3tl::Length eFrom, o3tl::Length eTo)
+{
+ const auto& [nNum, nDen] = o3tl::getConversionMulDiv(eFrom, eTo);
+ return FrPair(nNum, nDen);
+}
+
+// How many eU units fit into a mm, respectively an inch?
+// Or: How many mm, respectively inches, are there in an eU (and then give me the inverse)
+
+static FrPair GetInchOrMM(MapUnit eU)
+{
+ switch (eU) {
+ case MapUnit::Map1000thInch: return toPair(o3tl::Length::in, o3tl::Length::in1000);
+ case MapUnit::Map100thInch : return toPair(o3tl::Length::in, o3tl::Length::in100);
+ case MapUnit::Map10thInch : return toPair(o3tl::Length::in, o3tl::Length::in10);
+ case MapUnit::MapInch : return toPair(o3tl::Length::in, o3tl::Length::in);
+ case MapUnit::MapPoint : return toPair(o3tl::Length::in, o3tl::Length::pt);
+ case MapUnit::MapTwip : return toPair(o3tl::Length::in, o3tl::Length::twip);
+ case MapUnit::Map100thMM : return toPair(o3tl::Length::mm, o3tl::Length::mm100);
+ case MapUnit::Map10thMM : return toPair(o3tl::Length::mm, o3tl::Length::mm10);
+ case MapUnit::MapMM : return toPair(o3tl::Length::mm, o3tl::Length::mm);
+ case MapUnit::MapCM : return toPair(o3tl::Length::mm, o3tl::Length::cm);
+ case MapUnit::MapPixel : {
+ ScopedVclPtrInstance< VirtualDevice > pVD;
+ pVD->SetMapMode(MapMode(MapUnit::Map100thMM));
+ Point aP(pVD->PixelToLogic(Point(64,64))); // 64 pixels for more accuracy
+ return FrPair(6400,aP.X(),6400,aP.Y());
+ }
+ case MapUnit::MapAppFont: case MapUnit::MapSysFont: {
+ ScopedVclPtrInstance< VirtualDevice > pVD;
+ pVD->SetMapMode(MapMode(eU));
+ Point aP(pVD->LogicToPixel(Point(32,32))); // 32 units for more accuracy
+ pVD->SetMapMode(MapMode(MapUnit::Map100thMM));
+ aP=pVD->PixelToLogic(aP);
+ return FrPair(3200,aP.X(),3200,aP.Y());
+ }
+ default: break;
+ }
+ return Fraction(1,1);
+}
+
+// Calculate the factor that we need to convert units from eS to eD.
+// e. g. GetMapFactor(UNIT_MM,UNIT_100TH_MM) => 100.
+
+FrPair GetMapFactor(MapUnit eS, MapUnit eD)
+{
+ if (eS==eD) return FrPair(1,1,1,1);
+ const auto eFrom = MapToO3tlLength(eS, o3tl::Length::invalid);
+ const auto eTo = MapToO3tlLength(eD, o3tl::Length::invalid);
+ if (eFrom != o3tl::Length::invalid && eTo != o3tl::Length::invalid)
+ return toPair(eFrom, eTo);
+ FrPair aS(GetInchOrMM(eS));
+ FrPair aD(GetInchOrMM(eD));
+ bool bSInch=IsInch(eS);
+ bool bDInch=IsInch(eD);
+ FrPair aRet(aD.X()/aS.X(),aD.Y()/aS.Y());
+ if (bSInch && !bDInch) { aRet.X()*=Fraction(127,5); aRet.Y()*=Fraction(127,5); }
+ if (!bSInch && bDInch) { aRet.X()*=Fraction(5,127); aRet.Y()*=Fraction(5,127); }
+ return aRet;
+};
+
+FrPair GetMapFactor(FieldUnit eS, FieldUnit eD)
+{
+ if (eS==eD) return FrPair(1,1,1,1);
+ auto eFrom = FieldToO3tlLength(eS), eTo = FieldToO3tlLength(eD);
+ if (eFrom == o3tl::Length::invalid)
+ {
+ if (eTo == o3tl::Length::invalid)
+ return FrPair(1,1,1,1);
+ eFrom = IsInch(eD) ? o3tl::Length::in : o3tl::Length::mm;
+ }
+ else if (eTo == o3tl::Length::invalid)
+ eTo = IsInch(eS) ? o3tl::Length::in : o3tl::Length::mm;
+ return toPair(eFrom, eTo);
+};
+
+void SdrFormatter::Undirty()
+{
+ const o3tl::Length eFrom = MapToO3tlLength(eSrcMU, o3tl::Length::invalid);
+ const o3tl::Length eTo = MapToO3tlLength(eDstMU, o3tl::Length::invalid);
+ if (eFrom != o3tl::Length::invalid && eTo != o3tl::Length::invalid)
+ {
+ const auto& [mul, div] = o3tl::getConversionMulDiv(eFrom, eTo);
+ sal_Int64 nMul = mul;
+ sal_Int64 nDiv = div;
+ short nComma = 0;
+
+ // shorten trailing zeros for dividend
+ while (0 == (nMul % 10))
+ {
+ nComma--;
+ nMul /= 10;
+ }
+
+ // shorten trailing zeros for divisor
+ while (0 == (nDiv % 10))
+ {
+ nComma++;
+ nDiv /= 10;
+ }
+ nMul_ = nMul;
+ nDiv_ = nDiv;
+ nComma_ = nComma;
+ }
+ else
+ {
+ nMul_ = nDiv_ = 1;
+ nComma_ = 0;
+ }
+ bDirty=false;
+}
+
+
+OUString SdrFormatter::GetStr(tools::Long nVal) const
+{
+ const OUString aNullCode("0");
+
+ if(!nVal)
+ {
+ return aNullCode;
+ }
+
+ // we may lose some decimal places here, because of MulDiv instead of Real
+ bool bNeg(nVal < 0);
+ SvtSysLocale aSysLoc;
+ const LocaleDataWrapper& rLoc = aSysLoc.GetLocaleData();
+
+ if (bDirty)
+ const_cast<SdrFormatter*>(this)->Undirty();
+
+ sal_Int16 nC(nComma_);
+
+ if(bNeg)
+ nVal = -nVal;
+
+ while(nC <= -3)
+ {
+ nVal *= 1000;
+ nC += 3;
+ }
+
+ while(nC <= -1)
+ {
+ nVal *= 10;
+ nC++;
+ }
+
+ if(nMul_ != nDiv_)
+ nVal = BigMulDiv(nVal, nMul_, nDiv_);
+
+ OUStringBuffer aStr = OUString::number(nVal);
+
+ if(nC > 0 && aStr.getLength() <= nC )
+ {
+ // decimal separator necessary
+ sal_Int32 nCount(nC - aStr.getLength());
+
+ if(nCount >= 0 && LocaleDataWrapper::isNumLeadingZero())
+ nCount++;
+
+ for(sal_Int32 i=0; i<nCount; i++)
+ aStr.insert(0, aNullCode);
+
+ // remove superfluous decimal points
+ sal_Int32 nNumDigits(LocaleDataWrapper::getNumDigits());
+ sal_Int32 nWeg(nC - nNumDigits);
+
+ if(nWeg > 0)
+ {
+ // TODO: we should round here
+ aStr.remove(aStr.getLength() - nWeg, nWeg);
+ nC = nNumDigits;
+ }
+ }
+
+ // remember everything before the decimal separator for later
+ sal_Int32 nForComma(aStr.getLength() - nC);
+
+ if(nC > 0)
+ {
+ // insert comma char (decimal separator)
+ // remove trailing zeros
+ while(nC > 0 && aStr[aStr.getLength() - 1] == aNullCode[0])
+ {
+ aStr.remove(aStr.getLength() - 1, 1);
+ nC--;
+ }
+
+ if(nC > 0)
+ {
+ // do we still have decimal places?
+ sal_Unicode cDec(rLoc.getNumDecimalSep()[0]);
+ aStr.insert(nForComma, cDec);
+ }
+ }
+
+ // add in thousands separator (if necessary)
+ if( nForComma > 3 )
+ {
+ const OUString& aThoSep( rLoc.getNumThousandSep() );
+ if ( aThoSep.getLength() > 0 )
+ {
+ sal_Unicode cTho( aThoSep[0] );
+ sal_Int32 i(nForComma - 3);
+
+ while(i > 0)
+ {
+ aStr.insert(i, cTho);
+ i -= 3;
+ }
+ }
+ }
+
+ if(aStr.isEmpty())
+ aStr.append(aNullCode);
+
+ if(bNeg && (aStr.getLength() > 1 || aStr[0] != aNullCode[0]))
+ {
+ aStr.insert(0, "-");
+ }
+
+ return aStr.makeStringAndClear();
+}
+
+OUString SdrFormatter::GetUnitStr(MapUnit eUnit)
+{
+ switch(eUnit)
+ {
+ // metrically
+ case MapUnit::Map100thMM :
+ return "/100mm";
+ case MapUnit::Map10thMM :
+ return "/10mm";
+ case MapUnit::MapMM :
+ return "mm";
+ case MapUnit::MapCM :
+ return "cm";
+
+ // Inch
+ case MapUnit::Map1000thInch:
+ return "/1000\"";
+ case MapUnit::Map100thInch :
+ return "/100\"";
+ case MapUnit::Map10thInch :
+ return "/10\"";
+ case MapUnit::MapInch :
+ return "\"";
+ case MapUnit::MapPoint :
+ return "pt";
+ case MapUnit::MapTwip :
+ return "twip";
+
+ // others
+ case MapUnit::MapPixel :
+ return "pixel";
+ case MapUnit::MapSysFont :
+ return "sysfont";
+ case MapUnit::MapAppFont :
+ return "appfont";
+ case MapUnit::MapRelative :
+ return "%";
+ default:
+ return OUString();
+ }
+}
+
+OUString SdrFormatter::GetUnitStr(FieldUnit eUnit)
+{
+ switch(eUnit)
+ {
+ default :
+ case FieldUnit::NONE :
+ case FieldUnit::CUSTOM :
+ return OUString();
+
+ // metrically
+ case FieldUnit::MM_100TH:
+ return "/100mm";
+ case FieldUnit::MM :
+ return "mm";
+ case FieldUnit::CM :
+ return "cm";
+ case FieldUnit::M :
+ return "m";
+ case FieldUnit::KM :
+ return "km";
+
+ // Inch
+ case FieldUnit::TWIP :
+ return "twip";
+ case FieldUnit::POINT :
+ return "pt";
+ case FieldUnit::PICA :
+ return "pica";
+ case FieldUnit::INCH :
+ return "\"";
+ case FieldUnit::FOOT :
+ return "ft";
+ case FieldUnit::MILE :
+ return "mile(s)";
+
+ // others
+ case FieldUnit::PERCENT:
+ return "%";
+ }
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdundo.cxx b/svx/source/svdraw/svdundo.cxx
new file mode 100644
index 000000000..15c690427
--- /dev/null
+++ b/svx/source/svdraw/svdundo.cxx
@@ -0,0 +1,1834 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/drawing/FillStyle.hpp>
+
+#include <svx/svdundo.hxx>
+#include <svx/svdotext.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdlayer.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdview.hxx>
+#include <svx/xbtmpit.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include <svx/scene3d.hxx>
+#include <editeng/outlobj.hxx>
+#include <svx/svdogrp.hxx>
+#include <sdr/properties/itemsettools.hxx>
+#include <svx/sdr/properties/properties.hxx>
+#include <svx/svdocapt.hxx>
+#include <svl/whiter.hxx>
+#include <svx/e3dsceneupdater.hxx>
+#include <svx/svdviter.hxx>
+#include <svx/svdotable.hxx> // #i124389#
+#include <vcl/svapp.hxx>
+#include <sfx2/viewsh.hxx>
+#include <svx/svdoashp.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <svx/diagram/datamodel.hxx>
+#include <svx/diagram/IDiagramHelper.hxx>
+
+
+// iterates over all views and unmarks this SdrObject if it is marked
+static void ImplUnmarkObject( SdrObject* pObj )
+{
+ SdrViewIter aIter( pObj );
+ for ( SdrView* pView = aIter.FirstView(); pView; pView = aIter.NextView() )
+ {
+ pView->MarkObj( pObj, pView->GetSdrPageView(), true );
+ }
+}
+
+SdrUndoAction::SdrUndoAction(SdrModel& rNewMod)
+ : rMod(rNewMod), m_nViewShellId(-1)
+{
+ if (SfxViewShell* pViewShell = SfxViewShell::Current())
+ m_nViewShellId = pViewShell->GetViewShellId();
+}
+
+SdrUndoAction::~SdrUndoAction() {}
+
+bool SdrUndoAction::CanRepeat(SfxRepeatTarget& rView) const
+{
+ SdrView* pV=dynamic_cast<SdrView*>( &rView );
+ if (pV!=nullptr) return CanSdrRepeat(*pV);
+ return false;
+}
+
+void SdrUndoAction::Repeat(SfxRepeatTarget& rView)
+{
+ SdrView* pV=dynamic_cast<SdrView*>( &rView );
+ if (pV!=nullptr) SdrRepeat(*pV);
+ DBG_ASSERT(pV!=nullptr,"Repeat: SfxRepeatTarget that was handed over is not a SdrView");
+}
+
+OUString SdrUndoAction::GetRepeatComment(SfxRepeatTarget& rView) const
+{
+ SdrView* pV=dynamic_cast<SdrView*>( &rView );
+ if (pV!=nullptr) return GetSdrRepeatComment();
+ return OUString();
+}
+
+bool SdrUndoAction::CanSdrRepeat(SdrView& /*rView*/) const
+{
+ return false;
+}
+
+void SdrUndoAction::SdrRepeat(SdrView& /*rView*/)
+{
+}
+
+OUString SdrUndoAction::GetSdrRepeatComment() const
+{
+ return OUString();
+}
+
+ViewShellId SdrUndoAction::GetViewShellId() const
+{
+ return m_nViewShellId;
+}
+
+SdrUndoGroup::SdrUndoGroup(SdrModel& rNewMod)
+: SdrUndoAction(rNewMod),
+ eFunction(SdrRepeatFunc::NONE)
+{}
+
+SdrUndoGroup::~SdrUndoGroup()
+{
+}
+
+void SdrUndoGroup::AddAction(std::unique_ptr<SdrUndoAction> pAct)
+{
+ maActions.push_back(std::move(pAct));
+}
+
+void SdrUndoGroup::Undo()
+{
+ for (auto it = maActions.rbegin(); it != maActions.rend(); ++it)
+ (*it)->Undo();
+}
+
+void SdrUndoGroup::Redo()
+{
+ for (std::unique_ptr<SdrUndoAction> & pAction : maActions)
+ pAction->Redo();
+}
+
+OUString SdrUndoGroup::GetComment() const
+{
+ return aComment.replaceAll("%1", aObjDescription);
+}
+
+bool SdrUndoGroup::CanSdrRepeat(SdrView& rView) const
+{
+ switch (eFunction)
+ {
+ case SdrRepeatFunc::NONE : return false;
+ case SdrRepeatFunc::Delete : return rView.AreObjectsMarked();
+ case SdrRepeatFunc::CombinePolyPoly: return rView.IsCombinePossible();
+ case SdrRepeatFunc::CombineOnePoly : return rView.IsCombinePossible(true);
+ case SdrRepeatFunc::DismantlePolys : return rView.IsDismantlePossible();
+ case SdrRepeatFunc::DismantleLines : return rView.IsDismantlePossible(true);
+ case SdrRepeatFunc::ConvertToPoly : return rView.IsConvertToPolyObjPossible();
+ case SdrRepeatFunc::ConvertToPath : return rView.IsConvertToPathObjPossible();
+ case SdrRepeatFunc::Group : return rView.IsGroupPossible();
+ case SdrRepeatFunc::Ungroup : return rView.IsUnGroupPossible();
+ case SdrRepeatFunc::PutToTop : return rView.IsToTopPossible();
+ case SdrRepeatFunc::PutToBottom : return rView.IsToBtmPossible();
+ case SdrRepeatFunc::MoveToTop : return rView.IsToTopPossible();
+ case SdrRepeatFunc::MoveToBottom : return rView.IsToBtmPossible();
+ case SdrRepeatFunc::ReverseOrder : return rView.IsReverseOrderPossible();
+ case SdrRepeatFunc::ImportMtf : return rView.IsImportMtfPossible();
+ default: break;
+ } // switch
+ return false;
+}
+
+void SdrUndoGroup::SdrRepeat(SdrView& rView)
+{
+ switch (eFunction)
+ {
+ case SdrRepeatFunc::NONE : break;
+ case SdrRepeatFunc::Delete : rView.DeleteMarked(); break;
+ case SdrRepeatFunc::CombinePolyPoly : rView.CombineMarkedObjects(false); break;
+ case SdrRepeatFunc::CombineOnePoly : rView.CombineMarkedObjects(); break;
+ case SdrRepeatFunc::DismantlePolys : rView.DismantleMarkedObjects(); break;
+ case SdrRepeatFunc::DismantleLines : rView.DismantleMarkedObjects(true); break;
+ case SdrRepeatFunc::ConvertToPoly : rView.ConvertMarkedToPolyObj(); break;
+ case SdrRepeatFunc::ConvertToPath : rView.ConvertMarkedToPathObj(false); break;
+ case SdrRepeatFunc::Group : rView.GroupMarked(); break;
+ case SdrRepeatFunc::Ungroup : rView.UnGroupMarked(); break;
+ case SdrRepeatFunc::PutToTop : rView.PutMarkedToTop(); break;
+ case SdrRepeatFunc::PutToBottom : rView.PutMarkedToBtm(); break;
+ case SdrRepeatFunc::MoveToTop : rView.MovMarkedToTop(); break;
+ case SdrRepeatFunc::MoveToBottom : rView.MovMarkedToBtm(); break;
+ case SdrRepeatFunc::ReverseOrder : rView.ReverseOrderOfMarked(); break;
+ case SdrRepeatFunc::ImportMtf : rView.DoImportMarkedMtf(); break;
+ default: break;
+ } // switch
+}
+
+OUString SdrUndoGroup::GetSdrRepeatComment() const
+{
+ return aComment.replaceAll("%1", SvxResId(STR_ObjNameSingulPlural));
+}
+
+SdrUndoObj::SdrUndoObj(SdrObject& rNewObj)
+: SdrUndoAction(rNewObj.getSdrModelFromSdrObject())
+ ,pObj(&rNewObj)
+{
+}
+
+OUString SdrUndoObj::GetDescriptionStringForObject( const SdrObject& _rForObject, TranslateId pStrCacheID, bool bRepeat )
+{
+ const OUString rStr {SvxResId(pStrCacheID)};
+
+ const sal_Int32 nPos = rStr.indexOf("%1");
+ if (nPos < 0)
+ return rStr;
+
+ if (bRepeat)
+ return rStr.replaceAt(nPos, 2, SvxResId(STR_ObjNameSingulPlural));
+
+ return rStr.replaceAt(nPos, 2, _rForObject.TakeObjNameSingul());
+}
+
+OUString SdrUndoObj::ImpGetDescriptionStr(TranslateId pStrCacheID, bool bRepeat) const
+{
+ if ( pObj )
+ return GetDescriptionStringForObject( *pObj, pStrCacheID, bRepeat );
+ return OUString();
+}
+
+// common call method for possible change of the page when UNDO/REDO is triggered
+void SdrUndoObj::ImpShowPageOfThisObject()
+{
+ if(pObj && pObj->IsInserted() && pObj->getSdrPageFromSdrObject())
+ {
+ SdrHint aHint(SdrHintKind::SwitchToPage, *pObj, pObj->getSdrPageFromSdrObject());
+ pObj->getSdrModelFromSdrObject().Broadcast(aHint);
+ }
+}
+
+void SdrUndoAttrObj::ensureStyleSheetInStyleSheetPool(SfxStyleSheetBasePool& rStyleSheetPool, SfxStyleSheet& rSheet)
+{
+ SfxStyleSheetBase* pThere = rStyleSheetPool.Find(rSheet.GetName(), rSheet.GetFamily());
+
+ if(!pThere)
+ {
+ // re-insert remembered style which was removed in the meantime. To do this
+ // without assertion, do it without parent and set parent after insertion
+ const OUString aParent(rSheet.GetParent());
+
+ rSheet.SetParent(OUString());
+ rStyleSheetPool.Insert(&rSheet);
+ rSheet.SetParent(aParent);
+ }
+}
+
+SdrUndoAttrObj::SdrUndoAttrObj(SdrObject& rNewObj, bool bStyleSheet1, bool bSaveText)
+ : SdrUndoObj(rNewObj)
+ , bHaveToTakeRedoSet(true)
+{
+ bStyleSheet = bStyleSheet1;
+
+ SdrObjList* pOL = rNewObj.GetSubList();
+ bool bIsGroup(pOL!=nullptr && pOL->GetObjCount());
+ bool bIs3DScene(bIsGroup && dynamic_cast< E3dScene* >(pObj) != nullptr);
+
+ if(bIsGroup)
+ {
+ // it's a group object!
+ pUndoGroup.reset(new SdrUndoGroup(pObj->getSdrModelFromSdrObject()));
+ const size_t nObjCount(pOL->GetObjCount());
+
+ for(size_t nObjNum = 0; nObjNum < nObjCount; ++nObjNum)
+ {
+ pUndoGroup->AddAction(
+ std::make_unique<SdrUndoAttrObj>(*pOL->GetObj(nObjNum), bStyleSheet1));
+ }
+ }
+
+ if(bIsGroup && !bIs3DScene)
+ return;
+
+ moUndoSet.emplace( pObj->GetMergedItemSet() );
+
+ if(bStyleSheet)
+ mxUndoStyleSheet = pObj->GetStyleSheet();
+
+ if(bSaveText)
+ {
+ auto p = pObj->GetOutlinerParaObject();
+ if(p)
+ pTextUndo = *p;
+ }
+}
+
+SdrUndoAttrObj::~SdrUndoAttrObj()
+{
+ moUndoSet.reset();
+ moRedoSet.reset();
+ pUndoGroup.reset();
+ pTextUndo.reset();
+ pTextRedo.reset();
+}
+
+void SdrUndoAttrObj::Undo()
+{
+ E3DModifySceneSnapRectUpdater aUpdater(pObj);
+ bool bIs3DScene(dynamic_cast< E3dScene* >(pObj) != nullptr);
+
+ // Trigger PageChangeCall
+ ImpShowPageOfThisObject();
+
+ if(!pUndoGroup || bIs3DScene)
+ {
+ if(bHaveToTakeRedoSet)
+ {
+ bHaveToTakeRedoSet = false;
+
+ moRedoSet.emplace( pObj->GetMergedItemSet() );
+
+ if(bStyleSheet)
+ mxRedoStyleSheet = pObj->GetStyleSheet();
+
+ if(pTextUndo)
+ {
+ // #i8508#
+ auto p = pObj->GetOutlinerParaObject();
+ if(p)
+ pTextRedo = *p;
+ }
+ }
+
+ if(bStyleSheet)
+ {
+ mxRedoStyleSheet = pObj->GetStyleSheet();
+ SfxStyleSheet* pSheet = dynamic_cast< SfxStyleSheet* >(mxUndoStyleSheet.get());
+
+ if(pSheet && pObj->getSdrModelFromSdrObject().GetStyleSheetPool())
+ {
+ ensureStyleSheetInStyleSheetPool(*pObj->getSdrModelFromSdrObject().GetStyleSheetPool(), *pSheet);
+ pObj->SetStyleSheet(pSheet, true);
+ }
+ else
+ {
+ OSL_ENSURE(false, "OOps, something went wrong in SdrUndoAttrObj (!)");
+ }
+ }
+
+ sdr::properties::ItemChangeBroadcaster aItemChange(*pObj);
+
+ // Since ClearItem sets back everything to normal
+ // it also sets fit-to-size text to non-fit-to-size text and
+ // switches on autogrowheight (the default). That may lead to
+ // losing the geometry size info for the object when it is
+ // laid out again from AdjustTextFrameWidthAndHeight(). This makes
+ // rescuing the size of the object necessary.
+ const tools::Rectangle aSnapRect = pObj->GetSnapRect();
+ // SdrObjCustomShape::NbcSetSnapRect needs logic instead of snap rect
+ const tools::Rectangle aLogicRect = pObj->GetLogicRect();
+
+ if(moUndoSet)
+ {
+ if(dynamic_cast<const SdrCaptionObj*>( pObj) != nullptr)
+ {
+ // do a more smooth item deletion here, else the text
+ // rect will be reformatted, especially when information regarding
+ // vertical text is changed. When clearing only set items it's
+ // slower, but safer regarding such information (it's not changed
+ // usually)
+ SfxWhichIter aIter(*moUndoSet);
+ sal_uInt16 nWhich(aIter.FirstWhich());
+
+ while(nWhich)
+ {
+ if(SfxItemState::SET != aIter.GetItemState(false))
+ {
+ pObj->ClearMergedItem(nWhich);
+ }
+
+ nWhich = aIter.NextWhich();
+ }
+ }
+ else
+ {
+ pObj->ClearMergedItem();
+ }
+
+ pObj->SetMergedItemSet(*moUndoSet);
+ }
+
+ // Restore previous size here when it was changed.
+ if(aSnapRect != pObj->GetSnapRect())
+ {
+ if(dynamic_cast<const SdrObjCustomShape*>(pObj))
+ pObj->NbcSetSnapRect(aLogicRect);
+ else
+ pObj->NbcSetSnapRect(aSnapRect);
+ }
+
+ pObj->GetProperties().BroadcastItemChange(aItemChange);
+
+ if(pTextUndo)
+ {
+ pObj->SetOutlinerParaObject(*pTextUndo);
+ }
+ }
+
+ if(pUndoGroup)
+ {
+ pUndoGroup->Undo();
+ }
+}
+
+void SdrUndoAttrObj::Redo()
+{
+ E3DModifySceneSnapRectUpdater aUpdater(pObj);
+ bool bIs3DScene(dynamic_cast< E3dScene* >(pObj) != nullptr);
+
+ if(!pUndoGroup || bIs3DScene)
+ {
+ if(bStyleSheet)
+ {
+ mxUndoStyleSheet = pObj->GetStyleSheet();
+ SfxStyleSheet* pSheet = dynamic_cast< SfxStyleSheet* >(mxRedoStyleSheet.get());
+
+ if(pSheet && pObj->getSdrModelFromSdrObject().GetStyleSheetPool())
+ {
+ ensureStyleSheetInStyleSheetPool(*pObj->getSdrModelFromSdrObject().GetStyleSheetPool(), *pSheet);
+ pObj->SetStyleSheet(pSheet, true);
+ }
+ else
+ {
+ OSL_ENSURE(false, "OOps, something went wrong in SdrUndoAttrObj (!)");
+ }
+ }
+
+ sdr::properties::ItemChangeBroadcaster aItemChange(*pObj);
+
+ const tools::Rectangle aSnapRect = pObj->GetSnapRect();
+ const tools::Rectangle aLogicRect = pObj->GetLogicRect();
+
+ if(moRedoSet)
+ {
+ if(dynamic_cast<const SdrCaptionObj*>( pObj) != nullptr)
+ {
+ // do a more smooth item deletion here, else the text
+ // rect will be reformatted, especially when information regarding
+ // vertical text is changed. When clearing only set items it's
+ // slower, but safer regarding such information (it's not changed
+ // usually)
+ SfxWhichIter aIter(*moRedoSet);
+ sal_uInt16 nWhich(aIter.FirstWhich());
+
+ while(nWhich)
+ {
+ if(SfxItemState::SET != aIter.GetItemState(false))
+ {
+ pObj->ClearMergedItem(nWhich);
+ }
+
+ nWhich = aIter.NextWhich();
+ }
+ }
+ else
+ {
+ pObj->ClearMergedItem();
+ }
+
+ pObj->SetMergedItemSet(*moRedoSet);
+ }
+
+ // Restore previous size here when it was changed.
+ if(aSnapRect != pObj->GetSnapRect())
+ {
+ if(dynamic_cast<const SdrObjCustomShape*>(pObj))
+ pObj->NbcSetSnapRect(aLogicRect);
+ else
+ pObj->NbcSetSnapRect(aSnapRect);
+ }
+
+ pObj->GetProperties().BroadcastItemChange(aItemChange);
+
+ // #i8508#
+ if(pTextRedo)
+ {
+ pObj->SetOutlinerParaObject(*pTextRedo);
+ }
+ }
+
+ if(pUndoGroup)
+ {
+ pUndoGroup->Redo();
+ }
+
+ // Trigger PageChangeCall
+ ImpShowPageOfThisObject();
+}
+
+OUString SdrUndoAttrObj::GetComment() const
+{
+ if(bStyleSheet)
+ {
+ return ImpGetDescriptionStr(STR_EditSetStylesheet);
+ }
+ else
+ {
+ return ImpGetDescriptionStr(STR_EditSetAttributes);
+ }
+}
+
+OUString SdrUndoAttrObj::GetSdrRepeatComment() const
+{
+ if(bStyleSheet)
+ {
+ return ImpGetDescriptionStr(STR_EditSetStylesheet, true);
+ }
+ else
+ {
+ return ImpGetDescriptionStr(STR_EditSetAttributes, true);
+ }
+}
+
+
+SdrUndoMoveObj::~SdrUndoMoveObj() {}
+
+void SdrUndoMoveObj::Undo()
+{
+ // Trigger PageChangeCall
+ ImpShowPageOfThisObject();
+
+ pObj->Move(Size(-aDistance.Width(),-aDistance.Height()));
+}
+
+void SdrUndoMoveObj::Redo()
+{
+ pObj->Move(Size(aDistance.Width(),aDistance.Height()));
+
+ // Trigger PageChangeCall
+ ImpShowPageOfThisObject();
+}
+
+OUString SdrUndoMoveObj::GetComment() const
+{
+ return ImpGetDescriptionStr(STR_EditMove);
+}
+
+void SdrUndoMoveObj::SdrRepeat(SdrView& rView)
+{
+ rView.MoveMarkedObj(aDistance);
+}
+
+bool SdrUndoMoveObj::CanSdrRepeat(SdrView& rView) const
+{
+ return rView.AreObjectsMarked();
+}
+
+OUString SdrUndoMoveObj::GetSdrRepeatComment() const
+{
+ return ImpGetDescriptionStr(STR_EditMove,true);
+}
+
+
+SdrUndoGeoObj::SdrUndoGeoObj(SdrObject& rNewObj)
+ : SdrUndoObj(rNewObj)
+ , mbSkipChangeLayout(false)
+{
+ SdrObjList* pOL=rNewObj.GetSubList();
+ if (pOL!=nullptr && pOL->GetObjCount() && dynamic_cast<const E3dScene* >( &rNewObj) == nullptr)
+ {
+ // this is a group object!
+ // If this were 3D scene, we'd only add an Undo for the scene itself
+ // (which we do elsewhere).
+ pUndoGroup.reset(new SdrUndoGroup(pObj->getSdrModelFromSdrObject()));
+ const size_t nObjCount = pOL->GetObjCount();
+ for (size_t nObjNum = 0; nObjNum<nObjCount; ++nObjNum) {
+ pUndoGroup->AddAction(std::make_unique<SdrUndoGeoObj>(*pOL->GetObj(nObjNum)));
+ }
+ }
+ else
+ {
+ pUndoGeo = pObj->GetGeoData();
+ }
+}
+
+SdrUndoGeoObj::~SdrUndoGeoObj()
+{
+ pUndoGeo.reset();
+ pRedoGeo.reset();
+ pUndoGroup.reset();
+}
+
+void SdrUndoGeoObj::Undo()
+{
+ // Trigger PageChangeCall
+ ImpShowPageOfThisObject();
+
+ if(pUndoGroup)
+ {
+ pUndoGroup->Undo();
+
+ // only repaint, no objectchange
+ pObj->ActionChanged();
+ }
+ else
+ {
+ pRedoGeo = pObj->GetGeoData();
+
+ auto pTableObj = dynamic_cast<sdr::table::SdrTableObj*>(pObj);
+ if (pTableObj && mbSkipChangeLayout)
+ pTableObj->SetSkipChangeLayout(true);
+ pObj->SetGeoData(*pUndoGeo);
+ if (pTableObj && mbSkipChangeLayout)
+ pTableObj->SetSkipChangeLayout(false);
+ }
+}
+
+void SdrUndoGeoObj::Redo()
+{
+ if(pUndoGroup)
+ {
+ pUndoGroup->Redo();
+
+ // only repaint, no objectchange
+ pObj->ActionChanged();
+ }
+ else
+ {
+ pUndoGeo = pObj->GetGeoData();
+ pObj->SetGeoData(*pRedoGeo);
+ }
+
+ // Trigger PageChangeCall
+ ImpShowPageOfThisObject();
+}
+
+OUString SdrUndoGeoObj::GetComment() const
+{
+ return ImpGetDescriptionStr(STR_DragMethObjOwn);
+}
+
+SdrUndoDiagramModelData::SdrUndoDiagramModelData(SdrObject& rNewObj, svx::diagram::DiagramDataStatePtr& rStartState)
+: SdrUndoObj(rNewObj)
+, m_aStartState(rStartState)
+, m_aEndState()
+{
+ if(rNewObj.isDiagram())
+ m_aEndState = rNewObj.getDiagramHelper()->extractDiagramDataState();
+}
+
+SdrUndoDiagramModelData::~SdrUndoDiagramModelData()
+{
+}
+
+void SdrUndoDiagramModelData::implUndoRedo(bool bUndo)
+{
+ if(nullptr == pObj)
+ return;
+
+ if(!pObj->isDiagram())
+ return;
+
+ pObj->getDiagramHelper()->applyDiagramDataState(
+ bUndo ? m_aStartState : m_aEndState);
+ pObj->getDiagramHelper()->reLayout(*static_cast<SdrObjGroup*>(pObj));
+}
+
+void SdrUndoDiagramModelData::Undo()
+{
+ implUndoRedo(true);
+}
+
+void SdrUndoDiagramModelData::Redo()
+{
+ implUndoRedo(false);
+}
+
+OUString SdrUndoDiagramModelData::GetComment() const
+{
+ return ImpGetDescriptionStr(STR_DiagramModelDataChange);
+}
+
+SdrUndoObjList::SdrUndoObjList(SdrObject& rNewObj, bool bOrdNumDirect)
+ : SdrUndoObj(rNewObj)
+ , bOwner(false)
+{
+ pObjList=pObj->getParentSdrObjListFromSdrObject();
+ if (bOrdNumDirect)
+ {
+ nOrdNum=pObj->GetOrdNumDirect();
+ }
+ else
+ {
+ nOrdNum=pObj->GetOrdNum();
+ }
+}
+
+SdrUndoObjList::~SdrUndoObjList()
+{
+ SolarMutexGuard aGuard;
+
+ if (pObj!=nullptr && IsOwner())
+ {
+ // Attribute have to go back to the regular Pool
+ SetOwner(false);
+
+ // now delete
+ SdrObject::Free( pObj );
+ }
+}
+
+void SdrUndoObjList::SetOwner(bool bNew)
+{
+ bOwner = bNew;
+}
+
+
+void SdrUndoRemoveObj::Undo()
+{
+ // Trigger PageChangeCall
+ ImpShowPageOfThisObject();
+
+ DBG_ASSERT(!pObj->IsInserted(),"UndoRemoveObj: pObj has already been inserted.");
+ if (pObj->IsInserted())
+ return;
+
+ // #i11426#
+ // For UNDOs in Calc/Writer it is necessary to adapt the anchor
+ // position of the target object.
+ Point aOwnerAnchorPos(0, 0);
+
+ if (dynamic_cast< const SdrObjGroup* >(pObjList->getSdrObjectFromSdrObjList()) != nullptr)
+ {
+ aOwnerAnchorPos = pObjList->getSdrObjectFromSdrObjList()->GetAnchorPos();
+ }
+
+ E3DModifySceneSnapRectUpdater aUpdater(pObjList->getSdrObjectFromSdrObjList());
+ pObjList->InsertObject(pObj,nOrdNum);
+
+ // #i11426#
+ if(aOwnerAnchorPos.X() || aOwnerAnchorPos.Y())
+ {
+ pObj->NbcSetAnchorPos(aOwnerAnchorPos);
+ }
+}
+
+void SdrUndoRemoveObj::Redo()
+{
+ DBG_ASSERT(pObj->IsInserted(),"RedoRemoveObj: pObj is not inserted.");
+ if (pObj->IsInserted())
+ {
+ ImplUnmarkObject( pObj );
+ E3DModifySceneSnapRectUpdater aUpdater(pObj);
+ pObjList->RemoveObject(pObj->GetOrdNum());
+ }
+
+ // Trigger PageChangeCall
+ ImpShowPageOfThisObject();
+}
+
+SdrUndoRemoveObj::~SdrUndoRemoveObj()
+{
+}
+
+
+void SdrUndoInsertObj::Undo()
+{
+ // Trigger PageChangeCall
+ ImpShowPageOfThisObject();
+
+ DBG_ASSERT(pObj->IsInserted(),"UndoInsertObj: pObj is not inserted.");
+ if (pObj->IsInserted())
+ {
+ ImplUnmarkObject( pObj );
+
+ SdrObject* pChkObj= pObjList->RemoveObject(pObj->GetOrdNum());
+ DBG_ASSERT(pChkObj==pObj,"UndoInsertObj: RemoveObjNum!=pObj");
+ }
+}
+
+void SdrUndoInsertObj::Redo()
+{
+ DBG_ASSERT(!pObj->IsInserted(),"RedoInsertObj: pObj is already inserted");
+ if (!pObj->IsInserted())
+ {
+ // Restore anchor position of an object,
+ // which becomes a member of a group, because its cleared in method
+ // <InsertObject(..)>. Needed for correct Redo in Writer. (#i45952#)
+ Point aAnchorPos( 0, 0 );
+
+ if (dynamic_cast<const SdrObjGroup*>(pObjList->getSdrObjectFromSdrObjList()) != nullptr)
+ {
+ aAnchorPos = pObj->GetAnchorPos();
+ }
+
+ pObjList->InsertObject(pObj,nOrdNum);
+
+ // Arcs lose position when grouped (#i45952#)
+ if ( aAnchorPos.X() || aAnchorPos.Y() )
+ {
+ pObj->NbcSetAnchorPos( aAnchorPos );
+ }
+ }
+
+ // Trigger PageChangeCall
+ ImpShowPageOfThisObject();
+}
+
+SdrUndoDelObj::SdrUndoDelObj(SdrObject& rNewObj, bool bOrdNumDirect)
+: SdrUndoRemoveObj(rNewObj,bOrdNumDirect)
+{
+ SetOwner(true);
+}
+
+void SdrUndoDelObj::Undo()
+{
+ SdrUndoRemoveObj::Undo();
+ DBG_ASSERT(IsOwner(),"UndoDeleteObj: pObj does not belong to UndoAction");
+ SetOwner(false);
+}
+
+void SdrUndoDelObj::Redo()
+{
+ SdrUndoRemoveObj::Redo();
+ DBG_ASSERT(!IsOwner(),"RedoDeleteObj: pObj already belongs to UndoAction");
+ SetOwner(true);
+}
+
+OUString SdrUndoDelObj::GetComment() const
+{
+ return ImpGetDescriptionStr(STR_EditDelete);
+}
+
+void SdrUndoDelObj::SdrRepeat(SdrView& rView)
+{
+ rView.DeleteMarked();
+}
+
+bool SdrUndoDelObj::CanSdrRepeat(SdrView& rView) const
+{
+ return rView.AreObjectsMarked();
+}
+
+OUString SdrUndoDelObj::GetSdrRepeatComment() const
+{
+ return ImpGetDescriptionStr(STR_EditDelete,true);
+}
+
+
+void SdrUndoNewObj::Undo()
+{
+ SdrUndoInsertObj::Undo();
+ DBG_ASSERT(!IsOwner(),"RedoNewObj: pObj already belongs to UndoAction");
+ SetOwner(true);
+}
+
+void SdrUndoNewObj::Redo()
+{
+ SdrUndoInsertObj::Redo();
+ DBG_ASSERT(IsOwner(),"RedoNewObj: pObj does not belong to UndoAction");
+ SetOwner(false);
+}
+
+OUString SdrUndoNewObj::GetComment( const SdrObject& _rForObject )
+{
+ return GetDescriptionStringForObject( _rForObject, STR_UndoInsertObj );
+}
+
+OUString SdrUndoNewObj::GetComment() const
+{
+ return ImpGetDescriptionStr(STR_UndoInsertObj);
+}
+
+SdrUndoReplaceObj::SdrUndoReplaceObj(SdrObject& rOldObj1, SdrObject& rNewObj1)
+ : SdrUndoObj(rOldObj1)
+ , bOldOwner(false)
+ , bNewOwner(false)
+ , pNewObj(&rNewObj1)
+{
+ SetOldOwner(true);
+ pObjList=pObj->getParentSdrObjListFromSdrObject();
+}
+
+SdrUndoReplaceObj::~SdrUndoReplaceObj()
+{
+ if (pObj!=nullptr && IsOldOwner())
+ {
+ // Attribute have to go back into the Pool
+ SetOldOwner(false);
+
+ // now delete
+ SdrObject::Free( pObj );
+ }
+ if (pNewObj!=nullptr && IsNewOwner())
+ {
+ // Attribute have to go back into the Pool
+ SetNewOwner(false);
+
+ // now delete
+ SdrObject::Free( pNewObj );
+ }
+}
+
+void SdrUndoReplaceObj::Undo()
+{
+ // Trigger PageChangeCall
+ ImpShowPageOfThisObject();
+
+ if (IsOldOwner() && !IsNewOwner())
+ {
+ DBG_ASSERT(!pObj->IsInserted(),"SdrUndoReplaceObj::Undo(): Old object is already inserted!");
+ DBG_ASSERT(pNewObj->IsInserted(),"SdrUndoReplaceObj::Undo(): New object is not inserted!");
+ SetOldOwner(false);
+ SetNewOwner(true);
+
+ ImplUnmarkObject( pNewObj );
+ pObjList->ReplaceObject(pObj,pNewObj->GetOrdNum());
+ }
+ else
+ {
+ OSL_FAIL("SdrUndoReplaceObj::Undo(): Wrong IsMine flags. Did you call Undo twice?");
+ }
+}
+
+void SdrUndoReplaceObj::Redo()
+{
+ if (!IsOldOwner() && IsNewOwner())
+ {
+ DBG_ASSERT(!pNewObj->IsInserted(),"SdrUndoReplaceObj::Redo(): New object is already inserted!!");
+ DBG_ASSERT(pObj->IsInserted(),"SdrUndoReplaceObj::Redo(): Old object is not inserted!!");
+ SetOldOwner(true);
+ SetNewOwner(false);
+
+ ImplUnmarkObject( pObj );
+ pObjList->ReplaceObject(pNewObj,pObj->GetOrdNum());
+
+ }
+ else
+ {
+ OSL_FAIL("SdrUndoReplaceObj::Redo(): Wrong IsMine flags. Did you call Redo twice?");
+ }
+
+ // Trigger PageChangeCall
+ ImpShowPageOfThisObject();
+}
+
+void SdrUndoReplaceObj::SetNewOwner(bool bNew)
+{
+ bNewOwner = bNew;
+}
+
+void SdrUndoReplaceObj::SetOldOwner(bool bNew)
+{
+ bOldOwner = bNew;
+}
+
+
+OUString SdrUndoCopyObj::GetComment() const
+{
+ return ImpGetDescriptionStr(STR_UndoCopyObj);
+}
+
+
+// #i11702#
+
+SdrUndoObjectLayerChange::SdrUndoObjectLayerChange(SdrObject& rObj, SdrLayerID aOldLayer, SdrLayerID aNewLayer)
+ : SdrUndoObj(rObj)
+ , maOldLayer(aOldLayer)
+ , maNewLayer(aNewLayer)
+{
+}
+
+void SdrUndoObjectLayerChange::Undo()
+{
+ ImpShowPageOfThisObject();
+ pObj->SetLayer(maOldLayer);
+}
+
+void SdrUndoObjectLayerChange::Redo()
+{
+ pObj->SetLayer(maNewLayer);
+ ImpShowPageOfThisObject();
+}
+
+
+SdrUndoObjOrdNum::SdrUndoObjOrdNum(SdrObject& rNewObj, sal_uInt32 nOldOrdNum1, sal_uInt32 nNewOrdNum1)
+ : SdrUndoObj(rNewObj)
+ , nOldOrdNum(nOldOrdNum1)
+ , nNewOrdNum(nNewOrdNum1)
+{
+}
+
+void SdrUndoObjOrdNum::Undo()
+{
+ // Trigger PageChangeCall
+ ImpShowPageOfThisObject();
+
+ SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
+ if (pOL==nullptr)
+ {
+ OSL_FAIL("UndoObjOrdNum: pObj does not have an ObjList.");
+ return;
+ }
+ pOL->SetObjectOrdNum(nNewOrdNum,nOldOrdNum);
+}
+
+void SdrUndoObjOrdNum::Redo()
+{
+ SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
+ if (pOL==nullptr)
+ {
+ OSL_FAIL("RedoObjOrdNum: pObj does not have an ObjList.");
+ return;
+ }
+ pOL->SetObjectOrdNum(nOldOrdNum,nNewOrdNum);
+
+ // Trigger PageChangeCall
+ ImpShowPageOfThisObject();
+}
+
+OUString SdrUndoObjOrdNum::GetComment() const
+{
+ return ImpGetDescriptionStr(STR_UndoObjOrdNum);
+}
+
+SdrUndoSort::SdrUndoSort(const SdrPage & rPage,
+ ::std::vector<sal_Int32> const& rSortOrder)
+ : SdrUndoAction(rPage.getSdrModelFromSdrPage())
+ , m_OldSortOrder(rSortOrder.size())
+ , m_NewSortOrder(rSortOrder)
+ , m_nPage(rPage.GetPageNum())
+{
+ // invert order
+ for (size_t i = 0; i < rSortOrder.size(); ++i)
+ {
+ m_OldSortOrder[rSortOrder[i]] = i;
+ }
+}
+
+void SdrUndoSort::Do(::std::vector<sal_Int32> & rSortOrder)
+{
+ SdrPage & rPage(*rMod.GetPage(m_nPage));
+ if (rPage.GetObjCount() != rSortOrder.size())
+ {
+ // can probably happen with sw's cursed SdrVirtObj mess - no good solution for that
+ SAL_WARN("svx", "SdrUndoSort size mismatch");
+ return;
+ }
+
+ // hopefully this can't throw
+ rPage.sort(rSortOrder);
+}
+
+void SdrUndoSort::Undo()
+{
+ Do(m_OldSortOrder);
+}
+
+void SdrUndoSort::Redo()
+{
+ Do(m_NewSortOrder);
+}
+
+OUString SdrUndoSort::GetComment() const
+{
+ return SvxResId(STR_SortShapes);
+}
+
+SdrUndoObjSetText::SdrUndoObjSetText(SdrObject& rNewObj, sal_Int32 nText)
+ : SdrUndoObj(rNewObj)
+ , bNewTextAvailable(false)
+ , bEmptyPresObj(false)
+ , mnText(nText)
+{
+ SdrText* pText = static_cast< SdrTextObj*>( &rNewObj )->getText(mnText);
+ if( pText && pText->GetOutlinerParaObject() )
+ pOldText = *pText->GetOutlinerParaObject();
+
+ bEmptyPresObj = rNewObj.IsEmptyPresObj();
+}
+
+SdrUndoObjSetText::~SdrUndoObjSetText()
+{
+ pOldText.reset();
+ pNewText.reset();
+}
+
+bool SdrUndoObjSetText::IsDifferent() const
+{
+ if (!pOldText || !pNewText)
+ return pOldText || pNewText;
+ return *pOldText != *pNewText;
+}
+
+void SdrUndoObjSetText::AfterSetText()
+{
+ if (!bNewTextAvailable)
+ {
+ SdrText* pText = static_cast< SdrTextObj*>( pObj )->getText(mnText);
+ if( pText && pText->GetOutlinerParaObject() )
+ pNewText = *pText->GetOutlinerParaObject();
+ bNewTextAvailable=true;
+ }
+}
+
+void SdrUndoObjSetText::Undo()
+{
+ // only works with SdrTextObj
+ SdrTextObj* pTarget = dynamic_cast< SdrTextObj* >(pObj);
+
+ if(!pTarget)
+ {
+ OSL_ENSURE(false, "SdrUndoObjSetText::Undo with SdrObject not based on SdrTextObj (!)");
+ return;
+ }
+
+ // Trigger PageChangeCall
+ ImpShowPageOfThisObject();
+
+ // save old text for Redo
+ if(!bNewTextAvailable)
+ {
+ AfterSetText();
+ }
+
+ SdrText* pText = pTarget->getText(mnText);
+ if (pText)
+ {
+ // copy text for Undo, because the original now belongs to SetOutlinerParaObject()
+ pTarget->NbcSetOutlinerParaObjectForText(pOldText, pText);
+ }
+
+ pTarget->SetEmptyPresObj(bEmptyPresObj);
+ pTarget->ActionChanged();
+
+ // #i124389# if it's a table, also need to relayout TextFrame
+ if(dynamic_cast< sdr::table::SdrTableObj* >(pTarget) != nullptr)
+ {
+ pTarget->NbcAdjustTextFrameWidthAndHeight();
+ }
+
+ // #i122410# SetOutlinerParaObject at SdrText does not trigger a
+ // BroadcastObjectChange, but it is needed to make evtl. SlideSorters
+ // update their preview.
+ pTarget->BroadcastObjectChange();
+}
+
+void SdrUndoObjSetText::Redo()
+{
+ // only works with SdrTextObj
+ SdrTextObj* pTarget = dynamic_cast< SdrTextObj* >(pObj);
+
+ if(!pTarget)
+ {
+ OSL_ENSURE(false, "SdrUndoObjSetText::Redo with SdrObject not based on SdrTextObj (!)");
+ return;
+ }
+
+ SdrText* pText = pTarget->getText(mnText);
+ if (pText)
+ {
+ // copy text for Undo, because the original now belongs to SetOutlinerParaObject()
+ pTarget->NbcSetOutlinerParaObjectForText( pNewText, pText );
+ }
+
+ pTarget->ActionChanged();
+
+ // #i124389# if it's a table, also need to relayout TextFrame
+ if(dynamic_cast< sdr::table::SdrTableObj* >(pTarget) != nullptr)
+ {
+ pTarget->NbcAdjustTextFrameWidthAndHeight();
+ }
+
+ // #i122410# NbcSetOutlinerParaObjectForText at SdrTextObj does not trigger a
+ // BroadcastObjectChange, but it is needed to make evtl. SlideSorters
+ // update their preview.
+ pTarget->BroadcastObjectChange();
+
+ // Trigger PageChangeCall
+ ImpShowPageOfThisObject();
+}
+
+OUString SdrUndoObjSetText::GetComment() const
+{
+ return ImpGetDescriptionStr(STR_UndoObjSetText);
+}
+
+OUString SdrUndoObjSetText::GetSdrRepeatComment() const
+{
+ return ImpGetDescriptionStr(STR_UndoObjSetText);
+}
+
+void SdrUndoObjSetText::SdrRepeat(SdrView& rView)
+{
+ if (!(bNewTextAvailable && rView.AreObjectsMarked()))
+ return;
+
+ const SdrMarkList& rML=rView.GetMarkedObjectList();
+
+ const bool bUndo = rView.IsUndoEnabled();
+ if( bUndo )
+ {
+ OUString aStr = ImpGetDescriptionStr(STR_UndoObjSetText);
+ rView.BegUndo(aStr);
+ }
+
+ const size_t nCount=rML.GetMarkCount();
+ for (size_t nm=0; nm<nCount; ++nm)
+ {
+ SdrObject* pObj2=rML.GetMark(nm)->GetMarkedSdrObj();
+ SdrTextObj* pTextObj=dynamic_cast<SdrTextObj*>( pObj2 );
+ if (pTextObj!=nullptr)
+ {
+ if( bUndo )
+ rView.AddUndo(std::make_unique<SdrUndoObjSetText>(*pTextObj,0));
+
+ pTextObj->SetOutlinerParaObject(pNewText);
+ }
+ }
+
+ if( bUndo )
+ rView.EndUndo();
+}
+
+bool SdrUndoObjSetText::CanSdrRepeat(SdrView& rView) const
+{
+ bool bOk = false;
+ if (bNewTextAvailable && rView.AreObjectsMarked()) {
+ bOk=true;
+ }
+ return bOk;
+}
+
+// Undo/Redo for setting object's name (#i73249#)
+SdrUndoObjStrAttr::SdrUndoObjStrAttr( SdrObject& rNewObj,
+ const ObjStrAttrType eObjStrAttr,
+ const OUString& sOldStr,
+ const OUString& sNewStr)
+ : SdrUndoObj( rNewObj )
+ , meObjStrAttr( eObjStrAttr )
+ , msOldStr( sOldStr )
+ , msNewStr( sNewStr )
+{
+}
+
+void SdrUndoObjStrAttr::Undo()
+{
+ ImpShowPageOfThisObject();
+
+ switch ( meObjStrAttr )
+ {
+ case ObjStrAttrType::Name:
+ pObj->SetName( msOldStr );
+ break;
+ case ObjStrAttrType::Title:
+ pObj->SetTitle( msOldStr );
+ break;
+ case ObjStrAttrType::Description:
+ pObj->SetDescription( msOldStr );
+ break;
+ }
+}
+
+void SdrUndoObjStrAttr::Redo()
+{
+ switch ( meObjStrAttr )
+ {
+ case ObjStrAttrType::Name:
+ pObj->SetName( msNewStr );
+ break;
+ case ObjStrAttrType::Title:
+ pObj->SetTitle( msNewStr );
+ break;
+ case ObjStrAttrType::Description:
+ pObj->SetDescription( msNewStr );
+ break;
+ }
+
+ ImpShowPageOfThisObject();
+}
+
+OUString SdrUndoObjStrAttr::GetComment() const
+{
+ OUString aStr;
+ switch ( meObjStrAttr )
+ {
+ case ObjStrAttrType::Name:
+ aStr = ImpGetDescriptionStr( STR_UndoObjName) +
+ " '" + msNewStr + "'";
+ break;
+ case ObjStrAttrType::Title:
+ aStr = ImpGetDescriptionStr( STR_UndoObjTitle );
+ break;
+ case ObjStrAttrType::Description:
+ aStr = ImpGetDescriptionStr( STR_UndoObjDescription );
+ break;
+ }
+
+ return aStr;
+}
+
+
+SdrUndoLayer::SdrUndoLayer(sal_uInt16 nLayerNum, SdrLayerAdmin& rNewLayerAdmin, SdrModel& rNewModel)
+ : SdrUndoAction(rNewModel)
+ , pLayer(rNewLayerAdmin.GetLayer(nLayerNum))
+ , pLayerAdmin(&rNewLayerAdmin)
+ , nNum(nLayerNum)
+ , bItsMine(false)
+{
+}
+
+SdrUndoLayer::~SdrUndoLayer()
+{
+ if (bItsMine)
+ {
+ delete pLayer;
+ }
+}
+
+
+void SdrUndoNewLayer::Undo()
+{
+ DBG_ASSERT(!bItsMine,"SdrUndoNewLayer::Undo(): Layer already belongs to UndoAction.");
+ bItsMine=true;
+ SdrLayer* pCmpLayer= pLayerAdmin->RemoveLayer(nNum).release();
+ DBG_ASSERT(pCmpLayer==pLayer,"SdrUndoNewLayer::Undo(): Removed layer is != pLayer.");
+}
+
+void SdrUndoNewLayer::Redo()
+{
+ DBG_ASSERT(bItsMine,"SdrUndoNewLayer::Undo(): Layer does not belong to UndoAction.");
+ bItsMine=false;
+ pLayerAdmin->InsertLayer(std::unique_ptr<SdrLayer>(pLayer),nNum);
+}
+
+OUString SdrUndoNewLayer::GetComment() const
+{
+ return SvxResId(STR_UndoNewLayer);
+}
+
+
+void SdrUndoDelLayer::Undo()
+{
+ DBG_ASSERT(bItsMine,"SdrUndoDelLayer::Undo(): Layer does not belong to UndoAction.");
+ bItsMine=false;
+ pLayerAdmin->InsertLayer(std::unique_ptr<SdrLayer>(pLayer),nNum);
+}
+
+void SdrUndoDelLayer::Redo()
+{
+ DBG_ASSERT(!bItsMine,"SdrUndoDelLayer::Undo(): Layer already belongs to UndoAction.");
+ bItsMine=true;
+ SdrLayer* pCmpLayer= pLayerAdmin->RemoveLayer(nNum).release();
+ DBG_ASSERT(pCmpLayer==pLayer,"SdrUndoDelLayer::Redo(): Removed layer is != pLayer.");
+}
+
+OUString SdrUndoDelLayer::GetComment() const
+{
+ return SvxResId(STR_UndoDelLayer);
+}
+
+
+SdrUndoPage::SdrUndoPage(SdrPage& rNewPg)
+: SdrUndoAction(rNewPg.getSdrModelFromSdrPage())
+ ,mxPage(&rNewPg)
+{
+}
+
+SdrUndoPage::~SdrUndoPage() {}
+
+void SdrUndoPage::ImpInsertPage(sal_uInt16 nNum)
+{
+ DBG_ASSERT(!mxPage->IsInserted(),"SdrUndoPage::ImpInsertPage(): mxPage is already inserted.");
+ if (!mxPage->IsInserted())
+ {
+ if (mxPage->IsMasterPage())
+ {
+ rMod.InsertMasterPage(mxPage.get(), nNum);
+ }
+ else
+ {
+ rMod.InsertPage(mxPage.get(), nNum);
+ }
+ }
+}
+
+void SdrUndoPage::ImpRemovePage(sal_uInt16 nNum)
+{
+ DBG_ASSERT(mxPage->IsInserted(),"SdrUndoPage::ImpRemovePage(): mxPage is not inserted.");
+ if (!mxPage->IsInserted())
+ return;
+
+ rtl::Reference<SdrPage> pChkPg;
+ if (mxPage->IsMasterPage())
+ {
+ pChkPg = rMod.RemoveMasterPage(nNum);
+ }
+ else
+ {
+ pChkPg = rMod.RemovePage(nNum);
+ }
+ DBG_ASSERT(pChkPg==mxPage,"SdrUndoPage::ImpRemovePage(): RemovePage!=mxPage");
+}
+
+void SdrUndoPage::ImpMovePage(sal_uInt16 nOldNum, sal_uInt16 nNewNum)
+{
+ DBG_ASSERT(mxPage->IsInserted(),"SdrUndoPage::ImpMovePage(): mxPage is not inserted.");
+ if (mxPage->IsInserted())
+ {
+ if (mxPage->IsMasterPage())
+ {
+ rMod.MoveMasterPage(nOldNum,nNewNum);
+ }
+ else
+ {
+ rMod.MovePage(nOldNum,nNewNum);
+ }
+ }
+}
+
+OUString SdrUndoPage::ImpGetDescriptionStr(TranslateId pStrCacheID)
+{
+ return SvxResId(pStrCacheID);
+}
+
+
+SdrUndoPageList::SdrUndoPageList(SdrPage& rNewPg)
+ : SdrUndoPage(rNewPg)
+{
+ nPageNum=rNewPg.GetPageNum();
+}
+
+SdrUndoPageList::~SdrUndoPageList()
+{
+}
+
+
+SdrUndoDelPage::SdrUndoDelPage(SdrPage& rNewPg)
+ : SdrUndoPageList(rNewPg)
+ , mbHasFillBitmap(false)
+{
+ // keep fill bitmap separately to remove it from pool if not used elsewhere
+ if (mxPage->IsMasterPage())
+ {
+ SfxStyleSheet* const pStyleSheet = mxPage->getSdrPageProperties().GetStyleSheet();
+ if (pStyleSheet)
+ queryFillBitmap(pStyleSheet->GetItemSet());
+ }
+ else
+ {
+ queryFillBitmap(mxPage->getSdrPageProperties().GetItemSet());
+ }
+ if (bool(mpFillBitmapItem))
+ clearFillBitmap();
+
+ // now remember the master page relationships
+ if(!mxPage->IsMasterPage())
+ return;
+
+ sal_uInt16 nPageCnt(rMod.GetPageCount());
+
+ for(sal_uInt16 nPageNum2(0); nPageNum2 < nPageCnt; nPageNum2++)
+ {
+ SdrPage* pDrawPage = rMod.GetPage(nPageNum2);
+
+ if(pDrawPage->TRG_HasMasterPage())
+ {
+ SdrPage& rMasterPage = pDrawPage->TRG_GetMasterPage();
+
+ if(mxPage.get() == &rMasterPage)
+ {
+ if(!pUndoGroup)
+ {
+ pUndoGroup.reset( new SdrUndoGroup(rMod) );
+ }
+
+ pUndoGroup->AddAction(rMod.GetSdrUndoFactory().CreateUndoPageRemoveMasterPage(*pDrawPage));
+ }
+ }
+ }
+}
+
+SdrUndoDelPage::~SdrUndoDelPage()
+{
+}
+
+void SdrUndoDelPage::Undo()
+{
+ if (bool(mpFillBitmapItem))
+ restoreFillBitmap();
+ ImpInsertPage(nPageNum);
+ if (pUndoGroup!=nullptr)
+ {
+ // recover master page relationships
+ pUndoGroup->Undo();
+ }
+}
+
+void SdrUndoDelPage::Redo()
+{
+ ImpRemovePage(nPageNum);
+ if (bool(mpFillBitmapItem))
+ clearFillBitmap();
+}
+
+OUString SdrUndoDelPage::GetComment() const
+{
+ return ImpGetDescriptionStr(STR_UndoDelPage);
+}
+
+OUString SdrUndoDelPage::GetSdrRepeatComment() const
+{
+ return ImpGetDescriptionStr(STR_UndoDelPage);
+}
+
+void SdrUndoDelPage::SdrRepeat(SdrView& /*rView*/)
+{
+}
+
+bool SdrUndoDelPage::CanSdrRepeat(SdrView& /*rView*/) const
+{
+ return false;
+}
+
+void SdrUndoDelPage::queryFillBitmap(const SfxItemSet& rItemSet)
+{
+ if (const XFillBitmapItem *pItem = rItemSet.GetItemIfSet(XATTR_FILLBITMAP, false))
+ mpFillBitmapItem.reset(pItem->Clone());
+ if (const XFillStyleItem *pItem = rItemSet.GetItemIfSet(XATTR_FILLSTYLE, false))
+ mbHasFillBitmap = pItem->GetValue() == css::drawing::FillStyle_BITMAP;
+}
+
+void SdrUndoDelPage::clearFillBitmap()
+{
+ if (mxPage->IsMasterPage())
+ {
+ SfxStyleSheet* const pStyleSheet = mxPage->getSdrPageProperties().GetStyleSheet();
+ assert(bool(pStyleSheet)); // who took away my stylesheet?
+ if (pStyleSheet->GetListenerCount() == 1)
+ {
+ SfxItemSet& rItemSet = pStyleSheet->GetItemSet();
+ rItemSet.ClearItem(XATTR_FILLBITMAP);
+ if (mbHasFillBitmap)
+ rItemSet.ClearItem(XATTR_FILLSTYLE);
+ }
+ }
+ else
+ {
+ SdrPageProperties &rPageProps = mxPage->getSdrPageProperties();
+ rPageProps.ClearItem(XATTR_FILLBITMAP);
+ if (mbHasFillBitmap)
+ rPageProps.ClearItem(XATTR_FILLSTYLE);
+ }
+}
+
+void SdrUndoDelPage::restoreFillBitmap()
+{
+ if (mxPage->IsMasterPage())
+ {
+ SfxStyleSheet* const pStyleSheet = mxPage->getSdrPageProperties().GetStyleSheet();
+ assert(bool(pStyleSheet)); // who took away my stylesheet?
+ if (pStyleSheet->GetListenerCount() == 1)
+ {
+ SfxItemSet& rItemSet = pStyleSheet->GetItemSet();
+ rItemSet.Put(*mpFillBitmapItem);
+ if (mbHasFillBitmap)
+ rItemSet.Put(XFillStyleItem(css::drawing::FillStyle_BITMAP));
+ }
+ }
+ else
+ {
+ SdrPageProperties &rPageProps = mxPage->getSdrPageProperties();
+ rPageProps.PutItem(*mpFillBitmapItem);
+ if (mbHasFillBitmap)
+ rPageProps.PutItem(XFillStyleItem(css::drawing::FillStyle_BITMAP));
+ }
+}
+
+
+void SdrUndoNewPage::Undo()
+{
+ ImpRemovePage(nPageNum);
+}
+
+void SdrUndoNewPage::Redo()
+{
+ ImpInsertPage(nPageNum);
+}
+
+OUString SdrUndoNewPage::GetComment() const
+{
+ return ImpGetDescriptionStr(STR_UndoNewPage);
+}
+
+
+OUString SdrUndoCopyPage::GetComment() const
+{
+ return ImpGetDescriptionStr(STR_UndoCopPage);
+}
+
+OUString SdrUndoCopyPage::GetSdrRepeatComment() const
+{
+ return ImpGetDescriptionStr(STR_UndoCopPage);
+}
+
+void SdrUndoCopyPage::SdrRepeat(SdrView& /*rView*/)
+{
+
+}
+
+bool SdrUndoCopyPage::CanSdrRepeat(SdrView& /*rView*/) const
+{
+ return false;
+}
+
+
+void SdrUndoSetPageNum::Undo()
+{
+ ImpMovePage(nNewPageNum,nOldPageNum);
+}
+
+void SdrUndoSetPageNum::Redo()
+{
+ ImpMovePage(nOldPageNum,nNewPageNum);
+}
+
+OUString SdrUndoSetPageNum::GetComment() const
+{
+ return ImpGetDescriptionStr(STR_UndoMovPage);
+}
+
+SdrUndoPageMasterPage::SdrUndoPageMasterPage(SdrPage& rChangedPage)
+ : SdrUndoPage(rChangedPage)
+ , mbOldHadMasterPage(mxPage->TRG_HasMasterPage())
+ , maOldMasterPageNumber(0)
+{
+ // get current state from page
+ if(mbOldHadMasterPage)
+ {
+ maOldSet = mxPage->TRG_GetMasterPageVisibleLayers();
+ maOldMasterPageNumber = mxPage->TRG_GetMasterPage().GetPageNum();
+ }
+}
+
+SdrUndoPageMasterPage::~SdrUndoPageMasterPage()
+{
+}
+
+SdrUndoPageRemoveMasterPage::SdrUndoPageRemoveMasterPage(SdrPage& rChangedPage)
+: SdrUndoPageMasterPage(rChangedPage)
+{
+}
+
+void SdrUndoPageRemoveMasterPage::Undo()
+{
+ if(mbOldHadMasterPage)
+ {
+ mxPage->TRG_SetMasterPage(*mxPage->getSdrModelFromSdrPage().GetMasterPage(maOldMasterPageNumber));
+ mxPage->TRG_SetMasterPageVisibleLayers(maOldSet);
+ }
+}
+
+void SdrUndoPageRemoveMasterPage::Redo()
+{
+ mxPage->TRG_ClearMasterPage();
+}
+
+OUString SdrUndoPageRemoveMasterPage::GetComment() const
+{
+ return ImpGetDescriptionStr(STR_UndoDelPageMasterDscr);
+}
+
+SdrUndoPageChangeMasterPage::SdrUndoPageChangeMasterPage(SdrPage& rChangedPage)
+ : SdrUndoPageMasterPage(rChangedPage)
+ , mbNewHadMasterPage(false)
+ , maNewMasterPageNumber(0)
+{
+}
+
+void SdrUndoPageChangeMasterPage::Undo()
+{
+ // remember values from new page
+ if(mxPage->TRG_HasMasterPage())
+ {
+ mbNewHadMasterPage = true;
+ maNewSet = mxPage->TRG_GetMasterPageVisibleLayers();
+ maNewMasterPageNumber = mxPage->TRG_GetMasterPage().GetPageNum();
+ }
+
+ // restore old values
+ if(mbOldHadMasterPage)
+ {
+ mxPage->TRG_ClearMasterPage();
+ mxPage->TRG_SetMasterPage(*mxPage->getSdrModelFromSdrPage().GetMasterPage(maOldMasterPageNumber));
+ mxPage->TRG_SetMasterPageVisibleLayers(maOldSet);
+ }
+}
+
+void SdrUndoPageChangeMasterPage::Redo()
+{
+ // restore new values
+ if(mbNewHadMasterPage)
+ {
+ mxPage->TRG_ClearMasterPage();
+ mxPage->TRG_SetMasterPage(*mxPage->getSdrModelFromSdrPage().GetMasterPage(maNewMasterPageNumber));
+ mxPage->TRG_SetMasterPageVisibleLayers(maNewSet);
+ }
+}
+
+OUString SdrUndoPageChangeMasterPage::GetComment() const
+{
+ return ImpGetDescriptionStr(STR_UndoChgPageMasterDscr);
+}
+
+
+SdrUndoFactory::~SdrUndoFactory(){}
+
+// shapes
+
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoMoveObject( SdrObject& rObject, const Size& rDist )
+{
+ return std::make_unique<SdrUndoMoveObj>( rObject, rDist );
+}
+
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoGeoObject( SdrObject& rObject )
+{
+ return std::make_unique<SdrUndoGeoObj>( rObject );
+}
+
+// Diagram ModelData changes
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoDiagramModelData( SdrObject& rObject, std::shared_ptr< svx::diagram::DiagramDataState >& rStartState )
+{
+ return std::make_unique<SdrUndoDiagramModelData>( rObject, rStartState );
+}
+
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoAttrObject( SdrObject& rObject, bool bStyleSheet1, bool bSaveText )
+{
+ return std::make_unique<SdrUndoAttrObj>( rObject, bStyleSheet1, bSaveText );
+}
+
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoRemoveObject(SdrObject& rObject)
+{
+ return std::make_unique<SdrUndoRemoveObj>(rObject);
+}
+
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoInsertObject( SdrObject& rObject, bool bOrdNumDirect )
+{
+ return std::make_unique<SdrUndoInsertObj>( rObject, bOrdNumDirect );
+}
+
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoDeleteObject( SdrObject& rObject, bool bOrdNumDirect )
+{
+ return std::make_unique<SdrUndoDelObj>( rObject, bOrdNumDirect );
+}
+
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoNewObject( SdrObject& rObject, bool bOrdNumDirect )
+{
+ return std::make_unique<SdrUndoNewObj>( rObject, bOrdNumDirect );
+}
+
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoCopyObject( SdrObject& rObject, bool bOrdNumDirect )
+{
+ return std::make_unique<SdrUndoCopyObj>( rObject, bOrdNumDirect );
+}
+
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoObjectOrdNum( SdrObject& rObject, sal_uInt32 nOldOrdNum1, sal_uInt32 nNewOrdNum1)
+{
+ return std::make_unique<SdrUndoObjOrdNum>( rObject, nOldOrdNum1, nNewOrdNum1 );
+}
+
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoSort(SdrPage & rPage, ::std::vector<sal_Int32> const& rSortOrder)
+{
+ return std::make_unique<SdrUndoSort>(rPage, rSortOrder);
+}
+
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoReplaceObject( SdrObject& rOldObject, SdrObject& rNewObject )
+{
+ return std::make_unique<SdrUndoReplaceObj>( rOldObject, rNewObject );
+}
+
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoObjectLayerChange( SdrObject& rObject, SdrLayerID aOldLayer, SdrLayerID aNewLayer )
+{
+ return std::make_unique<SdrUndoObjectLayerChange>( rObject, aOldLayer, aNewLayer );
+}
+
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoObjectSetText( SdrObject& rNewObj, sal_Int32 nText )
+{
+ return std::make_unique<SdrUndoObjSetText>( rNewObj, nText );
+}
+
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoObjectStrAttr( SdrObject& rObject,
+ SdrUndoObjStrAttr::ObjStrAttrType eObjStrAttrType,
+ const OUString& sOldStr,
+ const OUString& sNewStr )
+{
+ return std::make_unique<SdrUndoObjStrAttr>( rObject, eObjStrAttrType, sOldStr, sNewStr );
+}
+
+
+// layer
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoNewLayer(sal_uInt16 nLayerNum, SdrLayerAdmin& rNewLayerAdmin, SdrModel& rNewModel)
+{
+ return std::make_unique<SdrUndoNewLayer>( nLayerNum, rNewLayerAdmin, rNewModel );
+}
+
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoDeleteLayer(sal_uInt16 nLayerNum, SdrLayerAdmin& rNewLayerAdmin, SdrModel& rNewModel)
+{
+ return std::make_unique<SdrUndoDelLayer>( nLayerNum, rNewLayerAdmin, rNewModel );
+}
+
+// page
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoDeletePage(SdrPage& rPage)
+{
+ return std::make_unique<SdrUndoDelPage>(rPage);
+}
+
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoNewPage(SdrPage& rPage)
+{
+ return std::make_unique<SdrUndoNewPage>( rPage );
+}
+
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoCopyPage(SdrPage& rPage)
+{
+ return std::make_unique<SdrUndoCopyPage>( rPage );
+}
+
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoSetPageNum(SdrPage& rNewPg, sal_uInt16 nOldPageNum1, sal_uInt16 nNewPageNum1)
+{
+ return std::make_unique<SdrUndoSetPageNum>( rNewPg, nOldPageNum1, nNewPageNum1 );
+}
+ // master page
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoPageRemoveMasterPage(SdrPage& rChangedPage)
+{
+ return std::make_unique<SdrUndoPageRemoveMasterPage>( rChangedPage );
+}
+
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoPageChangeMasterPage(SdrPage& rChangedPage)
+{
+ return std::make_unique<SdrUndoPageChangeMasterPage>(rChangedPage);
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdview.cxx b/svx/source/svdraw/svdview.cxx
new file mode 100644
index 000000000..d7d6d5d3a
--- /dev/null
+++ b/svx/source/svdraw/svdview.cxx
@@ -0,0 +1,1544 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <editeng/outlobj.hxx>
+
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdmrkv.hxx>
+#include <svx/svdedxv.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svdomedia.hxx>
+#include <svx/svdetc.hxx>
+
+#include <svx/sdr/table/tablecontroller.hxx>
+#include <svx/svdoutl.hxx>
+#include <svx/svdview.hxx>
+#include <editeng/flditem.hxx>
+#include <svx/obj3d.hxx>
+#include <svx/svddrgmt.hxx>
+#include <svx/svdotable.hxx>
+#include <tools/debug.hxx>
+#include <svx/sdr/overlay/overlaypolypolygon.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <svx/sdrpagewindow.hxx>
+#include <svx/sdrhittesthelper.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx>
+#include <svx/sdr/contact/objectcontactofpageview.hxx>
+#include <sal/log.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <vcl/window.hxx>
+#include <comphelper/lok.hxx>
+
+
+SdrViewEvent::SdrViewEvent()
+ : mpHdl(nullptr),
+ mpObj(nullptr),
+ mpRootObj(nullptr),
+ mpPV(nullptr),
+ mpURLField(nullptr),
+ meHit(SdrHitKind::NONE),
+ meEvent(SdrEventKind::NONE),
+ mnMouseClicks(0),
+ mnMouseMode(MouseEventModifiers::NONE),
+ mnMouseCode(0),
+ mnHlplIdx(0),
+ mnGlueId(0),
+ mbMouseDown(false),
+ mbMouseUp(false),
+ mbIsAction(false),
+ mbIsTextEdit(false),
+ mbAddMark(false),
+ mbUnmark(false),
+ mbPrevNextMark(false),
+ mbMarkPrev(false)
+{
+}
+
+// helper class for all D&D overlays
+
+void SdrDropMarkerOverlay::ImplCreateOverlays(
+ const SdrView& rView,
+ const basegfx::B2DPolyPolygon& rLinePolyPolygon)
+{
+ for(sal_uInt32 a(0); a < rView.PaintWindowCount(); a++)
+ {
+ SdrPaintWindow* pCandidate = rView.GetPaintWindow(a);
+ const rtl::Reference< sdr::overlay::OverlayManager >& xTargetOverlay = pCandidate->GetOverlayManager();
+
+ if (xTargetOverlay.is())
+ {
+ std::unique_ptr<sdr::overlay::OverlayPolyPolygonStripedAndFilled> pNew(new sdr::overlay::OverlayPolyPolygonStripedAndFilled(
+ rLinePolyPolygon));
+
+ xTargetOverlay->add(*pNew);
+ maObjects.append(std::move(pNew));
+ }
+ }
+}
+
+SdrDropMarkerOverlay::SdrDropMarkerOverlay(const SdrView& rView, const SdrObject& rObject)
+{
+ ImplCreateOverlays(
+ rView,
+ rObject.TakeXorPoly());
+}
+
+SdrDropMarkerOverlay::SdrDropMarkerOverlay(const SdrView& rView, const tools::Rectangle& rRectangle)
+{
+ basegfx::B2DPolygon aB2DPolygon;
+
+ aB2DPolygon.append(basegfx::B2DPoint(rRectangle.Left(), rRectangle.Top()));
+ aB2DPolygon.append(basegfx::B2DPoint(rRectangle.Right(), rRectangle.Top()));
+ aB2DPolygon.append(basegfx::B2DPoint(rRectangle.Right(), rRectangle.Bottom()));
+ aB2DPolygon.append(basegfx::B2DPoint(rRectangle.Left(), rRectangle.Bottom()));
+ aB2DPolygon.setClosed(true);
+
+ ImplCreateOverlays(
+ rView,
+ basegfx::B2DPolyPolygon(aB2DPolygon));
+}
+
+SdrDropMarkerOverlay::SdrDropMarkerOverlay(const SdrView& rView, const Point& rStart, const Point& rEnd)
+{
+ basegfx::B2DPolygon aB2DPolygon;
+
+ aB2DPolygon.append(basegfx::B2DPoint(rStart.X(), rStart.Y()));
+ aB2DPolygon.append(basegfx::B2DPoint(rEnd.X(), rEnd.Y()));
+ aB2DPolygon.setClosed(true);
+
+ ImplCreateOverlays(
+ rView,
+ basegfx::B2DPolyPolygon(aB2DPolygon));
+}
+
+SdrDropMarkerOverlay::~SdrDropMarkerOverlay()
+{
+ // The OverlayObjects are cleared using the destructor of OverlayObjectList.
+ // That destructor calls clear() at the list which removes all objects from the
+ // OverlayManager and deletes them.
+}
+
+SdrView::SdrView(
+ SdrModel& rSdrModel,
+ OutputDevice* pOut)
+: SdrCreateView(rSdrModel, pOut),
+ mbNoExtendedMouseDispatcher(false),
+ mbNoExtendedKeyDispatcher(false),
+ mbMasterPagePaintCaching(false)
+{
+ maAccessibilityOptions.AddListener(this);
+ onAccessibilityOptionsChanged();
+}
+
+SdrView::~SdrView()
+{
+ maAccessibilityOptions.RemoveListener(this);
+}
+
+bool SdrView::KeyInput(const KeyEvent& rKEvt, vcl::Window* pWin)
+{
+ SetActualWin(pWin ? pWin->GetOutDev() : nullptr);
+ bool bRet = SdrCreateView::KeyInput(rKEvt,pWin);
+ if (!bRet && !IsExtendedKeyInputDispatcherEnabled()) {
+ bRet = true;
+ switch (rKEvt.GetKeyCode().GetFullFunction()) {
+ case KeyFuncType::DELETE: DeleteMarked(); break;
+ case KeyFuncType::UNDO: mpModel->Undo(); break;
+ case KeyFuncType::REDO: mpModel->Redo(); break;
+ default: {
+ switch (rKEvt.GetKeyCode().GetFullCode()) {
+ case KEY_ESCAPE: {
+ if (IsTextEdit()) SdrEndTextEdit();
+ if (IsAction()) BrkAction();
+ if (pWin!=nullptr) pWin->ReleaseMouse();
+ } break;
+ case KEY_DELETE: DeleteMarked(); break;
+ case KEY_UNDO: case KEY_BACKSPACE+KEY_MOD2: mpModel->Undo(); break;
+ case KEY_BACKSPACE+KEY_MOD2+KEY_SHIFT: mpModel->Redo(); break;
+ case KEY_REPEAT: case KEY_BACKSPACE+KEY_MOD2+KEY_MOD1: mpModel->Repeat(*this); break;
+ case KEY_MOD1+KEY_A: MarkAll(); break;
+ default: bRet=false;
+ } // switch
+ }
+ } // switch
+ if (bRet && pWin!=nullptr) {
+ pWin->SetPointer(GetPreferredPointer(
+ pWin->PixelToLogic(pWin->ScreenToOutputPixel( pWin->GetPointerPosPixel() ) ),
+ pWin->GetOutDev(),
+ rKEvt.GetKeyCode().GetModifier()));
+ }
+ }
+ return bRet;
+}
+
+bool SdrView::MouseButtonDown(const MouseEvent& rMEvt, OutputDevice* pWin)
+{
+ SetActualWin(pWin);
+ if (rMEvt.IsLeft()) maDragStat.SetMouseDown(true);
+ bool bRet = SdrCreateView::MouseButtonDown(rMEvt,pWin);
+ if (!bRet && !IsExtendedMouseEventDispatcherEnabled()) {
+ SdrViewEvent aVEvt;
+ PickAnything(rMEvt,SdrMouseEventKind::BUTTONDOWN,aVEvt);
+ bRet = DoMouseEvent(aVEvt);
+ }
+ return bRet;
+}
+
+bool SdrView::MouseButtonUp(const MouseEvent& rMEvt, OutputDevice* pWin)
+{
+ SetActualWin(pWin);
+ if (rMEvt.IsLeft()) maDragStat.SetMouseDown(false);
+ bool bAction = IsAction();
+ bool bRet = !bAction && SdrCreateView::MouseButtonUp(rMEvt,pWin);
+ if (!bRet && !IsExtendedMouseEventDispatcherEnabled()) {
+ SdrViewEvent aVEvt;
+ PickAnything(rMEvt,SdrMouseEventKind::BUTTONUP,aVEvt);
+ bRet = DoMouseEvent(aVEvt);
+ }
+ return bRet;
+}
+
+bool SdrView::MouseMove(const MouseEvent& rMEvt, OutputDevice* pWin)
+{
+ SetActualWin(pWin);
+ maDragStat.SetMouseDown(rMEvt.IsLeft());
+ bool bRet = SdrCreateView::MouseMove(rMEvt,pWin);
+ if (!IsExtendedMouseEventDispatcherEnabled() && !IsTextEditInSelectionMode()) {
+ SdrViewEvent aVEvt;
+ PickAnything(rMEvt,SdrMouseEventKind::MOVE,aVEvt);
+ if (DoMouseEvent(aVEvt)) bRet=true;
+ }
+
+ return bRet;
+}
+
+bool SdrView::Command(const CommandEvent& rCEvt, vcl::Window* pWin)
+{
+ SetActualWin(pWin->GetOutDev());
+ bool bRet = SdrCreateView::Command(rCEvt,pWin);
+ return bRet;
+}
+
+void SdrView::GetAttributes(SfxItemSet& rTargetSet, bool bOnlyHardAttr) const
+{
+ SdrCreateView::GetAttributes(rTargetSet, bOnlyHardAttr);
+}
+
+SdrHitKind SdrView::PickAnything(const MouseEvent& rMEvt, SdrMouseEventKind nEventKind, SdrViewEvent& rVEvt) const
+{
+ rVEvt.mbMouseDown = nEventKind==SdrMouseEventKind::BUTTONDOWN;
+ rVEvt.mbMouseUp = nEventKind==SdrMouseEventKind::BUTTONUP;
+ rVEvt.mnMouseClicks = rMEvt.GetClicks();
+ rVEvt.mnMouseMode = rMEvt.GetMode();
+ rVEvt.mnMouseCode = rMEvt.GetButtons() | rMEvt.GetModifier();
+ const OutputDevice* pOut=mpActualOutDev;
+ if (pOut==nullptr)
+ {
+ pOut = GetFirstOutputDevice();
+ }
+ Point aPnt(rMEvt.GetPosPixel());
+ if (pOut!=nullptr) aPnt=pOut->PixelToLogic(aPnt);
+
+ if (mbNegativeX)
+ {
+ // Shape's x coordinates are all negated,
+ // Hence negate mouse event's x coord to match.
+ aPnt.setX(-aPnt.X());
+ }
+
+ rVEvt.maLogicPos = aPnt;
+ return PickAnything(aPnt,rVEvt);
+}
+
+// Dragging with the Mouse (Move)
+// Example when creating a rectangle: MouseDown has to happen without a ModKey,
+// else we usually force a selection (see below).
+// When pressing Shift, Ctrl and Alt at the same time while doing a MouseMove,
+// a centered, not snapped square is created.
+// The dual allocation of Ortho and Shift won't usually create a problem, as the
+// two functions are in most cases mutually exclusive. Only shearing (the kind
+// that happens when contorting, not when rotating) can use both functions at
+// the same time. To get around this, the user can use e. g. help lines.
+#define MODKEY_NoSnap bCtrl /* temporarily disable snapping */
+#define MODKEY_Ortho bShift /* ortho */
+#define MODKEY_Center bAlt /* create/resize centeredly */
+#define MODKEY_AngleSnap bShift
+#define MODKEY_CopyDrag bCtrl /* drag and copy */
+
+// click somewhere (MouseDown)
+#define MODKEY_PolyPoly bAlt /* new Poly at InsPt and at Create */
+#define MODKEY_MultiMark bShift /* MarkObj without doing UnmarkAll first */
+#define MODKEY_Unmark bAlt /* deselect with a dragged frame */
+#define MODKEY_ForceMark bCtrl /* force dragging a frame, even if there's an object at cursor position */
+#define MODKEY_DeepMark bAlt /* MarkNextObj */
+#define MODKEY_DeepBackw bShift /* MarkNextObj but backwards */
+
+SdrHitKind SdrView::PickAnything(const Point& rLogicPos, SdrViewEvent& rVEvt) const
+{
+ const OutputDevice* pOut=mpActualOutDev;
+ if (pOut==nullptr)
+ {
+ pOut = GetFirstOutputDevice();
+ }
+
+ // #i73628# Use a non-changeable copy of he logic position
+ const Point aLocalLogicPosition(rLogicPos);
+
+ bool bEditMode=IsEditMode();
+ bool bPointMode=bEditMode && HasMarkablePoints();
+ bool bGluePointMode=IsGluePointEditMode();
+ bool bInsPolyPt=bPointMode && IsInsObjPointMode() && IsInsObjPointPossible();
+ bool bInsGluePt=bGluePointMode && IsInsGluePointMode() && IsInsGluePointPossible();
+ bool bIsTextEdit=IsTextEdit();
+ bool bTextEditHit=IsTextEditHit(aLocalLogicPosition);
+ bool bTextEditSel=IsTextEditInSelectionMode();
+ bool bShift = (rVEvt.mnMouseCode & KEY_SHIFT) != 0;
+ bool bCtrl = (rVEvt.mnMouseCode & KEY_MOD1) != 0;
+ bool bAlt = (rVEvt.mnMouseCode & KEY_MOD2) != 0;
+ SdrHitKind eHit=SdrHitKind::NONE;
+ SdrHdl* pHdl=pOut!=nullptr && !bTextEditSel ? PickHandle(aLocalLogicPosition) : nullptr;
+ SdrPageView* pPV=nullptr;
+ SdrObject* pObj=nullptr;
+ SdrObject* pHitObj=nullptr;
+ bool bHitPassDirect=true;
+ sal_uInt16 nHlplIdx=0;
+ sal_uInt16 nGlueId=0;
+ if (bTextEditHit || bTextEditSel)
+ {
+ eHit=SdrHitKind::TextEdit;
+ }
+ else if (pHdl!=nullptr)
+ {
+ eHit=SdrHitKind::Handle; // handle is hit: highest priority
+ }
+ else if (bEditMode && IsHlplVisible() && IsHlplFront() && pOut!=nullptr && PickHelpLine(aLocalLogicPosition,mnHitTolLog,*pOut,nHlplIdx,pPV))
+ {
+ eHit=SdrHitKind::HelpLine; // help line in the foreground hit: can be moved now
+ }
+ else if (bGluePointMode && PickGluePoint(aLocalLogicPosition,pObj,nGlueId,pPV))
+ {
+ eHit=SdrHitKind::Gluepoint; // deselected gluepoint hit
+ }
+ else if ((pHitObj = PickObj(aLocalLogicPosition,mnHitTolLog,pPV,SdrSearchOptions::DEEP|SdrSearchOptions::MARKED,&pObj,&bHitPassDirect)))
+ {
+ eHit=SdrHitKind::MarkedObject;
+ sdr::table::SdrTableObj* pTableObj = dynamic_cast< sdr::table::SdrTableObj* >( pObj );
+ if( pTableObj )
+ {
+ sal_Int32 nX = 0, nY = 0;
+ switch( pTableObj->CheckTableHit( aLocalLogicPosition, nX, nY ) )
+ {
+ case sdr::table::TableHitKind::Cell:
+ eHit = SdrHitKind::Cell;
+ break;
+ case sdr::table::TableHitKind::CellTextArea:
+ eHit = SdrHitKind::TextEditObj;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ else if ((pHitObj = PickObj(aLocalLogicPosition,mnHitTolLog,pPV,SdrSearchOptions::DEEP|SdrSearchOptions::ALSOONMASTER|SdrSearchOptions::WHOLEPAGE,&pObj,&bHitPassDirect)))
+ {
+ // MasterPages and WholePage for Macro and URL
+ eHit=SdrHitKind::UnmarkedObject;
+ sdr::table::SdrTableObj* pTableObj = dynamic_cast< sdr::table::SdrTableObj* >( pObj );
+ if( pTableObj )
+ {
+ sal_Int32 nX = 0, nY = 0;
+ switch( pTableObj->CheckTableHit( aLocalLogicPosition, nX, nY, mnHitTolLog ) )
+ {
+ case sdr::table::TableHitKind::Cell:
+ eHit = SdrHitKind::Cell;
+ break;
+ case sdr::table::TableHitKind::CellTextArea:
+ // Keep state on UnmarkedObject to allow the below
+ // 'check for URL field' to be executed, else popups
+ // for e.g. URL links when hoovering and clicking
+ // them will not work. Tried several other changes,
+ // but this one safely keeps existing behaviour as-is.
+ // Except for the LOK. LOK doesn't have hoovering popup
+ // feature.
+ eHit = comphelper::LibreOfficeKit::isActive() ? SdrHitKind::TextEditObj : SdrHitKind::UnmarkedObject;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ else if (bEditMode && IsHlplVisible() && !IsHlplFront() && pOut!=nullptr && PickHelpLine(aLocalLogicPosition,mnHitTolLog,*pOut,nHlplIdx,pPV))
+ {
+ eHit=SdrHitKind::HelpLine; // help line in foreground hit: can be moved now
+ }
+ if (eHit==SdrHitKind::UnmarkedObject)
+ {
+ bool bRoot=pObj->HasMacro();
+ bool bDeep=pObj!=pHitObj && pHitObj->HasMacro();
+ bool bMid=false; // Have we hit upon a grouped group with a macro?
+ SdrObject* pMidObj=nullptr;
+ if (pObj!=pHitObj)
+ {
+ SdrObject* pObjTmp=pHitObj->getParentSdrObjectFromSdrObject();
+ if (pObjTmp==pObj) pObjTmp=nullptr;
+ while (pObjTmp!=nullptr)
+ {
+ if (pObjTmp->HasMacro())
+ {
+ bMid=true;
+ pMidObj=pObjTmp;
+ }
+ pObjTmp=pObjTmp->getParentSdrObjectFromSdrObject();
+ if (pObjTmp==pObj) pObjTmp=nullptr;
+ }
+ }
+
+ if (bDeep || bMid || bRoot)
+ {
+ SdrObjMacroHitRec aHitRec;
+ aHitRec.aPos=aLocalLogicPosition;
+ aHitRec.nTol=mnHitTolLog;
+ aHitRec.pVisiLayer=&pPV->GetVisibleLayers();
+ aHitRec.pPageView=pPV;
+ if (bDeep) bDeep=pHitObj->IsMacroHit(aHitRec);
+ if (bMid ) bMid =pMidObj->IsMacroHit(aHitRec);
+ if (bRoot) bRoot=pObj->IsMacroHit(aHitRec);
+ if (bRoot || bMid || bDeep)
+ {
+ // Priorities: 1. Root, 2. Mid, 3. Deep
+ rVEvt.mpRootObj = pObj;
+ if (!bRoot) pObj=pMidObj;
+ if (!bRoot && !bMid) pObj=pHitObj;
+ eHit=SdrHitKind::Macro;
+ }
+ }
+ }
+ // check for URL field
+ if (eHit==SdrHitKind::UnmarkedObject)
+ {
+ SdrTextObj* pTextObj=dynamic_cast<SdrTextObj*>( pHitObj );
+ if (pTextObj!=nullptr && pTextObj->HasText())
+ {
+ // use the primitive-based HitTest which is more accurate anyways. It
+ // will correctly handle rotated/mirrored/sheared/scaled text and can
+ // now return a HitContainer containing the primitive hierarchy of the
+ // primitive that triggered the hit. The first entry is that primitive,
+ // the others are the full stack of primitives leading to that one which
+ // includes grouping primitives (like TextHierarchyPrimitives we deed here)
+ // but also all decomposed ones which lead to the creation of that primitive
+ drawinglayer::primitive2d::Primitive2DContainer aHitContainer;
+ const bool bTEHit(pPV && SdrObjectPrimitiveHit(*pTextObj, aLocalLogicPosition, 0, *pPV, &pPV->GetVisibleLayers(), true, &aHitContainer));
+
+ if (bTEHit && !aHitContainer.empty())
+ {
+ // search for TextHierarchyFieldPrimitive2D which contains the needed information
+ // about a possible URLField
+ const drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D* pTextHierarchyFieldPrimitive2D = nullptr;
+
+ for (const drawinglayer::primitive2d::Primitive2DReference& xReference : aHitContainer)
+ {
+ auto pBasePrimitive = xReference.get();
+ if (pBasePrimitive && pBasePrimitive->getPrimitive2DID() == PRIMITIVE2D_ID_TEXTHIERARCHYFIELDPRIMITIVE2D)
+ {
+ pTextHierarchyFieldPrimitive2D = static_cast<const drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D*>(pBasePrimitive);
+ break;
+ }
+ }
+
+ if (nullptr != pTextHierarchyFieldPrimitive2D)
+ {
+ if (drawinglayer::primitive2d::FieldType::FIELD_TYPE_URL == pTextHierarchyFieldPrimitive2D->getType())
+ {
+ // problem with the old code is that a *pointer* to an instance of
+ // SvxURLField is set in the Event which is per se not good since that
+ // data comes from a temporary EditEngine's data and could vanish any
+ // moment. Have to replace for now with a static instance that gets
+ // filled/initialized from the original data held in the TextHierarchyField-
+ // Primitive2D (see impTextBreakupHandler::impCheckFieldPrimitive).
+ // Unfortunately things like 'TargetFrame' are still used in Calc, so this
+ // can currently not get replaced. For the future the Name/Value vector or
+ // the TextHierarchyFieldPrimitive2D itself should/will be used for handling
+ // that data
+ static SvxURLField aSvxURLField;
+
+ aSvxURLField.SetURL(pTextHierarchyFieldPrimitive2D->getValue("URL"));
+ aSvxURLField.SetRepresentation(pTextHierarchyFieldPrimitive2D->getValue("Representation"));
+ aSvxURLField.SetTargetFrame(pTextHierarchyFieldPrimitive2D->getValue("TargetFrame"));
+ const OUString aFormat(pTextHierarchyFieldPrimitive2D->getValue("SvxURLFormat"));
+
+ if (!aFormat.isEmpty())
+ {
+ aSvxURLField.SetFormat(static_cast<SvxURLFormat>(aFormat.toInt32()));
+ }
+
+ // set HitKind and pointer to local static instance in the Event
+ // to comply to old stuff
+ eHit = SdrHitKind::UrlField;
+ rVEvt.mpURLField = &aSvxURLField;
+ }
+ }
+ }
+ }
+ if (eHit==SdrHitKind::UnmarkedObject && !pHitObj->getHyperlink().isEmpty())
+ {
+ static SvxURLField aSvxURLField;
+ aSvxURLField.SetURL(pHitObj->getHyperlink());
+ rVEvt.mpURLField = &aSvxURLField;
+ eHit = SdrHitKind::UrlField;
+ }
+ }
+
+ if (bHitPassDirect &&
+ (eHit==SdrHitKind::MarkedObject || eHit==SdrHitKind::UnmarkedObject) &&
+ (IsTextTool() || (IsEditMode() && IsQuickTextEditMode())) && pHitObj->HasTextEdit())
+ {
+ // Around the TextEditArea there's a border to select without going into text edit mode.
+ tools::Rectangle aBoundRect(pHitObj->GetCurrentBoundRect());
+
+ // Force to SnapRect when Fontwork
+ if( auto pTextObj = dynamic_cast<const SdrTextObj*>(pHitObj) )
+ if( pTextObj->IsFontwork() )
+ aBoundRect = pHitObj->GetSnapRect();
+
+ sal_Int32 nTolerance(mnHitTolLog);
+ bool bBoundRectHit(false);
+
+ if(pOut)
+ {
+ nTolerance = pOut->PixelToLogic(Size(2, 0)).Width();
+ }
+
+ if( (aLocalLogicPosition.X() >= aBoundRect.Left() - nTolerance && aLocalLogicPosition.X() <= aBoundRect.Left() + nTolerance)
+ || (aLocalLogicPosition.X() >= aBoundRect.Right() - nTolerance && aLocalLogicPosition.X() <= aBoundRect.Right() + nTolerance)
+ || (aLocalLogicPosition.Y() >= aBoundRect.Top() - nTolerance && aLocalLogicPosition.Y() <= aBoundRect.Top() + nTolerance)
+ || (aLocalLogicPosition.Y() >= aBoundRect.Bottom() - nTolerance && aLocalLogicPosition.Y() <= aBoundRect.Bottom() + nTolerance))
+ {
+ bBoundRectHit = true;
+ }
+
+ if(!bBoundRectHit)
+ {
+ bool bTEHit(pPV &&
+ SdrObjectPrimitiveHit(*pHitObj, aLocalLogicPosition, 0, *pPV, &pPV->GetVisibleLayers(), true));
+
+ // TextEdit attached to an object in a locked layer
+ if (bTEHit && pPV->GetLockedLayers().IsSet(pHitObj->GetLayer()))
+ {
+ bTEHit=false;
+ }
+
+ if (bTEHit)
+ {
+ rVEvt.mpRootObj=pObj;
+ pObj=pHitObj;
+ eHit=SdrHitKind::TextEditObj;
+ }
+ }
+ }
+ if (!bHitPassDirect && eHit==SdrHitKind::UnmarkedObject) {
+ eHit=SdrHitKind::NONE;
+ pObj=nullptr;
+ pPV=nullptr;
+ }
+ bool bMouseLeft = (rVEvt.mnMouseCode & MOUSE_LEFT) != 0;
+ bool bMouseRight = (rVEvt.mnMouseCode & MOUSE_RIGHT) != 0;
+ bool bMouseDown = rVEvt.mbMouseDown;
+ bool bMouseUp = rVEvt.mbMouseUp;
+ SdrEventKind eEvent=SdrEventKind::NONE;
+ bool bIsAction=IsAction();
+
+ if (bIsAction)
+ {
+ if (bMouseDown)
+ {
+ if (bMouseRight) eEvent=SdrEventKind::BackAction;
+ }
+ else if (bMouseUp)
+ {
+ if (bMouseLeft)
+ {
+ eEvent=SdrEventKind::EndAction;
+ if (IsDragObj())
+ {
+ eEvent=SdrEventKind::EndDrag;
+ }
+ else if (IsCreateObj() || IsInsObjPoint())
+ {
+ eEvent=IsCreateObj() ? SdrEventKind::EndCreate : SdrEventKind::EndInsertObjPoint;
+ }
+ else if (IsMarking())
+ {
+ eEvent=SdrEventKind::EndMark;
+ if (!maDragStat.IsMinMoved())
+ {
+ eEvent=SdrEventKind::BrkMark;
+ rVEvt.mbAddMark = MODKEY_MultiMark;
+ }
+ }
+ }
+ }
+ else
+ {
+ eEvent=SdrEventKind::MoveAction;
+ }
+ }
+ else if (eHit==SdrHitKind::TextEdit)
+ {
+ eEvent=SdrEventKind::TextEdit;
+ }
+ else if (bMouseDown && bMouseLeft)
+ {
+ if (rVEvt.mnMouseClicks == 2 && rVEvt.mnMouseCode == MOUSE_LEFT && pObj!=nullptr && pHitObj!=nullptr && pHitObj->HasTextEdit() && eHit==SdrHitKind::MarkedObject)
+ {
+ rVEvt.mpRootObj = pObj;
+ pObj=pHitObj;
+ eEvent=SdrEventKind::BeginTextEdit;
+ }
+ else if (MODKEY_ForceMark && eHit!=SdrHitKind::UrlField)
+ {
+ eEvent=SdrEventKind::BeginMark; // AddMark,Unmark */
+ }
+ else if (eHit==SdrHitKind::HelpLine)
+ {
+ eEvent=SdrEventKind::BeginDragHelpline; // nothing, actually
+ }
+ else if (eHit==SdrHitKind::Gluepoint)
+ {
+ eEvent=SdrEventKind::MarkGluePoint; // AddMark+Drag
+ rVEvt.mbAddMark = MODKEY_MultiMark || MODKEY_DeepMark; // if not hit with Deep
+ }
+ else if (eHit==SdrHitKind::Handle)
+ {
+ eEvent=SdrEventKind::BeginDragObj; // Mark+Drag,AddMark+Drag,DeepMark+Drag,Unmark
+ bool bGlue=pHdl->GetKind()==SdrHdlKind::Glue;
+ bool bPoly=!bGlue && IsPointMarkable(*pHdl);
+ bool bMarked=bGlue || (bPoly && pHdl->IsSelected());
+ if (bGlue || bPoly)
+ {
+ eEvent=bGlue ? SdrEventKind::MarkGluePoint : SdrEventKind::MarkPoint;
+ if (MODKEY_DeepMark)
+ {
+ rVEvt.mbAddMark = true;
+ rVEvt.mbPrevNextMark = true;
+ rVEvt.mbMarkPrev = MODKEY_DeepBackw;
+ }
+ else if (MODKEY_MultiMark)
+ {
+ rVEvt.mbAddMark = true;
+ rVEvt.mbUnmark = bMarked; // Toggle
+ if (bGlue)
+ {
+ pObj=pHdl->GetObj();
+ nGlueId=static_cast<sal_uInt16>(pHdl->GetObjHdlNum());
+ }
+ }
+ else if (bMarked)
+ {
+ eEvent=SdrEventKind::BeginDragObj; // don't change MarkState, only change Drag
+ }
+ }
+ }
+ else if (bInsPolyPt && (MODKEY_PolyPoly || (!MODKEY_MultiMark && !MODKEY_DeepMark)))
+ {
+ eEvent=SdrEventKind::BeginInsertObjPoint;
+ }
+ else if (bInsGluePt && !MODKEY_MultiMark && !MODKEY_DeepMark)
+ {
+ eEvent=SdrEventKind::BeginInsertGluePoint;
+ }
+ else if (eHit==SdrHitKind::TextEditObj)
+ {
+ eEvent=SdrEventKind::BeginTextEdit; // AddMark+Drag,DeepMark+Drag,Unmark
+ if (MODKEY_MultiMark || MODKEY_DeepMark)
+ { // if not hit with Deep
+ eEvent=SdrEventKind::MarkObj;
+ }
+ }
+ else if (eHit==SdrHitKind::Macro)
+ {
+ eEvent=SdrEventKind::BeginMacroObj; // AddMark+Drag
+ if (MODKEY_MultiMark || MODKEY_DeepMark)
+ { // if not hit with Deep
+ eEvent=SdrEventKind::MarkObj;
+ }
+ }
+ else if (eHit==SdrHitKind::UrlField)
+ {
+ eEvent=SdrEventKind::ExecuteUrl; // AddMark+Drag
+ if (MODKEY_MultiMark || MODKEY_DeepMark)
+ { // if not hit with Deep
+ eEvent=SdrEventKind::MarkObj;
+ }
+ }
+ else if (eHit==SdrHitKind::MarkedObject)
+ {
+ eEvent=SdrEventKind::BeginDragObj; // DeepMark+Drag,Unmark
+
+ if (MODKEY_MultiMark || MODKEY_DeepMark)
+ { // if not hit with Deep
+ eEvent=SdrEventKind::MarkObj;
+ }
+ }
+ else if (IsCreateMode())
+ {
+ eEvent=SdrEventKind::BeginCreateObj; // nothing, actually
+ }
+ else if (eHit==SdrHitKind::UnmarkedObject)
+ {
+ eEvent=SdrEventKind::MarkObj; // AddMark+Drag
+ }
+ else
+ {
+ eEvent=SdrEventKind::BeginMark;
+ }
+
+ if (eEvent==SdrEventKind::MarkObj)
+ {
+ rVEvt.mbAddMark = MODKEY_MultiMark || MODKEY_DeepMark; // if not hit with Deep
+ rVEvt.mbPrevNextMark = MODKEY_DeepMark;
+ rVEvt.mbMarkPrev = MODKEY_DeepMark && MODKEY_DeepBackw;
+ }
+ if (eEvent==SdrEventKind::BeginMark)
+ {
+ rVEvt.mbAddMark = MODKEY_MultiMark;
+ rVEvt.mbUnmark = MODKEY_Unmark;
+ }
+ }
+ rVEvt.mbIsAction = bIsAction;
+ rVEvt.mbIsTextEdit = bIsTextEdit;
+ rVEvt.maLogicPos = aLocalLogicPosition;
+ rVEvt.mpHdl = pHdl;
+ rVEvt.mpObj = pObj;
+ if (rVEvt.mpRootObj == nullptr)
+ rVEvt.mpRootObj = pObj;
+ rVEvt.mpPV = pPV;
+ rVEvt.mnHlplIdx = nHlplIdx;
+ rVEvt.mnGlueId = nGlueId;
+ rVEvt.meHit = eHit;
+ rVEvt.meEvent = eEvent;
+#ifdef DGB_UTIL
+ if (rVEvt.mpRootObj != nullptr)
+ {
+ if (rVEvt.mpRootObj->getParentSdrObjListFromSdrObject() != rVEvt.mpPV->GetObjList())
+ {
+ OSL_FAIL("SdrView::PickAnything(): pRootObj->getParentSdrObjListFromSdrObject()!=pPV->GetObjList() !");
+ }
+ }
+#endif
+ return eHit;
+}
+
+bool SdrView::DoMouseEvent(const SdrViewEvent& rVEvt)
+{
+ bool bRet=false;
+ SdrHitKind eHit = rVEvt.meHit;
+ Point aLogicPos(rVEvt.maLogicPos);
+
+ bool bShift = (rVEvt.mnMouseCode & KEY_SHIFT) != 0;
+ bool bCtrl = (rVEvt.mnMouseCode & KEY_MOD1) != 0;
+ bool bAlt = (rVEvt.mnMouseCode & KEY_MOD2) != 0;
+ bool bMouseLeft = (rVEvt.mnMouseCode & MOUSE_LEFT) != 0;
+ bool bMouseDown = rVEvt.mbMouseDown;
+ bool bMouseUp = rVEvt.mbMouseUp;
+ if (bMouseDown) {
+ if (bMouseLeft) maDragStat.SetMouseDown(true);
+ } else if (bMouseUp) {
+ if (bMouseLeft) maDragStat.SetMouseDown(false);
+ } else { // else, MouseMove
+ maDragStat.SetMouseDown(bMouseLeft);
+ }
+
+#ifdef MODKEY_NoSnap
+ SetSnapEnabled(!MODKEY_NoSnap);
+#endif
+#ifdef MODKEY_Ortho
+ SetOrtho(MODKEY_Ortho!=IsOrthoDesired());
+#endif
+#ifdef MODKEY_AngleSnap
+ SetAngleSnapEnabled(MODKEY_AngleSnap);
+#endif
+#ifdef MODKEY_CopyDrag
+ SetDragWithCopy(MODKEY_CopyDrag);
+#endif
+#ifdef MODKEY_Center
+ SetCreate1stPointAsCenter(MODKEY_Center);
+ SetResizeAtCenter(MODKEY_Center);
+ SetCrookAtCenter(MODKEY_Center);
+#endif
+ if (bMouseLeft && bMouseDown && rVEvt.mbIsTextEdit && (eHit==SdrHitKind::UnmarkedObject || eHit==SdrHitKind::NONE)) {
+ SdrEndTextEdit(); // User has clicked beneath object, exit edit mode.
+ // pHdl is invalid, then, that shouldn't matter, though, as we expect
+ // pHdl==NULL (because of eHit).
+ }
+ switch (rVEvt.meEvent)
+ {
+ case SdrEventKind::NONE: bRet=false; break;
+ case SdrEventKind::TextEdit: bRet=false; break; // Events handled by the OutlinerView are not taken into account here.
+ case SdrEventKind::MoveAction: MovAction(aLogicPos); bRet=true; break;
+ case SdrEventKind::EndAction: EndAction(); bRet=true; break;
+ case SdrEventKind::BackAction: BckAction(); bRet=true; break;
+ case SdrEventKind::EndMark : EndAction(); bRet=true; break;
+ case SdrEventKind::BrkMark : {
+ BrkAction();
+ if (!MarkObj(aLogicPos, mnHitTolLog, rVEvt.mbAddMark))
+ {
+ // No object hit. Do the following:
+ // 1. deselect any selected gluepoints
+ // 2. deselect any selected polygon points
+ // 3. deselect any selected objects
+ if (!rVEvt.mbAddMark) UnmarkAll();
+ }
+ bRet=true;
+ } break;
+ case SdrEventKind::EndCreate: { // if necessary, MarkObj
+ SdrCreateCmd eCmd=SdrCreateCmd::NextPoint;
+ if (MODKEY_PolyPoly) eCmd=SdrCreateCmd::NextObject;
+ if (rVEvt.mnMouseClicks > 1) eCmd=SdrCreateCmd::ForceEnd;
+ if (!EndCreateObj(eCmd)) { // Don't evaluate event for Create? -> Select
+ if (eHit==SdrHitKind::UnmarkedObject || eHit==SdrHitKind::TextEdit) {
+ MarkObj(rVEvt.mpRootObj, rVEvt.mpPV);
+ if (eHit==SdrHitKind::TextEdit)
+ {
+ bool bRet2(mpActualOutDev && OUTDEV_WINDOW == mpActualOutDev->GetOutDevType() &&
+ SdrBeginTextEdit(rVEvt.mpObj, rVEvt.mpPV, mpActualOutDev->GetOwnerWindow()));
+
+ if(bRet2)
+ {
+ MouseEvent aMEvt(mpActualOutDev->LogicToPixel(aLogicPos), 1,
+ rVEvt.mnMouseMode,rVEvt.mnMouseCode,rVEvt.mnMouseCode);
+
+ OutlinerView* pOLV=GetTextEditOutlinerView();
+ if (pOLV!=nullptr) {
+ pOLV->MouseButtonDown(aMEvt); // event for the Outliner, but without double-click
+ pOLV->MouseButtonUp(aMEvt); // event for the Outliner, but without double-click
+ }
+ }
+ }
+ bRet=true; // object is selected and (if necessary) TextEdit is started
+ } else bRet=false; // canceled Create, nothing else
+ } else bRet=true; // return true for EndCreate
+ } break;
+ case SdrEventKind::EndDrag: {
+ bRet=EndDragObj(IsDragWithCopy());
+ ForceMarkedObjToAnotherPage(); // TODO: Undo+bracing missing!
+ } break;
+ case SdrEventKind::MarkObj: { // + (if applicable) BegDrag
+ if (!rVEvt.mbAddMark) UnmarkAllObj();
+ bool bUnmark = rVEvt.mbUnmark;
+ if (rVEvt.mbPrevNextMark) {
+ bRet=MarkNextObj(aLogicPos, mnHitTolLog, rVEvt.mbMarkPrev);
+ } else {
+ SortMarkedObjects();
+ const size_t nCount0=GetMarkedObjectCount();
+ bRet=MarkObj(aLogicPos, mnHitTolLog, rVEvt.mbAddMark);
+ SortMarkedObjects();
+ const size_t nCount1=GetMarkedObjectCount();
+ bUnmark=nCount1<nCount0;
+ }
+ if (!bUnmark) {
+ BegDragObj(aLogicPos,nullptr,nullptr,mnMinMovLog);
+ bRet=true;
+ }
+ } break;
+ case SdrEventKind::MarkPoint: { // + (if applicable) BegDrag
+ if (!rVEvt.mbAddMark) UnmarkAllPoints();
+ if (rVEvt.mbPrevNextMark) {
+ MarkNextPoint();
+ bRet=false;
+ } else {
+ bRet = MarkPoint(*rVEvt.mpHdl, rVEvt.mbUnmark);
+ }
+ if (!rVEvt.mbUnmark && !rVEvt.mbPrevNextMark) {
+ BegDragObj(aLogicPos, nullptr, rVEvt.mpHdl, mnMinMovLog);
+ bRet=true;
+ }
+ } break;
+ case SdrEventKind::MarkGluePoint: { // + (if applicable) BegDrag
+ if (!rVEvt.mbAddMark) UnmarkAllGluePoints();
+ if (rVEvt.mbPrevNextMark) {
+ MarkNextGluePoint();
+ bRet=false;
+ } else {
+ bRet=MarkGluePoint(rVEvt.mpObj,rVEvt.mnGlueId,rVEvt.mbUnmark);
+ }
+ if (!rVEvt.mbUnmark && !rVEvt.mbPrevNextMark) {
+ SdrHdl* pHdl = GetGluePointHdl(rVEvt.mpObj, rVEvt.mnGlueId);
+ BegDragObj(aLogicPos,nullptr,pHdl,mnMinMovLog);
+ bRet=true;
+ }
+ } break;
+ case SdrEventKind::BeginMark: bRet = BegMark(aLogicPos,rVEvt.mbAddMark,rVEvt.mbUnmark); break;
+ case SdrEventKind::BeginInsertObjPoint: bRet = BegInsObjPoint(aLogicPos, MODKEY_PolyPoly); break;
+ case SdrEventKind::EndInsertObjPoint: {
+ SdrCreateCmd eCmd=SdrCreateCmd::NextPoint;
+ if (MODKEY_PolyPoly) eCmd=SdrCreateCmd::NextObject;
+ if (rVEvt.mnMouseClicks > 1) eCmd = SdrCreateCmd::ForceEnd;
+ EndInsObjPoint(eCmd);
+ bRet=true;
+ } break;
+ case SdrEventKind::BeginInsertGluePoint: bRet=BegInsGluePoint(aLogicPos); break;
+ case SdrEventKind::BeginDragHelpline: bRet = BegDragHelpLine(rVEvt.mnHlplIdx,rVEvt.mpPV); break;
+ case SdrEventKind::BeginDragObj: bRet=BegDragObj(aLogicPos, nullptr, rVEvt.mpHdl, mnMinMovLog); break;
+ case SdrEventKind::BeginCreateObj: {
+ if (mnCurrentInvent==SdrInventor::Default && mnCurrentIdent==SdrObjKind::Caption) {
+ tools::Long nHgt=SdrEngineDefaults::GetFontHeight();
+ bRet=BegCreateCaptionObj(aLogicPos,Size(5*nHgt,2*nHgt));
+ } else bRet=BegCreateObj(aLogicPos);
+ } break;
+ case SdrEventKind::BeginMacroObj: {
+ BegMacroObj(aLogicPos, mnHitTolLog, rVEvt.mpObj, rVEvt.mpPV, mpActualOutDev->GetOwnerWindow());
+ bRet=false;
+ } break;
+ case SdrEventKind::BeginTextEdit: {
+ if (!IsObjMarked(rVEvt.mpObj)) {
+ UnmarkAllObj();
+ MarkObj(rVEvt.mpRootObj,rVEvt.mpPV);
+ }
+
+ bRet = mpActualOutDev && OUTDEV_WINDOW == mpActualOutDev->GetOutDevType()&&
+ SdrBeginTextEdit(rVEvt.mpObj, rVEvt.mpPV, mpActualOutDev->GetOwnerWindow());
+
+ if(bRet)
+ {
+ MouseEvent aMEvt(mpActualOutDev->LogicToPixel(aLogicPos),
+ 1, rVEvt.mnMouseMode, rVEvt.mnMouseCode, rVEvt.mnMouseCode);
+ OutlinerView* pOLV=GetTextEditOutlinerView();
+ if (pOLV!=nullptr) pOLV->MouseButtonDown(aMEvt); // event for the Outliner, but without double-click
+ }
+ } break;
+ default: break;
+ } // switch
+ if (bRet && mpActualOutDev && mpActualOutDev->GetOutDevType()==OUTDEV_WINDOW) {
+ vcl::Window* pWin=mpActualOutDev->GetOwnerWindow();
+ // left mouse button pressed?
+ bool bLeftDown = (rVEvt.mnMouseCode & MOUSE_LEFT) != 0 && rVEvt.mbMouseDown;
+ // left mouse button released?
+ bool bLeftUp = (rVEvt.mnMouseCode & MOUSE_LEFT) != 0 && rVEvt.mbMouseUp;
+ // left mouse button pressed or held?
+ bool bLeftDown1=(rVEvt.mnMouseCode & MOUSE_LEFT) != 0 && !rVEvt.mbMouseUp;
+ pWin->SetPointer(GetPreferredPointer(rVEvt.maLogicPos, pWin->GetOutDev(),
+ rVEvt.mnMouseCode & (KEY_SHIFT|KEY_MOD1|KEY_MOD2),bLeftDown1));
+ bool bAction=IsAction();
+ if (bLeftDown && bAction)
+ pWin->CaptureMouse();
+ else if (bLeftUp || (rVEvt.mbIsAction && !bAction))
+ pWin->ReleaseMouse();
+ }
+ return bRet;
+}
+
+PointerStyle SdrView::GetPreferredPointer(const Point& rMousePos, const OutputDevice* pOut, sal_uInt16 nModifier, bool bLeftDown) const
+{
+ // Actions
+ if (IsCreateObj())
+ {
+ return mpCurrentCreate->GetCreatePointer();
+ }
+ if (mpCurrentSdrDragMethod)
+ {
+ return mpCurrentSdrDragMethod->GetSdrDragPointer();
+ }
+ if (IsMarkObj() || IsMarkPoints() || IsMarkGluePoints() || IsSetPageOrg()) return PointerStyle::Arrow;
+ if (IsDragHelpLine()) return GetDraggedHelpLinePointer();
+ if (IsMacroObj()) {
+ SdrObjMacroHitRec aHitRec;
+ aHitRec.aPos=pOut->LogicToPixel(rMousePos);
+ aHitRec.nTol=nMacroTol;
+ aHitRec.pVisiLayer=&pMacroPV->GetVisibleLayers();
+ aHitRec.pPageView=pMacroPV;
+ return pMacroObj->GetMacroPointer(aHitRec);
+ }
+
+ // TextEdit, ObjEdit, Macro
+ if (IsTextEdit() && (IsTextEditInSelectionMode() || IsTextEditHit(rMousePos)))
+ {
+ if(!pOut || IsTextEditInSelectionMode())
+ {
+ if (mpTextEditOutliner->IsVertical())
+ return PointerStyle::TextVertical;
+ else
+ return PointerStyle::Text;
+ }
+ // Outliner should return something here...
+ Point aPos(pOut->LogicToPixel(rMousePos));
+ PointerStyle aPointer(mpTextEditOutlinerView->GetPointer(aPos));
+ if (aPointer==PointerStyle::Arrow)
+ {
+ if (mpTextEditOutliner->IsVertical())
+ aPointer = PointerStyle::TextVertical;
+ else
+ aPointer = PointerStyle::Text;
+ }
+ return aPointer;
+ }
+
+ SdrViewEvent aVEvt;
+ aVEvt.mnMouseCode = (nModifier&(KEY_SHIFT|KEY_MOD1|KEY_MOD2))|MOUSE_LEFT; // to see what would happen on MouseLeftDown
+ aVEvt.mbMouseDown = !bLeftDown; // What if ..?
+ aVEvt.mbMouseUp = bLeftDown; // What if ..?
+ if (pOut!=nullptr)
+ const_cast<SdrView*>(this)->SetActualWin(pOut);
+ SdrHitKind eHit=PickAnything(rMousePos,aVEvt);
+ SdrEventKind eEvent = aVEvt.meEvent;
+ switch (eEvent)
+ {
+ case SdrEventKind::BeginCreateObj:
+ return maCurrentCreatePointer;
+ case SdrEventKind::MarkObj:
+ return PointerStyle::Move;
+ case SdrEventKind::BeginMark:
+ return PointerStyle::Arrow;
+ case SdrEventKind::MarkPoint:
+ case SdrEventKind::MarkGluePoint:
+ return PointerStyle::MovePoint;
+ case SdrEventKind::BeginInsertObjPoint:
+ case SdrEventKind::BeginInsertGluePoint:
+ return PointerStyle::Cross;
+ case SdrEventKind::ExecuteUrl:
+ return PointerStyle::RefHand;
+ case SdrEventKind::BeginMacroObj:
+ {
+ SdrObjMacroHitRec aHitRec;
+ aHitRec.aPos = aVEvt.maLogicPos;
+ aHitRec.nTol=mnHitTolLog;
+ aHitRec.pVisiLayer = &aVEvt.mpPV->GetVisibleLayers();
+ aHitRec.pPageView = aVEvt.mpPV;
+ return aVEvt.mpObj->GetMacroPointer(aHitRec);
+ }
+ default: break;
+ } // switch
+
+ switch(eHit)
+ {
+ case SdrHitKind::Cell:
+ return PointerStyle::Arrow;
+ case SdrHitKind::HelpLine :
+ return aVEvt.mpPV->GetHelpLines()[aVEvt.mnHlplIdx].GetPointer();
+ case SdrHitKind::Gluepoint:
+ return PointerStyle::MovePoint;
+ case SdrHitKind::TextEdit :
+ case SdrHitKind::TextEditObj:
+ {
+ SdrTextObj* pText = dynamic_cast< SdrTextObj* >(aVEvt.mpObj);
+ if(pText && pText->HasText())
+ {
+ OutlinerParaObject* pParaObj = pText->GetOutlinerParaObject();
+ if(pParaObj && pParaObj->IsEffectivelyVertical())
+ return PointerStyle::TextVertical;
+ }
+ return PointerStyle::Text;
+ }
+ default: break;
+ }
+
+ bool bMarkHit=eHit==SdrHitKind::MarkedObject;
+ SdrHdl* pHdl = aVEvt.mpHdl;
+ // now check the pointers for dragging
+ if (pHdl!=nullptr || bMarkHit) {
+ SdrHdlKind eHdl= pHdl!=nullptr ? pHdl->GetKind() : SdrHdlKind::Move;
+ bool bCorner=pHdl!=nullptr && pHdl->IsCornerHdl();
+ bool bVertex=pHdl!=nullptr && pHdl->IsVertexHdl();
+ bool bMov=eHdl==SdrHdlKind::Move;
+ if (bMov && (meDragMode==SdrDragMode::Move || meDragMode==SdrDragMode::Resize || mbMarkedHitMovesAlways)) {
+ if (!IsMoveAllowed()) return PointerStyle::Arrow; // because double click or drag & drop is possible
+ return PointerStyle::Move;
+ }
+ switch (meDragMode) {
+ case SdrDragMode::Rotate: {
+ if ((bCorner || bMov) && !IsRotateAllowed(true))
+ return PointerStyle::NotAllowed;
+
+ // are 3D objects selected?
+ bool b3DObjSelected = false;
+ for (size_t a=0; !b3DObjSelected && a<GetMarkedObjectCount(); ++a) {
+ SdrObject* pObj = GetMarkedObjectByIndex(a);
+ if(dynamic_cast<const E3dObject* >(pObj) != nullptr)
+ b3DObjSelected = true;
+ }
+ // If we have a 3D object, go on despite !IsShearAllowed,
+ // because then we have a rotation instead of a shear.
+ if (bVertex && !IsShearAllowed() && !b3DObjSelected)
+ return PointerStyle::NotAllowed;
+ if (bMov)
+ return PointerStyle::Rotate;
+ } break;
+ case SdrDragMode::Shear: {
+ if (bCorner) {
+ if (!IsDistortAllowed(true) && !IsDistortAllowed()) return PointerStyle::NotAllowed;
+ else return PointerStyle::RefHand;
+ }
+ if (bVertex && !IsShearAllowed()) return PointerStyle::NotAllowed;
+ if (bMov) {
+ if (!IsMoveAllowed()) return PointerStyle::Arrow; // because double click or drag & drop is possible
+ return PointerStyle::Move;
+ }
+ } break;
+ case SdrDragMode::Mirror: {
+ if (bCorner || bVertex || bMov) {
+ SdrHdl* pH1=maHdlList.GetHdl(SdrHdlKind::Ref1);
+ SdrHdl* pH2=maHdlList.GetHdl(SdrHdlKind::Ref2);
+ bool b90=false;
+ bool b45=false;
+ if (pH1!=nullptr && pH2!=nullptr) {
+ Point aDif = pH2->GetPos()-pH1->GetPos();
+ b90=(aDif.X()==0) || aDif.Y()==0;
+ b45=b90 || (std::abs(aDif.X())==std::abs(aDif.Y()));
+ }
+ bool bNo=false;
+ if (!IsMirrorAllowed(true,true)) bNo=true; // any mirroring is forbidden
+ if (!IsMirrorAllowed() && !b45) bNo=true; // mirroring freely is forbidden
+ if (!IsMirrorAllowed(true) && !b90) bNo=true; // mirroring horizontally/vertically is allowed
+ if (bNo) return PointerStyle::NotAllowed;
+ if (b90) {
+ return PointerStyle::Mirror;
+ }
+ return PointerStyle::Mirror;
+ }
+ } break;
+
+ case SdrDragMode::Transparence:
+ {
+ if(!IsTransparenceAllowed())
+ return PointerStyle::NotAllowed;
+
+ return PointerStyle::RefHand;
+ }
+
+ case SdrDragMode::Gradient:
+ {
+ if(!IsGradientAllowed())
+ return PointerStyle::NotAllowed;
+
+ return PointerStyle::RefHand;
+ }
+
+ case SdrDragMode::Crook: {
+ if (bCorner || bVertex || bMov) {
+ if (!IsCrookAllowed(true) && !IsCrookAllowed()) return PointerStyle::NotAllowed;
+ return PointerStyle::Crook;
+ }
+ break;
+ }
+
+ case SdrDragMode::Crop:
+ {
+ return PointerStyle::Crop;
+ }
+
+ default: {
+ if ((bCorner || bVertex) && !IsResizeAllowed(true)) return PointerStyle::NotAllowed;
+ }
+ }
+ if (pHdl!=nullptr) return pHdl->GetPointer();
+ if (bMov) {
+ if (!IsMoveAllowed()) return PointerStyle::Arrow; // because double click or drag & drop is possible
+ return PointerStyle::Move;
+ }
+ }
+ if (meEditMode==SdrViewEditMode::Create) return maCurrentCreatePointer;
+ return PointerStyle::Arrow;
+}
+
+constexpr OUStringLiteral STR_NOTHING = u"nothing";
+OUString SdrView::GetStatusText()
+{
+ OUString aName;
+ OUString aStr = STR_NOTHING;
+
+ if (mpCurrentCreate!=nullptr)
+ {
+ aStr=mpCurrentCreate->getSpecialDragComment(maDragStat);
+
+ if(aStr.isEmpty())
+ {
+ aName = mpCurrentCreate->TakeObjNameSingul();
+ aStr = SvxResId(STR_ViewCreateObj);
+ }
+ }
+ else if (mpCurrentSdrDragMethod)
+ {
+ if (mbInsPolyPoint || IsInsertGluePoint())
+ {
+ aStr=maInsPointUndoStr;
+ }
+ else
+ {
+ if (maDragStat.IsMinMoved())
+ {
+ SAL_INFO(
+ "svx.svdraw",
+ "(" << this << ") " << mpCurrentSdrDragMethod.get());
+ aStr = mpCurrentSdrDragMethod->GetSdrDragComment();
+ }
+ }
+ }
+ else if(IsMarkObj())
+ {
+ if(AreObjectsMarked())
+ {
+ aStr = SvxResId(STR_ViewMarkMoreObjs);
+ }
+ else
+ {
+ aStr = SvxResId(STR_ViewMarkObjs);
+ }
+ }
+ else if(IsMarkPoints())
+ {
+ if(HasMarkedPoints())
+ {
+ aStr = SvxResId(STR_ViewMarkMorePoints);
+ }
+ else
+ {
+ aStr = SvxResId(STR_ViewMarkPoints);
+ }
+ } else if (IsMarkGluePoints())
+ {
+ if(HasMarkedGluePoints())
+ {
+ aStr = SvxResId(STR_ViewMarkMoreGluePoints);
+ }
+ else
+ {
+ aStr = SvxResId(STR_ViewMarkGluePoints);
+ }
+ }
+ else if (IsTextEdit() && mpTextEditOutlinerView != nullptr) {
+ aStr=SvxResId(STR_ViewTextEdit); // "TextEdit - Row y, Column x";
+ ESelection aSel(mpTextEditOutlinerView->GetSelection());
+ tools::Long nPar = aSel.nEndPara,nLin=0,nCol=aSel.nEndPos;
+ if (aSel.nEndPara>0) {
+ for (sal_Int32 nParaNum=0; nParaNum<aSel.nEndPara; nParaNum++) {
+ nLin += mpTextEditOutliner->GetLineCount(nParaNum);
+ }
+ }
+ // A little imperfection:
+ // At the end of a line of any multi-line paragraph, we display the
+ // position of the next line of the same paragraph, if there is one.
+ sal_uInt16 nParaLine = 0;
+ sal_uLong nParaLineCount = mpTextEditOutliner->GetLineCount(aSel.nEndPara);
+ bool bBrk = false;
+ while (!bBrk)
+ {
+ sal_uInt16 nLen = mpTextEditOutliner->GetLineLen(aSel.nEndPara, nParaLine);
+ bool bLastLine = (nParaLine == nParaLineCount - 1);
+ if (nCol>nLen || (!bLastLine && nCol == nLen))
+ {
+ nCol -= nLen;
+ nLin++;
+ nParaLine++;
+ }
+ else
+ bBrk = true;
+
+ if (nLen == 0)
+ bBrk = true; // to be sure
+ }
+
+ aStr = aStr.replaceFirst("%1", OUString::number(nPar + 1));
+ aStr = aStr.replaceFirst("%2", OUString::number(nLin + 1));
+ aStr = aStr.replaceFirst("%3", OUString::number(nCol + 1));
+
+#ifdef DBG_UTIL
+ aStr += ", Level " + OUString::number(mpTextEditOutliner->GetDepth( aSel.nEndPara ));
+#endif
+ }
+
+ if(aStr == STR_NOTHING)
+ {
+ if (AreObjectsMarked()) {
+ aStr = ImpGetDescriptionString(STR_ViewMarked);
+ if (IsGluePointEditMode()) {
+ if (HasMarkedGluePoints()) {
+ aStr = ImpGetDescriptionString(STR_ViewMarked, ImpGetDescriptionOptions::GLUEPOINTS);
+ }
+ } else {
+ if (HasMarkedPoints()) {
+ aStr = ImpGetDescriptionString(STR_ViewMarked, ImpGetDescriptionOptions::POINTS);
+ }
+ }
+ } else {
+ aStr.clear();
+ }
+ }
+ else if(!aName.isEmpty())
+ {
+ aStr = aStr.replaceFirst("%1", aName);
+ }
+
+ if(!aStr.isEmpty())
+ {
+ // capitalize first letter
+ aStr = aStr.replaceAt(0, 1, OUString(aStr[0]).toAsciiUpperCase());
+ }
+ return aStr;
+}
+
+SdrViewContext SdrView::GetContext() const
+{
+ if( IsGluePointEditMode() )
+ return SdrViewContext::GluePointEdit;
+
+ const size_t nMarkCount = GetMarkedObjectCount();
+
+ if( HasMarkablePoints() && !IsFrameHandles() )
+ {
+ bool bPath=true;
+ for( size_t nMarkNum = 0; nMarkNum < nMarkCount && bPath; ++nMarkNum )
+ if (dynamic_cast<const SdrPathObj*>(GetMarkedObjectByIndex(nMarkNum)) == nullptr)
+ bPath=false;
+
+ if( bPath )
+ return SdrViewContext::PointEdit;
+ }
+
+ if( GetMarkedObjectCount() )
+ {
+ bool bGraf = true, bMedia = true, bTable = true;
+
+ for( size_t nMarkNum = 0; nMarkNum < nMarkCount && ( bGraf || bMedia ); ++nMarkNum )
+ {
+ const SdrObject* pMarkObj = GetMarkedObjectByIndex( nMarkNum );
+ DBG_ASSERT( pMarkObj, "SdrView::GetContext(), null pointer in mark list!" );
+
+ if( !pMarkObj )
+ continue;
+
+ if( dynamic_cast<const SdrGrafObj*>( pMarkObj) == nullptr )
+ bGraf = false;
+
+ if( dynamic_cast<const SdrMediaObj*>( pMarkObj) == nullptr )
+ bMedia = false;
+
+ if( dynamic_cast<const sdr::table::SdrTableObj* >( pMarkObj ) == nullptr )
+ bTable = false;
+ }
+
+ if( bGraf )
+ return SdrViewContext::Graphic;
+ else if( bMedia )
+ return SdrViewContext::Media;
+ else if( bTable )
+ return SdrViewContext::Table;
+ }
+
+ return SdrViewContext::Standard;
+}
+
+void SdrView::MarkAll()
+{
+ if (IsTextEdit()) {
+ GetTextEditOutlinerView()->SetSelection(ESelection(0,0,EE_PARA_ALL,EE_TEXTPOS_ALL));
+ } else if (IsGluePointEditMode()) MarkAllGluePoints();
+ else if (HasMarkablePoints()) MarkAllPoints();
+ else {
+ // check for table
+ bool bMarkAll = true;
+ const SdrMarkList& rMarkList = GetMarkedObjectList();
+ if (rMarkList.GetMarkCount() == 1)
+ {
+ const SdrObject* pObj(rMarkList.GetMark(0)->GetMarkedSdrObj());
+ SdrView* pView = this;
+ if (pObj && pView && (pObj->GetObjInventor() == SdrInventor::Default)
+ && (pObj->GetObjIdentifier() == SdrObjKind::Table))
+ {
+ mxSelectionController.clear();
+ mxSelectionController = sdr::table::CreateTableController(
+ *pView, static_cast<const sdr::table::SdrTableObj&>(*pObj),
+ mxLastSelectionController);
+
+ if (mxSelectionController.is())
+ {
+ mxLastSelectionController.clear();
+ mxSelectionController->onSelectAll();
+ bMarkAll = false;
+ }
+ }
+ }
+ if ( bMarkAll )
+ MarkAllObj();
+ }
+}
+
+void SdrView::UnmarkAll()
+{
+ if (IsTextEdit()) {
+ ESelection eSel=GetTextEditOutlinerView()->GetSelection();
+ eSel.nStartPara=eSel.nEndPara;
+ eSel.nStartPos=eSel.nEndPos;
+ GetTextEditOutlinerView()->SetSelection(eSel);
+ } else if (HasMarkedGluePoints()) UnmarkAllGluePoints();
+ else if (HasMarkedPoints()) UnmarkAllPoints(); // Marked, not Markable!
+ else UnmarkAllObj();
+}
+
+const tools::Rectangle& SdrView::GetMarkedRect() const
+{
+ if (IsGluePointEditMode() && HasMarkedGluePoints()) {
+ return GetMarkedGluePointsRect();
+ }
+ if (HasMarkedPoints()) {
+ return GetMarkedPointsRect();
+ }
+ return GetMarkedObjRect();
+}
+
+void SdrView::DeleteMarked()
+{
+ if (IsTextEdit())
+ {
+ SdrObjEditView::KeyInput(KeyEvent(0, vcl::KeyCode(KeyFuncType::DELETE)), mpTextEditWin);
+ }
+ else
+ {
+ if( mxSelectionController.is() && mxSelectionController->DeleteMarked() )
+ {
+ // action already performed by current selection controller, do nothing
+ }
+ else if (IsGluePointEditMode() && HasMarkedGluePoints())
+ {
+ DeleteMarkedGluePoints();
+ }
+ else if (GetContext()==SdrViewContext::PointEdit && HasMarkedPoints())
+ {
+ DeleteMarkedPoints();
+ }
+ else
+ {
+ DeleteMarkedObj();
+ }
+ }
+}
+
+bool SdrView::BegMark(const Point& rPnt, bool bAddMark, bool bUnmark)
+{
+ if (bUnmark) bAddMark=true;
+ if (IsGluePointEditMode()) {
+ if (!bAddMark) UnmarkAllGluePoints();
+ return BegMarkGluePoints(rPnt,bUnmark);
+ } else if (HasMarkablePoints()) {
+ if (!bAddMark) UnmarkAllPoints();
+ return BegMarkPoints(rPnt,bUnmark);
+ } else {
+ if (!bAddMark) UnmarkAllObj();
+ BegMarkObj(rPnt,bUnmark);
+ return true;
+ }
+}
+
+bool SdrView::MoveShapeHandle(const sal_uInt32 handleNum, const Point& aEndPoint, const sal_Int32 aObjectOrdNum)
+{
+ if (GetHdlList().IsMoveOutside())
+ return false;
+
+ if (!GetMarkedObjectList().GetMarkCount())
+ return false;
+
+ SdrHdl * pHdl = GetHdlList().GetHdl(handleNum);
+ if (pHdl == nullptr)
+ return false;
+
+ SdrDragStat& rDragStat = const_cast<SdrDragStat&>(GetDragStat());
+ // start dragging
+ BegDragObj(pHdl->GetPos(), nullptr, pHdl, 0);
+ if (!IsDragObj())
+ return false;
+
+ bool bWasNoSnap = rDragStat.IsNoSnap();
+ bool bWasSnapEnabled = IsSnapEnabled();
+
+ // switch snapping off
+ if(!bWasNoSnap)
+ rDragStat.SetNoSnap();
+ if(bWasSnapEnabled)
+ SetSnapEnabled(false);
+
+ if (aObjectOrdNum != -1)
+ {
+ rDragStat.GetGlueOptions().objectOrdNum = aObjectOrdNum;
+ }
+ MovDragObj(aEndPoint);
+ EndDragObj();
+
+ // Clear Glue Options
+ rDragStat.GetGlueOptions().objectOrdNum = -1;
+
+ if (!bWasNoSnap)
+ rDragStat.SetNoSnap(bWasNoSnap);
+ if (bWasSnapEnabled)
+ SetSnapEnabled(bWasSnapEnabled);
+
+ return true;
+}
+
+void SdrView::ConfigurationChanged( ::utl::ConfigurationBroadcaster*p, ConfigurationHints nHint)
+{
+ onAccessibilityOptionsChanged();
+ SdrCreateView::ConfigurationChanged(p, nHint);
+}
+
+
+/** method is called whenever the global SvtAccessibilityOptions is changed */
+void SdrView::onAccessibilityOptionsChanged()
+{
+}
+
+void SdrView::SetMasterPagePaintCaching(bool bOn)
+{
+ if(mbMasterPagePaintCaching == bOn)
+ return;
+
+ mbMasterPagePaintCaching = bOn;
+
+ // reset at all SdrPageWindows
+ SdrPageView* pPageView = GetSdrPageView();
+
+ if(!pPageView)
+ return;
+
+ for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
+ {
+ SdrPageWindow* pPageWindow = pPageView->GetPageWindow(b);
+ assert(pPageWindow && "SdrView::SetMasterPagePaintCaching: Corrupt SdrPageWindow list (!)");
+
+ // force deletion of ObjectContact, so at re-display all VOCs
+ // will be re-created with updated flag setting
+ pPageWindow->ResetObjectContact();
+ }
+
+ // force redraw of this view
+ pPageView->InvalidateAllWin();
+}
+
+// Default ObjectContact is ObjectContactOfPageView
+sdr::contact::ObjectContact* SdrView::createViewSpecificObjectContact(
+ SdrPageWindow& rPageWindow,
+ const char* pDebugName) const
+{
+ return new sdr::contact::ObjectContactOfPageView(rPageWindow, pDebugName);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdviter.cxx b/svx/source/svdraw/svdviter.cxx
new file mode 100644
index 000000000..ff2d6da5f
--- /dev/null
+++ b/svx/source/svdraw/svdviter.cxx
@@ -0,0 +1,164 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/svdviter.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdview.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdsob.hxx>
+
+void SdrViewIter::ImpInitVars()
+{
+ mnListenerNum = 0;
+ mpCurrentView = nullptr;
+}
+
+SdrViewIter::SdrViewIter(const SdrPage* pPage)
+{
+ mpPage = pPage;
+ mpModel = pPage ? &pPage->getSdrModelFromSdrPage() : nullptr;
+ mpObject = nullptr;
+ ImpInitVars();
+}
+
+SdrViewIter::SdrViewIter(const SdrObject* pObject)
+{
+ mpObject = pObject;
+ mpModel = pObject ? &pObject->getSdrModelFromSdrObject() : nullptr;
+ mpPage = pObject ? pObject->getSdrPageFromSdrObject() : nullptr;
+
+ if (!mpModel || !mpPage)
+ {
+ mpModel = nullptr;
+ mpPage = nullptr;
+ }
+
+ ImpInitVars();
+}
+
+bool SdrViewIter::ImpCheckPageView(SdrPageView const* pPV) const
+{
+ if (!mpPage)
+ return true;
+
+ bool bMaster(mpPage->IsMasterPage());
+ SdrPage* pPg = pPV->GetPage();
+
+ if (pPg == mpPage)
+ {
+ if (mpObject)
+ {
+ // Looking for an object? First, determine if it visible in
+ // this PageView.
+ SdrLayerIDSet aObjLay;
+ mpObject->getMergedHierarchySdrLayerIDSet(aObjLay);
+ aObjLay &= pPV->GetVisibleLayers();
+ return !aObjLay.IsEmpty();
+ }
+ else
+ {
+ return true;
+ }
+ }
+ else if (bMaster && (!mpObject || !mpObject->IsNotVisibleAsMaster()))
+ {
+ if (pPg->TRG_HasMasterPage())
+ {
+ SdrPage& rMasterPage = pPg->TRG_GetMasterPage();
+
+ if (&rMasterPage == mpPage)
+ {
+ // the page we're looking for is a master page in this PageView
+ if (mpObject)
+ {
+ // Looking for an object? First, determine if it visible in
+ // this PageView.
+ SdrLayerIDSet aObjLay;
+ mpObject->getMergedHierarchySdrLayerIDSet(aObjLay);
+ aObjLay &= pPV->GetVisibleLayers();
+ aObjLay &= pPg->TRG_GetMasterPageVisibleLayers();
+
+ if (!aObjLay.IsEmpty())
+ {
+ return true;
+ } // else, look at the next master page of this page...
+ }
+ else
+ {
+ return true;
+ }
+ }
+ }
+ }
+
+ // master page forbidden or no fitting master page found
+ return false;
+}
+
+SdrView* SdrViewIter::ImpFindView()
+{
+ if (mpModel)
+ {
+ const size_t nLsCnt(mpModel->GetSizeOfVector());
+
+ while (mnListenerNum < nLsCnt)
+ {
+ SfxListener* pLs = mpModel->GetListener(mnListenerNum);
+ mpCurrentView = dynamic_cast<SdrView*>(pLs);
+
+ if (mpCurrentView)
+ {
+ if (mpPage)
+ {
+ SdrPageView* pPV = mpCurrentView->GetSdrPageView();
+
+ if (pPV && ImpCheckPageView(pPV))
+ {
+ return mpCurrentView;
+ }
+ }
+ else
+ {
+ return mpCurrentView;
+ }
+ }
+
+ mnListenerNum++;
+ }
+ }
+
+ mpCurrentView = nullptr;
+ return mpCurrentView;
+}
+
+SdrView* SdrViewIter::FirstView()
+{
+ ImpInitVars();
+ return ImpFindView();
+}
+
+SdrView* SdrViewIter::NextView()
+{
+ mnListenerNum++;
+ return ImpFindView();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdxcgv.cxx b/svx/source/svdraw/svdxcgv.cxx
new file mode 100644
index 000000000..a9b720f30
--- /dev/null
+++ b/svx/source/svdraw/svdxcgv.cxx
@@ -0,0 +1,784 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vector>
+#include <unordered_set>
+#include <editeng/editdata.hxx>
+#include <rtl/strbuf.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/svdxcgv.hxx>
+#include <svx/svdoutl.hxx>
+#include <svx/svdundo.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdorect.hxx>
+#include <svx/svdopage.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdtrans.hxx>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include <tools/bigint.hxx>
+#include <clonelist.hxx>
+#include <vcl/virdev.hxx>
+#include <svl/style.hxx>
+#include <fmobj.hxx>
+#include <vcl/vectorgraphicdata.hxx>
+#include <drawinglayer/primitive2d/groupprimitive2d.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <drawinglayer/converters.hxx>
+#include <svx/sdr/contact/viewcontact.hxx>
+#include <sdr/contact/objectcontactofobjlistpainter.hxx>
+#include <svx/sdr/contact/displayinfo.hxx>
+#include <svx/svdotable.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <comphelper/lok.hxx>
+
+using namespace com::sun::star;
+
+SdrExchangeView::SdrExchangeView(
+ SdrModel& rSdrModel,
+ OutputDevice* pOut)
+: SdrObjEditView(rSdrModel, pOut)
+{
+}
+
+bool SdrExchangeView::ImpLimitToWorkArea(Point& rPt) const
+{
+ bool bRet(false);
+
+ if(!maMaxWorkArea.IsEmpty())
+ {
+ if(rPt.X()<maMaxWorkArea.Left())
+ {
+ rPt.setX( maMaxWorkArea.Left() );
+ bRet = true;
+ }
+
+ if(rPt.X()>maMaxWorkArea.Right())
+ {
+ rPt.setX( maMaxWorkArea.Right() );
+ bRet = true;
+ }
+
+ if(rPt.Y()<maMaxWorkArea.Top())
+ {
+ rPt.setY( maMaxWorkArea.Top() );
+ bRet = true;
+ }
+
+ if(rPt.Y()>maMaxWorkArea.Bottom())
+ {
+ rPt.setY( maMaxWorkArea.Bottom() );
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+void SdrExchangeView::ImpGetPasteObjList(Point& /*rPos*/, SdrObjList*& rpLst)
+{
+ if (rpLst==nullptr)
+ {
+ SdrPageView* pPV = GetSdrPageView();
+
+ if (pPV!=nullptr) {
+ rpLst=pPV->GetObjList();
+ }
+ }
+}
+
+bool SdrExchangeView::ImpGetPasteLayer(const SdrObjList* pObjList, SdrLayerID& rLayer) const
+{
+ bool bRet=false;
+ rLayer=SdrLayerID(0);
+ if (pObjList!=nullptr) {
+ const SdrPage* pPg=pObjList->getSdrPageFromSdrObjList();
+ if (pPg!=nullptr) {
+ rLayer=pPg->GetLayerAdmin().GetLayerID(maActualLayer);
+ if (rLayer==SDRLAYER_NOTFOUND) rLayer=SdrLayerID(0);
+ SdrPageView* pPV = GetSdrPageView();
+ if (pPV!=nullptr) {
+ bRet=!pPV->GetLockedLayers().IsSet(rLayer) && pPV->GetVisibleLayers().IsSet(rLayer);
+ }
+ }
+ }
+ return bRet;
+}
+
+bool SdrExchangeView::Paste(const OUString& rStr, const Point& rPos, SdrObjList* pLst, SdrInsertFlags nOptions)
+{
+ if (rStr.isEmpty())
+ return false;
+
+ Point aPos(rPos);
+ ImpGetPasteObjList(aPos,pLst);
+ ImpLimitToWorkArea( aPos );
+ if (pLst==nullptr) return false;
+ SdrLayerID nLayer;
+ if (!ImpGetPasteLayer(pLst,nLayer)) return false;
+ bool bUnmark = (nOptions & (SdrInsertFlags::DONTMARK|SdrInsertFlags::ADDMARK))==SdrInsertFlags::NONE && !IsTextEdit();
+ if (bUnmark) UnmarkAllObj();
+ tools::Rectangle aTextRect(0,0,500,500);
+ SdrPage* pPage=pLst->getSdrPageFromSdrObjList();
+ if (pPage!=nullptr) {
+ aTextRect.SetSize(pPage->GetSize());
+ }
+ SdrRectObj* pObj = new SdrRectObj(
+ getSdrModelFromSdrView(),
+ SdrObjKind::Text,
+ aTextRect);
+
+ pObj->SetLayer(nLayer);
+ pObj->NbcSetText(rStr); // SetText before SetAttr, else SetAttr doesn't work!
+ if (mpDefaultStyleSheet!=nullptr) pObj->NbcSetStyleSheet(mpDefaultStyleSheet, false);
+
+ pObj->SetMergedItemSet(maDefaultAttr);
+
+ SfxItemSet aTempAttr(mpModel->GetItemPool()); // no fill, no line
+ aTempAttr.Put(XLineStyleItem(drawing::LineStyle_NONE));
+ aTempAttr.Put(XFillStyleItem(drawing::FillStyle_NONE));
+
+ pObj->SetMergedItemSet(aTempAttr);
+
+ pObj->FitFrameToTextSize();
+ Size aSiz(pObj->GetLogicRect().GetSize());
+ MapUnit eMap=mpModel->GetScaleUnit();
+ Fraction aMap=mpModel->GetScaleFraction();
+ ImpPasteObject(pObj,*pLst,aPos,aSiz,MapMode(eMap,Point(0,0),aMap,aMap),nOptions);
+ return true;
+}
+
+bool SdrExchangeView::Paste(SvStream& rInput, EETextFormat eFormat, const Point& rPos, SdrObjList* pLst, SdrInsertFlags nOptions)
+{
+ Point aPos(rPos);
+ ImpGetPasteObjList(aPos,pLst);
+ ImpLimitToWorkArea( aPos );
+ if (pLst==nullptr) return false;
+ SdrLayerID nLayer;
+ if (!ImpGetPasteLayer(pLst,nLayer)) return false;
+ bool bUnmark=(nOptions&(SdrInsertFlags::DONTMARK|SdrInsertFlags::ADDMARK))==SdrInsertFlags::NONE && !IsTextEdit();
+ if (bUnmark) UnmarkAllObj();
+ tools::Rectangle aTextRect(0,0,500,500);
+ SdrPage* pPage=pLst->getSdrPageFromSdrObjList();
+ if (pPage!=nullptr) {
+ aTextRect.SetSize(pPage->GetSize());
+ }
+ SdrRectObj* pObj = new SdrRectObj(
+ getSdrModelFromSdrView(),
+ SdrObjKind::Text,
+ aTextRect);
+
+ pObj->SetLayer(nLayer);
+ if (mpDefaultStyleSheet!=nullptr) pObj->NbcSetStyleSheet(mpDefaultStyleSheet, false);
+
+ pObj->SetMergedItemSet(maDefaultAttr);
+
+ SfxItemSet aTempAttr(mpModel->GetItemPool()); // no fill, no line
+ aTempAttr.Put(XLineStyleItem(drawing::LineStyle_NONE));
+ aTempAttr.Put(XFillStyleItem(drawing::FillStyle_NONE));
+
+ pObj->SetMergedItemSet(aTempAttr);
+
+ pObj->NbcSetText(rInput,OUString(),eFormat);
+ pObj->FitFrameToTextSize();
+ Size aSiz(pObj->GetLogicRect().GetSize());
+ MapUnit eMap=mpModel->GetScaleUnit();
+ Fraction aMap=mpModel->GetScaleFraction();
+ ImpPasteObject(pObj,*pLst,aPos,aSiz,MapMode(eMap,Point(0,0),aMap,aMap),nOptions);
+
+ // b4967543
+ if(pObj->GetOutlinerParaObject())
+ {
+ SdrOutliner& rOutliner = pObj->getSdrModelFromSdrObject().GetHitTestOutliner();
+ rOutliner.SetText(*pObj->GetOutlinerParaObject());
+
+ if(1 == rOutliner.GetParagraphCount())
+ {
+ SfxStyleSheet* pCandidate = rOutliner.GetStyleSheet(0);
+
+ if(pCandidate)
+ {
+ if(pObj->getSdrModelFromSdrObject().GetStyleSheetPool() == pCandidate->GetPool())
+ {
+ pObj->NbcSetStyleSheet(pCandidate, true);
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+bool SdrExchangeView::Paste(
+ const SdrModel& rMod, const Point& rPos, SdrObjList* pLst, SdrInsertFlags nOptions)
+{
+ const SdrModel* pSrcMod=&rMod;
+ if (pSrcMod==mpModel)
+ return false; // this can't work, right?
+
+ const bool bUndo = IsUndoEnabled();
+
+ if( bUndo )
+ BegUndo(SvxResId(STR_ExchangePaste));
+
+ if( mxSelectionController.is() && mxSelectionController->PasteObjModel( rMod ) )
+ {
+ if( bUndo )
+ EndUndo();
+ return true;
+ }
+
+ Point aPos(rPos);
+ ImpGetPasteObjList(aPos,pLst);
+ SdrPageView* pMarkPV=nullptr;
+ SdrPageView* pPV = GetSdrPageView();
+
+ if(pPV && pPV->GetObjList() == pLst )
+ pMarkPV=pPV;
+
+ ImpLimitToWorkArea( aPos );
+ if (pLst==nullptr)
+ return false;
+
+ bool bUnmark=(nOptions&(SdrInsertFlags::DONTMARK|SdrInsertFlags::ADDMARK))==SdrInsertFlags::NONE && !IsTextEdit();
+ if (bUnmark)
+ UnmarkAllObj();
+
+ // Rescale, if the Model uses a different MapUnit.
+ // Calculate the necessary factors first.
+ MapUnit eSrcUnit=pSrcMod->GetScaleUnit();
+ MapUnit eDstUnit=mpModel->GetScaleUnit();
+ bool bResize=eSrcUnit!=eDstUnit;
+ Fraction aXResize,aYResize;
+ Point aPt0;
+ if (bResize)
+ {
+ FrPair aResize(GetMapFactor(eSrcUnit,eDstUnit));
+ aXResize=aResize.X();
+ aYResize=aResize.Y();
+ }
+ SdrObjList* pDstLst=pLst;
+ sal_uInt16 nPg,nPgCount=pSrcMod->GetPageCount();
+ for (nPg=0; nPg<nPgCount; nPg++)
+ {
+ const SdrPage* pSrcPg=pSrcMod->GetPage(nPg);
+
+ // Use SnapRect, not BoundRect here
+ tools::Rectangle aR=pSrcPg->GetAllObjSnapRect();
+
+ if (bResize)
+ ResizeRect(aR,aPt0,aXResize,aYResize);
+ Point aDist(aPos-aR.Center());
+ Size aSiz(aDist.X(),aDist.Y());
+ size_t nCloneErrCnt = 0;
+ const size_t nObjCount = pSrcPg->GetObjCount();
+ bool bMark = pMarkPV!=nullptr && !IsTextEdit() && (nOptions&SdrInsertFlags::DONTMARK)==SdrInsertFlags::NONE;
+
+ // #i13033#
+ // New mechanism to re-create the connections of cloned connectors
+ CloneList aCloneList;
+ std::unordered_set<rtl::OUString> aNameSet;
+ for (size_t nOb=0; nOb<nObjCount; ++nOb)
+ {
+ const SdrObject* pSrcOb=pSrcPg->GetObj(nOb);
+
+ SdrObject* pNewObj(pSrcOb->CloneSdrObject(*mpModel));
+
+ if (pNewObj!=nullptr)
+ {
+ if(bResize)
+ {
+ pNewObj->getSdrModelFromSdrObject().SetPasteResize(true);
+ pNewObj->NbcResize(aPt0,aXResize,aYResize);
+ pNewObj->getSdrModelFromSdrObject().SetPasteResize(false);
+ }
+
+ // #i39861#
+ pNewObj->NbcMove(aSiz);
+
+ const SdrPage* pPg = pDstLst->getSdrPageFromSdrObjList();
+
+ if(pPg)
+ {
+ // #i72535#
+ const SdrLayerAdmin& rAd = pPg->GetLayerAdmin();
+ SdrLayerID nLayer(0);
+
+ if(dynamic_cast<const FmFormObj*>( pNewObj) != nullptr)
+ {
+ // for FormControls, force to form layer
+ nLayer = rAd.GetLayerID(rAd.GetControlLayerName());
+ }
+ else
+ {
+ nLayer = rAd.GetLayerID(maActualLayer);
+ }
+
+ if(SDRLAYER_NOTFOUND == nLayer)
+ {
+ nLayer = SdrLayerID(0);
+ }
+
+ pNewObj->SetLayer(nLayer);
+ }
+
+ pDstLst->InsertObjectThenMakeNameUnique(pNewObj, aNameSet);
+
+ if( bUndo )
+ AddUndo(getSdrModelFromSdrView().GetSdrUndoFactory().CreateUndoNewObject(*pNewObj));
+
+ if (bMark) {
+ // Don't already set Markhandles!
+ // That is instead being done by ModelHasChanged in MarkView.
+ MarkObj(pNewObj,pMarkPV,false,true);
+ }
+
+ // #i13033#
+ aCloneList.AddPair(pSrcOb, pNewObj);
+ }
+ else
+ {
+ nCloneErrCnt++;
+ }
+ }
+
+ // #i13033#
+ // New mechanism to re-create the connections of cloned connectors
+ aCloneList.CopyConnections();
+
+ if(0 != nCloneErrCnt)
+ {
+#ifdef DBG_UTIL
+ OStringBuffer aStr("SdrExchangeView::Paste(): Error when cloning ");
+
+ if(nCloneErrCnt == 1)
+ {
+ aStr.append("a drawing object.");
+ }
+ else
+ {
+ aStr.append(static_cast<sal_Int32>(nCloneErrCnt));
+ aStr.append(" drawing objects.");
+ }
+
+ aStr.append(" Not copying object connectors.");
+
+ OSL_FAIL(aStr.getStr());
+#endif
+ }
+ }
+
+ if( bUndo )
+ EndUndo();
+
+ return true;
+}
+
+void SdrExchangeView::ImpPasteObject(SdrObject* pObj, SdrObjList& rLst, const Point& rCenter, const Size& rSiz, const MapMode& rMap, SdrInsertFlags nOptions)
+{
+ BigInt nSizX(rSiz.Width());
+ BigInt nSizY(rSiz.Height());
+ MapUnit eSrcMU=rMap.GetMapUnit();
+ MapUnit eDstMU=mpModel->GetScaleUnit();
+ FrPair aMapFact(GetMapFactor(eSrcMU,eDstMU));
+ Fraction aDstFr(mpModel->GetScaleFraction());
+ nSizX *= double(aMapFact.X() * rMap.GetScaleX() * aDstFr);
+ nSizX *= aDstFr.GetDenominator();
+ nSizY *= double(aMapFact.Y() * rMap.GetScaleY());
+ nSizY /= aDstFr.GetNumerator();
+ tools::Long xs=nSizX;
+ tools::Long ys=nSizY;
+ // set the pos to 0, 0 for online case
+ bool isLOK = comphelper::LibreOfficeKit::isActive();
+ Point aPos(isLOK ? 0 : rCenter.X()-xs/2, isLOK ? 0 : rCenter.Y()-ys/2);
+ tools::Rectangle aR(aPos.X(),aPos.Y(),aPos.X()+xs,aPos.Y()+ys);
+ pObj->SetLogicRect(aR);
+ rLst.InsertObject(pObj, SAL_MAX_SIZE);
+
+ if( IsUndoEnabled() )
+ AddUndo(getSdrModelFromSdrView().GetSdrUndoFactory().CreateUndoNewObject(*pObj));
+
+ SdrPageView* pMarkPV=nullptr;
+ SdrPageView* pPV = GetSdrPageView();
+
+ if(pPV && pPV->GetObjList()==&rLst)
+ pMarkPV=pPV;
+
+ bool bMark = pMarkPV!=nullptr && !IsTextEdit() && (nOptions&SdrInsertFlags::DONTMARK)==SdrInsertFlags::NONE;
+ if (bMark)
+ { // select object the first PageView we found
+ MarkObj(pObj,pMarkPV);
+ }
+}
+
+BitmapEx SdrExchangeView::GetMarkedObjBitmapEx(bool bNoVDevIfOneBmpMarked, const sal_uInt32 nMaximumQuadraticPixels, const std::optional<Size>& rTargetDPI) const
+{
+ BitmapEx aBmp;
+
+ if( AreObjectsMarked() )
+ {
+ if(1 == GetMarkedObjectCount())
+ {
+ if(bNoVDevIfOneBmpMarked)
+ {
+ SdrObject* pGrafObjTmp = GetMarkedObjectByIndex( 0 );
+ SdrGrafObj* pGrafObj = dynamic_cast<SdrGrafObj*>( pGrafObjTmp );
+
+ if( pGrafObj && ( pGrafObj->GetGraphicType() == GraphicType::Bitmap ) )
+ {
+ aBmp = pGrafObj->GetTransformedGraphic().GetBitmapEx();
+ }
+ }
+ else
+ {
+ const SdrGrafObj* pSdrGrafObj = dynamic_cast< const SdrGrafObj* >(GetMarkedObjectByIndex(0));
+
+ if(pSdrGrafObj && pSdrGrafObj->isEmbeddedVectorGraphicData())
+ {
+ aBmp = pSdrGrafObj->GetGraphic().getVectorGraphicData()->getReplacement();
+ }
+ }
+ }
+
+ if( aBmp.IsEmpty() )
+ {
+ // choose conversion directly using primitives to bitmap to avoid
+ // rendering errors with tiled bitmap fills (these will be tiled in a
+ // in-between metafile, but tend to show 'gaps' since the target is *no*
+ // bitmap rendering)
+ ::std::vector< SdrObject* > aSdrObjects(GetMarkedObjects());
+ const sal_uInt32 nCount(aSdrObjects.size());
+
+ if(nCount)
+ {
+ // collect sub-primitives as group objects, thus no expensive append
+ // to existing sequence is needed
+ drawinglayer::primitive2d::Primitive2DContainer xPrimitives(nCount);
+
+ for(sal_uInt32 a(0); a < nCount; a++)
+ {
+ SdrObject* pCandidate = aSdrObjects[a];
+ SdrGrafObj* pSdrGrafObj = dynamic_cast< SdrGrafObj* >(pCandidate);
+
+ if(pSdrGrafObj)
+ {
+ // #122753# To ensure existence of graphic content, force swap in
+ pSdrGrafObj->ForceSwapIn();
+ }
+
+ drawinglayer::primitive2d::Primitive2DContainer xRetval;
+ pCandidate->GetViewContact().getViewIndependentPrimitive2DContainer(xRetval);
+ xPrimitives[a] = new drawinglayer::primitive2d::GroupPrimitive2D(
+ std::move(xRetval));
+ }
+
+ // get logic range
+ const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
+ const basegfx::B2DRange aRange(xPrimitives.getB2DRange(aViewInformation2D));
+
+ if(!aRange.isEmpty())
+ {
+ o3tl::Length eRangeUnit = o3tl::Length::mm100;
+
+ if (GetModel()->IsWriter())
+ {
+ eRangeUnit = o3tl::Length::twip;
+ }
+
+ // if we have geometry and it has a range, convert to BitmapEx using
+ // common tooling
+ aBmp = drawinglayer::convertPrimitive2DContainerToBitmapEx(
+ std::move(xPrimitives),
+ aRange,
+ nMaximumQuadraticPixels,
+ eRangeUnit,
+ rTargetDPI);
+ }
+ }
+ }
+ }
+
+ return aBmp;
+}
+
+
+GDIMetaFile SdrExchangeView::GetMarkedObjMetaFile(bool bNoVDevIfOneMtfMarked) const
+{
+ GDIMetaFile aMtf;
+
+ if( AreObjectsMarked() )
+ {
+ tools::Rectangle aBound( GetMarkedObjBoundRect() );
+ Size aBoundSize( aBound.GetWidth(), aBound.GetHeight() );
+ MapMode aMap( mpModel->GetScaleUnit(), Point(), mpModel->GetScaleFraction(), mpModel->GetScaleFraction() );
+
+ if( bNoVDevIfOneMtfMarked )
+ {
+ SdrObject* pGrafObjTmp = GetMarkedObjectByIndex( 0 );
+ SdrGrafObj* pGrafObj = ( GetMarkedObjectCount() ==1 ) ? dynamic_cast<SdrGrafObj*>( pGrafObjTmp ) : nullptr;
+
+ if( pGrafObj )
+ {
+ Graphic aGraphic( pGrafObj->GetTransformedGraphic() );
+
+ // #119735# just use GetGDIMetaFile, it will create a buffered version of contained bitmap now automatically
+ aMtf = aGraphic.GetGDIMetaFile();
+ }
+ }
+
+ if( !aMtf.GetActionSize() )
+ {
+ ScopedVclPtrInstance< VirtualDevice > pOut;
+ const Size aDummySize(2, 2);
+
+ pOut->SetOutputSizePixel(aDummySize);
+ pOut->EnableOutput(false);
+ pOut->SetMapMode(aMap);
+ aMtf.Clear();
+ aMtf.Record(pOut);
+
+ DrawMarkedObj(*pOut);
+
+ aMtf.Stop();
+ aMtf.WindStart();
+
+ // moving the result is more reliable then setting a relative MapMode at the VDev (used
+ // before), also see #i99268# in GetObjGraphic() below. Some draw actions at
+ // the OutDev are simply not handled correctly when a MapMode is set at the
+ // target device, e.g. MetaFloatTransparentAction. Even the Move for this action
+ // was missing the manipulation of the embedded Metafile
+ aMtf.Move(-aBound.Left(), -aBound.Top());
+
+ aMtf.SetPrefMapMode( aMap );
+
+ // removed PrefSize extension. It is principally wrong to set a reduced size at
+ // the created MetaFile. The mentioned errors occur at output time since the integer
+ // MapModes from VCL lead to errors. It is now corrected in the VCLRenderer for
+ // primitives (and may later be done in breaking up a MetaFile to primitives)
+ aMtf.SetPrefSize(aBoundSize);
+ }
+ }
+
+ return aMtf;
+}
+
+
+Graphic SdrExchangeView::GetAllMarkedGraphic() const
+{
+ Graphic aRet;
+
+ if( AreObjectsMarked() )
+ {
+ if( ( 1 == GetMarkedObjectCount() ) && GetSdrMarkByIndex( 0 ) )
+ aRet = SdrExchangeView::GetObjGraphic(*GetMarkedObjectByIndex(0));
+ else
+ aRet = GetMarkedObjMetaFile();
+ }
+
+ return aRet;
+}
+
+
+Graphic SdrExchangeView::GetObjGraphic(const SdrObject& rSdrObject)
+{
+ Graphic aRet;
+
+ // try to get a graphic from the object first
+ const SdrGrafObj* pSdrGrafObj(dynamic_cast< const SdrGrafObj* >(&rSdrObject));
+ const SdrOle2Obj* pSdrOle2Obj(dynamic_cast< const SdrOle2Obj* >(&rSdrObject));
+
+ if(pSdrGrafObj)
+ {
+ if(pSdrGrafObj->isEmbeddedVectorGraphicData())
+ {
+ // get Metafile for Svg content
+ aRet = pSdrGrafObj->getMetafileFromEmbeddedVectorGraphicData();
+ }
+ else
+ {
+ // Make behaviour coherent with metafile
+ // recording below (which of course also takes
+ // view-transformed objects)
+ aRet = pSdrGrafObj->GetTransformedGraphic();
+ }
+ }
+ else if(pSdrOle2Obj)
+ {
+ if(pSdrOle2Obj->GetGraphic())
+ {
+ aRet = *pSdrOle2Obj->GetGraphic();
+ }
+ }
+
+ // if graphic could not be retrieved => go the hard way and create a MetaFile
+ if((GraphicType::NONE == aRet.GetType()) || (GraphicType::Default == aRet.GetType()))
+ {
+ ScopedVclPtrInstance< VirtualDevice > pOut;
+ GDIMetaFile aMtf;
+ const tools::Rectangle aBoundRect(rSdrObject.GetCurrentBoundRect());
+ const MapMode aMap(rSdrObject.getSdrModelFromSdrObject().GetScaleUnit(),
+ Point(),
+ rSdrObject.getSdrModelFromSdrObject().GetScaleFraction(),
+ rSdrObject.getSdrModelFromSdrObject().GetScaleFraction());
+
+ pOut->EnableOutput(false);
+ pOut->SetMapMode(aMap);
+ aMtf.Record(pOut);
+ rSdrObject.SingleObjectPainter(*pOut);
+ aMtf.Stop();
+ aMtf.WindStart();
+
+ // #i99268# replace the original offset from using XOutDev's SetOffset
+ // NOT (as tried with #i92760#) with another MapMode which gets recorded
+ // by the Metafile itself (what always leads to problems), but by
+ // moving the result directly
+ aMtf.Move(-aBoundRect.Left(), -aBoundRect.Top());
+ aMtf.SetPrefMapMode(aMap);
+ aMtf.SetPrefSize(aBoundRect.GetSize());
+
+ if(aMtf.GetActionSize())
+ {
+ aRet = aMtf;
+ }
+ }
+
+ return aRet;
+}
+
+
+::std::vector< SdrObject* > SdrExchangeView::GetMarkedObjects() const
+{
+ SortMarkedObjects();
+ ::std::vector< SdrObject* > aRetval;
+
+ ::std::vector< ::std::vector< SdrMark* > > aObjVectors( 2 );
+ ::std::vector< SdrMark* >& rObjVector1 = aObjVectors[ 0 ];
+ ::std::vector< SdrMark* >& rObjVector2 = aObjVectors[ 1 ];
+ const SdrLayerAdmin& rLayerAdmin = mpModel->GetLayerAdmin();
+ const SdrLayerID nControlLayerId = rLayerAdmin.GetLayerID( rLayerAdmin.GetControlLayerName() );
+
+ for( size_t n = 0, nCount = GetMarkedObjectCount(); n < nCount; ++n )
+ {
+ SdrMark* pMark = GetSdrMarkByIndex( n );
+
+ // paint objects on control layer on top of all other objects
+ if( nControlLayerId == pMark->GetMarkedSdrObj()->GetLayer() )
+ rObjVector2.push_back( pMark );
+ else
+ rObjVector1.push_back( pMark );
+ }
+
+ for(const std::vector<SdrMark*> & rObjVector : aObjVectors)
+ {
+ for(SdrMark* pMark : rObjVector)
+ {
+ aRetval.push_back(pMark->GetMarkedSdrObj());
+ }
+ }
+
+ return aRetval;
+}
+
+
+void SdrExchangeView::DrawMarkedObj(OutputDevice& rOut) const
+{
+ ::std::vector< SdrObject* > aSdrObjects(GetMarkedObjects());
+
+ if(!aSdrObjects.empty())
+ {
+ sdr::contact::ObjectContactOfObjListPainter aPainter(rOut, std::move(aSdrObjects), aSdrObjects[0]->getSdrPageFromSdrObject());
+ sdr::contact::DisplayInfo aDisplayInfo;
+
+ // do processing
+ aPainter.ProcessDisplay(aDisplayInfo);
+ }
+}
+
+std::unique_ptr<SdrModel> SdrExchangeView::CreateMarkedObjModel() const
+{
+ // Sorting the MarkList here might be problematic in the future, so
+ // use a copy.
+ SortMarkedObjects();
+ std::unique_ptr<SdrModel> pNewModel(mpModel->AllocModel());
+ rtl::Reference<SdrPage> pNewPage = pNewModel->AllocPage(false);
+ pNewModel->InsertPage(pNewPage.get());
+ ::std::vector< SdrObject* > aSdrObjects(GetMarkedObjects());
+
+ // #i13033#
+ // New mechanism to re-create the connections of cloned connectors
+ CloneList aCloneList;
+
+ for(SdrObject* pObj : aSdrObjects)
+ {
+ SdrObject* pNewObj(nullptr);
+
+ if(nullptr != dynamic_cast< const SdrPageObj* >(pObj))
+ {
+ // convert SdrPageObj's to a graphic representation, because
+ // virtual connection to referenced page gets lost in new model
+ pNewObj = new SdrGrafObj(
+ *pNewModel,
+ GetObjGraphic(*pObj),
+ pObj->GetLogicRect());
+ }
+ else if(nullptr != dynamic_cast< const sdr::table::SdrTableObj* >(pObj))
+ {
+ // check if we have a valid selection *different* from whole table
+ // being selected
+ if(mxSelectionController.is())
+ {
+ pNewObj = mxSelectionController->GetMarkedSdrObjClone(*pNewModel);
+ }
+ }
+
+ if(nullptr == pNewObj)
+ {
+ // not cloned yet
+ if(pObj->GetObjIdentifier() == SdrObjKind::OLE2 && nullptr == mpModel->GetPersist())
+ {
+ // tdf#125520 - former fix was wrong, the SdrModel
+ // has to have a GetPersist() already, see task.
+ // We can still warn here when this is not the case
+ SAL_WARN( "svx", "OLE gets cloned Persist, EmbeddedObjectContainer will not be copied" );
+ }
+
+ // use default way
+ pNewObj = pObj->CloneSdrObject(*pNewModel);
+ }
+
+ if(pNewObj)
+ {
+ pNewPage->InsertObject(pNewObj, SAL_MAX_SIZE);
+
+ // #i13033#
+ aCloneList.AddPair(pObj, pNewObj);
+ }
+ }
+
+ // #i13033#
+ // New mechanism to re-create the connections of cloned connectors
+ aCloneList.CopyConnections();
+
+ return pNewModel;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/textchain.cxx b/svx/source/svdraw/textchain.cxx
new file mode 100644
index 000000000..32c1f9b4e
--- /dev/null
+++ b/svx/source/svdraw/textchain.cxx
@@ -0,0 +1,128 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <textchain.hxx>
+#include <svx/svdotext.hxx>
+
+/*
+ * Definition of Properties Interface
+*/
+
+CursorChainingEvent const & TextChain::GetCursorEvent(const SdrTextObj *pTarget)
+{
+ ImpChainLinkProperties *pLinkProperties = GetLinkProperties(pTarget);
+ return pLinkProperties->aCursorEvent;
+}
+void TextChain::SetCursorEvent(const SdrTextObj *pTarget, CursorChainingEvent const & rPropParam)
+{
+ ImpChainLinkProperties *pLinkProperties = GetLinkProperties(pTarget);
+ pLinkProperties->aCursorEvent = rPropParam;
+}
+
+bool TextChain::GetNilChainingEvent(const SdrTextObj *pTarget)
+{
+ ImpChainLinkProperties *pLinkProperties = GetLinkProperties(pTarget);
+ return pLinkProperties->aNilChainingEvent;
+}
+void TextChain::SetNilChainingEvent(const SdrTextObj *pTarget, bool b)
+{
+ ImpChainLinkProperties *pLinkProperties = GetLinkProperties(pTarget);
+ pLinkProperties->aNilChainingEvent = b;
+}
+
+ESelection const & TextChain::GetPreChainingSel(const SdrTextObj *pTarget)
+{
+ ImpChainLinkProperties *pLinkProperties = GetLinkProperties(pTarget);
+ return pLinkProperties->aPreChainingSel;
+}
+void TextChain::SetPreChainingSel(const SdrTextObj *pTarget, ESelection const & rPropParam)
+{
+ ImpChainLinkProperties *pLinkProperties = GetLinkProperties(pTarget);
+ pLinkProperties->aPreChainingSel = rPropParam;
+}
+
+ESelection const & TextChain::GetPostChainingSel(const SdrTextObj *pTarget)
+{
+ ImpChainLinkProperties *pLinkProperties = GetLinkProperties(pTarget);
+ return pLinkProperties->aPostChainingSel;
+}
+void TextChain::SetPostChainingSel(const SdrTextObj *pTarget, ESelection const & rPropParam)
+{
+ ImpChainLinkProperties *pLinkProperties = GetLinkProperties(pTarget);
+ pLinkProperties->aPostChainingSel = rPropParam;
+}
+
+bool TextChain::GetIsPartOfLastParaInNextLink(const SdrTextObj *pTarget)
+{
+ ImpChainLinkProperties *pLinkProperties = GetLinkProperties(pTarget);
+ return pLinkProperties->aIsPartOfLastParaInNextLink;
+}
+void TextChain::SetIsPartOfLastParaInNextLink(const SdrTextObj *pTarget, bool b)
+{
+ ImpChainLinkProperties *pLinkProperties = GetLinkProperties(pTarget);
+ pLinkProperties->aIsPartOfLastParaInNextLink = b;
+}
+
+bool TextChain::GetSwitchingToNextBox(const SdrTextObj *pTarget)
+{
+ ImpChainLinkProperties *pLinkProperties = GetLinkProperties(pTarget);
+ return pLinkProperties->aSwitchingToNextBox;
+}
+void TextChain::SetSwitchingToNextBox(const SdrTextObj *pTarget, bool b)
+{
+ ImpChainLinkProperties *pLinkProperties = GetLinkProperties(pTarget);
+ pLinkProperties->aSwitchingToNextBox = b;
+}
+
+/* End Definition of Properties Interface */
+
+/* TextChain */
+
+// NOTE: All getters in the class assume that the guy is in the chain
+
+TextChain::TextChain()
+{
+}
+
+TextChain::~TextChain()
+{
+ // XXX: Should free all LinkProperties
+}
+
+namespace {
+
+ChainLinkId GetId(const SdrTextObj *pLink)
+{
+ return pLink->GetName();
+}
+
+}
+
+ImpChainLinkProperties *TextChain::GetLinkProperties(const SdrTextObj *pLink)
+{
+ // if the guy does not already have properties in the map make them
+ ChainLinkId aLinkId = GetId(pLink);
+ if (maLinkPropertiesMap.find(aLinkId) == maLinkPropertiesMap.end()) {
+ maLinkPropertiesMap[aLinkId] = new ImpChainLinkProperties;
+ }
+
+ return maLinkPropertiesMap[aLinkId];
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/textchaincursor.cxx b/svx/source/svdraw/textchaincursor.cxx
new file mode 100644
index 000000000..51c4d1d8e
--- /dev/null
+++ b/svx/source/svdraw/textchaincursor.cxx
@@ -0,0 +1,203 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <textchain.hxx>
+#include <textchaincursor.hxx>
+#include <svx/svdedxv.hxx>
+#include <svx/svdoutl.hxx>
+#include <vcl/event.hxx>
+
+// XXX: Possible duplication of code in behavior with stuff in ImpEditView (or ImpEditEngine) and OutlinerView
+
+// XXX: We violate Demeter's Law several times here, I'm afraid
+
+TextChainCursorManager::TextChainCursorManager(SdrObjEditView *pEditView, const SdrTextObj *pTextObj) :
+ mpEditView(pEditView),
+ mpTextObj(pTextObj),
+ mbHandlingDel(false)
+{
+ assert(mpEditView);
+ assert(mpTextObj);
+
+}
+
+bool TextChainCursorManager::HandleKeyEvent( const KeyEvent& rKEvt )
+{
+ ESelection aNewSel;
+ CursorChainingEvent aCursorEvent;
+
+ // check what the cursor/event situation looks like
+ bool bCompletelyHandled = false;
+ impDetectEvent(rKEvt, aCursorEvent, aNewSel, bCompletelyHandled);
+
+ if (aCursorEvent == CursorChainingEvent::NULL_EVENT)
+ return false;
+ else {
+ HandleCursorEvent(aCursorEvent, aNewSel);
+ // return value depends on the situation we are in
+ return bCompletelyHandled;
+ }
+}
+
+void TextChainCursorManager::impDetectEvent(const KeyEvent& rKEvt,
+ CursorChainingEvent& rOutCursorEvt,
+ ESelection& rOutSel,
+ bool& rOutHandled)
+{
+ SdrOutliner *pOutl = mpEditView->GetTextEditOutliner();
+ OutlinerView *pOLV = mpEditView->GetTextEditOutlinerView();
+
+ SdrTextObj *pNextLink = mpTextObj->GetNextLinkInChain();
+ SdrTextObj *pPrevLink = mpTextObj->GetPrevLinkInChain();
+
+ KeyFuncType eFunc = rKEvt.GetKeyCode().GetFunction();
+
+ // We need to have this KeyFuncType
+ if (eFunc != KeyFuncType::DONTKNOW && eFunc != KeyFuncType::DELETE)
+ {
+ rOutCursorEvt = CursorChainingEvent::NULL_EVENT;
+ return;
+ }
+
+ sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode();
+ ESelection aCurSel = pOLV->GetSelection();
+
+ ESelection aEndSelPrevBox(100000, 100000);
+
+ sal_Int32 nLastPara = pOutl->GetParagraphCount()-1;
+ OUString aLastParaText = pOutl->GetText(pOutl->GetParagraph(nLastPara));
+ sal_Int32 nLastParaLen = aLastParaText.getLength();
+
+ ESelection aEndSel(nLastPara, nLastParaLen);
+ bool bAtEndOfTextContent = aCurSel == aEndSel;
+
+ // Possibility: Are we "pushing" at the end of the object?
+ if (nCode == KEY_RIGHT && bAtEndOfTextContent && pNextLink)
+ {
+ rOutCursorEvt = CursorChainingEvent::TO_NEXT_LINK;
+ // Selection unchanged: we are at the beginning of the box
+ rOutHandled = true; // Nothing more to do than move cursor
+ return;
+ }
+
+ // Possibility: Are we "pushing" at the end of the object?
+ if (eFunc == KeyFuncType::DELETE && bAtEndOfTextContent && pNextLink)
+ {
+ rOutCursorEvt = CursorChainingEvent::TO_NEXT_LINK;
+ // Selection unchanged: we are at the beginning of the box
+ rOutHandled = false; // We still need to delete the characters
+ mbHandlingDel = true;
+ return;
+ }
+
+ ESelection aStartSel(0, 0);
+ bool bAtStartOfTextContent = aCurSel == aStartSel;
+
+ // Possibility: Are we "pushing" at the start of the object?
+ if (nCode == KEY_LEFT && bAtStartOfTextContent && pPrevLink)
+ {
+ rOutCursorEvt = CursorChainingEvent::TO_PREV_LINK;
+ rOutSel = aEndSelPrevBox; // Set at end of selection
+ rOutHandled = true; // Nothing more to do than move cursor
+ return;
+ }
+
+ // Possibility: Are we "pushing" at the start of the object and deleting left?
+ if (nCode == KEY_BACKSPACE && bAtStartOfTextContent && pPrevLink)
+ {
+ rOutCursorEvt = CursorChainingEvent::TO_PREV_LINK;
+ rOutSel = aEndSelPrevBox; // Set at end of selection
+ rOutHandled = false; // We need to delete characters after moving cursor
+ return;
+ }
+
+ // If arrived here there is no event detected
+ rOutCursorEvt = CursorChainingEvent::NULL_EVENT;
+
+}
+
+void TextChainCursorManager::HandleCursorEventAfterChaining(
+ const CursorChainingEvent aCurEvt,
+ const ESelection& aNewSel)
+
+{
+ // Special case for DELETE handling: we need to get back at the end of the prev box
+ if (mbHandlingDel) {
+ // reset flag
+ mbHandlingDel = false;
+
+ // Move to end of prev box
+ SdrTextObj *pPrevLink = mpTextObj->GetPrevLinkInChain();
+ ESelection aEndSel(100000, 100000);
+ impChangeEditingTextObj(pPrevLink, aEndSel);
+ return;
+ }
+
+ // Standard handling
+ HandleCursorEvent(aCurEvt, aNewSel);
+}
+
+
+void TextChainCursorManager::HandleCursorEvent(
+ const CursorChainingEvent aCurEvt,
+ const ESelection& aNewSel)
+
+{
+
+ OutlinerView* pOLV = mpEditView->GetTextEditOutlinerView();
+ SdrTextObj *pNextLink = mpTextObj->GetNextLinkInChain();
+ SdrTextObj *pPrevLink = mpTextObj->GetPrevLinkInChain();
+
+
+ switch ( aCurEvt ) {
+ case CursorChainingEvent::UNCHANGED:
+ // Set same selection as before the chaining (which is saved as PostChainingSel)
+ // We need an explicit set because the Outliner is messed up
+ // after text transfer and otherwise it brings us at arbitrary positions.
+ pOLV->SetSelection(aNewSel);
+ break;
+ case CursorChainingEvent::TO_NEXT_LINK:
+ mpTextObj->GetTextChain()->SetSwitchingToNextBox(mpTextObj, true);
+ impChangeEditingTextObj(pNextLink, aNewSel);
+ break;
+ case CursorChainingEvent::TO_PREV_LINK:
+ impChangeEditingTextObj(pPrevLink, aNewSel);
+ break;
+ case CursorChainingEvent::NULL_EVENT:
+ // Do nothing here
+ break;
+ }
+
+}
+
+void TextChainCursorManager::impChangeEditingTextObj(SdrTextObj *pTargetTextObj, ESelection aNewSel)
+{
+ assert(pTargetTextObj);
+
+ mpEditView->SdrEndTextEdit();
+ mpEditView->SdrBeginTextEdit(pTargetTextObj);
+ // OutlinerView has changed, so we update the pointer
+ OutlinerView *pOLV = mpEditView->GetTextEditOutlinerView();
+ pOLV->SetSelection(aNewSel);
+
+ // Update reference text obj
+ mpTextObj = pTargetTextObj;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/textchainflow.cxx b/svx/source/svdraw/textchainflow.cxx
new file mode 100644
index 000000000..9763ea501
--- /dev/null
+++ b/svx/source/svdraw/textchainflow.cxx
@@ -0,0 +1,314 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <svx/svdotext.hxx>
+#include <svx/svdoutl.hxx>
+#include <editeng/outlobj.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/overflowingtxt.hxx>
+#include <textchainflow.hxx>
+#include <sal/log.hxx>
+
+TextChainFlow::TextChainFlow(SdrTextObj *pChainTarget)
+ : mpTargetLink(pChainTarget)
+{
+ SAL_INFO("svx.chaining", "[TEXTCHAINFLOW] Creating a new TextChainFlow");
+
+ mpTextChain = mpTargetLink->GetTextChain();
+ mpNextLink = mpTargetLink->GetNextLinkInChain();
+
+ bUnderflow = bOverflow = false;
+
+ mbOFisUFinduced = false;
+
+ mpOverflChText = nullptr;
+ mpUnderflChText = nullptr;
+
+ mbPossiblyCursorOut = false;
+}
+
+
+TextChainFlow::~TextChainFlow()
+{
+ mpOverflChText.reset();
+ mpUnderflChText.reset();
+}
+
+void TextChainFlow::impSetFlowOutlinerParams(SdrOutliner *, SdrOutliner *)
+{
+ // Nothing to do if not in editing mode
+}
+
+/*
+ * Check for overflow in the state of pFlowOutl.
+ * If pParamOutl is not NULL sets some parameters from there.
+ * This is useful in case the outliner is not set for overflow
+ * (e.g. in editing mode we check for overflow in drawing outl but
+ * parameters come from editing outliner)
+*/
+void TextChainFlow::impCheckForFlowEvents(SdrOutliner *pFlowOutl, SdrOutliner *pParamOutl)
+{
+ bool bOldUpdateMode = pFlowOutl->IsUpdateLayout();
+
+ // XXX: This could be reorganized moving most of this stuff inside EditingTextChainFlow
+ if (pParamOutl != nullptr)
+ {
+ // We need this since it's required to check overflow
+ pFlowOutl->SetUpdateLayout(true);
+
+ // XXX: does this work if you do it before setting the text? Seems so.
+ impSetFlowOutlinerParams(pFlowOutl, pParamOutl);
+ }
+
+ bool bIsPageOverflow = pFlowOutl->IsPageOverflow();
+
+ // NOTE: overflow and underflow cannot be both true
+ bOverflow = bIsPageOverflow && mpNextLink;
+ bUnderflow = !bIsPageOverflow && mpNextLink && mpNextLink->HasText();
+
+ // Get old state on whether to merge para-s or not
+ // NOTE: We handle UF/OF using the _old_ state. The new one is simply saved
+ bool bMustMergeParaAmongLinks = GetTextChain()->GetIsPartOfLastParaInNextLink(mpTargetLink);
+
+ // Set (Non)OverflowingTxt here (if any)
+
+ // If we had an underflow before we have to deep merge paras anyway
+ bool bMustMergeParaOF = bMustMergeParaAmongLinks || mbOFisUFinduced;
+
+ mpOverflChText.reset( bOverflow ?
+ new OFlowChainedText(pFlowOutl, bMustMergeParaOF) :
+ nullptr );
+
+ // Set current underflowing text (if any)
+ mpUnderflChText.reset( bUnderflow ?
+ new UFlowChainedText(pFlowOutl, bMustMergeParaAmongLinks) :
+ nullptr );
+
+ // Reset update mode // Reset it here because we use WriteRTF (needing updatemode = true) in the two constructors above
+ if (!bOldUpdateMode) // Reset only if the old value was false
+ pFlowOutl->SetUpdateLayout(bOldUpdateMode);
+
+ // NOTE: Must be called after mp*ChText abd b*flow have been set but before mbOFisUFinduced is reset
+ impUpdateCursorInfo();
+
+ // To check whether an overflow is underflow induced or not (useful in cursor checking)
+ mbOFisUFinduced = bUnderflow;
+}
+
+void TextChainFlow::impUpdateCursorInfo()
+{
+ // XXX: Maybe we can get rid of mbOFisUFinduced by not allowing handling of more than one event by the same TextChainFlow
+ // if this is not an OF triggered during an UF
+
+ mbPossiblyCursorOut = bOverflow;
+
+ if(mbPossiblyCursorOut ) {
+ maOverflowPosSel = mpOverflChText->GetOverflowPointSel();
+ ESelection aSelAtUFTime = GetTextChain()->GetPreChainingSel(GetLinkTarget());
+ // Might be an invalid selection if the cursor at UF time was before
+ // the (possibly UF-induced) Overflowing point but we don't use it in that case
+ maPostChainingSel = ESelection(aSelAtUFTime.nStartPara-maOverflowPosSel.nStartPara,
+ aSelAtUFTime.nStartPos-maOverflowPosSel.nStartPos );
+ }
+
+ // XXX: It may not be necessary anymore to keep this method separated from EditingTextChainFlow::impBroadcastCursorInfo
+}
+
+void TextChainFlow::CheckForFlowEvents(SdrOutliner *pFlowOutl)
+{
+ impCheckForFlowEvents(pFlowOutl, nullptr);
+}
+
+
+bool TextChainFlow::IsOverflow() const
+{
+ return bOverflow;
+}
+
+bool TextChainFlow::IsUnderflow() const
+{
+ return bUnderflow;
+}
+
+
+// XXX: In editing mode you need to get "underflowing" text from editing outliner, so it's kinda separate from the drawing one!
+
+// XXX:Would it be possible to unify underflow and its possibly following overflow?
+void TextChainFlow::ExecuteUnderflow(SdrOutliner *pOutl)
+{
+ //GetTextChain()->SetNilChainingEvent(mpTargetLink, true);
+ // making whole text
+ // merges underflowing text with the one in the next box
+ std::optional<OutlinerParaObject> pNewText = mpUnderflChText->CreateMergedUnderflowParaObject(pOutl, mpNextLink->GetOutlinerParaObject());
+
+ // Set the other box empty; it will be replaced by the rest of the text if overflow occurs
+ if (!mpTargetLink->GetPreventChainable())
+ mpNextLink->NbcSetOutlinerParaObject(pOutl->GetEmptyParaObject());
+
+ // We store the size since NbcSetOutlinerParaObject can change it
+ //Size aOldSize = pOutl->GetMaxAutoPaperSize();
+
+ // This should not be done in editing mode!! //XXX
+ if (!mpTargetLink->IsInEditMode())
+ {
+ mpTargetLink->NbcSetOutlinerParaObject(pNewText);
+ }
+
+ // Restore size and set new text
+ //pOutl->SetMaxAutoPaperSize(aOldSize); // XXX (it seems to be working anyway without this)
+ pOutl->SetText(*pNewText);
+
+ //GetTextChain()->SetNilChainingEvent(mpTargetLink, false);
+
+ // Check for new overflow
+ CheckForFlowEvents(pOutl);
+}
+
+void TextChainFlow::ExecuteOverflow(SdrOutliner *pNonOverflOutl, SdrOutliner *pOverflOutl)
+{
+ //GetTextChain()->SetNilChainingEvent(mpTargetLink, true);
+ // Leave only non overflowing text
+ impLeaveOnlyNonOverflowingText(pNonOverflOutl);
+
+ // Transfer of text to next link
+ if (!mpTargetLink->GetPreventChainable() ) // we don't transfer text while dragging because of resizing
+ {
+ impMoveChainedTextToNextLink(pOverflOutl);
+ }
+
+ //GetTextChain()->SetNilChainingEvent(mpTargetLink, false);
+}
+
+void TextChainFlow::impLeaveOnlyNonOverflowingText(SdrOutliner *pNonOverflOutl)
+{
+ std::optional<OutlinerParaObject> pNewText = mpOverflChText->RemoveOverflowingText(pNonOverflOutl);
+
+ SAL_INFO("svx.chaining", "[TEXTCHAINFLOW - OF] SOURCE box set to "
+ << pNewText->GetTextObject().GetParagraphCount() << " paras");
+
+ // adds it to current outliner anyway (useful in static decomposition)
+ pNonOverflOutl->SetText(*pNewText);
+
+ mpTargetLink->NbcSetOutlinerParaObject(std::move(pNewText));
+ // For some reason the paper size is lost after last instruction, so we set it.
+ pNonOverflOutl->SetPaperSize(Size(pNonOverflOutl->GetPaperSize().Width(),
+ pNonOverflOutl->GetTextHeight()));
+
+}
+
+void TextChainFlow::impMoveChainedTextToNextLink(SdrOutliner *pOverflOutl)
+{
+ // prevent copying text in same box
+ if ( mpNextLink == mpTargetLink ) {
+ SAL_INFO("svx.chaining", "[CHAINING] Trying to copy text for next link in same object");
+ return;
+ }
+
+ std::optional<OutlinerParaObject> pNewText =
+ mpOverflChText->InsertOverflowingText(pOverflOutl,
+ mpNextLink->GetOutlinerParaObject());
+ SAL_INFO("svx.chaining", "[TEXTCHAINFLOW - OF] DEST box set to "
+ << pNewText->GetTextObject().GetParagraphCount() << " paras");
+
+ if (pNewText)
+ mpNextLink->NbcSetOutlinerParaObject(std::move(pNewText));
+
+ // Set Deep Merge status
+ SAL_INFO("svx.chaining", "[DEEPMERGE] Setting deepMerge to "
+ << mpOverflChText->IsLastParaInterrupted());
+ GetTextChain()->SetIsPartOfLastParaInNextLink(
+ mpTargetLink,
+ mpOverflChText->IsLastParaInterrupted());
+}
+
+SdrTextObj *TextChainFlow::GetLinkTarget() const
+{
+ return mpTargetLink;
+}
+
+TextChain *TextChainFlow::GetTextChain() const
+{
+ return mpTextChain;
+}
+
+// EditingTextChainFlow
+
+EditingTextChainFlow::EditingTextChainFlow(SdrTextObj *pLinkTarget) :
+ TextChainFlow(pLinkTarget)
+{
+ SAL_INFO("svx.chaining", "[TEXTCHAINFLOW] Creating a new EditingTextChainFlow");
+}
+
+void EditingTextChainFlow::CheckForFlowEvents(SdrOutliner *pFlowOutl)
+{
+ // if this is editing outliner no need to set parameters
+ if (pFlowOutl == GetLinkTarget()->mpEditingOutliner)
+ impCheckForFlowEvents(pFlowOutl, nullptr);
+ else
+ impCheckForFlowEvents(pFlowOutl, GetLinkTarget()->mpEditingOutliner);
+
+ // Broadcast events for cursor handling
+ impBroadcastCursorInfo();
+}
+
+void EditingTextChainFlow::impLeaveOnlyNonOverflowingText(SdrOutliner *pNonOverflOutl)
+{
+ mpOverflChText->RemoveOverflowingText(pNonOverflOutl);
+ //impSetTextForEditingOutliner(pNewText); //XXX: Don't call it since we do everything with NonOverflowingText::ToParaObject // XXX: You may need this for Underflow
+
+ // XXX: I'm not sure whether we need this (after all operations such as Paste don't change this - as far as I understand)
+ //GetLinkTarget()->NbcSetOutlinerParaObject(pNewText);
+}
+
+void EditingTextChainFlow::impSetFlowOutlinerParams(SdrOutliner *pFlowOutl, SdrOutliner *pParamOutl)
+{
+ // Set right size for overflow
+ pFlowOutl->SetMaxAutoPaperSize(pParamOutl->GetMaxAutoPaperSize());
+ pFlowOutl->SetMinAutoPaperSize(pParamOutl->GetMinAutoPaperSize());
+ pFlowOutl->SetPaperSize(pParamOutl->GetPaperSize());
+}
+
+void EditingTextChainFlow::impBroadcastCursorInfo() const
+{
+ ESelection aPreChainingSel = GetTextChain()->GetPreChainingSel(GetLinkTarget()) ;
+
+ // Test whether the cursor is out of the box.
+ bool bCursorOut = mbPossiblyCursorOut && maOverflowPosSel < aPreChainingSel;
+
+ // NOTE: I handled already the stuff for the comments below. They will be kept temporarily till stuff settles down.
+ // Possibility: 1) why don't we stop passing the actual event to the TextChain and instead we pass
+ // the overflow pos and mbPossiblyCursorOut
+ // 2) We pass the current selection before anything happens and we make impBroadcastCursorInfo compute it.
+
+
+ if (bCursorOut) {
+ //maCursorEvent = CursorChainingEvent::TO_NEXT_LINK;
+ GetTextChain()->SetPostChainingSel(GetLinkTarget(), maPostChainingSel);
+ GetTextChain()->SetCursorEvent(GetLinkTarget(), CursorChainingEvent::TO_NEXT_LINK);
+ } else {
+ //maCursorEvent = CursorChainingEvent::UNCHANGED;
+ GetTextChain()->SetPostChainingSel(GetLinkTarget(), aPreChainingSel);
+ GetTextChain()->SetCursorEvent(GetLinkTarget(), CursorChainingEvent::UNCHANGED);
+ }
+
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */