From 267c6f2ac71f92999e969232431ba04678e7437e Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 15 Apr 2024 07:54:39 +0200 Subject: Adding upstream version 4:24.2.0. Signed-off-by: Daniel Baumann --- svx/source/engine3d/view3d.cxx | 1574 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1574 insertions(+) create mode 100644 svx/source/engine3d/view3d.cxx (limited to 'svx/source/engine3d/view3d.cxx') diff --git a/svx/source/engine3d/view3d.cxx b/svx/source/engine3d/view3d.cxx new file mode 100644 index 0000000000..844d96b487 --- /dev/null +++ b/svx/source/engine3d/view3d.cxx @@ -0,0 +1,1574 @@ +/* -*- 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace com::sun::star; + + +// Migrate Marking + +class Impl3DMirrorConstructOverlay +{ + // The OverlayObjects + sdr::overlay::OverlayObjectList maObjects; + + // the view + const E3dView& mrView; + + // the object count + size_t mnCount; + + // the unmirrored polygons + basegfx::B2DPolyPolygon* mpPolygons; + + // the overlay geometry from selected objects + drawinglayer::primitive2d::Primitive2DContainer maFullOverlay; + + // Copy assignment is forbidden and not implemented. + Impl3DMirrorConstructOverlay (const Impl3DMirrorConstructOverlay &) = delete; + Impl3DMirrorConstructOverlay & operator= (const Impl3DMirrorConstructOverlay &) = delete; + +public: + explicit Impl3DMirrorConstructOverlay(const E3dView& rView); + ~Impl3DMirrorConstructOverlay(); + + void SetMirrorAxis(Point aMirrorAxisA, Point aMirrorAxisB); +}; + +Impl3DMirrorConstructOverlay::Impl3DMirrorConstructOverlay(const E3dView& rView) +: mrView(rView), + mnCount(rView.GetMarkedObjectCount()), + mpPolygons(nullptr) +{ + if(!mnCount) + return; + + if(mrView.IsSolidDragging()) + { + SdrPageView* pPV = rView.GetSdrPageView(); + + if(pPV && pPV->PageWindowCount()) + { + for(size_t a = 0; a < mnCount; ++a) + { + SdrObject* pObject = mrView.GetMarkedObjectByIndex(a); + + if(pObject) + { + // use the view-independent primitive representation (without + // evtl. GridOffset, that may be applied to the DragEntry individually) + pObject->GetViewContact().getViewIndependentPrimitive2DContainer(maFullOverlay); + } + } + } + } + else + { + mpPolygons = new basegfx::B2DPolyPolygon[mnCount]; + + for(size_t a = 0; a < mnCount; ++a) + { + SdrObject* pObject = mrView.GetMarkedObjectByIndex(a); + mpPolygons[mnCount - (a + 1)] = pObject->TakeXorPoly(); + } + } +} + +Impl3DMirrorConstructOverlay::~Impl3DMirrorConstructOverlay() +{ + // 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. + delete[] mpPolygons; +} + +void Impl3DMirrorConstructOverlay::SetMirrorAxis(Point aMirrorAxisA, Point aMirrorAxisB) +{ + // get rid of old overlay objects + maObjects.clear(); + + // create new ones + for(sal_uInt32 a(0); a < mrView.PaintWindowCount(); a++) + { + SdrPaintWindow* pCandidate = mrView.GetPaintWindow(a); + const rtl::Reference< sdr::overlay::OverlayManager >& xTargetOverlay = pCandidate->GetOverlayManager(); + + if(xTargetOverlay.is()) + { + // build transformation: translate and rotate so that given edge is + // on x axis, them mirror in y and translate back + const basegfx::B2DVector aEdge(aMirrorAxisB.X() - aMirrorAxisA.X(), aMirrorAxisB.Y() - aMirrorAxisA.Y()); + basegfx::B2DHomMatrix aMatrixTransform(basegfx::utils::createTranslateB2DHomMatrix( + -aMirrorAxisA.X(), -aMirrorAxisA.Y())); + aMatrixTransform.rotate(-atan2(aEdge.getY(), aEdge.getX())); + aMatrixTransform.scale(1.0, -1.0); + aMatrixTransform.rotate(atan2(aEdge.getY(), aEdge.getX())); + aMatrixTransform.translate(aMirrorAxisA.X(), aMirrorAxisA.Y()); + + if(mrView.IsSolidDragging()) + { + if(!maFullOverlay.empty()) + { + drawinglayer::primitive2d::Primitive2DContainer aContent(maFullOverlay); + + if(!aMatrixTransform.isIdentity()) + { + // embed in transformation group + drawinglayer::primitive2d::Primitive2DReference aTransformPrimitive2D(new drawinglayer::primitive2d::TransformPrimitive2D(aMatrixTransform, std::move(aContent))); + aContent = drawinglayer::primitive2d::Primitive2DContainer { aTransformPrimitive2D }; + } + + // if we have full overlay from selected objects, embed with 50% transparence, the + // transformation is added to the OverlayPrimitive2DSequenceObject + drawinglayer::primitive2d::Primitive2DReference aUnifiedTransparencePrimitive2D(new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(std::move(aContent), 0.5)); + aContent = drawinglayer::primitive2d::Primitive2DContainer { aUnifiedTransparencePrimitive2D }; + + std::unique_ptr pNew(new sdr::overlay::OverlayPrimitive2DSequenceObject(std::move(aContent))); + + xTargetOverlay->add(*pNew); + maObjects.append(std::move(pNew)); + } + } + else + { + for(size_t b = 0; b < mnCount; ++b) + { + // apply to polygon + basegfx::B2DPolyPolygon aPolyPolygon(mpPolygons[b]); + aPolyPolygon.transform(aMatrixTransform); + + std::unique_ptr pNew(new sdr::overlay::OverlayPolyPolygonStripedAndFilled( + std::move(aPolyPolygon))); + xTargetOverlay->add(*pNew); + maObjects.append(std::move(pNew)); + } + } + } + } +} + +E3dView::E3dView( + SdrModel& rSdrModel, + OutputDevice* pOut) +: SdrView(rSdrModel, pOut) +{ + InitView(); +} + +// DrawMarkedObj override, since possibly only a single 3D object is to be +// drawn + +void E3dView::DrawMarkedObj(OutputDevice& rOut) const +{ + // Does 3D objects exist which scenes are not selected? + bool bSpecialHandling = false; + E3dScene *pScene = nullptr; + + const size_t nCnt = GetMarkedObjectCount(); + for(size_t nObjs = 0; nObjs < nCnt; ++nObjs) + { + SdrObject *pObj = GetMarkedObjectByIndex(nObjs); + if(auto pCompoundObject = dynamic_cast(pObj)) + { + // related scene + pScene = pCompoundObject->getRootE3dSceneFromE3dObject(); + + if(nullptr != pScene && !IsObjMarked(pScene)) + { + bSpecialHandling = true; + } + } + // Reset all selection flags + if(auto p3dObject = DynCastE3dObject(pObj)) + { + pScene = p3dObject->getRootE3dSceneFromE3dObject(); + + if(nullptr != pScene) + { + pScene->SetSelected(false); + } + } + } + + if(bSpecialHandling) + { + // Set selection flag to "not selected" for scenes related to all 3D + // objects + for(size_t nObjs = 0; nObjs < nCnt; ++nObjs) + { + SdrObject *pObj = GetMarkedObjectByIndex(nObjs); + if(auto pCompoundObject = dynamic_cast(pObj)) + { + // related scene + pScene = pCompoundObject->getRootE3dSceneFromE3dObject(); + + if(nullptr != pScene) + { + pScene->SetSelected(false); + } + } + } + + for(size_t nObjs = 0; nObjs < nCnt; ++nObjs) + { + SdrObject *pObj = GetMarkedObjectByIndex(nObjs); + if(auto p3DObj = DynCastE3dObject(pObj)) + { + // Select object + p3DObj->SetSelected(true); + pScene = p3DObj->getRootE3dSceneFromE3dObject(); + } + } + + if(nullptr != pScene) + { + // code from parent + SortMarkedObjects(); + + pScene->SetDrawOnlySelected(true); + pScene->SingleObjectPainter(rOut); + pScene->SetDrawOnlySelected(false); + } + + // Reset selection flag + for(size_t nObjs = 0; nObjs < nCnt; ++nObjs) + { + SdrObject *pObj = GetMarkedObjectByIndex(nObjs); + if(auto pCompoundObject = dynamic_cast(pObj)) + { + // related scene + pScene = pCompoundObject->getRootE3dSceneFromE3dObject(); + + if(nullptr != pScene) + { + pScene->SetSelected(false); + } + } + } + } + else + { + // call parent + SdrExchangeView::DrawMarkedObj(rOut); + } +} + +// override get model, since in some 3D objects an additional scene +// must be pushed in + +std::unique_ptr E3dView::CreateMarkedObjModel() const +{ + // Does 3D objects exist which scenes are not selected? + bool bSpecialHandling(false); + const size_t nCount(GetMarkedObjectCount()); + E3dScene *pScene = nullptr; + + for(size_t nObjs = 0; nObjs < nCount; ++nObjs) + { + const SdrObject* pObj = GetMarkedObjectByIndex(nObjs); + + if(!bSpecialHandling) + if(auto pCompoundObj = dynamic_cast< const E3dCompoundObject*>(pObj)) + { + // if the object is selected, but it's scene not, + // we need special handling + pScene = pCompoundObj->getRootE3dSceneFromE3dObject(); + + if(nullptr != pScene && !IsObjMarked(pScene)) + { + bSpecialHandling = true; + } + } + + if(auto p3dObject = DynCastE3dObject(pObj)) + { + // reset all selection flags at 3D objects + pScene = p3dObject->getRootE3dSceneFromE3dObject(); + + if(nullptr != pScene) + { + pScene->SetSelected(false); + } + } + } + + if(!bSpecialHandling) + { + // call parent + return SdrView::CreateMarkedObjModel(); + } + + std::unique_ptr pNewModel; + tools::Rectangle aSelectedSnapRect; + + // set 3d selection flags at all directly selected objects + // and collect SnapRect of selected objects + for(size_t nObjs = 0; nObjs < nCount; ++nObjs) + { + SdrObject *pObj = GetMarkedObjectByIndex(nObjs); + + if(auto p3DObj = dynamic_cast(pObj)) + { + // mark object, but not scenes + p3DObj->SetSelected(true); + aSelectedSnapRect.Union(p3DObj->GetSnapRect()); + } + } + + // create new mark list which contains all indirectly selected3d + // scenes as selected objects + SdrMarkList aOldML(GetMarkedObjectList()); + SdrMarkList aNewML; + SdrMarkList& rCurrentMarkList = const_cast(this)->GetMarkedObjectListWriteAccess(); + rCurrentMarkList = aNewML; + + for(size_t nObjs = 0; nObjs < nCount; ++nObjs) + { + SdrObject *pObj = aOldML.GetMark(nObjs)->GetMarkedSdrObj(); + + if(auto p3dObject = DynCastE3dObject(pObj)) + { + pScene = p3dObject->getRootE3dSceneFromE3dObject(); + + if(nullptr != pScene && !IsObjMarked(pScene) && GetSdrPageView()) + { + const_cast(this)->MarkObj(pScene, GetSdrPageView(), false, true); + } + } + } + + // call parent. This will copy all scenes and the selection flags at the 3D objects. So + // it will be possible to delete all non-selected 3d objects from the cloned 3d scenes + pNewModel = SdrView::CreateMarkedObjModel(); + + if(pNewModel) + { + for(sal_uInt16 nPg(0); nPg < pNewModel->GetPageCount(); nPg++) + { + const SdrPage* pSrcPg=pNewModel->GetPage(nPg); + + for (const rtl::Reference& pSrcOb : *pSrcPg) + { + if(const E3dScene* p3dscene = DynCastE3dScene( pSrcOb.get())) + { + pScene = const_cast(p3dscene); + + // delete all not intentionally cloned 3d objects + pScene->removeAllNonSelectedObjects(); + + // reset select flags and set SnapRect of all selected objects + pScene->SetSelected(false); + pScene->SetSnapRect(aSelectedSnapRect); + } + } + } + } + + // restore old selection + rCurrentMarkList = aOldML; + + return pNewModel; +} + +// When pasting objects have to integrated if a scene is inserted, but +// not the scene itself + +bool E3dView::Paste( + const SdrModel& rMod, const Point& rPos, SdrObjList* pLst, SdrInsertFlags nOptions) +{ + bool bRetval = false; + + // Get list + Point aPos(rPos); + SdrObjList* pDstList = pLst; + ImpGetPasteObjList(aPos, pDstList); + + if(!pDstList) + return false; + + // Get owner of the list + E3dScene* pDstScene(DynCastE3dScene(pDstList->getSdrObjectFromSdrObjList())); + + if(nullptr != pDstScene) + { + BegUndo(SvxResId(RID_SVX_3D_UNDO_EXCHANGE_PASTE)); + + // Copy all objects from E3dScenes and insert them directly + for(sal_uInt16 nPg(0); nPg < rMod.GetPageCount(); nPg++) + { + const SdrPage* pSrcPg=rMod.GetPage(nPg); + + // calculate offset for paste + tools::Rectangle aR = pSrcPg->GetAllObjBoundRect(); + Point aDist(aPos - aR.Center()); + + // Insert sub-objects for scenes + for (const rtl::Reference& pSrcOb : *pSrcPg) + { + if(const E3dScene* p3dscene = DynCastE3dScene(pSrcOb.get())) + { + E3dScene* pSrcScene = const_cast(p3dscene); + ImpCloneAll3DObjectsToDestScene(pSrcScene, pDstScene, aDist); + } + } + } + EndUndo(); + } + else + { + // call parent + bRetval = SdrView::Paste(rMod, rPos, pLst, nOptions); + } + + return bRetval; +} + +// Service routine used from local Clone() and from SdrCreateView::EndCreateObj(...) +bool E3dView::ImpCloneAll3DObjectsToDestScene(E3dScene const * pSrcScene, E3dScene* pDstScene, Point /*aOffset*/) +{ + bool bRetval(false); + + if(pSrcScene && pDstScene) + { + for (const rtl::Reference& pObj : *pSrcScene->GetSubList()) + { + E3dCompoundObject* pCompoundObj = dynamic_cast< E3dCompoundObject* >(pObj.get()); + + if(pCompoundObj) + { + rtl::Reference pNewCompoundObj = SdrObject::Clone(*pCompoundObj, pDstScene->getSdrModelFromSdrObject()); + + if(pNewCompoundObj) + { + // get dest scene's current range in 3D world coordinates + const basegfx::B3DHomMatrix aSceneToWorldTrans(pDstScene->GetFullTransform()); + basegfx::B3DRange aSceneRange(pDstScene->GetBoundVolume()); + aSceneRange.transform(aSceneToWorldTrans); + + // get new object's implied object transformation + const basegfx::B3DHomMatrix aNewObjectTrans(pNewCompoundObj->GetTransform()); + + // get new object's range in 3D world coordinates in dest scene + // as if it were already added + const basegfx::B3DHomMatrix aObjectToWorldTrans(aSceneToWorldTrans * aNewObjectTrans); + basegfx::B3DRange aObjectRange(pNewCompoundObj->GetBoundVolume()); + aObjectRange.transform(aObjectToWorldTrans); + + // get scale adaptation + const basegfx::B3DVector aSceneScale(aSceneRange.getRange()); + const basegfx::B3DVector aObjectScale(aObjectRange.getRange()); + double fScale(1.0); + + // if new object's size in X,Y or Z is bigger that 80% of dest scene, adapt scale + // to not change the scene by the inserted object + const double fSizeFactor(0.5); + + if(aObjectScale.getX() * fScale > aSceneScale.getX() * fSizeFactor) + { + const double fObjSize(aObjectScale.getX() * fScale); + const double fFactor((aSceneScale.getX() * fSizeFactor) / (basegfx::fTools::equalZero(fObjSize) ? 1.0 : fObjSize)); + fScale *= fFactor; + } + + if(aObjectScale.getY() * fScale > aSceneScale.getY() * fSizeFactor) + { + const double fObjSize(aObjectScale.getY() * fScale); + const double fFactor((aSceneScale.getY() * fSizeFactor) / (basegfx::fTools::equalZero(fObjSize) ? 1.0 : fObjSize)); + fScale *= fFactor; + } + + if(aObjectScale.getZ() * fScale > aSceneScale.getZ() * fSizeFactor) + { + const double fObjSize(aObjectScale.getZ() * fScale); + const double fFactor((aSceneScale.getZ() * fSizeFactor) / (basegfx::fTools::equalZero(fObjSize) ? 1.0 : fObjSize)); + fScale *= fFactor; + } + + // get translation adaptation + const basegfx::B3DPoint aSceneCenter(aSceneRange.getCenter()); + const basegfx::B3DPoint aObjectCenter(aObjectRange.getCenter()); + + // build full modification transform. The object's transformation + // shall be modified, so start at object coordinates; transform to 3d world coor + basegfx::B3DHomMatrix aModifyingTransform(aObjectToWorldTrans); + + // translate to absolute center in 3d world coor + aModifyingTransform.translate(-aObjectCenter.getX(), -aObjectCenter.getY(), -aObjectCenter.getZ()); + + // scale to dest size in 3d world coor + aModifyingTransform.scale(fScale, fScale, fScale); + + // translate to dest scene center in 3d world coor + aModifyingTransform.translate(aSceneCenter.getX(), aSceneCenter.getY(), aSceneCenter.getZ()); + + // transform from 3d world to dest object coordinates + basegfx::B3DHomMatrix aWorldToObject(aObjectToWorldTrans); + aWorldToObject.invert(); + aModifyingTransform = aWorldToObject * aModifyingTransform; + + // correct implied object transform by applying changing one in object coor + pNewCompoundObj->SetTransform(aModifyingTransform * aNewObjectTrans); + + // fill and insert new object + pNewCompoundObj->NbcSetLayer(pCompoundObj->GetLayer()); + pNewCompoundObj->NbcSetStyleSheet(pCompoundObj->GetStyleSheet(), true); + pDstScene->InsertObject(pNewCompoundObj.get()); + bRetval = true; + + // Create undo + if( GetModel().IsUndoEnabled() ) + AddUndo(GetModel().GetSdrUndoFactory().CreateUndoNewObject(*pNewCompoundObj)); + } + } + } + } + + return bRetval; +} + +bool E3dView::IsConvertTo3DObjPossible() const +{ + bool bAny3D(false); + bool bGroupSelected(false); + bool bRetval(true); + + for(size_t a=0; !bAny3D && aIsGroupObject()) + { + SdrObjListIter aIter(*pObj, SdrIterMode::DeepNoGroups); + while(aIter.IsMore()) + { + SdrObject* pNewObj = aIter.Next(); + ImpIsConvertTo3DPossible(pNewObj, rAny3D, rGroupSelected); + } + rGroupSelected = true; + } + } +} + +void E3dView::ImpChangeSomeAttributesFor3DConversion(SdrObject* pObj) +{ + if(DynCastSdrTextObj( pObj) == nullptr) + return; + + const SfxItemSet& rSet = pObj->GetMergedItemSet(); + const SvxColorItem& rTextColorItem = rSet.Get(EE_CHAR_COLOR); + if(rTextColorItem.GetValue() != COL_BLACK) + return; + + //For black text objects, the color set to gray + if(pObj->getSdrPageFromSdrObject()) + { + // if black is only default attribute from + // pattern set it hard so that it is used in undo. + pObj->SetMergedItem(SvxColorItem(COL_BLACK, EE_CHAR_COLOR)); + + // add undo now + if (GetModel().IsUndoEnabled()) + AddUndo(GetModel().GetSdrUndoFactory().CreateUndoAttrObject(*pObj)); + } + + pObj->SetMergedItem(SvxColorItem(COL_GRAY, EE_CHAR_COLOR)); +} + +void E3dView::ImpChangeSomeAttributesFor3DConversion2(SdrObject* pObj) +{ + auto pPathObj = dynamic_cast( pObj); + if(!pPathObj) + return; + + const SfxItemSet& rSet = pObj->GetMergedItemSet(); + sal_Int32 nLineWidth = rSet.Get(XATTR_LINEWIDTH).GetValue(); + drawing::LineStyle eLineStyle = rSet.Get(XATTR_LINESTYLE).GetValue(); + drawing::FillStyle eFillStyle = rSet.Get(XATTR_FILLSTYLE).GetValue(); + + if(pPathObj->IsClosed() + && eLineStyle == drawing::LineStyle_SOLID + && !nLineWidth + && eFillStyle != drawing::FillStyle_NONE) + { + if (pObj->getSdrPageFromSdrObject() && GetModel().IsUndoEnabled()) + { + AddUndo(GetModel().GetSdrUndoFactory().CreateUndoAttrObject(*pObj)); + } + + pObj->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE)); + pObj->SetMergedItem(XLineWidthItem(0)); + } +} + +void E3dView::ImpCreateSingle3DObjectFlat(E3dScene* pScene, SdrObject* pObj, bool bExtrude, double fDepth, basegfx::B2DHomMatrix const & rLatheMat) +{ + // Single PathObject, transform this + SdrPathObj* pPath = dynamic_cast( pObj ); + + if(!pPath) + return; + + E3dDefaultAttributes aDefault = Get3DDefaultAttributes(); + + if(bExtrude) + { + aDefault.SetDefaultExtrudeCharacterMode(true); + } + else + { + aDefault.SetDefaultLatheCharacterMode(true); + } + + // Get Itemset of the original object + SfxItemSet aSet(pObj->GetMergedItemSet()); + + drawing::FillStyle eFillStyle = aSet.Get(XATTR_FILLSTYLE).GetValue(); + + // line style turned off + aSet.Put(XLineStyleItem(drawing::LineStyle_NONE)); + + //Determining if FILL_Attribute is set. + if(!pPath->IsClosed() || eFillStyle == drawing::FillStyle_NONE) + { + // This SdrPathObj is not filled, leave the front and rear face out. + // Moreover, a two-sided representation necessary. + aDefault.SetDefaultExtrudeCloseFront(false); + aDefault.SetDefaultExtrudeCloseBack(false); + + aSet.Put(makeSvx3DDoubleSidedItem(true)); + + // Set fill attribute + aSet.Put(XFillStyleItem(drawing::FillStyle_SOLID)); + + // Fill color must be the color line, because the object was + // previously just a line + Color aColorLine = aSet.Get(XATTR_LINECOLOR).GetColorValue(); + aSet.Put(XFillColorItem(OUString(), aColorLine)); + } + + // Create a new extrude object + rtl::Reference p3DObj; + if(bExtrude) + { + p3DObj = new E3dExtrudeObj(pObj->getSdrModelFromSdrObject(), aDefault, pPath->GetPathPoly(), fDepth); + } + else + { + // rLatheMat expects coordinates with y-axis up, pPath uses y-axis down + basegfx::B2DHomMatrix aFlipVerticalMat(1.0, 0.0, 0.0, 0.0, -1.0, 0.0); + basegfx::B2DPolyPolygon aPolyPoly2D(pPath->GetPathPoly()); + aPolyPoly2D.transform(aFlipVerticalMat); + aPolyPoly2D.transform(rLatheMat); + // ctor E3dLatheObj expects coordinates with y-axis down + aPolyPoly2D.transform(aFlipVerticalMat); + p3DObj = new E3dLatheObj(pObj->getSdrModelFromSdrObject(), aDefault, std::move(aPolyPoly2D)); + } + + // Set attribute + p3DObj->NbcSetLayer(pObj->GetLayer()); + + p3DObj->SetMergedItemSet(aSet); + + p3DObj->NbcSetStyleSheet(pObj->GetStyleSheet(), true); + + // Insert a new extrude object + pScene->InsertObject(p3DObj.get()); +} + +void E3dView::ImpCreate3DObject(E3dScene* pScene, SdrObject* pObj, bool bExtrude, double fDepth, basegfx::B2DHomMatrix const & rLatheMat) +{ + if(!pObj) + return; + + // change text color attribute for not so dark colors + if(pObj->IsGroupObject()) + { + SdrObjListIter aIter(*pObj, SdrIterMode::DeepWithGroups); + while(aIter.IsMore()) + { + SdrObject* pGroupMember = aIter.Next(); + ImpChangeSomeAttributesFor3DConversion(pGroupMember); + } + } + else + ImpChangeSomeAttributesFor3DConversion(pObj); + + // convert completely to path objects + rtl::Reference pNewObj1 = pObj->ConvertToPolyObj(false, false); + + if(!pNewObj1) + return; + + // change text color attribute for not so dark colors + if(pNewObj1->IsGroupObject()) + { + SdrObjListIter aIter(*pNewObj1, SdrIterMode::DeepWithGroups); + while(aIter.IsMore()) + { + SdrObject* pGroupMember = aIter.Next(); + ImpChangeSomeAttributesFor3DConversion2(pGroupMember); + } + } + else + ImpChangeSomeAttributesFor3DConversion2(pNewObj1.get()); + + // convert completely to path objects + rtl::Reference pNewObj2 = pObj->ConvertToContourObj(pNewObj1.get(), true); + + if(pNewObj2) + { + // add all to flat scene + if(pNewObj2->IsGroupObject()) + { + SdrObjListIter aIter(*pNewObj2, SdrIterMode::DeepWithGroups); + while(aIter.IsMore()) + { + SdrObject* pGroupMember = aIter.Next(); + ImpCreateSingle3DObjectFlat(pScene, pGroupMember, bExtrude, fDepth, rLatheMat); + } + } + else + ImpCreateSingle3DObjectFlat(pScene, pNewObj2.get(), bExtrude, fDepth, rLatheMat); + } +} + +void E3dView::ConvertMarkedObjTo3D(bool bExtrude, const basegfx::B2DPoint& rPnt1, const basegfx::B2DPoint& rPnt2) +{ + if(!AreObjectsMarked()) + return; + + // Create undo + if(bExtrude) + BegUndo(SvxResId(RID_SVX_3D_UNDO_EXTRUDE)); + else + BegUndo(SvxResId(RID_SVX_3D_UNDO_LATHE)); + + SdrModel& rSdrModel(GetSdrMarkByIndex(0)->GetMarkedSdrObj()->getSdrModelFromSdrObject()); + + // Create a new scene for the created 3D object + rtl::Reference pScene = new E3dScene(rSdrModel); + + // Determine rectangle and possibly correct it + tools::Rectangle aRect = GetAllMarkedRect(); + if(aRect.GetWidth() <= 1) + aRect.SetSize(Size(500, aRect.GetHeight())); + if(aRect.GetHeight() <= 1) + aRect.SetSize(Size(aRect.GetWidth(), 500)); + + // Determine the depth relative to the size of the selection + double fDepth = 0.0; + double fRot3D = 0.0; + basegfx::B2DHomMatrix aLatheMat; + + if(bExtrude) + { + fDepth = std::hypot(aRect.GetWidth(), aRect.GetHeight()) / 6.0; + } + if(!bExtrude) + { + // Create transformation for the polygons rotating body + if (rPnt1 != rPnt2) + { + // Rotation around control point #1 with set angle + // for 3D coordinates + basegfx::B2DPoint aDiff(rPnt1 - rPnt2); + fRot3D = atan2(aDiff.getY(), aDiff.getX()) - M_PI_2; + + if(basegfx::fTools::equalZero(fabs(fRot3D))) + fRot3D = 0.0; + + if(fRot3D != 0.0) + { + aLatheMat = basegfx::utils::createRotateAroundPoint(rPnt2, -fRot3D) + * aLatheMat; + } + } + + if (rPnt2.getX() != 0.0) + { + // Translation to Y=0 - axis + aLatheMat.translate(-rPnt2.getX(), 0.0); + } + else + { + aLatheMat.translate(static_cast(-aRect.Left()), 0.0); + } + + // Form the inverse matrix to determine the target expansion + basegfx::B2DHomMatrix aInvLatheMat(aLatheMat); + aInvLatheMat.invert(); + + // SnapRect extension enables mirroring in the axis of rotation + for(size_t a=0; aGetMarkedSdrObj(); + tools::Rectangle aTurnRect = pObj->GetSnapRect(); + basegfx::B2DPoint aRot; + Point aRotPnt; + + aRot = basegfx::B2DPoint(aTurnRect.Left(), -aTurnRect.Top()); + aRot *= aLatheMat; + aRot.setX(-aRot.getX()); + aRot *= aInvLatheMat; + aRotPnt = Point(static_cast(aRot.getX() + 0.5), static_cast(-aRot.getY() - 0.5)); + aRect.Union(tools::Rectangle(aRotPnt, aRotPnt)); + + aRot = basegfx::B2DPoint(aTurnRect.Left(), -aTurnRect.Bottom()); + aRot *= aLatheMat; + aRot.setX(-aRot.getX()); + aRot *= aInvLatheMat; + aRotPnt = Point(static_cast(aRot.getX() + 0.5), static_cast(-aRot.getY() - 0.5)); + aRect.Union(tools::Rectangle(aRotPnt, aRotPnt)); + + aRot = basegfx::B2DPoint(aTurnRect.Right(), -aTurnRect.Top()); + aRot *= aLatheMat; + aRot.setX(-aRot.getX()); + aRot *= aInvLatheMat; + aRotPnt = Point(static_cast(aRot.getX() + 0.5), static_cast(-aRot.getY() - 0.5)); + aRect.Union(tools::Rectangle(aRotPnt, aRotPnt)); + + aRot = basegfx::B2DPoint(aTurnRect.Right(), -aTurnRect.Bottom()); + aRot *= aLatheMat; + aRot.setX(-aRot.getX()); + aRot *= aInvLatheMat; + aRotPnt = Point(static_cast(aRot.getX() + 0.5), static_cast(-aRot.getY() - 0.5)); + aRect.Union(tools::Rectangle(aRotPnt, aRotPnt)); + } + } + + // Walk through the selection and convert it into 3D, complete with + // Conversion to SdrPathObject, also fonts + for(size_t a=0; aGetMarkedSdrObj(); + + ImpCreate3DObject(pScene.get(), pObj, bExtrude, fDepth, aLatheMat); + } + + if(pScene->GetSubList() && pScene->GetSubList()->GetObjCount() != 0) + { + // Arrange all created objects by depth + if(bExtrude) + DoDepthArrange(pScene.get(), fDepth); + + // Center 3D objects in the middle of the overall rectangle + basegfx::B3DPoint aCenter(pScene->GetBoundVolume().getCenter()); + basegfx::B3DHomMatrix aMatrix; + + aMatrix.translate(-aCenter.getX(), -aCenter.getY(), -aCenter.getZ()); + pScene->SetTransform(aMatrix * pScene->GetTransform()); + + // Initialize scene + pScene->NbcSetSnapRect(aRect); + basegfx::B3DRange aBoundVol = pScene->GetBoundVolume(); + InitScene(pScene.get(), static_cast(aRect.GetWidth()), static_cast(aRect.GetHeight()), aBoundVol.getDepth()); + + // Insert scene instead of the first selected object and throw away + // all the old objects + SdrObject* pRepObj = GetMarkedObjectByIndex(0); + SdrPageView* pPV = GetSdrPageViewOfMarkedByIndex(0); + MarkObj(pRepObj, pPV, true); + ReplaceObjectAtView(pRepObj, *pPV, pScene.get(), false); + DeleteMarked(); + MarkObj(pScene.get(), pPV); + + // Rotate Rotation body around the axis of rotation + if(!bExtrude && fRot3D != 0.0) + { + basegfx::B3DHomMatrix aRotate; + aRotate.rotate(0.0, 0.0, fRot3D); + pScene->SetTransform(aRotate * pScene->GetTransform()); + } + + // Set default rotation + { + basegfx::B3DHomMatrix aRotate; + aRotate.rotate(basegfx::deg2rad(20.0), 0.0, 0.0); + // E3DModifySceneSnapRectUpdater updates the 2D representation of the scene. + // It prepares things in ctor and acts in dtor. + E3DModifySceneSnapRectUpdater aUpdater(pScene->getSdrObjectFromSdrObjList()); + pScene->SetTransform(aRotate * pScene->GetTransform()); + } + } + else + pScene.clear(); + + EndUndo(); +} + +//Arrange all created extrude objects by depth + +namespace { + +struct E3dDepthNeighbour +{ + E3dExtrudeObj* mpObj; + basegfx::B2DPolyPolygon maPreparedPolyPolygon; + + E3dDepthNeighbour(E3dExtrudeObj* pObj, basegfx::B2DPolyPolygon aPreparedPolyPolygon) + : mpObj(pObj), + maPreparedPolyPolygon(std::move(aPreparedPolyPolygon)) + { + } +}; + +struct E3dDepthLayer +{ + E3dDepthLayer* mpDown; + std::vector mvNeighbours; + + E3dDepthLayer() + : mpDown(nullptr) + { + } +}; + +} + +void E3dView::DoDepthArrange(E3dScene const * pScene, double fDepth) +{ + if(!(pScene && pScene->GetSubList() && pScene->GetSubList()->GetObjCount() > 1)) + return; + + SdrObjList* pSubList = pScene->GetSubList(); + SdrObjListIter aIter(pSubList, SdrIterMode::Flat); + E3dDepthLayer* pBaseLayer = nullptr; + E3dDepthLayer* pLayer = nullptr; + sal_Int32 nNumLayers = 0; + + while(aIter.IsMore()) + { + E3dExtrudeObj* pExtrudeObj = dynamic_cast< E3dExtrudeObj* >(aIter.Next()); + + if(pExtrudeObj) + { + const basegfx::B2DPolyPolygon aExtrudePoly( + basegfx::utils::prepareForPolygonOperation(pExtrudeObj->GetExtrudePolygon())); + const SfxItemSet& rLocalSet = pExtrudeObj->GetMergedItemSet(); + const drawing::FillStyle eLocalFillStyle = rLocalSet.Get(XATTR_FILLSTYLE).GetValue(); + const Color aLocalColor = rLocalSet.Get(XATTR_FILLCOLOR).GetColorValue(); + + // sort in ExtrudeObj + if(pLayer) + { + // do we have overlap with an object of this layer? + bool bOverlap(false); + + for(const auto& rAct : pLayer->mvNeighbours) + { + // do rAct.mpObj and pExtrudeObj overlap? Check by + // using logical AND clipping + const basegfx::B2DPolyPolygon aAndPolyPolygon( + basegfx::utils::solvePolygonOperationAnd( + aExtrudePoly, + rAct.maPreparedPolyPolygon)); + + if(aAndPolyPolygon.count() != 0) + { + // second criteria: is another fillstyle or color used? + const SfxItemSet& rCompareSet = rAct.mpObj->GetMergedItemSet(); + + drawing::FillStyle eCompareFillStyle = rCompareSet.Get(XATTR_FILLSTYLE).GetValue(); + + if(eLocalFillStyle == eCompareFillStyle) + { + if(eLocalFillStyle == drawing::FillStyle_SOLID) + { + Color aCompareColor = rCompareSet.Get(XATTR_FILLCOLOR).GetColorValue(); + + if(aCompareColor == aLocalColor) + { + continue; + } + } + else if(eLocalFillStyle == drawing::FillStyle_NONE) + { + continue; + } + } + + bOverlap = true; + break; + } + } + + if(bOverlap) + { + // yes, start a new layer + pLayer->mpDown = new E3dDepthLayer; + pLayer = pLayer->mpDown; + nNumLayers++; + pLayer->mvNeighbours.emplace_back(pExtrudeObj, aExtrudePoly); + } + else + { + // no, add to current layer + pLayer->mvNeighbours.emplace(pLayer->mvNeighbours.begin(), pExtrudeObj, aExtrudePoly); + } + } + else + { + // first layer ever + pBaseLayer = new E3dDepthLayer; + pLayer = pBaseLayer; + nNumLayers++; + pLayer->mvNeighbours.emplace_back(pExtrudeObj, aExtrudePoly); + } + } + } + + // number of layers is done + if(nNumLayers > 1) + { + // need to be arranged + double fMinDepth = fDepth * 0.8; + double fStep = (fDepth - fMinDepth) / static_cast(nNumLayers); + pLayer = pBaseLayer; + + while(pLayer) + { + // move along layer + for(auto& rAct : pLayer->mvNeighbours) + { + // adapt extrude value + rAct.mpObj->SetMergedItem(SfxUInt32Item(SDRATTR_3DOBJ_DEPTH, sal_uInt32(fMinDepth + 0.5))); + } + + // next layer + pLayer = pLayer->mpDown; + fMinDepth += fStep; + } + } + + // cleanup + while(pBaseLayer) + { + pLayer = pBaseLayer->mpDown; + delete pBaseLayer; + pBaseLayer = pLayer; + } +} + +// Start drag, create for 3D objects before possibly drag method + +bool E3dView::BegDragObj(const Point& rPnt, OutputDevice* pOut, + SdrHdl* pHdl, short nMinMov, + SdrDragMethod* pForcedMeth) +{ + if(Is3DRotationCreationActive() && GetMarkedObjectCount()) + { + // Determine all selected polygons and return the mirrored helper overlay + mpMirrorOverlay->SetMirrorAxis(maRef1, maRef2); + } + else + { + bool bOwnActionNecessary; + if (pHdl == nullptr) + { + bOwnActionNecessary = true; + } + else if (pHdl->IsVertexHdl() || pHdl->IsCornerHdl()) + { + bOwnActionNecessary = true; + } + else + { + bOwnActionNecessary = false; + } + + if(bOwnActionNecessary && GetMarkedObjectCount() > 0) + { + E3dDragConstraint eConstraint = E3dDragConstraint::XYZ; + bool bThereAreRootScenes = false; + bool bThereAre3DObjects = false; + const size_t nCnt = GetMarkedObjectCount(); + for(size_t nObjs = 0; nObjs < nCnt; ++nObjs) + { + SdrObject *pObj = GetMarkedObjectByIndex(nObjs); + if(pObj) + { + if( const E3dScene* pScene = DynCastE3dScene(pObj) ) + if( pScene->getRootE3dSceneFromE3dObject() == pObj ) + bThereAreRootScenes = true; + + if(DynCastE3dObject(pObj)) + { + bThereAre3DObjects = true; + } + } + } + if( bThereAre3DObjects ) + { + meDragHdl = ( pHdl == nullptr ? SdrHdlKind::Move : pHdl->GetKind() ); + switch ( meDragMode ) + { + case SdrDragMode::Rotate: + case SdrDragMode::Shear: + { + switch ( meDragHdl ) + { + case SdrHdlKind::Left: + case SdrHdlKind::Right: + { + eConstraint = E3dDragConstraint::X; + } + break; + + case SdrHdlKind::Upper: + case SdrHdlKind::Lower: + { + eConstraint = E3dDragConstraint::Y; + } + break; + + case SdrHdlKind::UpperLeft: + case SdrHdlKind::UpperRight: + case SdrHdlKind::LowerLeft: + case SdrHdlKind::LowerRight: + { + eConstraint = E3dDragConstraint::Z; + } + break; + default: break; + } + + // do not mask the allowed rotations + eConstraint &= E3dDragConstraint::XYZ; + pForcedMeth = new E3dDragRotate(*this, GetMarkedObjectList(), eConstraint, IsSolidDragging()); + } + break; + + case SdrDragMode::Move: + { + if(!bThereAreRootScenes) + { + pForcedMeth = new E3dDragMove(*this, GetMarkedObjectList(), meDragHdl, eConstraint, IsSolidDragging()); + } + } + break; + + // later on + case SdrDragMode::Mirror: + case SdrDragMode::Crook: + case SdrDragMode::Transparence: + case SdrDragMode::Gradient: + default: + { + } + break; + } + } + } + } + return SdrView::BegDragObj(rPnt, pOut, pHdl, nMinMov, pForcedMeth); +} + +// Set current 3D drawing object, create the scene for this +rtl::Reference E3dView::SetCurrent3DObj(E3dObject* p3DObj) +{ + DBG_ASSERT(p3DObj != nullptr, "Who puts in a NULL-pointer here"); + + // get transformed BoundVolume of the object + basegfx::B3DRange aVolume(p3DObj->GetBoundVolume()); + aVolume.transform(p3DObj->GetTransform()); + double fW(aVolume.getWidth()); + double fH(aVolume.getHeight()); + + tools::Rectangle aRect(0,0, static_cast(fW), static_cast(fH)); + + rtl::Reference pScene = new E3dScene(p3DObj->getSdrModelFromSdrObject()); + + InitScene(pScene.get(), fW, fH, aVolume.getMaxZ() + ((fW + fH) / 4.0)); + + pScene->InsertObject(p3DObj); + pScene->NbcSetSnapRect(aRect); + + return pScene; +} + +void E3dView::InitScene(E3dScene* pScene, double fW, double fH, double fCamZ) +{ + Camera3D aCam(pScene->GetCamera()); + + aCam.SetAutoAdjustProjection(false); + aCam.SetViewWindow(- fW / 2, - fH / 2, fW, fH); + basegfx::B3DPoint aLookAt; + + double fDefaultCamPosZ = GetDefaultCamPosZ(); + basegfx::B3DPoint aCamPos(0.0, 0.0, fCamZ < fDefaultCamPosZ ? fDefaultCamPosZ : fCamZ); + + aCam.SetPosAndLookAt(aCamPos, aLookAt); + aCam.SetFocalLength(GetDefaultCamFocal()); + pScene->SetCamera(aCam); +} + +void E3dView::Start3DCreation() +{ + if (!GetMarkedObjectCount()) + return; + + //positioned + tools::Long nOutMin = 0; + tools::Long nOutMax = 0; + tools::Long nMinLen = 0; + tools::Long nObjDst = 0; + tools::Long nOutHgt = 0; + OutputDevice* pOut = GetFirstOutputDevice(); + + // first determine representation boundaries + if (pOut != nullptr) + { + nMinLen = pOut->PixelToLogic(Size(0,50)).Height(); + nObjDst = pOut->PixelToLogic(Size(0,20)).Height(); + + tools::Long nDst = pOut->PixelToLogic(Size(0,10)).Height(); + + nOutMin = -pOut->GetMapMode().GetOrigin().Y(); + nOutMax = pOut->GetOutputSize().Height() - 1 + nOutMin; + nOutMin += nDst; + nOutMax -= nDst; + + if (nOutMax - nOutMin < nDst) + { + nOutMin += nOutMax + 1; + nOutMin /= 2; + nOutMin -= (nDst + 1) / 2; + nOutMax = nOutMin + nDst; + } + + nOutHgt = nOutMax - nOutMin; + + tools::Long nTemp = nOutHgt / 4; + if (nTemp > nMinLen) nMinLen = nTemp; + } + + // and then attach the marks at the top and bottom of the object + basegfx::B2DRange aR; + for(size_t nMark = 0; nMark < GetMarkedObjectCount(); ++nMark) + { + SdrObject* pMark = GetMarkedObjectByIndex(nMark); + basegfx::B2DPolyPolygon aXPP(pMark->TakeXorPoly()); + aR.expand(basegfx::utils::getRange(aXPP)); + } + + basegfx::B2DPoint aCenter(aR.getCenter()); + tools::Long nMarkHgt = basegfx::fround(aR.getHeight()) - 1; + tools::Long nHgt = nMarkHgt + nObjDst * 2; + + if (nHgt < nMinLen) nHgt = nMinLen; + + tools::Long nY1 = basegfx::fround(aCenter.getY()) - (nHgt + 1) / 2; + tools::Long nY2 = nY1 + nHgt; + + if (pOut && (nMinLen > nOutHgt)) nMinLen = nOutHgt; + if (pOut) + { + 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( basegfx::fround(aR.getMinX()) ); // Initial move axis 2/100mm to the left + maRef1.setY( nY1 ); + maRef2.setX( maRef1.X() ); + maRef2.setY( nY2 ); + + // Turn on marks + SetMarkHandles(nullptr); + + //HMHif (bVis) ShowMarkHdl(); + if (AreObjectsMarked()) MarkListHasChanged(); + + // Show mirror polygon IMMEDIATELY + const SdrHdlList &aHdlList = GetHdlList(); + mpMirrorOverlay.reset(new Impl3DMirrorConstructOverlay(*this)); + mpMirrorOverlay->SetMirrorAxis(aHdlList.GetHdl(SdrHdlKind::Ref1)->GetPos(), aHdlList.GetHdl(SdrHdlKind::Ref2)->GetPos()); +} + +// what happens with a mouse movement when the object is created? + +void E3dView::MovAction(const Point& rPnt) +{ + if(Is3DRotationCreationActive()) + { + SdrHdl* pHdl = GetDragHdl(); + + if (pHdl) + { + SdrHdlKind eHdlKind = pHdl->GetKind(); + + // reacts only due to a mirror axis + if ((eHdlKind == SdrHdlKind::Ref1) || + (eHdlKind == SdrHdlKind::Ref2) || + (eHdlKind == SdrHdlKind::MirrorAxis)) + { + const SdrHdlList &aHdlList = GetHdlList (); + + // delete the mirrored polygon, mirrors the original and draws + // it anew + SdrView::MovAction (rPnt); + mpMirrorOverlay->SetMirrorAxis( + aHdlList.GetHdl (SdrHdlKind::Ref1)->GetPos(), + aHdlList.GetHdl (SdrHdlKind::Ref2)->GetPos()); + } + } + else + { + SdrView::MovAction (rPnt); + } + } + else + { + SdrView::MovAction (rPnt); + } +} + +// The End. Create object and any child objects through ImpCreate3DLathe. +// With the parameter value sal_True (SDefault: sal_False) is simply a +// rotation body created, without letting the user set the position of the +// axis. It is sufficient with this call, if an object is selected. +// (No initialization necessary) + +void E3dView::End3DCreation(bool bUseDefaultValuesForMirrorAxes) +{ + ResetCreationActive(); + + if(!AreObjectsMarked()) + return; + + if(bUseDefaultValuesForMirrorAxes) + { + tools::Rectangle aRect = GetAllMarkedRect(); + if(aRect.GetWidth() <= 1) + aRect.SetSize(Size(500, aRect.GetHeight())); + if(aRect.GetHeight() <= 1) + aRect.SetSize(Size(aRect.GetWidth(), 500)); + + basegfx::B2DPoint aPnt1(aRect.Left(), -aRect.Top()); + basegfx::B2DPoint aPnt2(aRect.Left(), -aRect.Bottom()); + + ConvertMarkedObjTo3D(false, aPnt1, aPnt2); + } + else + { + // Turn off helper overlay + // Determine from the handle positions and the displacement of + // the points + const SdrHdlList &aHdlList = GetHdlList(); + Point aMirrorRef1 = aHdlList.GetHdl(SdrHdlKind::Ref1)->GetPos(); + Point aMirrorRef2 = aHdlList.GetHdl(SdrHdlKind::Ref2)->GetPos(); + + basegfx::B2DPoint aPnt1(aMirrorRef1.X(), -aMirrorRef1.Y()); + basegfx::B2DPoint aPnt2(aMirrorRef2.X(), -aMirrorRef2.Y()); + + ConvertMarkedObjTo3D(false, aPnt1, aPnt2); + } +} + +E3dView::~E3dView () +{ +} + +void E3dView::ResetCreationActive () +{ + mpMirrorOverlay.reset(); +} + +void E3dView::InitView () +{ + mpMirrorOverlay = nullptr; +} + +bool E3dView::IsBreak3DObjPossible() const +{ + const size_t nCount = GetMarkedObjectCount(); + + if (nCount > 0) + { + for (size_t i = 0; i < nCount; ++i) + { + SdrObject* pObj = GetMarkedObjectByIndex(i); + + if (auto p3dObject = DynCastE3dObject(pObj)) + { + if(!p3dObject->IsBreakObjPossible()) + return false; + } + else + { + return false; + } + } + } + else + { + return false; + } + + return true; +} + +void E3dView::Break3DObj() +{ + if(!IsBreak3DObjPossible()) + return; + + // ALL selected objects are changed + const size_t nCount = GetMarkedObjectCount(); + + BegUndo(SvxResId(RID_SVX_3D_UNDO_BREAK_LATHE)); + for(size_t a=0; a(GetMarkedObjectByIndex(a)); + BreakSingle3DObj(pObj); + } + DeleteMarked(); + EndUndo(); +} + +void E3dView::BreakSingle3DObj(E3dObject* pObj) +{ + if(DynCastE3dScene(pObj)) + { + SdrObjList* pSubList = pObj->GetSubList(); + SdrObjListIter aIter(pSubList, SdrIterMode::Flat); + + while(aIter.IsMore()) + { + E3dObject* pSubObj = static_cast(aIter.Next()); + BreakSingle3DObj(pSubObj); + } + } + else + { + rtl::Reference pNewObj = pObj->GetBreakObj(); + if (pNewObj) + { + if (InsertObjectAtView(pNewObj.get(), *GetSdrPageView(), SdrInsertFlags::DONTMARK)) + { + pNewObj->SetChanged(); + pNewObj->BroadcastObjectChange(); + } + } + } +} + +void E3dView::CheckPossibilities() +{ + // call parent + SdrView::CheckPossibilities(); + + // Set other flags + if(!(m_bGroupPossible || m_bUnGroupPossible || m_bGrpEnterPossible)) + return; + + const size_t nMarkCnt = GetMarkedObjectCount(); + bool bCompound = false; + bool b3DObject = false; + for(size_t nObjs = 0; (nObjs < nMarkCnt) && !bCompound; ++nObjs) + { + SdrObject *pObj = GetMarkedObjectByIndex(nObjs); + if(dynamic_cast< const E3dCompoundObject* >(pObj)) + bCompound = true; + if(DynCastE3dObject(pObj)) + b3DObject = true; + } + + // So far: there are two or more of any objects selected. See if + // compound objects are involved. If yes, ban grouping. + if(m_bGroupPossible && bCompound) + m_bGroupPossible = false; + + if(m_bUnGroupPossible && b3DObject) + m_bUnGroupPossible = false; + + if(m_bGrpEnterPossible && bCompound) + m_bGrpEnterPossible = false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ -- cgit v1.2.3