include/svx/diagram/IDiagramHelper.hxx | 19 +++ include/svx/diagram/datamodel.hxx | 5 include/svx/svdobj.hxx | 9 + include/svx/svdogrp.hxx | 2 include/svx/svxids.hrc | 1 officecfg/registry/data/org/openoffice/Office/UI/GenericCommands.xcu | 5 oox/source/drawingml/diagram/diagramhelper.cxx | 24 ++++ oox/source/drawingml/diagram/diagramhelper.hxx | 3 oox/source/drawingml/shape.cxx | 18 +++ oox/source/ppt/pptshapegroupcontext.cxx | 9 + sc/sdi/drawsh.sdi | 5 sc/source/ui/drawfunc/drawsh2.cxx | 1 sc/source/ui/drawfunc/drawsh5.cxx | 9 + sd/qa/unit/export-tests-ooxml2.cxx | 15 +- sd/sdi/_drvwsh.sdi | 5 sd/source/filter/eppt/pptx-epptooxml.cxx | 8 + sd/source/ui/view/drviews3.cxx | 11 + sd/source/ui/view/drviewsj.cxx | 1 svx/qa/unit/data/tdf132368.pptx |binary svx/qa/unit/svdraw.cxx | 57 +++++----- svx/sdi/svx.sdi | 17 ++ svx/source/diagram/IDiagramHelper.cxx | 26 ++++ svx/source/diagram/datamodel.cxx | 47 ++++++++ svx/source/svdraw/svdoashp.cxx | 13 ++ svx/source/svdraw/svdobj.cxx | 36 ++++-- svx/source/svdraw/svdogrp.cxx | 38 ------ sw/sdi/_drwbase.sdi | 7 + sw/source/uibase/shells/drwbassh.cxx | 11 + 28 files changed, 309 insertions(+), 93 deletions(-)
New commits: commit 3ad22de97dc8a96b8b7df832aa5fa3e5a36c6bda Author: Armin Le Grand (collabora) <[email protected]> AuthorDate: Fri Dec 5 16:09:36 2025 +0100 Commit: Armin Le Grand <[email protected]> CommitDate: Wed Dec 10 18:19:10 2025 +0100 SmartArt: Add posssibility to edit (simple) text A loaded SmartArt allows to start TextEdit when one of the included nodes gets klicked. TextEdit seems to work, but the object degrades from a SmartArt to a GroupObject with content. I try to add the preconditions here which are needed to be able to do change at a SmartArt in the future. Basic problem is that the ModelData for SmartArt are 'attached' to the GroupObject that is used for representation, but no ways exist to support edit/change and promote that to that seperated model. One of the main hindrances is that the needed IDs that create the associations between that model and the Shapes is missing, added that. This needs to be added for two scenarios (what makes it complicated): - loaded replacement visualization - re-created SmartArt (add/rmove used) To test this I added a simple text edit that does a first change at that SmartArt model data. For now just simple text, of course TextAttributes need to follow, as do changes when Attributes get changed (Line/Fill/style/...) Thus it's a 1st step and needed quite some experimenting. As long as this is not further progressed (which I try to make happen) the default will stay to have SmartArt as GroupObject with content - as it is today. Unified connect/disconnect, added slot and commands for disconnect, adapted test to use that. Needed to adapt UnitTest testTextEditEmptyGrabBag, it needs to work with a *real* SmartArt/Diagram. The former test created a 'fake' one and checked if on TextEdit the GrabBag gets deleted. The updated SmartArt text edit *tries* to keep the Data (in IDiagramHelper attached to SdrObjGroup). It clears the GrabBag, but only if a 'real' SmartArt exists. Thus I added a bugdoc for that task which contains a 'real' SmartArt to test if GrabBag gets cleared on change of the model data. Also added a modified state to the SmartArt model data (which is in IDiagramHelper). This is needed to decide what shall be done at export time. As long as we have no SmartArt model data export, the fallback to conversion to SdrObjGroup has to be used (as before). Change-Id: Ie6ef3f3a8bbc5da99984d85f79f89e5df308e3c9 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/195097 Tested-by: Jenkins Reviewed-by: Armin Le Grand <[email protected]> diff --git a/include/svx/diagram/IDiagramHelper.hxx b/include/svx/diagram/IDiagramHelper.hxx index a6d23b1ab63c..d36e5eb861d5 100644 --- a/include/svx/diagram/IDiagramHelper.hxx +++ b/include/svx/diagram/IDiagramHelper.hxx @@ -83,8 +83,13 @@ private: // and the layouting stuff bool mbSelfCreated; + // set to true if the model contained here for the Diagram was + // modified + bool mbModified; // false + protected: - void anchorToSdrObjGroup(SdrObjGroup& rTarget); + // remember associated SdrObjGroup + SdrObjGroup* mpAssociatedSdrObjGroup; public: IDiagramHelper(bool bSelfCreated); @@ -118,6 +123,18 @@ public: bool isSelfCreated() const { return mbSelfCreated; } void setSelfCreated() { mbSelfCreated = true; } + // connect/disconnect to/from Group + void connectToSdrObjGroup(SdrObjGroup& rTarget); + void disconnectFromSdrObjGroup(); + + // modified state of this local model. Needs to be set to know + // how to do correct export + bool isModified() const { return mbModified; } + void setModified() { mbModified = true; } + + // react on changes to objects identified by DiagramDataModelID, returns true if a change was done + virtual void TextInformationChange(const OUString& rDiagramDataModelID, SdrOutliner& rOutl) = 0; + static void AddAdditionalVisualization(const SdrObjGroup& rTarget, SdrHdlList& rHdlList); }; diff --git a/include/svx/diagram/datamodel.hxx b/include/svx/diagram/datamodel.hxx index 2b62c0b75ec2..86d083f2c953 100644 --- a/include/svx/diagram/datamodel.hxx +++ b/include/svx/diagram/datamodel.hxx @@ -34,6 +34,8 @@ #include <com/sun/star/drawing/XShape.hpp> #include <oox/token/tokens.hxx> +class Outliner; + namespace svx::diagram { enum TypeConstant { @@ -230,6 +232,9 @@ public: DiagramDataStatePtr extractDiagramDataState() const; void applyDiagramDataState(const DiagramDataStatePtr& rState); + // react on changes to objects identified by DiagramDataModelID, returns true if a change was done + bool TextInformationChange(const OUString& rDiagramDataModelID, Outliner& rOutl); + protected: // helpers void getChildrenString(OUStringBuffer& rBuf, const Point* pPoint, sal_Int32 nLevel) const; diff --git a/include/svx/svdobj.hxx b/include/svx/svdobj.hxx index 7917d988cd20..9a788d4eee0c 100644 --- a/include/svx/svdobj.hxx +++ b/include/svx/svdobj.hxx @@ -227,8 +227,11 @@ class SVXCORE_DLLPUBLIC SdrObject : public SfxListener, public cppu::OWeakObject { public: // Basic DiagramHelper support + virtual bool isDiagram() const; virtual const std::shared_ptr< svx::diagram::IDiagramHelper >& getDiagramHelper() const; - bool isDiagram() const { return bool(getDiagramHelper()); } + const std::shared_ptr< svx::diagram::IDiagramHelper >& getDiagramHelperFromDiagramOrMember() const; + void setDiagramDataModelID(const OUString& rID) { msDiagramDataModelID = rID; } + const OUString& getDiagramDataModelID() const { return msDiagramDataModelID; } private: friend class SdrObjListIter; @@ -984,9 +987,13 @@ private: // HACK: Do not automatically insert newly created object into a page. // The user needs to do it manually later. bool mbDoNotInsertIntoPageAutomatically; + // Hyperlink for the whole shape OUString msHyperlink; + // if this object is a representation of Diagram Sub-Data, hold the DataModelID + OUString msDiagramDataModelID; + // only for internal use! SvxShape* getSvxShape(); diff --git a/include/svx/svdogrp.hxx b/include/svx/svdogrp.hxx index 32a7750e90d4..5949f3d4b4b0 100644 --- a/include/svx/svdogrp.hxx +++ b/include/svx/svdogrp.hxx @@ -29,6 +29,7 @@ class SVXCORE_DLLPUBLIC SdrObjGroup final : public SdrObject, public SdrObjList { public: // Basic DiagramHelper support + virtual bool isDiagram() const override; virtual const std::shared_ptr< svx::diagram::IDiagramHelper >& getDiagramHelper() const override; private: @@ -66,7 +67,6 @@ public: virtual void handlePageChange(SdrPage* pOldPage, SdrPage* pNewPage) override; virtual SdrObjList* GetSubList() const override; - virtual void SetGrabBagItem(const css::uno::Any& rVal) override; virtual const tools::Rectangle& GetCurrentBoundRect() const override; virtual const tools::Rectangle& GetSnapRect() const override; diff --git a/include/svx/svxids.hrc b/include/svx/svxids.hrc index 4563591571aa..a93ad0c3bed9 100644 --- a/include/svx/svxids.hrc +++ b/include/svx/svxids.hrc @@ -385,6 +385,7 @@ class XFillGradientItem; // CAUTION! Range <250 .. 250> used by EditEngine (!) +#define SID_DIAGRAM_TO_GROUP ( SID_SVX_START + 252 ) #define SID_DRAW_TEXT ( SID_SVX_START + 253 ) #define SID_DRAW_CAPTION ( SID_SVX_START + 254 ) #define SID_DRAW_SELECT ( SID_SVX_START + 255 ) diff --git a/officecfg/registry/data/org/openoffice/Office/UI/GenericCommands.xcu b/officecfg/registry/data/org/openoffice/Office/UI/GenericCommands.xcu index 88718fecb8f5..e23423904154 100644 --- a/officecfg/registry/data/org/openoffice/Office/UI/GenericCommands.xcu +++ b/officecfg/registry/data/org/openoffice/Office/UI/GenericCommands.xcu @@ -7915,6 +7915,11 @@ bit 3 (0x8): #define UICOMMANDDESCRIPTION_PROPERTIES_TOGGLEBUTTON 8 <value xml:lang="en-US">Regenerate Diagram</value> </prop> </node> + <node oor:name=".uno:DiagramToGroup" oor:op="replace"> + <prop oor:name="Label" oor:type="xs:string"> + <value xml:lang="en-US">Convert Diagram to Group Object</value> + </prop> + </node> <node oor:name=".uno:EditDiagram" oor:op="replace"> <prop oor:name="Label" oor:type="xs:string"> <value xml:lang="en-US">Edit Diagram</value> diff --git a/oox/source/drawingml/diagram/diagramhelper.cxx b/oox/source/drawingml/diagram/diagramhelper.cxx index cfd0b45b790f..d19e1a5e0bd7 100644 --- a/oox/source/drawingml/diagram/diagramhelper.cxx +++ b/oox/source/drawingml/diagram/diagramhelper.cxx @@ -25,6 +25,7 @@ #include <oox/ppt/pptimport.hxx> #include <drawingml/fillproperties.hxx> #include <svx/svdmodel.hxx> +#include <svx/svdoutl.hxx> #include <comphelper/processfactory.hxx> #include <oox/drawingml/themefragmenthandler.hxx> #include <com/sun/star/xml/sax/XFastSAXSerializable.hpp> @@ -233,6 +234,26 @@ void AdvancedDiagramHelper::applyDiagramDataState(const svx::diagram::DiagramDat mpDiagramPtr->getData()->applyDiagramDataState(rState); } +void AdvancedDiagramHelper::TextInformationChange(const OUString& rDiagramDataModelID, SdrOutliner& rOutl) +{ + if(!mpDiagramPtr) + { + return; + } + + // try text change for model part in DiagramData + const bool bChanged(mpDiagramPtr->getData()->TextInformationChange(rDiagramDataModelID, rOutl)); + + if(bChanged && nullptr != mpAssociatedSdrObjGroup) + { + // if change was done, reset GrabBagItem to delete buffered DiagramData which is no longer valid + mpAssociatedSdrObjGroup->SetGrabBagItem(uno::Any(uno::Sequence<beans::PropertyValue>())); + + // also set self to modified to get correct exports + setModified(); + } +} + void AdvancedDiagramHelper::doAnchor(SdrObjGroup& rTarget, ::oox::drawingml::Shape& rRootShape) { if(!mpDiagramPtr) @@ -247,7 +268,8 @@ void AdvancedDiagramHelper::doAnchor(SdrObjGroup& rTarget, ::oox::drawingml::Sha // secure that data at the Diagram ModelData by copying. mpDiagramPtr->getData()->secureDataFromShapeToModelAfterDiagramImport(rRootShape); - anchorToSdrObjGroup(rTarget); + // initialize connection to GroupObject + connectToSdrObjGroup(rTarget); } const std::shared_ptr< ::oox::drawingml::Theme >& AdvancedDiagramHelper::getOrCreateThemePtr( diff --git a/oox/source/drawingml/diagram/diagramhelper.hxx b/oox/source/drawingml/diagram/diagramhelper.hxx index 2cb88f471b29..22915e7b87e2 100644 --- a/oox/source/drawingml/diagram/diagramhelper.hxx +++ b/oox/source/drawingml/diagram/diagramhelper.hxx @@ -82,6 +82,9 @@ public: void doAnchor(SdrObjGroup& rTarget, ::oox::drawingml::Shape& rRootShape); const std::shared_ptr< ::oox::drawingml::Theme >& getOrCreateThemePtr( const rtl::Reference< oox::shape::ShapeFilterBase>& rxFilter ) const; + + // react on changes to objects identified by DiagramDataModelID, returns true if a change was done + virtual void TextInformationChange(const OUString& rDiagramDataModelID, SdrOutliner& rOutl) override; }; } diff --git a/oox/source/drawingml/shape.cxx b/oox/source/drawingml/shape.cxx index 4f2f9dc8d6a9..0d28032ab50e 100644 --- a/oox/source/drawingml/shape.cxx +++ b/oox/source/drawingml/shape.cxx @@ -1435,6 +1435,24 @@ Reference< XShape > const & Shape::createAndInsert( ActionLockGuard const alg(mxShape); + if (!getDiagramDataModelID().isEmpty()) + { + SdrObject* pShape(SdrObject::getSdrObjectFromXShape(mxShape)); + + if (nullptr != pShape) + { + // check if we have a DiagramHelper + std::shared_ptr< svx::diagram::IDiagramHelper > pIDiagramHelper( + pShape->getDiagramHelperFromDiagramOrMember()); + + if (pIDiagramHelper) + { + // only needed when DiagramHelper exists + pShape->setDiagramDataModelID(getDiagramDataModelID()); + } + } + } + // sj: removing default text of placeholder objects such as SlideNumberShape or HeaderShape if ( bClearText ) { diff --git a/oox/source/ppt/pptshapegroupcontext.cxx b/oox/source/ppt/pptshapegroupcontext.cxx index ef8934835bed..91684cf8d47b 100644 --- a/oox/source/ppt/pptshapegroupcontext.cxx +++ b/oox/source/ppt/pptshapegroupcontext.cxx @@ -112,7 +112,14 @@ ContextHandlerRef PPTShapeGroupContext::onCreateContext( sal_Int32 aElementToken { pShape->getFillProperties().moFillType = XML_noFill; } - pShape->setModelId(rAttribs.getStringDefaulted( XML_modelId )); + const OUString aModelID(rAttribs.getStringDefaulted( XML_modelId )); + pShape->setModelId(aModelID); + + // also set DataModelID to have these also available for the imported + // replacement visualization. This is key to allow changes to + // DiagramHelper model changes + pShape->setDiagramDataModelID(aModelID); + return new PPTShapeContext( *this, mpSlidePersistPtr, mpGroupShapePtr, pShape ); } case PPT_TOKEN( pic ): // CT_Picture diff --git a/sc/sdi/drawsh.sdi b/sc/sdi/drawsh.sdi index dc13528984e0..281ea24aab09 100644 --- a/sc/sdi/drawsh.sdi +++ b/sc/sdi/drawsh.sdi @@ -136,8 +136,9 @@ interface TableDraw SID_UNGROUP [ ExecMethod = ExecDrawFunc; StateMethod = GetDrawFuncState; ] SID_ENTER_GROUP [ ExecMethod = ExecDrawFunc; StateMethod = GetDrawFuncState; ] SID_LEAVE_GROUP [ ExecMethod = ExecDrawFunc; StateMethod = GetDrawFuncState; ] - SID_REGENERATE_DIAGRAM [ ExecMethod = ExecDrawFunc; StateMethod = GetDrawFuncState ] - SID_EDIT_DIAGRAM [ ExecMethod = ExecDrawFunc; StateMethod = GetDrawFuncState ] + SID_REGENERATE_DIAGRAM [ ExecMethod = ExecDrawFunc; StateMethod = GetDrawFuncState; ] + SID_DIAGRAM_TO_GROUP [ ExecMethod = ExecDrawFunc; StateMethod = GetDrawFuncState; ] + SID_EDIT_DIAGRAM [ ExecMethod = ExecDrawFunc; StateMethod = GetDrawFuncState; ] // !!! special SID_DELETE [ExecMethod = ExecDrawFunc ;StateMethod = GetDrawFuncState; ] diff --git a/sc/source/ui/drawfunc/drawsh2.cxx b/sc/source/ui/drawfunc/drawsh2.cxx index a82741dbb51f..9e8c009fcf74 100644 --- a/sc/source/ui/drawfunc/drawsh2.cxx +++ b/sc/source/ui/drawfunc/drawsh2.cxx @@ -284,6 +284,7 @@ void ScDrawShell::GetDrawFuncState( SfxItemSet& rSet ) // disable functions if (!pObj->isDiagram()) { rSet.DisableItem( SID_REGENERATE_DIAGRAM ); + rSet.DisableItem( SID_DIAGRAM_TO_GROUP ); rSet.DisableItem( SID_EDIT_DIAGRAM ); } } diff --git a/sc/source/ui/drawfunc/drawsh5.cxx b/sc/source/ui/drawfunc/drawsh5.cxx index 51235d7a0696..992e7b1b161c 100644 --- a/sc/source/ui/drawfunc/drawsh5.cxx +++ b/sc/source/ui/drawfunc/drawsh5.cxx @@ -276,6 +276,7 @@ void ScDrawShell::ExecDrawFunc( SfxRequest& rReq ) break; case SID_REGENERATE_DIAGRAM: + case SID_DIAGRAM_TO_GROUP: case SID_EDIT_DIAGRAM: { const SdrMarkList& rMarkList = pView->GetMarkedObjectList(); @@ -293,7 +294,7 @@ void ScDrawShell::ExecDrawFunc( SfxRequest& rReq ) pObj->getDiagramHelper()->reLayout(*static_cast<SdrObjGroup*>(pObj)); pView->MarkObj(pObj, pView->GetSdrPageView()); } - else // SID_EDIT_DIAGRAM + else if (SID_EDIT_DIAGRAM == nSlotId) { VclAbstractDialogFactory* pFact = VclAbstractDialogFactory::Create(); vcl::Window* pWin = rViewData.GetActiveWin(); @@ -307,6 +308,12 @@ void ScDrawShell::ExecDrawFunc( SfxRequest& rReq ) } ); } + else // SID_DIAGRAM_TO_GROUP + { + pView->UnmarkAll(); + pObj->getDiagramHelper()->disconnectFromSdrObjGroup(); + pView->MarkObj(pObj, pView->GetSdrPageView()); + } } } diff --git a/sd/qa/unit/export-tests-ooxml2.cxx b/sd/qa/unit/export-tests-ooxml2.cxx index d009c43cde0f..a9ed1af551b2 100644 --- a/sd/qa/unit/export-tests-ooxml2.cxx +++ b/sd/qa/unit/export-tests-ooxml2.cxx @@ -14,6 +14,8 @@ #include <svx/svdomedia.hxx> #include <svx/svdotable.hxx> #include <svx/svdpage.hxx> +#include <svx/svdogrp.hxx> +#include <svx/diagram/IDiagramHelper.hxx> #include <docmodel/uno/UnoGradientTools.hxx> #include <com/sun/star/animations/TransitionType.hpp> @@ -1595,12 +1597,13 @@ CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest2, testSmartartRotation2) createSdImpressDoc("pptx/smartart-rotation2.pptx"); // clear SmartArt data to check how group shapes with double-rotated children are exported, not smartart - // NOTE: Resetting the GrabBag data is a *very* indirect way to reset the SmartArt functionality. - // Since this worked before and there is not (yet?) a better way to do it using UNO API, I added - // code to support this for now - uno::Reference<beans::XPropertySet> xShape(getShapeFromPage(0, 0)); - uno::Sequence<beans::PropertyValue> aInteropGrabBag; - xShape->setPropertyValue(u"InteropGrabBag"_ustr, uno::Any(aInteropGrabBag)); + // NOTE: Now we have a slot (comphelper::dispatchCommand(u".uno:DiagramToGroup"_ustr, {})) and method + // at IDiagramHelper to do that, adapted removal of Diagram/SmartArt functionality + uno::Reference<drawing::XDrawPage> xPage(getPage(0)); + uno::Reference<drawing::XShape> xShape(xPage->getByIndex(0), uno::UNO_QUERY); + SdrObjGroup* pSdrObjGroup + = dynamic_cast<SdrObjGroup*>(SdrObject::getSdrObjectFromXShape(xShape)); + pSdrObjGroup->getDiagramHelper()->disconnectFromSdrObjGroup(); save(TestFilter::PPTX); diff --git a/sd/sdi/_drvwsh.sdi b/sd/sdi/_drvwsh.sdi index c574c2cf62ef..a87109cb8b9e 100644 --- a/sd/sdi/_drvwsh.sdi +++ b/sd/sdi/_drvwsh.sdi @@ -2928,6 +2928,11 @@ interface DrawView ExecMethod = ExecCtrl ; StateMethod = GetMenuState ; ] + SID_DIAGRAM_TO_GROUP + [ + ExecMethod = ExecCtrl ; + StateMethod = GetMenuState ; + ] SID_EDIT_DIAGRAM [ ExecMethod = ExecCtrl ; diff --git a/sd/source/filter/eppt/pptx-epptooxml.cxx b/sd/source/filter/eppt/pptx-epptooxml.cxx index 558a142e5d62..2964e0d2db25 100644 --- a/sd/source/filter/eppt/pptx-epptooxml.cxx +++ b/sd/source/filter/eppt/pptx-epptooxml.cxx @@ -88,6 +88,7 @@ #include <svx/unoapi.hxx> #include <svx/svdogrp.hxx> #include <svx/ColorSets.hxx> +#include <svx/diagram/IDiagramHelper.hxx> #include <sdmod.hxx> #include <sdpage.hxx> #include <unomodel.hxx> @@ -2348,7 +2349,12 @@ void PowerPointExport::WriteShapeTree(const FSHelperPtr& pFS, PageType ePageType { SAL_INFO("sd.eppt", "mType: " << mType); const SdrObjGroup* pDiagramCandidate(dynamic_cast<const SdrObjGroup*>(SdrObject::getSdrObjectFromXShape(mXShape))); - const bool bIsDiagram(nullptr != pDiagramCandidate && pDiagramCandidate->isDiagram()); + bool bIsDiagram(nullptr != pDiagramCandidate && pDiagramCandidate->isDiagram()); + + // check if it was modified. For now, since we have no ppt export for Diagram, + // do not export as Diagram, that would be empty if the GrabBag was deleted + if (bIsDiagram && pDiagramCandidate->getDiagramHelper()->isModified()) + bIsDiagram = false; if (bIsDiagram) WriteDiagram(pFS, aDML, mXShape, mnDiagramId++); diff --git a/sd/source/ui/view/drviews3.cxx b/sd/source/ui/view/drviews3.cxx index ae6c27645807..169c8886cdbe 100644 --- a/sd/source/ui/view/drviews3.cxx +++ b/sd/source/ui/view/drviews3.cxx @@ -488,6 +488,7 @@ void DrawViewShell::ExecCtrl(SfxRequest& rReq) break; case SID_REGENERATE_DIAGRAM: + case SID_DIAGRAM_TO_GROUP: case SID_EDIT_DIAGRAM: { if (1 == rMarkList.GetMarkCount()) @@ -497,13 +498,13 @@ void DrawViewShell::ExecCtrl(SfxRequest& rReq) // Support advanced DiagramHelper if(nullptr != pObj && pObj->isDiagram()) { - if(SID_REGENERATE_DIAGRAM == nSlot) + if (SID_REGENERATE_DIAGRAM == nSlot) { mpDrawView->UnmarkAll(); pObj->getDiagramHelper()->reLayout(*static_cast<SdrObjGroup*>(pObj)); mpDrawView->MarkObj(pObj, mpDrawView->GetSdrPageView()); } - else // SID_EDIT_DIAGRAM + else if (SID_EDIT_DIAGRAM == nSlot) { VclAbstractDialogFactory* pFact = VclAbstractDialogFactory::Create(); VclPtr<VclAbstractDialog> pDlg = pFact->CreateDiagramDialog( @@ -516,6 +517,12 @@ void DrawViewShell::ExecCtrl(SfxRequest& rReq) } ); } + else // SID_DIAGRAM_TO_GROUP + { + mpDrawView->UnmarkAll(); + pObj->getDiagramHelper()->disconnectFromSdrObjGroup(); + mpDrawView->MarkObj(pObj, mpDrawView->GetSdrPageView()); + } } } diff --git a/sd/source/ui/view/drviewsj.cxx b/sd/source/ui/view/drviewsj.cxx index 1cfa0f16bc5f..016755683e9e 100644 --- a/sd/source/ui/view/drviewsj.cxx +++ b/sd/source/ui/view/drviewsj.cxx @@ -158,6 +158,7 @@ void DrawViewShell::GetMenuStateSel( SfxItemSet &rSet ) if(!pSdrObjGroup || !pSdrObjGroup->isDiagram()) { rSet.DisableItem( SID_REGENERATE_DIAGRAM ); + rSet.DisableItem( SID_DIAGRAM_TO_GROUP ); rSet.DisableItem( SID_EDIT_DIAGRAM ); } diff --git a/svx/qa/unit/data/tdf132368.pptx b/svx/qa/unit/data/tdf132368.pptx new file mode 100644 index 000000000000..3941d37661e5 Binary files /dev/null and b/svx/qa/unit/data/tdf132368.pptx differ diff --git a/svx/qa/unit/svdraw.cxx b/svx/qa/unit/svdraw.cxx index b3b8ce14e227..b1f26780aea6 100644 --- a/svx/qa/unit/svdraw.cxx +++ b/svx/qa/unit/svdraw.cxx @@ -237,46 +237,49 @@ CPPUNIT_TEST_FIXTURE(SvdrawTest, testHandlePathObjScale) CPPUNIT_TEST_FIXTURE(SvdrawTest, testTextEditEmptyGrabBag) { - // Given a document with a groupshape, which has 2 children. - loadFromURL(u"private:factory/sdraw"_ustr); - uno::Reference<lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY); - uno::Reference<drawing::XShape> xRect1( - xFactory->createInstance(u"com.sun.star.drawing.RectangleShape"_ustr), uno::UNO_QUERY); - xRect1->setPosition(awt::Point(1000, 1000)); - xRect1->setSize(awt::Size(10000, 10000)); - uno::Reference<drawing::XShape> xRect2( - xFactory->createInstance(u"com.sun.star.drawing.RectangleShape"_ustr), uno::UNO_QUERY); - xRect2->setPosition(awt::Point(1000, 1000)); - xRect2->setSize(awt::Size(10000, 10000)); - uno::Reference<drawing::XShapes> xGroup( - xFactory->createInstance(u"com.sun.star.drawing.GroupShape"_ustr), uno::UNO_QUERY); + // Adapted this test to work with a *real* SmartArt/Diagram (SA). The former + // test created a 'fake' one and checked if on TextEdit The GrabBag gets deleted. + // The updated SA text edit *tries* to keep the SA Data (in IDiagramHelper + // attached to SdrObjGroup). It clears the GrabBag, but only if a 'real' SA + // exists. + // Thus I added a bugdoc for that task which contains a 'real' SA to test + // if GrabBag gets cleared on change of the SA model data. + + // load document + loadFromFile(u"tdf132368.pptx"); + + // get DrawPage uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(mxComponent, uno::UNO_QUERY); uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0), uno::UNO_QUERY); - uno::Reference<drawing::XShape> xGroupShape(xGroup, uno::UNO_QUERY); - xDrawPage->add(xGroupShape); - xGroup->add(xRect1); - xGroup->add(xRect2); - uno::Reference<text::XTextRange> xRect2Text(xRect2, uno::UNO_QUERY); - xRect2Text->setString(u"x"_ustr); - uno::Sequence<beans::PropertyValue> aGrabBag = { - comphelper::makePropertyValue(u"OOXLayout"_ustr, true), - }; - uno::Reference<beans::XPropertySet> xGroupProps(xGroup, uno::UNO_QUERY); - xGroupProps->setPropertyValue(u"InteropGrabBag"_ustr, uno::Any(aGrabBag)); - // When editing the shape text of the 2nd rectangle (insert a char at the start). + // get 1st object, this is the SA. Get as GroupObject + uno::Reference<drawing::XShape> xShape(xDrawPage->getByIndex(0), uno::UNO_QUERY); + uno::Reference<drawing::XShapes> xGroupShape(xShape, uno::UNO_QUERY); + + // get the GrabBag. After import is *has* to be set, check that + uno::Sequence<beans::PropertyValue> aGrabBag; + uno::Reference<beans::XPropertySet> xGroupProps(xGroupShape, uno::UNO_QUERY); + xGroupProps->getPropertyValue(u"InteropGrabBag"_ustr) >>= aGrabBag; + CPPUNIT_ASSERT(aGrabBag.hasElements()); + + // get the 1st text shape (containing 'A'). There is a BGShape and an Arrow + // in indices before + uno::Reference<drawing::XShape> xShapeA(xGroupShape->getByIndex(2), uno::UNO_QUERY); + + // TextEdit: insert a char at the start SfxViewShell* pViewShell = SfxViewShell::Current(); CPPUNIT_ASSERT(pViewShell); SdrView* pSdrView = pViewShell->GetDrawView(); - SdrObject* pObject = SdrObject::getSdrObjectFromXShape(xRect2); + SdrObject* pObject = SdrObject::getSdrObjectFromXShape(xShapeA); pSdrView->SdrBeginTextEdit(pObject); EditView& rEditView = pSdrView->GetTextEditOutlinerView()->GetEditView(); - rEditView.InsertText(u"y"_ustr); + rEditView.InsertText(u"X"_ustr); pSdrView->SdrEndTextEdit(); // Then make sure that grab-bag is empty to avoid losing the new text. xGroupProps->getPropertyValue(u"InteropGrabBag"_ustr) >>= aGrabBag; + // Without the accompanying fix in place, this test would have failed with: // assertion failed // - Expression: !aGrabBag.hasElements() diff --git a/svx/sdi/svx.sdi b/svx/sdi/svx.sdi index 8fb8cf205f10..cd0a9ab59ac1 100644 --- a/svx/sdi/svx.sdi +++ b/svx/sdi/svx.sdi @@ -2660,6 +2660,23 @@ SfxVoidItem RegenerateDiagram SID_REGENERATE_DIAGRAM GroupId = SfxGroupId::Modify; ] +SfxVoidItem DiagramToGroup SID_DIAGRAM_TO_GROUP +() +[ + AutoUpdate = FALSE, + FastCall = FALSE, + ReadOnlyDoc = FALSE, + Toggle = FALSE, + Container = FALSE, + RecordAbsolute = FALSE, + RecordPerSet; + + AccelConfig = TRUE, + MenuConfig = TRUE, + ToolBoxConfig = TRUE, + GroupId = SfxGroupId::Modify; +] + SfxVoidItem EditDiagram SID_EDIT_DIAGRAM () [ diff --git a/svx/source/diagram/IDiagramHelper.cxx b/svx/source/diagram/IDiagramHelper.cxx index 5f17433a6181..5ccffe123507 100644 --- a/svx/source/diagram/IDiagramHelper.cxx +++ b/svx/source/diagram/IDiagramHelper.cxx @@ -39,6 +39,8 @@ #include <svx/strings.hrc> #include <svx/dialmgr.hxx> +using namespace ::com::sun::star; + namespace { // helper to create the geometry for a rounded polygon, maybe @@ -408,13 +410,35 @@ IDiagramHelper::IDiagramHelper(bool bSelfCreated) , mbUseDiagramModelData(true) , mbForceThemePtrRecreation(false) , mbSelfCreated(bSelfCreated) +, mbModified(false) +, mpAssociatedSdrObjGroup(nullptr) { } IDiagramHelper::~IDiagramHelper() {} -void IDiagramHelper::anchorToSdrObjGroup(SdrObjGroup& rTarget) +void IDiagramHelper::disconnectFromSdrObjGroup() { + if (nullptr != mpAssociatedSdrObjGroup) + { + // if change was done, reset GrabBagItem to delete buffered DiagramData which is no longer valid + mpAssociatedSdrObjGroup->SetGrabBagItem(uno::Any(uno::Sequence<beans::PropertyValue>())); + mpAssociatedSdrObjGroup->mp_DiagramHelper.reset(); + mpAssociatedSdrObjGroup = nullptr; + } +} + +void IDiagramHelper::connectToSdrObjGroup(SdrObjGroup& rTarget) +{ + if (mpAssociatedSdrObjGroup == &rTarget && rTarget.mp_DiagramHelper.get() == this) + // connection already established + return; + + // ensure unconnect if already connected + disconnectFromSdrObjGroup(); + + // connect to target + mpAssociatedSdrObjGroup = &rTarget; rTarget.mp_DiagramHelper.reset(this); } diff --git a/svx/source/diagram/datamodel.cxx b/svx/source/diagram/datamodel.cxx index 2dc9e345a825..02fea52cf493 100644 --- a/svx/source/diagram/datamodel.cxx +++ b/svx/source/diagram/datamodel.cxx @@ -22,6 +22,7 @@ #include <fstream> #include <svx/diagram/datamodel.hxx> +#include <svx/svdoutl.hxx> #include <comphelper/xmltools.hxx> #include <sal/log.hxx> #include <utility> @@ -493,6 +494,52 @@ void DiagramData::buildDiagramDataModel(bool /*bClearOoxShapes*/) #endif } +bool DiagramData::TextInformationChange(const OUString& rDiagramDataModelID, Outliner& rOutl) +{ + // try to get the Point fo rthe associated ID + const auto pDataNode = maPointNameMap.find(rDiagramDataModelID); + if (pDataNode == maPointNameMap.end()) + return false; + + // use PresentationAssociationId to get to the text containing Point + const OUString& rPresentationAssociationId(pDataNode->second->msPresentationAssociationId); + if (rPresentationAssociationId.isEmpty()) + return false; + + // try to get the text-associated Point + const auto pTextNode = maPointNameMap.find(rPresentationAssociationId); + if (pTextNode == maPointNameMap.end()) + return false; + + // check if it has a text node - it should + if (!pTextNode->second->msTextBody) + return false; + + // access TextBody + TextBodyPtr pTextBody(pTextNode->second->msTextBody); + + // now for outliner/source: Do we have data at all? + if(0 == rOutl.GetParagraphCount()) + return false; + + // if yes, use 1st paragraph (for now) + const Paragraph* pPara(rOutl.GetParagraph(0)); + if (nullptr == pPara) + return false; + + // extract 1st para as text + const OUString aCurrentText(rOutl.GetText(pPara)); + + // check if text differs at all + if (aCurrentText == pTextBody->msText) + return false; + + // do change and return true (change was done) + // NOTE: for now only rough text change, attributes are missing and will need to be added + pTextBody->msText = aCurrentText; + return true; +} + } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/svdraw/svdoashp.cxx b/svx/source/svdraw/svdoashp.cxx index 4c4b94abfe26..44d1e6ec4ff3 100644 --- a/svx/source/svdraw/svdoashp.cxx +++ b/svx/source/svdraw/svdoashp.cxx @@ -76,6 +76,7 @@ #include <svx/sdsxyitm.hxx> #include <svx/sdtmfitm.hxx> #include <svx/sdasitm.hxx> +#include <svx/diagram/IDiagramHelper.hxx> #include <basegfx/polygon/b2dpolypolygontools.hxx> #include <basegfx/matrix/b2dhommatrix.hxx> #include <basegfx/matrix/b2dhommatrixtools.hxx> @@ -2608,8 +2609,20 @@ void SdrObjCustomShape::TakeTextEditArea(Size* pPaperMin, Size* pPaperMax, tools if (pPaperMax!=nullptr) *pPaperMax=aPaperMax; if (pViewInit!=nullptr) *pViewInit=aViewInit; } + void SdrObjCustomShape::EndTextEdit( SdrOutliner& rOutl ) { + if (!getDiagramDataModelID().isEmpty()) + { + std::shared_ptr< svx::diagram::IDiagramHelper > pIDiagramHelper(getDiagramHelperFromDiagramOrMember()); + + if (pIDiagramHelper) + { + // try to do the needed changes at the associated DiagramData model + pIDiagramHelper->TextInformationChange(getDiagramDataModelID(), rOutl); + } + } + SdrTextObj::EndTextEdit( rOutl ); InvalidateRenderGeometry(); } diff --git a/svx/source/svdraw/svdobj.cxx b/svx/source/svdraw/svdobj.cxx index 66c35a1086da..898dae7d6228 100644 --- a/svx/source/svdraw/svdobj.cxx +++ b/svx/source/svdraw/svdobj.cxx @@ -201,12 +201,31 @@ struct SdrObject::Impl meRelativeHeightRelation(text::RelOrientation::PAGE_FRAME) {} }; +bool SdrObject::isDiagram() const +{ + return false; +} + const std::shared_ptr< svx::diagram::IDiagramHelper >& SdrObject::getDiagramHelper() const { static std::shared_ptr< svx::diagram::IDiagramHelper > aEmpty; return aEmpty; } +const std::shared_ptr< svx::diagram::IDiagramHelper >& SdrObject::getDiagramHelperFromDiagramOrMember() const +{ + SdrObject* pCurrent(const_cast<SdrObject*>(this)); + + while (pCurrent) + { + if (pCurrent->isDiagram()) + return pCurrent->getDiagramHelper(); + pCurrent = pCurrent->getParentSdrObjectFromSdrObject(); + } + + return getDiagramHelper(); +} + // BaseProperties section sdr::properties::BaseProperties& SdrObject::GetProperties() const @@ -347,6 +366,8 @@ SdrObject::SdrObject(SdrModel& rSdrModel) , mnLayerID(0) , mpSvxShape( nullptr ) , mbDoNotInsertIntoPageAutomatically(false) + , msHyperlink() + , msDiagramDataModelID() { m_bVirtObj =false; m_bSnapRectDirty =true; @@ -383,6 +404,8 @@ SdrObject::SdrObject(SdrModel& rSdrModel, SdrObject const & rSource) , mnLayerID(0) , mpSvxShape( nullptr ) , mbDoNotInsertIntoPageAutomatically(false) + , msHyperlink() + , msDiagramDataModelID() { m_bVirtObj =false; m_bSnapRectDirty =true; @@ -1855,19 +1878,6 @@ void SdrObject::SetOutlinerParaObject(std::optional<OutlinerParaObject> pTextObj if (GetCurrentBoundRect()!=aBoundRect0) { SendUserCall(SdrUserCallType::Resize,aBoundRect0); } - - if (!getSdrModelFromSdrObject().IsUndoEnabled()) - return; - - // Don't do this during import. - if (SdrObject* pTopGroupObj = getParentSdrObjectFromSdrObject()) - { - while (SdrObject* pParent = pTopGroupObj->getParentSdrObjectFromSdrObject()) - pTopGroupObj = pParent; - // A shape was modified, which is in a group shape. Empty the group shape's grab-bag, - // which potentially contains the old text of the shapes in case of diagrams. - pTopGroupObj->SetGrabBagItem(uno::Any(uno::Sequence<beans::PropertyValue>())); - } } void SdrObject::NbcSetOutlinerParaObject(std::optional<OutlinerParaObject> /*pTextObject*/, bool /*bAdjustTextFrameWidthAndHeight = true*/) diff --git a/svx/source/svdraw/svdogrp.cxx b/svx/source/svdraw/svdogrp.cxx index 849a0f106053..6342efc796b8 100644 --- a/svx/source/svdraw/svdogrp.cxx +++ b/svx/source/svdraw/svdogrp.cxx @@ -34,6 +34,11 @@ #include <vcl/canvastools.hxx> #include <svx/diagram/IDiagramHelper.hxx> +bool SdrObjGroup::isDiagram() const +{ + return bool(mp_DiagramHelper); +} + const std::shared_ptr< svx::diagram::IDiagramHelper >& SdrObjGroup::getDiagramHelper() const { return mp_DiagramHelper; @@ -204,39 +209,6 @@ SdrObjList* SdrObjGroup::GetSubList() const { return const_cast< SdrObjGroup* >(this); } - -static bool containsOOXData(const css::uno::Any& rVal) -{ - const css::uno::Sequence<css::beans::PropertyValue> propList(rVal.get< css::uno::Sequence<css::beans::PropertyValue> >()); - for (const auto& rProp : propList) - { - if(rProp.Name.startsWith("OOX")) - { - return true; - } - } - - return false; -} - -void SdrObjGroup::SetGrabBagItem(const css::uno::Any& rVal) -{ - // detect if the intention is to disable Diagram functionality - if(isDiagram() && !containsOOXData(rVal)) - { - css::uno::Any aOld; - GetGrabBagItem(aOld); - - if(containsOOXData(aOld)) - { - mp_DiagramHelper.reset(); - } - } - - // call parent - SdrObject::SetGrabBagItem(rVal); -} - const tools::Rectangle& SdrObjGroup::GetCurrentBoundRect() const { // <aOutRect> has to contain the bounding rectangle diff --git a/sw/sdi/_drwbase.sdi b/sw/sdi/_drwbase.sdi index 4fa17e1f96eb..7517d5556e80 100644 --- a/sw/sdi/_drwbase.sdi +++ b/sw/sdi/_drwbase.sdi @@ -67,6 +67,13 @@ interface BaseTextDrawBase StateMethod = GetState ; ] + SID_DIAGRAM_TO_GROUP + [ + AutoUpdate = TRUE , + ExecMethod = Execute ; + StateMethod = GetState ; + ] + SID_EDIT_DIAGRAM [ AutoUpdate = TRUE , diff --git a/sw/source/uibase/shells/drwbassh.cxx b/sw/source/uibase/shells/drwbassh.cxx index 85eeb0c70072..46a6e7dac306 100644 --- a/sw/source/uibase/shells/drwbassh.cxx +++ b/sw/source/uibase/shells/drwbassh.cxx @@ -460,6 +460,7 @@ void SwDrawBaseShell::Execute(SfxRequest& rReq) break; case SID_REGENERATE_DIAGRAM: + case SID_DIAGRAM_TO_GROUP: case SID_EDIT_DIAGRAM: { const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList(); @@ -471,13 +472,13 @@ void SwDrawBaseShell::Execute(SfxRequest& rReq) // Support advanced DiagramHelper if(nullptr != pObj && pObj->isDiagram()) { - if(SID_REGENERATE_DIAGRAM == nSlotId) + if (SID_REGENERATE_DIAGRAM == nSlotId) { pSdrView->UnmarkAll(); pObj->getDiagramHelper()->reLayout(*static_cast<SdrObjGroup*>(pObj)); pSdrView->MarkObj(pObj, pSdrView->GetSdrPageView()); } - else // SID_EDIT_DIAGRAM + else if(SID_EDIT_DIAGRAM == nSlotId) { VclAbstractDialogFactory* pFact = VclAbstractDialogFactory::Create(); VclPtr<VclAbstractDialog> pDlg = pFact->CreateDiagramDialog( @@ -490,6 +491,12 @@ void SwDrawBaseShell::Execute(SfxRequest& rReq) } ); } + else // SID_DIAGRAM_TO_GROUP + { + pSdrView->UnmarkAll(); + pObj->getDiagramHelper()->disconnectFromSdrObjGroup(); + pSdrView->MarkObj(pObj, pSdrView->GetSdrPageView()); + } } } }
