diff --git a/cui/source/dialogs/DiagramDialog.cxx b/cui/source/dialogs/DiagramDialog.cxx index 5e7cebe37e8c..8cefc7c25842 100644 --- a/cui/source/dialogs/DiagramDialog.cxx +++ b/cui/source/dialogs/DiagramDialog.cxx @@ -69,30 +69,18 @@ IMPL_LINK_NOARG(DiagramDialog, OnAddClick, weld::Button&, void) if (pDiagramHelper && !sText.isEmpty()) { SdrModel& rDrawModel(m_rDiagram.getSdrModelFromSdrObject()); - const bool bUndo(rDrawModel.IsUndoEnabled()); - svx::diagram::DiagramDataStatePtr aStartState; + OUString sNodeId = pDiagramHelper->addDiagramNode(sText, rDrawModel); - if (bUndo) + if (!sNodeId.isEmpty()) { - // rescue all start state Diagram-defining data - aStartState = pDiagramHelper->extractDiagramDataState(); + if (rDrawModel.IsUndoEnabled()) + m_nUndos++; + + std::unique_ptr pEntry(mpTreeDiagram->make_iterator()); + mpTreeDiagram->insert(nullptr, -1, &sText, &sNodeId, nullptr, nullptr, false, pEntry.get()); + mpTreeDiagram->select(*pEntry); + comphelper::dispatchCommand(u".uno:RegenerateDiagram"_ustr, {}); } - - OUString sNodeId = pDiagramHelper->addDiagramNode(sText); - - if (bUndo) - { - // create undo action. That will internally secure the - // current Diagram-defining data as end state - rDrawModel.AddUndo( - rDrawModel.GetSdrUndoFactory().CreateUndoDiagramModelData(m_rDiagram, aStartState)); - m_nUndos++; - } - - std::unique_ptr pEntry(mpTreeDiagram->make_iterator()); - mpTreeDiagram->insert(nullptr, -1, &sText, &sNodeId, nullptr, nullptr, false, pEntry.get()); - mpTreeDiagram->select(*pEntry); - comphelper::dispatchCommand(u".uno:RegenerateDiagram"_ustr, {}); } } @@ -107,26 +95,10 @@ IMPL_LINK_NOARG(DiagramDialog, OnRemoveClick, weld::Button&, void) if (pDiagramHelper && pEntry) { SdrModel& rDrawModel(m_rDiagram.getSdrModelFromSdrObject()); - const bool bUndo(rDrawModel.IsUndoEnabled()); - svx::diagram::DiagramDataStatePtr aStartState; - - if (bUndo) + if (pDiagramHelper->removeDiagramNode(mpTreeDiagram->get_id(*pEntry), rDrawModel)) { - // rescue all start state Diagram-defining data - aStartState = pDiagramHelper->extractDiagramDataState(); - } - - if (pDiagramHelper->removeDiagramNode(mpTreeDiagram->get_id(*pEntry))) - { - if (bUndo) - { - // create undo action. That will internally secure the - // current Diagram-defining data as end state - rDrawModel.AddUndo(rDrawModel.GetSdrUndoFactory().CreateUndoDiagramModelData( - m_rDiagram, aStartState)); + if (rDrawModel.IsUndoEnabled()) m_nUndos++; - } - mpTreeDiagram->remove(*pEntry); comphelper::dispatchCommand(u".uno:RegenerateDiagram"_ustr, {}); } diff --git a/include/oox/drawingml/diagram/diagramhelper_oox.hxx b/include/oox/drawingml/diagram/diagramhelper_oox.hxx index 3c911981d473..7b1ff709fb51 100644 --- a/include/oox/drawingml/diagram/diagramhelper_oox.hxx +++ b/include/oox/drawingml/diagram/diagramhelper_oox.hxx @@ -83,9 +83,10 @@ public: virtual std::vector> getDiagramChildren(const OUString& rParentId) const override; // add/remove new top-level node to data model, returns its id - virtual OUString addDiagramNode(const OUString& rText) override; - virtual bool removeDiagramNode(const OUString& rNodeId) override; + virtual OUString addDiagramNode(const OUString& rText, SdrModel& rDrawModel) override; + virtual bool removeDiagramNode(const OUString& rNodeId, SdrModel& rDrawModel) override; virtual void TextInformationChange() override; + virtual void ItemSetInformationChange(std::span< const SfxPoolItem* const > aChangedItems) override; // Undo/Redo helpers to extract/restore Diagram-defining data virtual std::shared_ptr< svx::diagram::DiagramDataState > extractDiagramDataState() const override; @@ -111,6 +112,8 @@ public: // write data to boost::property_tree virtual void addDiagramModelData(boost::property_tree::ptree& rTarget) const override; + virtual const OUString& getBackgroundShapeModelID() const override; + virtual bool isTextNodeModelID(const OUString& rModelID) const override; }; class DiagramHelperFactory_oox : public svx::diagram::DiagramHelperFactory_svx diff --git a/include/svx/diagram/DiagramHelper_svx.hxx b/include/svx/diagram/DiagramHelper_svx.hxx index b1cf20441266..0bdc15fd5457 100644 --- a/include/svx/diagram/DiagramHelper_svx.hxx +++ b/include/svx/diagram/DiagramHelper_svx.hxx @@ -55,6 +55,8 @@ class DiagramDataState; class SVXCORE_DLLPUBLIC DiagramHelper_svx { private: + OUString msSelectedModelID; + // These values define behaviour to where take data from at re-creation time. // Different definitions will have different consequences for re-creation // of Diagram visualization (if needed/triggered). @@ -80,7 +82,14 @@ private: // a newly created oox::drawingml::Theme object bool mbForceThemePtrRecreation; // false + // flag to remember if locking of scale/translate was + // already done + bool mbDiagramObjectsLocked : 1; + protected: + // helpers + void applyLocksToDiagramObjects(bool bActivate); + // access associated SdrObjGroup/XShape/RootShape virtual css::uno::Reference< css::drawing::XShape >& accessRootShape() = 0; @@ -105,9 +114,10 @@ public: getDiagramChildren(const OUString& rParentId) const = 0; // add/remove new top-level node to data model, returns its id - virtual OUString addDiagramNode(const OUString& rText) = 0; - virtual bool removeDiagramNode(const OUString& rNodeId) = 0; + virtual OUString addDiagramNode(const OUString& rText, SdrModel& rDrawModel) = 0; + virtual bool removeDiagramNode(const OUString& rNodeId, SdrModel& rDrawModel) = 0; virtual void TextInformationChange() = 0; + virtual void ItemSetInformationChange(std::span< const SfxPoolItem* const > aChangedItems) = 0; // Undo/Redo helpers for extracting/restoring Diagram-defining data virtual std::shared_ptr extractDiagramDataState() const = 0; @@ -121,7 +131,7 @@ public: void connectToSdrObjGroup(const css::uno::Reference< css::drawing::XShape >& rTarget); void disconnectFromSdrObjGroup(); - static void AddAdditionalVisualization(const SdrObjGroup& rTarget, SdrHdlList& rHdlList); + void AddAdditionalVisualization(SdrHdlList& rHdlList); // access to PropertyValues virtual css::uno::Any getOOXDomValue(svx::diagram::DomMapFlag aDomMapFlag) const = 0; @@ -137,6 +147,18 @@ public: // write data to boost::property_tree virtual void addDiagramModelData(boost::property_tree::ptree& rTarget) const = 0; + virtual const OUString& getBackgroundShapeModelID() const = 0; + virtual bool isTextNodeModelID(const OUString& rModelID) const = 0; + + const OUString& getSelectedModelID() const; + void setSelectedModelID(const OUString& rNew) { msSelectedModelID = rNew; } + SdrObject* getDiagramSubSelection(); + void markDirectDiagramSubSelection(SdrObject& rCandidate); + bool markNextDiagramSubSelection(bool bPrev); + void initSelectionByKeyboard(bool bPrev); + + // simple access to AssociatedRootShape + SdrObjGroup* getAssociatedRootShape(); }; class SVXCORE_DLLPUBLIC DiagramHelperFactory_svx diff --git a/include/svx/diagram/datamodel_svx.hxx b/include/svx/diagram/datamodel_svx.hxx index 85eed09b99ec..c96aae887b44 100644 --- a/include/svx/diagram/datamodel_svx.hxx +++ b/include/svx/diagram/datamodel_svx.hxx @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -150,12 +151,16 @@ class UNLESS_MERGELIBS(SVXCORE_DLLPUBLIC) DiagramDataState { Connections maConnections; Points maPoints; + std::vector> mxShapes; + basegfx::B2DHomMatrix maTransformation; public: - DiagramDataState(Connections aConnections, Points aPoints); + DiagramDataState(const Connections& aConnections, const Points& aPoints, const css::uno::Reference< css::drawing::XShape >& rRootShape); - Connections& getConnections() { return maConnections; } - Points& getPoints() { return maPoints; } + const Connections& getConnections() const { return maConnections; } + const Points& getPoints() const { return maPoints; } + const std::vector>& getXShapes() const { return mxShapes;} + const basegfx::B2DHomMatrix& getTransformation() const { return maTransformation; } }; typedef std::shared_ptr< DiagramDataState > DiagramDataStatePtr; diff --git a/include/svx/svdobj.hxx b/include/svx/svdobj.hxx index 60dc4040dbeb..890052069575 100644 --- a/include/svx/svdobj.hxx +++ b/include/svx/svdobj.hxx @@ -228,10 +228,14 @@ public: // Basic DiagramHelper support virtual bool isDiagram() const; virtual const std::shared_ptr< svx::diagram::DiagramHelper_svx >& getDiagramHelper() const; - const std::shared_ptr< svx::diagram::DiagramHelper_svx >& getDiagramHelperFromDiagramOrMember() const; void setDiagramDataModelID(const OUString& rID); const OUString& getDiagramDataModelID() const; + virtual SdrObject* getDiagramSubSelection(); + bool isDiagramBackgroundShape() const; + bool isDiagramTextNode() const; + bool removeDiagramNode(); + private: // check if a ObjectName is set and it is in DiagramModelID syntax bool ObjectNameIsDiagramModelID(); diff --git a/include/svx/svdogrp.hxx b/include/svx/svdogrp.hxx index e5e24770d2db..2f8977c0eadd 100644 --- a/include/svx/svdogrp.hxx +++ b/include/svx/svdogrp.hxx @@ -31,6 +31,7 @@ public: // Basic DiagramHelper support virtual bool isDiagram() const override; virtual const std::shared_ptr< svx::diagram::DiagramHelper_svx >& getDiagramHelper() const override; + virtual SdrObject* getDiagramSubSelection() override; private: virtual std::unique_ptr CreateObjectSpecificViewContact() override; diff --git a/include/svx/svdundo.hxx b/include/svx/svdundo.hxx index bb83dc944c15..53fdfdbf6419 100644 --- a/include/svx/svdundo.hxx +++ b/include/svx/svdundo.hxx @@ -233,8 +233,6 @@ class UNLESS_MERGELIBS(SVXCORE_DLLPUBLIC) SdrUndoDiagramModelData final : public std::shared_ptr< svx::diagram::DiagramDataState > m_aStartState; std::shared_ptr< svx::diagram::DiagramDataState > m_aEndState; - void implUndoRedo(bool bUndo); - public: SdrUndoDiagramModelData(SdrObject& rNewObj, const std::shared_ptr< svx::diagram::DiagramDataState >& rStartState); virtual ~SdrUndoDiagramModelData() override; diff --git a/oox/source/drawingml/diagram/diagram.cxx b/oox/source/drawingml/diagram/diagram.cxx index 05700c83112d..3b01c15244e2 100644 --- a/oox/source/drawingml/diagram/diagram.cxx +++ b/oox/source/drawingml/diagram/diagram.cxx @@ -121,7 +121,6 @@ static void removeUnneededGroupShapes(const ShapePtr& pShape) } } - void SmartArtDiagram::createShapeHierarchyFromModel( const ShapePtr & pParentShape, bool bCreate ) { if (pParentShape->getSize().Width == 0 || pParentShape->getSize().Height == 0) @@ -152,11 +151,6 @@ void SmartArtDiagram::createShapeHierarchyFromModel( const ShapePtr & pParentSha pBackground->setSize(pParentShape->getSize()); if (mpData->getBackgroundShapeFillProperties()) pBackground->getFillProperties() = *mpData->getBackgroundShapeFillProperties(); - // MoveProtect/SizeProtect..? Keep for now, but may be removed - // when IA needs change - the Diagram will be a closed GroupObject. - // If it gets 'broken' (un-grouped) the BGShape will keep that attributes, - // despite main reason to break that Diagram is probably to edit it. - pBackground->setLocked(true); // create and set ModelID for BackgroundShape to allow later association getData()->setBackgroundShapeModelID(OStringToOUString(comphelper::xml::generateGUIDString(), RTL_TEXTENCODING_UTF8)); @@ -166,10 +160,26 @@ void SmartArtDiagram::createShapeHierarchyFromModel( const ShapePtr & pParentSha aChildren.insert(aChildren.begin(), pBackground); } +uno::Reference SmartArtDiagram::convertAndSet(std::u16string_view rDOM, svx::diagram::DomMapFlag aDomMapFlag) +{ + // construct MemoryStream and OStreamWrapper + const OString sUtf8(OUStringToOString(rDOM, RTL_TEXTENCODING_UTF8)); + SvMemoryStream aStream(const_cast(sUtf8.getStr()), sUtf8.getLength(), StreamMode::READ); + rtl::Reference pStreamWrapper = new utl::OStreamWrapper(aStream); + + // create the dom parser & create DomTree + uno::Reference xDomBuilder(xml::dom::DocumentBuilder::create(comphelper::getProcessComponentContext())); + uno::Reference aDomTree(xDomBuilder->parse(pStreamWrapper->getInputStream())); + + // set DomTree locally + setOOXDomValue(aDomMapFlag, uno::Any(aDomTree)); + return aDomTree; +} + SmartArtDiagram::SmartArtDiagram() : maDiagramFontHeights() -, mpData() -, mpLayout() +, mpData(std::make_shared()) +, mpLayout(std::make_shared(*this)) , maStyles() , maColors() , maDiagramPRDomMap() @@ -209,67 +219,33 @@ SmartArtDiagram::SmartArtDiagram(const boost::property_tree::ptree& rDiagramMode if (!aOOXLayoutDOM.isEmpty()) { - // construct MemoryStream and OStreamWrapper with SVG in OUString - const OString sUtf8(OUStringToOString(aOOXLayoutDOM, RTL_TEXTENCODING_UTF8)); - SvMemoryStream aStream(const_cast(sUtf8.getStr()), sUtf8.getLength(), StreamMode::READ); - rtl::Reference pStreamWrapper = new utl::OStreamWrapper(aStream); - - // create the dom parser & create DomTree - uno::Reference xDomBuilder(xml::dom::DocumentBuilder::create(comphelper::getProcessComponentContext())); - uno::Reference aDomTree(xDomBuilder->parse(pStreamWrapper->getInputStream())); - - // set DomTree locally - setOOXDomValue(svx::diagram::DomMapFlag::OOXLayout, uno::Any(aDomTree)); + // create and set DomTree locally + uno::Reference xDom(convertAndSet(aOOXLayoutDOM, svx::diagram::DomMapFlag::OOXLayout)); // import DomTree to mpLayout - uno::Reference xSerializer(aDomTree, uno::UNO_QUERY_THROW); + uno::Reference xSerializer(xDom, uno::UNO_QUERY_THROW); rtl::Reference< core::FragmentHandler > xRefLayout(new DiagramLayoutFragmentHandler(*this, *xPPTImport, "internal", mpLayout)); xPPTImport->importFragment(xRefLayout, xSerializer); - - // Data is loaded, so we already have Points and Connections. Also - // need to rebuild buffers for layouting (maPointsPresNameMap, - // maConnectionNameMap, maPresOfNameMap). Call with false due to - // we are in ODF import and have no oox::Shapes to delete and do - // *not* want these to be created (would not hurt, but not needed) - // getData()->buildDiagramDataModel(false); } if (!aOOXStyleDOM.isEmpty()) { - // construct MemoryStream and OStreamWrapper with SVG in OUString - const OString sUtf8(OUStringToOString(aOOXStyleDOM, RTL_TEXTENCODING_UTF8)); - SvMemoryStream aStream(const_cast(sUtf8.getStr()), sUtf8.getLength(), StreamMode::READ); - rtl::Reference pStreamWrapper = new utl::OStreamWrapper(aStream); - - // create the dom parser & create DomTree - uno::Reference xDomBuilder(xml::dom::DocumentBuilder::create(comphelper::getProcessComponentContext())); - uno::Reference aDomTree(xDomBuilder->parse(pStreamWrapper->getInputStream())); - - // set DomTree locally - setOOXDomValue(svx::diagram::DomMapFlag::OOXStyle, uno::Any(aDomTree)); + // create and set DomTree locally + uno::Reference xDom(convertAndSet(aOOXStyleDOM, svx::diagram::DomMapFlag::OOXStyle)); // import DomTree to maStyles - uno::Reference xSerializer(aDomTree, uno::UNO_QUERY_THROW); + uno::Reference xSerializer(xDom, uno::UNO_QUERY_THROW); rtl::Reference< core::FragmentHandler > xRefLayout(new DiagramQStylesFragmentHandler(*xPPTImport, "internal", maStyles)); xPPTImport->importFragment(xRefLayout, xSerializer); } if (!aOOXColorDOM.isEmpty()) { - // construct MemoryStream and OStreamWrapper with SVG in OUString - const OString sUtf8(OUStringToOString(aOOXColorDOM, RTL_TEXTENCODING_UTF8)); - SvMemoryStream aStream(const_cast(sUtf8.getStr()), sUtf8.getLength(), StreamMode::READ); - rtl::Reference pStreamWrapper = new utl::OStreamWrapper(aStream); - - // create the dom parser & create DomTree - uno::Reference xDomBuilder(xml::dom::DocumentBuilder::create(comphelper::getProcessComponentContext())); - uno::Reference aDomTree(xDomBuilder->parse(pStreamWrapper->getInputStream())); - - // set DomTree locally - setOOXDomValue(svx::diagram::DomMapFlag::OOXColor, uno::Any(aDomTree)); + // create and set DomTree locally + uno::Reference xDom(convertAndSet(aOOXColorDOM, svx::diagram::DomMapFlag::OOXColor)); // import DomTree to maColors - uno::Reference xSerializer(aDomTree, uno::UNO_QUERY_THROW); + uno::Reference xSerializer(xDom, uno::UNO_QUERY_THROW); rtl::Reference< core::FragmentHandler > xRefLayout(new ColorFragmentHandler(*xPPTImport, "internal", maColors)); xPPTImport->importFragment(xRefLayout, xSerializer); } @@ -598,12 +574,6 @@ void loadDiagram( ShapePtr const & pShape, { DiagramPtr pDiagram = std::make_shared(); - OoxDiagramDataPtr pData = std::make_shared(); - pDiagram->setData( pData ); - - DiagramLayoutPtr pLayout = std::make_shared(*pDiagram); - pDiagram->setLayout( pLayout ); - try { // set DiagramFontHeights at filter @@ -613,7 +583,7 @@ void loadDiagram( ShapePtr const & pShape, if( !rDataModelPath.isEmpty() ) { rtl::Reference< core::FragmentHandler > xRefDataModel( - new DiagramDataFragmentHandler( rFilter, rDataModelPath, pData )); + new DiagramDataFragmentHandler( rFilter, rDataModelPath, pDiagram->getData() )); importFragment(rFilter, loadFragment(rFilter,xRefDataModel), @@ -634,7 +604,7 @@ void loadDiagram( ShapePtr const & pShape, uno::Any(aDataHlinkRelsMap)); // Pass the info to pShape - for (auto const& extDrawing : pData->getExtDrawings()) + for (auto const& extDrawing : pDiagram->getData()->getExtDrawings()) { OUString aFragmentPath = rRelations.getFragmentPathFromRelId(extDrawing); // Ignore RelIds which don't resolve to a fragment path. @@ -665,8 +635,7 @@ void loadDiagram( ShapePtr const & pShape, if (!rLayoutPath.isEmpty()) { rtl::Reference< core::FragmentHandler > xRefLayout( - new DiagramLayoutFragmentHandler( *pDiagram, rFilter, rLayoutPath, - std::move(pLayout) )); + new DiagramLayoutFragmentHandler( *pDiagram, rFilter, rLayoutPath, pDiagram->getLayout())); importFragment(rFilter, loadFragment(rFilter,xRefLayout), @@ -701,7 +670,7 @@ void loadDiagram( ShapePtr const & pShape, xRefColorStyle); } - if( !pData->getExtDrawings().empty() ) + if( !pDiagram->getData()->getExtDrawings().empty() ) { const DiagramColorMap::const_iterator aColor = pDiagram->getColors().find(u"node0"_ustr); if( aColor != pDiagram->getColors().end() && !aColor->second.maTextFillColors.empty()) @@ -714,9 +683,9 @@ void loadDiagram( ShapePtr const & pShape, // collect data, init maps // for Diagram import, do - for now - NOT clear all oox::drawingml::Shape - pData->buildDiagramDataModel(false); + pDiagram->getData()->buildDiagramDataModel(false); #ifdef DBG_UTIL - pData->dump(); + pDiagram->getData()->dump(); #endif // diagram loaded. now lump together & attach to shape @@ -735,9 +704,11 @@ void loadDiagram( ShapePtr const & pShape, // original ImportData directly to the Diagram ModelData std::shared_ptr<::oox::drawingml::Theme> aTheme(rFilter.getCurrentThemePtr()); if(aTheme) - pData->setThemeDocument(aTheme->getFragment()); //getTempFile()); + pDiagram->getData()->setThemeDocument(aTheme->getFragment()); // Prepare support for the advanced DiagramHelper using Diagram & Theme data + // This is where pDiagram is moved to where it will stay, else it wil get + // cleaned up (what is intended) pShape->prepareDiagramHelper(pDiagram, rFilter.getCurrentThemePtr()); } catch (...) diff --git a/oox/source/drawingml/diagram/diagram.hxx b/oox/source/drawingml/diagram/diagram.hxx index 327787d9f0b5..51bd73fa6067 100644 --- a/oox/source/drawingml/diagram/diagram.hxx +++ b/oox/source/drawingml/diagram/diagram.hxx @@ -132,14 +132,8 @@ public: explicit SmartArtDiagram(SmartArtDiagram const& rSource); explicit SmartArtDiagram(const boost::property_tree::ptree& rDiagramModel); ~SmartArtDiagram(); - void setData( const OoxDiagramDataPtr& pData ) - { mpData = pData; } - const OoxDiagramDataPtr& getData() const - { return mpData; } - void setLayout( const DiagramLayoutPtr & pLayout ) - { mpLayout = pLayout; } - const DiagramLayoutPtr& getLayout() const - { return mpLayout; } + const OoxDiagramDataPtr& getData() const { return mpData; } + const DiagramLayoutPtr& getLayout() const { return mpLayout; } DiagramQStyleMap& getStyles() { return maStyles; } const DiagramQStyleMap& getStyles() const { return maStyles; } @@ -165,8 +159,9 @@ public: void addDiagramModelData(boost::property_tree::ptree& rTarget) const; private: - // helper for addDiagramModelData + // helpers void addDomTreeToModelData(svx::diagram::DomMapFlag aId, std::u16string_view aName, boost::property_tree::ptree& rTarget) const; + css::uno::Reference convertAndSet(std::u16string_view rDOM, svx::diagram::DomMapFlag aDomMapFlag); // This contains groups of shapes: automatic font size is the same in each group. oox::core::NamedShapePairs maDiagramFontHeights; diff --git a/oox/source/drawingml/diagram/diagramhelper_oox.cxx b/oox/source/drawingml/diagram/diagramhelper_oox.cxx index 479e5472db73..61db150f97ca 100644 --- a/oox/source/drawingml/diagram/diagramhelper_oox.cxx +++ b/oox/source/drawingml/diagram/diagramhelper_oox.cxx @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -224,6 +225,11 @@ void DiagramHelper_oox::reLayout() // Re-apply remembered geometry pTarget->TRSetBaseGeometry(aTransformation, aPolyPolygon); + // new SdrObjects created, re-apply geometry change locks as needed + // and reset SubSelection + applyLocksToDiagramObjects(true); + setSelectedModelID(EMPTY_OUSTRING); + // extract newly created DrawingLayerModelData std::vector> xNewXShapes; uno::Reference xNewShape; @@ -335,70 +341,137 @@ DiagramHelper_oox::getDiagramChildren(const OUString& rParentId) const return std::vector>(); } -OUString DiagramHelper_oox::addDiagramNode(const OUString& rText) +OUString DiagramHelper_oox::addDiagramNode(const OUString& rText, SdrModel& rDrawModel) { OUString aRetval; if (hasDiagramData()) { + const bool bUndo(rDrawModel.IsUndoEnabled()); + svx::diagram::DiagramDataStatePtr aStartState; + + if (bUndo) + { + // rescue all start state Diagram-defining data + aStartState = extractDiagramDataState(); + } + const std::pair aResult = mpDiagramPtr->getData()->addDiagramNode(); aRetval = aResult.first; - // reset Dom properties at DiagramData - mpDiagramPtr->resetOOXDomValues(aResult.second); - - // reset temporary buffered ModelData association lists & rebuild them - // and the Diagram DataModel - mpDiagramPtr->getData()->buildDiagramDataModel(true); - - // also reset temporary buffered layout data - that might - // still refer to changed oox::Shape data - mpDiagramPtr->getLayout()->getPresPointShapeMap().clear(); - - // we have the text node in aRetval, but we need the ModelID - // of the node referring that one, that is the one that will be used - // as ModelID in the XShape/SdrObject. Loop and look for it - for (const auto& rCandidate : mpDiagramPtr->getData()->getPoints()) + if (!aRetval.isEmpty()) { - if (!rCandidate.msPresentationAssociationId.isEmpty() - && rCandidate.msPresentationAssociationId == aRetval) + // reset Dom properties at DiagramData + mpDiagramPtr->resetOOXDomValues(aResult.second); + + // reset temporary buffered ModelData association lists & rebuild them + // and the Diagram DataModel + mpDiagramPtr->getData()->buildDiagramDataModel(true); + + // also reset temporary buffered layout data - that might + // still refer to changed oox::Shape data + mpDiagramPtr->getLayout()->getPresPointShapeMap().clear(); + + // we have the text node in aRetval, but we need the ModelID + // of the node referring that one, that is the one that will be used + // as ModelID in the XShape/SdrObject. Loop and look for it + for (const auto& rCandidate : mpDiagramPtr->getData()->getPoints()) { - msNewNodeId = rCandidate.msModelId; - break; + if (!rCandidate.msPresentationAssociationId.isEmpty() + && rCandidate.msPresentationAssociationId == aRetval) + { + msNewNodeId = rCandidate.msModelId; + break; + } + } + + msNewNodeText = rText; + + if (bUndo) + { + // create undo action. That will internally secure the + // current Diagram-defining data as end state + SdrObject* pRootShape(SdrObject::getSdrObjectFromXShape(accessRootShape())); + assert(nullptr != pRootShape && "Missing RootShape in DiagramHelper_oox (!)"); + rDrawModel.AddUndo(rDrawModel.GetSdrUndoFactory().CreateUndoDiagramModelData( + *pRootShape, aStartState)); } } - - // msNewNodeId = aRetval; - msNewNodeText = rText; } return aRetval; } -bool DiagramHelper_oox::removeDiagramNode(const OUString& rNodeId) +bool DiagramHelper_oox::removeDiagramNode(const OUString& rNodeId, SdrModel& rDrawModel) { bool bRetval(false); if (hasDiagramData()) { - DomMapFlags aResult = mpDiagramPtr->getData()->removeDiagramNode(rNodeId); - bRetval = !aResult.empty(); + const bool bUndo(rDrawModel.IsUndoEnabled()); + svx::diagram::DiagramDataStatePtr aStartState; - // reset Dom properties at DiagramData - mpDiagramPtr->resetOOXDomValues(std::move(aResult)); + if (bUndo) + { + // rescue all start state Diagram-defining data + aStartState = extractDiagramDataState(); + } - // reset temporary buffered ModelData association lists & rebuild them - // and the Diagram DataModel - mpDiagramPtr->getData()->buildDiagramDataModel(true); + DomMapFlags aResult(mpDiagramPtr->getData()->removeDiagramNode(rNodeId)); - // also reset temporary buffered layout data - that might - // still refer to changed oox::Shape data - mpDiagramPtr->getLayout()->getPresPointShapeMap().clear(); + if (!aResult.empty()) + { + bRetval = true; + + // reset Dom properties at DiagramData + mpDiagramPtr->resetOOXDomValues(std::move(aResult)); + + // reset temporary buffered ModelData association lists & rebuild them + // and the Diagram DataModel + mpDiagramPtr->getData()->buildDiagramDataModel(true); + + // also reset temporary buffered layout data - that might + // still refer to changed oox::Shape data + mpDiagramPtr->getLayout()->getPresPointShapeMap().clear(); + + if (bUndo) + { + // create undo action. That will internally secure the + // current Diagram-defining data as end state + SdrObject* pRootShape(SdrObject::getSdrObjectFromXShape(accessRootShape())); + assert(nullptr != pRootShape && "Missing RootShape in DiagramHelper_oox (!)"); + rDrawModel.AddUndo(rDrawModel.GetSdrUndoFactory().CreateUndoDiagramModelData( + *pRootShape, aStartState)); + } + } } return bRetval; } +void DiagramHelper_oox::ItemSetInformationChange(std::span aChangedItems) +{ + bool bFillChanged(false); + bool bLineChanged(false); + bool bTextChanged(false); + + for (const SfxPoolItem* pItem : aChangedItems) + { + bFillChanged |= (pItem->Which() >= XATTR_FILL_FIRST && pItem->Which() <= XATTR_FILL_LAST); + bLineChanged |= (pItem->Which() >= XATTR_LINE_FIRST && pItem->Which() <= XATTR_LINE_LAST); + bTextChanged |= (pItem->Which() >= XATTR_TEXT_FIRST && pItem->Which() <= XATTR_TEXT_LAST); + } + + if (bFillChanged || bLineChanged || bTextChanged) + { + // attributes of one of the contained shapes have + // changed. Maybe other actions are needed in the + // future, but for now do general reset like + // TextInformationChange() does + TextInformationChange(); + } +} + void DiagramHelper_oox::TextInformationChange() { if (!mpDiagramPtr) @@ -543,6 +616,29 @@ void DiagramHelper_oox::addDiagramModelData(boost::property_tree::ptree& rTarget mpDiagramPtr->addDiagramModelData(rTarget); } +const OUString& DiagramHelper_oox::getBackgroundShapeModelID() const +{ + if (!mpDiagramPtr) + return EMPTY_OUSTRING; + + return mpDiagramPtr->getData()->getBackgroundShapeModelID(); +} + +bool DiagramHelper_oox::isTextNodeModelID(const OUString& rModelID) const +{ + if (!mpDiagramPtr || rModelID.isEmpty()) + return false; + + for (const auto& rCandidate : mpDiagramPtr->getData()->getPoints()) + { + if (rCandidate.msModelId == rModelID + && rCandidate.msPresentationLayoutName == u"textNode"_ustr) + return true; + } + + return false; +} + DiagramHelperFactory_oox::DiagramHelperFactory_oox() : DiagramHelperFactory_svx() { diff --git a/oox/source/export/drawingml.cxx b/oox/source/export/drawingml.cxx index 473e1f7b6c45..78842f2f0e6f 100644 --- a/oox/source/export/drawingml.cxx +++ b/oox/source/export/drawingml.cxx @@ -2326,7 +2326,7 @@ void DrawingML::WriteShapeTransformation( const Reference< XShape >& rXShape, sa if (nullptr != pShape) { - std::shared_ptr< svx::diagram::DiagramHelper_svx > pDiagramHelper(pShape->getDiagramHelperFromDiagramOrMember()); + std::shared_ptr< svx::diagram::DiagramHelper_svx > pDiagramHelper(pShape->getDiagramHelper()); if (pDiagramHelper) { diff --git a/sc/source/ui/drawfunc/fusel.cxx b/sc/source/ui/drawfunc/fusel.cxx index 5b0b7188608e..e0cce98850e2 100644 --- a/sc/source/ui/drawfunc/fusel.cxx +++ b/sc/source/ui/drawfunc/fusel.cxx @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -359,6 +360,7 @@ bool FuSelection::MouseButtonUp(const MouseEvent& rMEvt) SdrPage* pPage = ( pPageView ? pPageView->GetPage() : nullptr ); ::std::vector< OUString > aExcludedChartNames; ScRangeListVector aProtectedChartRangesVector; + bool bWasDragged = false; if (comphelper::LibreOfficeKit::isActive() && rDocument.IsNegativePage(rViewData.CurrentTabForData())) aPnt.setX(-aPnt.X()); @@ -406,7 +408,7 @@ bool FuSelection::MouseButtonUp(const MouseEvent& rMEvt) return true; } } - pView->EndDragObj( rMEvt.IsMod1() ); + bWasDragged = pView->EndDragObj( rMEvt.IsMod1() ); pView->ForceMarkedToAnotherPage(); bReturn = true; @@ -529,6 +531,32 @@ bool FuSelection::MouseButtonUp(const MouseEvent& rMEvt) bReturn = true; } + if (!bWasDragged) + { + const SdrMarkList& rMarkList(pView->GetMarkedObjectList()); + if (rMarkList.GetMarkCount() == 1) + { + SdrObject* pSingleObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + + if(nullptr != pSingleObj && pSingleObj->isDiagram()) + { + const std::shared_ptr< svx::diagram::DiagramHelper_svx >& rDiagramHelper(pSingleObj->getDiagramHelper()); + if (rDiagramHelper) + { + SdrPageView* pPV; + SdrObject* pCandidate = pView->PickObj(aMDPos, pView->getHitTolLog(), pPV, SdrSearchOptions::DEEP); + if (pCandidate && !pCandidate->isDiagram()) + { + rDiagramHelper->markDirectDiagramSubSelection(*pCandidate); + pView->UnmarkAllObj(); + pView->MarkObj(pSingleObj,pPV); + return true; + } + } + } + } + } + ForcePointer(&rMEvt); if (pWindow->IsMouseCaptured()) diff --git a/sd/qa/unit/tiledrendering/tiledrendering.cxx b/sd/qa/unit/tiledrendering/tiledrendering.cxx index a0fc63542e0a..038903eebe60 100644 --- a/sd/qa/unit/tiledrendering/tiledrendering.cxx +++ b/sd/qa/unit/tiledrendering/tiledrendering.cxx @@ -2273,6 +2273,12 @@ CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testRegenerateDiagram) // as the group shape is a diagram. Do the same as before done by triggering UI // events directly in the model // Remove and free top-left entry (Box showing "A") + // NOTE: DiagramSubObjects are now DeleteProtected and + // inside RemoveObject IsDeleteProtect is checked, so + // this intentionally no longer removes that object. + // To remove it the diagram command to remove it including + // the correct DiagramModelID of that object is needed, as + // is done from the DiagramDialog pActualPage->GetObj(0)->GetSubList()->RemoveObject(1); // select diagram @@ -2280,12 +2286,15 @@ CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testRegenerateDiagram) pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::TAB); Scheduler::ProcessEventsToIdle(); - CPPUNIT_ASSERT_EQUAL(static_cast(3), pActualPage->GetObj(0)->GetSubList()->GetObjCount()); + // see note above: four is correct for now + CPPUNIT_ASSERT_EQUAL(static_cast(4), pActualPage->GetObj(0)->GetSubList()->GetObjCount()); // regenerate diagram dispatchCommand(mxComponent, u".uno:RegenerateDiagram"_ustr, uno::Sequence()); // diagram content (child shape count) should be the same as in the beginning + // NOTE: This is due to regenerate-diagram.pptx *not* having a drawing.xml + // and regenerate had to be done already at load time CPPUNIT_ASSERT_EQUAL(static_cast(4), pActualPage->GetObj(0)->GetSubList()->GetObjCount()); } diff --git a/sd/source/ui/docshell/docshel4.cxx b/sd/source/ui/docshell/docshel4.cxx index ae00224932e8..1e070280503a 100644 --- a/sd/source/ui/docshell/docshel4.cxx +++ b/sd/source/ui/docshell/docshel4.cxx @@ -455,7 +455,9 @@ bool DrawDocShell::ImportFrom(SfxMedium &rMedium, mpDoc->EnableUndo(false); } + mpDoc->incImportExport(); const bool bRet = SfxObjectShell::ImportFrom(rMedium, xInsertPosition); + mpDoc->decImportExport(); SfxItemSet& rSet = rMedium.GetItemSet(); if (SfxItemState::SET == rSet.GetItemState(SID_DOC_STARTPRESENTATION)) diff --git a/sd/source/ui/func/fusel.cxx b/sd/source/ui/func/fusel.cxx index ccde638f8f34..9b7c244d0200 100644 --- a/sd/source/ui/func/fusel.cxx +++ b/sd/source/ui/func/fusel.cxx @@ -734,6 +734,7 @@ bool FuSelection::MouseButtonUp(const MouseEvent& rMEvt) **************************************************************/ SdrPageView* pPV; SdrObject* pObj = mpView->PickObj(aMDPos, mpView->getHitTolLog(), pPV, SdrSearchOptions::ALSOONMASTER | SdrSearchOptions::BEFOREMARK); + if (pObj && pPV->IsObjMarkable(pObj)) { mpView->UnmarkAllObj(); @@ -750,13 +751,12 @@ bool FuSelection::MouseButtonUp(const MouseEvent& rMEvt) pSingleObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); } - // Check for click on svx::diagram::DiagramFrameHdl - // - if we hit a SdrHdl - // - if it was not moved - // - if single object is selected - // - and it is a Diagram - if(pHdl && !bWasDragged && nullptr != pSingleObj && pSingleObj->isDiagram()) + if(!bWasDragged && nullptr != pSingleObj && pSingleObj->isDiagram() && pHdl) { + // Check for click on svx::diagram::DiagramFrameHdl + // - if we hit a SdrHdl + // - if it was not moved + // - if single object is selected and it is a Diagram svx::diagram::DiagramFrameHdl* pDiagramFrameHdl(dynamic_cast(pHdl)); if(nullptr != pDiagramFrameHdl) { @@ -910,6 +910,23 @@ bool FuSelection::MouseButtonUp(const MouseEvent& rMEvt) return true; } + if(!bWasDragged && nullptr != pSingleObj && pSingleObj->isDiagram()) + { + const std::shared_ptr< svx::diagram::DiagramHelper_svx >& rDiagramHelper(pSingleObj->getDiagramHelper()); + if (rDiagramHelper) + { + SdrPageView* pPV; + SdrObject* pCandidate = mpView->PickObj(aMDPos, mpView->getHitTolLog(), pPV, SdrSearchOptions::DEEP); + if (pCandidate && !pCandidate->isDiagram()) + { + rDiagramHelper->markDirectDiagramSubSelection(*pCandidate); + mpView->UnmarkAllObj(); + mpView->MarkObj(pSingleObj,pPV); + return true; + } + } + } + if ( (nSlotId != SID_OBJECT_SELECT && nMarkCount==0) || ( mpView->GetDragMode() == SdrDragMode::Crook && !mpView->IsCrookAllowed( mpView->IsCrookNoContortion() ) ) || diff --git a/svx/source/diagram/DiagramHelper_svx.cxx b/svx/source/diagram/DiagramHelper_svx.cxx index 016a1722807c..2d88e9f5768b 100644 --- a/svx/source/diagram/DiagramHelper_svx.cxx +++ b/svx/source/diagram/DiagramHelper_svx.cxx @@ -38,6 +38,8 @@ #include #include #include +#include +#include using namespace ::com::sun::star; @@ -406,40 +408,52 @@ DiagramFrameHdl::DiagramFrameHdl(const basegfx::B2DHomMatrix& rTransformation) } DiagramHelper_svx::DiagramHelper_svx() -: mbUseDiagramThemeData(false) +: msSelectedModelID() +, mbUseDiagramThemeData(false) , mbUseDiagramModelData(true) , mbForceThemePtrRecreation(false) +, mbDiagramObjectsLocked(false) { } DiagramHelper_svx::DiagramHelper_svx(DiagramHelper_svx const& rSource) -: mbUseDiagramThemeData(rSource.mbUseDiagramThemeData) +: msSelectedModelID() +, mbUseDiagramThemeData(rSource.mbUseDiagramThemeData) , mbUseDiagramModelData(rSource.mbUseDiagramModelData) , mbForceThemePtrRecreation(rSource.mbForceThemePtrRecreation) +, mbDiagramObjectsLocked(false) { } DiagramHelper_svx::~DiagramHelper_svx() {} -void DiagramHelper_svx::disconnectFromSdrObjGroup() +SdrObjGroup* DiagramHelper_svx::getAssociatedRootShape() { uno::Reference< drawing::XShape >& rxRootShape(accessRootShape()); - SdrObjGroup* pGroupObject(dynamic_cast(SdrObject::getSdrObjectFromXShape(rxRootShape))); - if (nullptr != pGroupObject) + return dynamic_cast(SdrObject::getSdrObjectFromXShape(rxRootShape)); +} + +void DiagramHelper_svx::disconnectFromSdrObjGroup() +{ + SdrObjGroup* pRootObject(getAssociatedRootShape()); + + if (nullptr != pRootObject) { - rxRootShape.clear(); - pGroupObject->mp_DiagramHelper.reset(); + // remove locks. Do this before resetting RootShape below, it uses that + applyLocksToDiagramObjects(false); + accessRootShape().clear(); + pRootObject->mp_DiagramHelper.reset(); } } void DiagramHelper_svx::connectToSdrObjGroup(const css::uno::Reference< css::drawing::XShape >& rTarget) { - SdrObjGroup* pGroupObject(nullptr); + SdrObjGroup* pRootObject(nullptr); uno::Reference< drawing::XShape >& rxRootShape(accessRootShape()); if (rxRootShape && rTarget == rxRootShape) { - pGroupObject = dynamic_cast(SdrObject::getSdrObjectFromXShape(rxRootShape)); - if (pGroupObject != nullptr && pGroupObject->mp_DiagramHelper.get() == this) + pRootObject = dynamic_cast(SdrObject::getSdrObjectFromXShape(rxRootShape)); + if (pRootObject != nullptr && pRootObject->mp_DiagramHelper.get() == this) { // connection already established return; @@ -451,20 +465,169 @@ void DiagramHelper_svx::connectToSdrObjGroup(const css::uno::Reference< css::dra // connect to target rxRootShape = rTarget; - pGroupObject = dynamic_cast(SdrObject::getSdrObjectFromXShape(rxRootShape)); - if (nullptr != pGroupObject) - pGroupObject->mp_DiagramHelper.reset(this); + pRootObject = dynamic_cast(SdrObject::getSdrObjectFromXShape(rxRootShape)); + if (nullptr != pRootObject) + pRootObject->mp_DiagramHelper.reset(this); + + applyLocksToDiagramObjects(true); } -void DiagramHelper_svx::AddAdditionalVisualization(const SdrObjGroup& rTarget, SdrHdlList& rHdlList) +void DiagramHelper_svx::AddAdditionalVisualization(SdrHdlList& rHdlList) { + SdrObjGroup* pRootObject(getAssociatedRootShape()); + if (nullptr == pRootObject) + return; + // create an extra frame visualization here basegfx::B2DHomMatrix aTransformation; basegfx::B2DPolyPolygon aPolyPolygon; - rTarget.TRGetBaseGeometry(aTransformation, aPolyPolygon); + pRootObject->TRGetBaseGeometry(aTransformation, aPolyPolygon); std::unique_ptr pHdl(new DiagramFrameHdl(aTransformation)); rHdlList.AddHdl(std::move(pHdl)); + + applyLocksToDiagramObjects(true); +} + +void DiagramHelper_svx::applyLocksToDiagramObjects(bool bActivate) +{ + if (mbDiagramObjectsLocked == bActivate) + return; + + SdrObjGroup* pRootObject(getAssociatedRootShape()); + if (nullptr == pRootObject) + return; + + mbDiagramObjectsLocked = bActivate; + SdrObjListIter aIterator(*pRootObject, SdrIterMode::DeepNoGroups); + while (aIterator.IsMore()) + { + SdrObject* pCandidate(aIterator.Next()); + + if (nullptr != pCandidate) + { + if (pRootObject != pCandidate) + { + pCandidate->SetMoveProtect(bActivate); + pCandidate->SetResizeProtect(bActivate); + pCandidate->SetDeleteProtect(bActivate); + } + + // reset name/DiagramDataModelID when deactivating which + // means that gets a regular SdrObjGroup + if (!bActivate) + pCandidate->setDiagramDataModelID(EMPTY_OUSTRING); + } + } +} + +const OUString& DiagramHelper_svx::getSelectedModelID() const +{ + if (msSelectedModelID.isEmpty()) + return getBackgroundShapeModelID(); + return msSelectedModelID; +} + +SdrObject* DiagramHelper_svx::getDiagramSubSelection() +{ + SdrObjGroup* pRootObject(getAssociatedRootShape()); + if (nullptr == pRootObject || 0 == pRootObject->GetObjCount()) + return nullptr; + + // look for shape with that ID + const OUString& rID(getSelectedModelID()); + SdrObjListIter aIter(pRootObject); + + while(aIter.IsMore()) + { + SdrObject* pObj(aIter.Next()); + if (nullptr != pObj && pObj->getDiagramDataModelID() == rID) + return pObj; + } + + OSL_FAIL("Diagram Sub-Shape with SelectedModelID not found (!)"); + aIter.Reset(); + return aIter.Next(); +} + +bool DiagramHelper_svx::markNextDiagramSubSelection(bool bPrev) +{ + SdrObjGroup* pRootObject(getAssociatedRootShape()); + if (nullptr == pRootObject || 0 == pRootObject->GetObjCount()) + return false; + + SdrObject* pObj(nullptr); + const OUString& rID(getSelectedModelID()); + + // find selected shape + SdrObjListIter aIter(pRootObject, SdrIterMode::DeepNoGroups, !bPrev); + + while(aIter.IsMore()) + { + pObj = aIter.Next(); + + if (nullptr != pObj && pObj->getDiagramDataModelID() == rID) + { + // a is currently selected shape + if (!aIter.IsMore()) + // last shape reached, return without usage + return false; + + pObj = aIter.Next(); + break; + } + } + + if (nullptr == pObj) + return false; + + setSelectedModelID(pObj->getDiagramDataModelID()); + return true; +} + +void DiagramHelper_svx::initSelectionByKeyboard(bool bPrev) +{ + SdrObjGroup* pRootObject(getAssociatedRootShape()); + if (nullptr == pRootObject || 0 == pRootObject->GetObjCount()) + return; + + SdrObjListIter aIter(pRootObject, SdrIterMode::DeepNoGroups, !bPrev); + SdrObject* pObj(aIter.Next()); + + if (nullptr == pObj) + return; + + setSelectedModelID(pObj->getDiagramDataModelID()); +} + +void DiagramHelper_svx::markDirectDiagramSubSelection(SdrObject& rCandidate) +{ + if (rCandidate.isDiagram() || rCandidate.getDiagramDataModelID().isEmpty()) + // it's directly the root of a Diagram or not a SdrObject associated + // with Diagrams at all + return; + + SdrObjGroup* pRootObject(getAssociatedRootShape()); + if (nullptr == pRootObject || 0 == pRootObject->GetObjCount()) + return; + + // find selected shape + SdrObjListIter aIter(pRootObject); + + while(aIter.IsMore()) + { + SdrObject* pObj(aIter.Next()); + + if (pObj == &rCandidate) + { + // found object in Diagram based on pRootObject. Set + // selected object + setSelectedModelID(rCandidate.getDiagramDataModelID()); + return; + } + } + + setSelectedModelID(getBackgroundShapeModelID()); } DiagramHelperFactory_svx::DiagramHelperFactory_svx() diff --git a/svx/source/diagram/datamodel_svx.cxx b/svx/source/diagram/datamodel_svx.cxx index 738403b3f5f0..d7daa2a235a8 100644 --- a/svx/source/diagram/datamodel_svx.cxx +++ b/svx/source/diagram/datamodel_svx.cxx @@ -24,11 +24,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include using namespace ::oox; @@ -510,17 +512,36 @@ DomMapFlags DiagramData_svx::removeDiagramNode(const OUString& rNodeId) return aRetval; } -DiagramDataState::DiagramDataState(Connections aConnections, Points aPoints) -: maConnections(std::move(aConnections)) -, maPoints(std::move(aPoints)) +DiagramDataState::DiagramDataState(const Connections& aConnections, const Points& aPoints, const uno::Reference< drawing::XShape >& rRootShape) +: maConnections(aConnections) +, maPoints(aPoints) +, mxShapes() +, maTransformation() { + SdrObjGroup* pSource(dynamic_cast(SdrObject::getSdrObjectFromXShape(rRootShape))); + if (nullptr != pSource) + { + basegfx::B2DPolyPolygon aPolyPolygon; + pSource->TRGetBaseGeometry(maTransformation, aPolyPolygon); + + for(size_t a(0); a < pSource->GetObjCount(); a++) + { + SdrObject* pCandidate(pSource->GetObj(a)); + + if (nullptr != pCandidate) + { + uno::Reference xCandidate(pCandidate->getUnoShape()); + mxShapes.push_back(xCandidate); + } + } + } } DiagramDataStatePtr DiagramData_svx::extractDiagramDataState() const { // Just copy all Connections && Points. The shared_ptr data in // Point-entries is no problem, it just continues exiting shared - return std::make_shared< DiagramDataState >(maConnections, maPoints); + return std::make_shared< DiagramDataState >(maConnections, maPoints, accessRootShape()); } void DiagramData_svx::applyDiagramDataState(const DiagramDataStatePtr& rState) @@ -530,6 +551,25 @@ void DiagramData_svx::applyDiagramDataState(const DiagramDataStatePtr& rState) maConnections = rState->getConnections(); maPoints = rState->getPoints(); + uno::Reference xRootShape(accessRootShape(), uno::UNO_QUERY); + if (xRootShape.is()) + { + SdrObjGroup* pTarget(dynamic_cast(SdrObject::getSdrObjectFromXShape(xRootShape))); + + if (nullptr != pTarget) + { + // Delete all existing shapes in that group + pTarget->getChildrenOfSdrObject()->ClearSdrObjList(); + } + + const std::vector>& rXShapes(rState->getXShapes()); + for (auto& rShape : rXShapes) + xRootShape->add(rShape); + + basegfx::B2DPolyPolygon aPolyPolygon; + pTarget->TRSetBaseGeometry(rState->getTransformation(), aPolyPolygon); + } + // Reset temporary buffered ModelData association lists & rebuild them // and the Diagram DataModel. Do that here *immediately* to prevent // re-usage of potentially invalid Connection/Point objects diff --git a/svx/source/sdr/properties/attributeproperties.cxx b/svx/source/sdr/properties/attributeproperties.cxx index 697fdb88620d..8482d8f950c1 100644 --- a/svx/source/sdr/properties/attributeproperties.cxx +++ b/svx/source/sdr/properties/attributeproperties.cxx @@ -35,6 +35,7 @@ #include #include #include +#include #include namespace sdr::properties @@ -270,7 +271,7 @@ namespace sdr::properties return *moItemSet; } - void AttributeProperties::ItemSetChanged(std::span< const SfxPoolItem* const > /*aChangedItems*/, sal_uInt16 /*nDeletedWhich*/, bool /*bAdjustTextFrameWidthAndHeight*/) + void AttributeProperties::ItemSetChanged(std::span< const SfxPoolItem* const > aChangedItems, sal_uInt16 /*nDeletedWhich*/, bool /*bAdjustTextFrameWidthAndHeight*/) { // own modifications SdrObject& rObj = GetSdrObject(); @@ -278,6 +279,13 @@ namespace sdr::properties rObj.SetBoundRectDirty(); rObj.SetBoundAndSnapRectsDirty(/*bNotMyself*/true); rObj.SetChanged(); + + if (!rObj.getDiagramDataModelID().isEmpty() && !rObj.getSdrModelFromSdrObject().isInImportExport()) + { + const std::shared_ptr< svx::diagram::DiagramHelper_svx >& pDiagramHelper(rObj.getDiagramHelper()); + if (pDiagramHelper) + pDiagramHelper->ItemSetInformationChange(aChangedItems); + } } void AttributeProperties::ItemChange(const sal_uInt16 nWhich, const SfxPoolItem* pNewItem) diff --git a/svx/source/svdraw/svdedtv.cxx b/svx/source/svdraw/svdedtv.cxx index 15fff60a8490..1eb49733fd0b 100644 --- a/svx/source/svdraw/svdedtv.cxx +++ b/svx/source/svdraw/svdedtv.cxx @@ -36,6 +36,7 @@ #include #include #include +#include #include @@ -559,7 +560,16 @@ void SdrEditView::CheckPossibilities() for (size_t nm=0; nmGetMarkedSdrObj(); + SdrObject* pObj=pM->GetMarkedSdrObj(); + + // try to get DiagramSubSelection + if(nullptr != pObj) + { + SdrObject* pSubSelection(pObj->getDiagramSubSelection()); + if(nullptr != pSubSelection && !pSubSelection->isDiagramBackgroundShape()) + pObj = pSubSelection; + } + const SdrPageView* pPV=pM->GetPageView(); if (pPV!=pPV0) { if (pPV->IsReadOnly()) m_bReadOnly=true; @@ -799,10 +809,29 @@ static void lcl_LazyDelete(std::vector> & rLazyDelete) void SdrEditView::DeleteMarkedObj() { const SdrMarkList& rMarkList = GetMarkedObjectList(); + const size_t nMarkCount(rMarkList.GetMarkCount()); + // #i110981# return when nothing is to be done at all - if(!rMarkList.GetMarkCount()) - { + if(0 == nMarkCount) return; + + if (1 == nMarkCount) + { + SdrObject* pObj(rMarkList.GetMark(0)->GetMarkedSdrObj()); + + if (nullptr != pObj) + { + SdrObject* pSubSelection(pObj->getDiagramSubSelection()); + + if (nullptr != pSubSelection && pSubSelection->isDiagramTextNode()) + { + if (pSubSelection->removeDiagramNode()) + { + comphelper::dispatchCommand(u".uno:RegenerateDiagram"_ustr, {}); + return; + } + } + } } // moved breaking action and undo start outside loop diff --git a/svx/source/svdraw/svdedtv1.cxx b/svx/source/svdraw/svdedtv1.cxx index 77c0585d63dc..2ccc9778464a 100644 --- a/svx/source/svdraw/svdedtv1.cxx +++ b/svx/source/svdraw/svdedtv1.cxx @@ -954,6 +954,8 @@ void SdrEditView::MergeAttrFromMarked(SfxItemSet& rAttr, bool bOnlyHardAttr) con continue; } + pObj = pObj->getDiagramSubSelection(); + const SfxItemSet& rSet = pObj->GetMergedItemSet(); SfxWhichIter aIter(rSet); sal_uInt16 nWhich(aIter.FirstWhich()); @@ -1199,6 +1201,8 @@ void SdrEditView::SetAttrToMarked(const SfxItemSet& rAttr, bool bReplaceAll) SdrMark* pM=rMarkList.GetMark(nm); SdrObject* pObj = pM->GetMarkedSdrObj(); + pObj = pObj->getDiagramSubSelection(); + if( bUndo ) { SdrEdgeObj* pEdgeObj = dynamic_cast< SdrEdgeObj* >( pObj ); diff --git a/svx/source/svdraw/svdmark.cxx b/svx/source/svdraw/svdmark.cxx index fc5d815ab9a2..8c965cbd34b7 100644 --- a/svx/source/svdraw/svdmark.cxx +++ b/svx/source/svdraw/svdmark.cxx @@ -26,6 +26,7 @@ #include #include #include +#include #include @@ -591,9 +592,11 @@ bool SdrMarkList::TakeBoundRect(SdrPageView const * pPV, tools::Rectangle& rRect if(!pPV || pMark->GetPageView() == pPV) { - if(pMark->GetMarkedSdrObj()) + SdrObject* pCandidate(pMark->GetMarkedSdrObj()); + + if(pCandidate) { - aR = pMark->GetMarkedSdrObj()->GetCurrentBoundRect(); + aR = pCandidate->GetCurrentBoundRect(); if(bFnd) { @@ -621,9 +624,11 @@ bool SdrMarkList::TakeSnapRect(SdrPageView const * pPV, tools::Rectangle& rRect) if(!pPV || pMark->GetPageView() == pPV) { - if(pMark->GetMarkedSdrObj()) + SdrObject* pCandidate(pMark->GetMarkedSdrObj()); + + if(pCandidate) { - tools::Rectangle aR(pMark->GetMarkedSdrObj()->GetSnapRect()); + tools::Rectangle aR(pCandidate->GetSnapRect()); if(bFnd) { diff --git a/svx/source/svdraw/svdmrkv.cxx b/svx/source/svdraw/svdmrkv.cxx index 71ca451f6d64..d98cdfb40b27 100644 --- a/svx/source/svdraw/svdmrkv.cxx +++ b/svx/source/svdraw/svdmrkv.cxx @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include #include @@ -1457,6 +1459,7 @@ void SdrMarkView::SetMarkHandles(SfxViewShell* pOtherShell) { // otherwise nothing is found const size_t nSiz0(maHdlList.GetHdlCount()); + SdrObject* pSubSelection(nullptr); if( bSingleTextObjMark ) { @@ -1464,6 +1467,21 @@ void SdrMarkView::SetMarkHandles(SfxViewShell* pOtherShell) } else { + // caution! mpMarkedObj and pSubSelection are and may only be set for + // single object selection + if (nullptr != mpMarkedObj) + pSubSelection = mpMarkedObj->getDiagramSubSelection(); + + // use SubSelection not for BGShape, in that case use SdrObjGroup to + // allow group actions on it + if(nullptr != pSubSelection) + { + if (pSubSelection->isDiagramBackgroundShape()) + pSubSelection = nullptr; + else + aRect = pSubSelection->GetSnapRect(); + } + const bool bWdt0(aRect.Left() == aRect.Right()); const bool bHgt0(aRect.Top() == aRect.Bottom()); @@ -1532,7 +1550,7 @@ void SdrMarkView::SetMarkHandles(SfxViewShell* pOtherShell) for (size_t i=nSiz0; iSetObj(mpMarkedObj); + pHdl->SetObj(nullptr != pSubSelection ? pSubSelection : mpMarkedObj); pHdl->SetPageView(mpMarkedPV); pHdl->SetObjHdlNum(sal_uInt16(i-nSiz0)); } @@ -2101,7 +2119,13 @@ bool SdrMarkView::IsMarkedObjHit(const Point& rPnt, short nTol) const const SdrMarkList& rMarkList = GetMarkedObjectList(); for (size_t nm=0; nmGetMarkedSdrObj(),pM->GetPageView(),SdrSearchOptions::NONE,nullptr); + SdrObject* pObj(pM->GetMarkedSdrObj()); + + // access possible SubSelectio + if (nullptr != pObj) + pObj = pObj->getDiagramSubSelection(); + + bRet = nullptr != CheckSingleSdrObjectHit(rPnt,sal_uInt16(nTol),pObj,pM->GetPageView(),SdrSearchOptions::NONE,nullptr); } return bRet; } @@ -2121,7 +2145,31 @@ bool SdrMarkView::MarkObj(const Point& rPnt, short nTol, bool bToggle, bool bDee SdrSearchOptions nOptions=SdrSearchOptions::PICKMARKABLE; if (bDeep) nOptions=nOptions|SdrSearchOptions::DEEP; SdrObject* pObj = PickObj(rPnt, static_cast(nTol), pPV, nOptions); - if (pObj) { + + if (pObj) + { + const std::shared_ptr< svx::diagram::DiagramHelper_svx >& rDiagramHelper(pObj->getDiagramHelper()); + if (rDiagramHelper) + { + if (pObj->isDiagram() && !bDeep) + { + // Klick on Diagram, look again but deep to get possibly an object + // inside the SdrObjGroup associated with it + SdrObject* pCandidate = PickObj(rPnt, static_cast(nTol), pPV, nOptions|SdrSearchOptions::DEEP); + + if (pCandidate && !pCandidate->isDiagram()) + rDiagramHelper->markDirectDiagramSubSelection(*pCandidate); + } + else + { + rDiagramHelper->markDirectDiagramSubSelection(*pObj); + pObj = rDiagramHelper->getAssociatedRootShape(); + } + } + } + + if (pObj) + { bool bUnmark=bToggle && IsObjMarked(pObj); MarkObj(pObj,pPV,bUnmark); } @@ -2139,35 +2187,67 @@ bool SdrMarkView::MarkNextObj(bool bPrev) const SdrMarkList& rMarkList = GetMarkedObjectList(); rMarkList.ForceSort(); - const size_t nMarkCount=rMarkList.GetMarkCount(); - 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=rMarkList.GetMark(nChgMarkNum); - assert(pM != nullptr); - if (pM->GetMarkedSdrObj() != nullptr) - nSearchObjNum = pM->GetMarkedSdrObj()->GetNavigationPosition(); - } + const size_t nMarkCount(rMarkList.GetMarkCount()); + size_t nChgMarkNum(SAL_MAX_SIZE); // number of the MarkEntry we want to replace + SdrObject* pMarkObj(nullptr); - 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 && nSearchObjNumGetObjectForNavigationPosition(nSearchObjNum); - if (IsObjMarkable(pSearchObj,pPageView)) + SdrObject* pCandidate(pM->GetMarkedSdrObj()); + + if (nullptr != pCandidate) { - if (rMarkList.FindObject(pSearchObj)==SAL_MAX_SIZE) + const std::shared_ptr< svx::diagram::DiagramHelper_svx >& rDiagramHelper(pCandidate->getDiagramHelper()); + + if (rDiagramHelper) { - pMarkObj=pSearchObj; + if (rDiagramHelper->markNextDiagramSubSelection(bPrev)) + pMarkObj=pCandidate; } } - if (bPrev) nSearchObjNum++; + } + } + + if (nullptr == pMarkObj) + { + size_t nSearchObjNum = bPrev ? 0 : SAL_MAX_SIZE; + if (nMarkCount!=0) { + nChgMarkNum=bPrev ? 0 : nMarkCount-1; + SdrMark* pM=rMarkList.GetMark(nChgMarkNum); + assert(pM != nullptr); + if (pM->GetMarkedSdrObj() != nullptr) + nSearchObjNum = pM->GetMarkedSdrObj()->GetNavigationPosition(); + } + + 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 && nSearchObjNumGetObjectForNavigationPosition(nSearchObjNum); + if (IsObjMarkable(pSearchObj,pPageView)) + { + if (rMarkList.FindObject(pSearchObj)==SAL_MAX_SIZE) + { + pMarkObj=pSearchObj; + } + } + if (bPrev) nSearchObjNum++; + } + + if (nullptr != pMarkObj && pMarkObj->IsGroupObject() && pMarkObj->isDiagram()) + { + // newly selected by keyboard, reset old sub selection + const std::shared_ptr< svx::diagram::DiagramHelper_svx >& rDiagramHelper(pMarkObj->getDiagramHelper()); + if (rDiagramHelper) + rDiagramHelper->initSelectionByKeyboard(bPrev); + } } } @@ -2809,6 +2889,9 @@ tools::Rectangle SdrMarkView::GetMarkedObjBoundRect() const for (size_t nm=0; nmGetMarkedSdrObj(); + if (!pO) + continue; + tools::Rectangle aR1(pO->GetCurrentBoundRect()); if (aRect.IsEmpty()) aRect=aR1; else aRect.Union(aR1); @@ -2828,6 +2911,7 @@ const tools::Rectangle& SdrMarkView::GetMarkedObjRect() const SdrObject* pO = pM->GetMarkedSdrObj(); if (!pO) continue; + tools::Rectangle aR1(pO->GetSnapRect()); if (aRect.IsEmpty()) aRect=aR1; else aRect.Union(aR1); diff --git a/svx/source/svdraw/svdoashp.cxx b/svx/source/svdraw/svdoashp.cxx index 27d585532ff8..91cafc08df19 100644 --- a/svx/source/svdraw/svdoashp.cxx +++ b/svx/source/svdraw/svdoashp.cxx @@ -2611,7 +2611,7 @@ void SdrObjCustomShape::EndTextEdit( SdrOutliner& rOutl ) { if (!getDiagramDataModelID().isEmpty()) { - std::shared_ptr< svx::diagram::DiagramHelper_svx > pIDiagramHelper(getDiagramHelperFromDiagramOrMember()); + std::shared_ptr< svx::diagram::DiagramHelper_svx > pIDiagramHelper(getDiagramHelper()); if (pIDiagramHelper) { diff --git a/svx/source/svdraw/svdobj.cxx b/svx/source/svdraw/svdobj.cxx index 842f16438787..c3a940c48467 100644 --- a/svx/source/svdraw/svdobj.cxx +++ b/svx/source/svdraw/svdobj.cxx @@ -50,6 +50,7 @@ #include #include +#include #include #include @@ -244,6 +245,45 @@ const OUString& SdrObject::getDiagramDataModelID() const return EMPTY_OUSTRING; } +bool SdrObject::isDiagramBackgroundShape() const +{ + const OUString& rID(getDiagramDataModelID()); + if (rID.isEmpty()) + return false; + + const std::shared_ptr< svx::diagram::DiagramHelper_svx >& rDiagramHelper(getDiagramHelper()); + if (!rDiagramHelper) + return false; + + return rID == rDiagramHelper->getBackgroundShapeModelID(); +} + +bool SdrObject::isDiagramTextNode() const +{ + const OUString& rID(getDiagramDataModelID()); + if (rID.isEmpty()) + return false; + + const std::shared_ptr< svx::diagram::DiagramHelper_svx >& rDiagramHelper(getDiagramHelper()); + if (!rDiagramHelper) + return false; + + return rDiagramHelper->isTextNodeModelID(rID); +} + +bool SdrObject::removeDiagramNode() +{ + const OUString& rID(getDiagramDataModelID()); + if (rID.isEmpty()) + return false; + + const std::shared_ptr< svx::diagram::DiagramHelper_svx >& rDiagramHelper(getDiagramHelper()); + if (!rDiagramHelper) + return false; + + return rDiagramHelper->removeDiagramNode(rID, getSdrModelFromSdrObject()); +} + bool SdrObject::isDiagram() const { return false; @@ -251,22 +291,21 @@ bool SdrObject::isDiagram() const const std::shared_ptr< svx::diagram::DiagramHelper_svx >& SdrObject::getDiagramHelper() const { + if (!getDiagramDataModelID().isEmpty()) + { + SdrObject* pParent(getParentSdrObjectFromSdrObject()); + + if (nullptr != pParent) + return pParent->getDiagramHelper(); + } + static std::shared_ptr< svx::diagram::DiagramHelper_svx > aEmpty; return aEmpty; } -const std::shared_ptr< svx::diagram::DiagramHelper_svx >& SdrObject::getDiagramHelperFromDiagramOrMember() const +SdrObject* SdrObject::getDiagramSubSelection() { - SdrObject* pCurrent(const_cast(this)); - - while (pCurrent) - { - if (pCurrent->isDiagram()) - return pCurrent->getDiagramHelper(); - pCurrent = pCurrent->getParentSdrObjectFromSdrObject(); - } - - return getDiagramHelper(); + return this; } // BaseProperties section diff --git a/svx/source/svdraw/svdogrp.cxx b/svx/source/svdraw/svdogrp.cxx index 262951347e2e..800f70d18bcf 100644 --- a/svx/source/svdraw/svdogrp.cxx +++ b/svx/source/svdraw/svdogrp.cxx @@ -46,6 +46,16 @@ const std::shared_ptr< svx::diagram::DiagramHelper_svx >& SdrObjGroup::getDiagra return mp_DiagramHelper; } +SdrObject* SdrObjGroup::getDiagramSubSelection() +{ + if (!mp_DiagramHelper) + return this; + + SdrObject* pRetval(mp_DiagramHelper->getDiagramSubSelection()); + + return nullptr == pRetval ? this : pRetval; +} + // BaseProperties section std::unique_ptr SdrObjGroup::CreateObjectSpecificProperties() { @@ -109,7 +119,7 @@ void SdrObjGroup::AddToHdlList(SdrHdlList& rHdlList) const if(!isDiagram()) return; - svx::diagram::DiagramHelper_svx::AddAdditionalVisualization(*this, rHdlList); + getDiagramHelper()->AddAdditionalVisualization(rHdlList); } SdrObjGroup::~SdrObjGroup() diff --git a/svx/source/svdraw/svdpage.cxx b/svx/source/svdraw/svdpage.cxx index 97cf50a4fb4a..e01ea69e527b 100644 --- a/svx/source/svdraw/svdpage.cxx +++ b/svx/source/svdraw/svdpage.cxx @@ -408,7 +408,7 @@ rtl::Reference SdrObjList::RemoveObject(size_t nObjNum) const size_t nCount = GetObjCount(); rtl::Reference pObj=maList[nObjNum]; - if (pObj->IsDeleteProtect()) + if (pObj->IsDeleteProtect() && !pObj->getSdrModelFromSdrObject().isInImportExport()) return nullptr; RemoveObjectFromContainer(nObjNum); diff --git a/svx/source/svdraw/svdundo.cxx b/svx/source/svdraw/svdundo.cxx index fe735d1e32b4..018168f7e68f 100644 --- a/svx/source/svdraw/svdundo.cxx +++ b/svx/source/svdraw/svdundo.cxx @@ -635,15 +635,13 @@ SdrUndoDiagramModelData::SdrUndoDiagramModelData(SdrObject& rNewObj, const svx:: , m_aStartState(rStartState) , m_aEndState() { - if(rNewObj.isDiagram()) - m_aEndState = rNewObj.getDiagramHelper()->extractDiagramDataState(); } SdrUndoDiagramModelData::~SdrUndoDiagramModelData() { } -void SdrUndoDiagramModelData::implUndoRedo(bool bUndo) +void SdrUndoDiagramModelData::Undo() { if(!mxObj) return; @@ -651,19 +649,26 @@ void SdrUndoDiagramModelData::implUndoRedo(bool bUndo) if(!mxObj->isDiagram()) return; - mxObj->getDiagramHelper()->applyDiagramDataState( - bUndo ? m_aStartState : m_aEndState); - mxObj->getDiagramHelper()->reLayout(); -} + if (!m_aEndState) + m_aEndState = mxObj->getDiagramHelper()->extractDiagramDataState(); -void SdrUndoDiagramModelData::Undo() -{ - implUndoRedo(true); + mxObj->getDiagramHelper()->applyDiagramDataState(m_aStartState); + mxObj->getDiagramHelper()->reLayout(); } void SdrUndoDiagramModelData::Redo() { - implUndoRedo(false); + if (!m_aEndState) + return; + + if(!mxObj) + return; + + if(!mxObj->isDiagram()) + return; + + mxObj->getDiagramHelper()->applyDiagramDataState(m_aEndState); + mxObj->getDiagramHelper()->reLayout(); } OUString SdrUndoDiagramModelData::GetComment() const