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());
+                        }
                     }
                 }
             }

Reply via email to