sw/inc/frmfmt.hxx                            |   10 -
 sw/inc/textboxhelper.hxx                     |   94 ++++++++++-
 sw/qa/extras/layout/layout.cxx               |    2 
 sw/qa/extras/uiwriter/uiwriter3.cxx          |   10 -
 sw/qa/uitest/data/tdf143574.odt              |binary
 sw/qa/uitest/writer_tests7/tdf143574.py      |   39 ++++
 sw/source/core/doc/DocumentLayoutManager.cxx |    7 
 sw/source/core/doc/textboxhelper.cxx         |  220 +++++++++++++++++++++++----
 sw/source/core/draw/dflyobj.cxx              |    2 
 sw/source/core/layout/atrfrm.cxx             |   52 +-----
 sw/source/core/layout/flycnt.cxx             |    2 
 sw/source/core/undo/undobj1.cxx              |   22 +-
 sw/source/core/unocore/unodraw.cxx           |    4 
 sw/source/uibase/shells/drawsh.cxx           |    4 
 14 files changed, 368 insertions(+), 100 deletions(-)

New commits:
commit 504d78acb866495fd954fcd6db22ea68f174a5ab
Author:     Attila Bakos (NISZ) <bakos.attilakar...@nisz.hu>
AuthorDate: Fri Aug 13 14:11:24 2021 +0200
Commit:     László Németh <nem...@numbertext.org>
CommitDate: Wed Sep 1 10:09:37 2021 +0200

    tdf#143574 sw: textboxes in group shapes - part 1
    
    Introduce SwTextBoxNode class to support grouped
    textboxes, fixing the crash when entering a group
    shape, trying to add a textbox to one of the shapes.
    
    Test of crash fix: right click on a group shape.
    Select the menu item "Enter group". Select one of
    the shapes, and right click on it, and choose "Add
    Text Box".
    
    Note: textboxes in Writer are linked in SwFrameFormat
    class. Each textbox consists of a text frame and a shape.
    This object pair makes it possible to have complex
    content inside any kind of shape (pictures, tables etc.
    Group shapes have only one SwFrameFormat, but can have
    many shapes so that means they can only have one textbox.
    From now, each shape could have a textbox in a group.
    Please note this is only a preparation for that. Filter
    implementation and handlers are under development.
    
    Change-Id: Iae35c118f0e67697b289c30d0fad4f5e16501c02
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/120452
    Tested-by: László Németh <nem...@numbertext.org>
    Reviewed-by: László Németh <nem...@numbertext.org>

diff --git a/sw/inc/frmfmt.hxx b/sw/inc/frmfmt.hxx
index 24554c1171f2..59aee54a2f4a 100644
--- a/sw/inc/frmfmt.hxx
+++ b/sw/inc/frmfmt.hxx
@@ -27,6 +27,7 @@
 #include "hintids.hxx"
 #include "swdllapi.h"
 #include <list>
+#include "textboxhelper.hxx"
 
 class SwFlyFrame;
 class SwFlyDrawContact;
@@ -73,7 +74,7 @@ class SW_DLLPUBLIC SwFrameFormat
     // The assigned SwFrmFmt list.
     SwFrameFormats *m_ffList;
 
-    SwFrameFormat *m_pOtherTextBoxFormat;
+    SwTextBoxNode* m_pOtherTextBoxFormat;
 
     struct change_name
     {
@@ -99,10 +100,11 @@ protected:
 
     virtual void SwClientNotify(const SwModify&, const SfxHint&) override;
 
-    SwFrameFormat* GetOtherTextBoxFormat() const { return 
m_pOtherTextBoxFormat; }
-    void SetOtherTextBoxFormat( SwFrameFormat *pFormat );
-
 public:
+
+    SwTextBoxNode* GetOtherTextBoxFormat() const { return 
m_pOtherTextBoxFormat; };
+    void SetOtherTextBoxFormat(SwTextBoxNode* pNew) { m_pOtherTextBoxFormat = 
pNew; };
+
     virtual ~SwFrameFormat() override;
 
     SwFrameFormat(SwFrameFormat const &) = default;
diff --git a/sw/inc/textboxhelper.hxx b/sw/inc/textboxhelper.hxx
index 3d1d4dce5827..3cd442ed7194 100644
--- a/sw/inc/textboxhelper.hxx
+++ b/sw/inc/textboxhelper.hxx
@@ -56,11 +56,14 @@ public:
     using SavedLink = std::map<const SwFrameFormat*, const SwFrameFormat*>;
     /// Maps a draw format to content.
     using SavedContent = std::map<const SwFrameFormat*, SwFormatContent>;
-    /// Create a TextBox for a shape. If the second parameter is true,
+    /// Create a TextBox for a shape. If the third parameter is true,
     /// the original text in the shape will be copied to the frame
-    static void create(SwFrameFormat* pShape, bool bCopyText = false);
-    /// Destroy a TextBox for a shape.
-    static void destroy(SwFrameFormat* pShape);
+    /// The textbox is created for the shape given by the pObject parameter.
+    static void create(SwFrameFormat* pShape, SdrObject* pObject, bool 
bCopyText = false);
+    /// Destroy a TextBox for a shape. If the format has more textboxes
+    /// like group shapes, it will destroy only that textbox what belongs
+    /// to the given pObject shape.
+    static void destroy(SwFrameFormat* pShape, SdrObject* pObject);
     /// Get interface of a shape's TextBox, if there is any.
     static css::uno::Any queryInterface(const SwFrameFormat* pShape, const 
css::uno::Type& rType);
 
@@ -114,12 +117,16 @@ public:
     /**
      * If we have an associated TextFrame, then return that.
      *
+     * If we have more textboxes for this format (group shape), that one will 
be
+     * returned, what belongs to the pObject.
+     *
      * @param nType Expected frame format type.
      *              Valid types are RES_DRAWFRMFMT and RES_FLYFRMFMT.
      *
      * @see isTextBox
      */
-    static SwFrameFormat* getOtherTextBoxFormat(const SwFrameFormat* pFormat, 
sal_uInt16 nType);
+    static SwFrameFormat* getOtherTextBoxFormat(const SwFrameFormat* pFormat, 
sal_uInt16 nType,
+                                                SdrObject* pObject = nullptr);
     /// If we have an associated TextFrame, then return that.
     static SwFrameFormat*
     getOtherTextBoxFormat(css::uno::Reference<css::drawing::XShape> const& 
xShape);
@@ -135,10 +142,18 @@ public:
      * A text box consists of a coupled fly and draw format. Most times you
      * just want to check for a single type, otherwise you get duplicate 
results.
      *
-     * @param nType Expected frame format input type.
+     * @param pFormat: Is this format have a textbox?
+     *
+     * @param nType: Expected frame format input type.
      *              Valid types are RES_DRAWFRMFMT and RES_FLYFRMFMT.
+     *
+     * @param pObject: If the pFormat has more textboxes than one, like
+     *                 groupshapes, the textbox what belongs to the given
+     *                 pObject will be inspected. If this parameter nullptr,
+     *                 the textbox what belongs to the pObject will only be 
inspected.
      */
-    static bool isTextBox(const SwFrameFormat* pFormat, sal_uInt16 nType);
+    static bool isTextBox(const SwFrameFormat* pFormat, sal_uInt16 nType,
+                          SdrObject* pObject = nullptr);
 
     /// Returns true if the SdrObject has a SwTextFrame otherwise false
     static bool hasTextFrame(const SdrObject* pObj);
@@ -164,6 +179,71 @@ public:
                              SavedLink& rSavedLinks);
 };
 
+/// Textboxes are basically textframe + shape pairs. This means one shape has 
one frame.
+/// This is not enough for group shapes, because they have only one shape 
format and
+/// can have many frame formats. This class provides if there is a group shape 
for example,
+/// it can have multiple textboxes.
+class SwTextBoxNode
+{
+    // One TextBox-entry
+    struct SwTextBoxElement
+    {
+        // The textframe format
+        SwFrameFormat* m_pTextBoxFormat;
+        // The Draw object where the textbox belongs to
+        SdrObject* m_pDrawObject;
+        // This is for indicating if the textbox is in special case: for 
example during undo.
+        bool m_bIsActive;
+    };
+
+    // This vector stores the textboxes what belongs to this node
+    std::vector<SwTextBoxElement> m_pTextBoxes;
+    // This is the pointer to the shape format, which has this node
+    // (and the textboxes)
+    SwFrameFormat* m_pOwnerShapeFormat;
+
+public:
+    // Not needed.
+    SwTextBoxNode() = delete;
+
+    // ctor
+    SwTextBoxNode(SwFrameFormat* pOwnerShapeFormat);
+    // dtor
+    ~SwTextBoxNode();
+
+    // default copy ctor is enough
+    SwTextBoxNode(SwTextBoxNode&) = default;
+
+    // This method adds a textbox entry to the shape
+    // Parameters:
+    //     pDrawObject: The shape what the textbox be added to.
+    //     pNewTextBox: The newly created textbox format what will be added to 
the shape.
+    void AddTextBox(SdrObject* pDrawObject, SwFrameFormat* pNewTextBox);
+
+    // This will remove the textbox entry.
+    // Parameters:
+    //     pDrawObject: The shape which have the textbox to be deleted.
+    void DelTextBox(SdrObject* pDrawObject);
+
+    // This will return with the frame format of the textbox what belongs
+    // to the given shape (pDrawObject)
+    SwFrameFormat* GetTextBox(const SdrObject* pDrawObject) const;
+
+    // Is this textbox has special state, undo for example?
+    bool IsTextBoxActive(const SdrObject* pDrawObject) const;
+
+    // Setters for the state flag.
+    void SetTextBoxInactive(SdrObject* pDrawObject);
+    void SetTextBoxActive(SdrObject* pDrawObject);
+
+    // If this is a group shape, that returns true.
+    bool IsGroupTextBox() const;
+    // This returns with the shape what this class belongs to.
+    SwFrameFormat* GetOwnerShape() { return m_pOwnerShapeFormat; };
+    // This will give the current number of textboxes.
+    size_t GetTextBoxCount() const { return m_pTextBoxes.size(); };
+};
+
 #endif // INCLUDED_SW_INC_TEXTBOXHELPER_HXX
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/qa/extras/layout/layout.cxx b/sw/qa/extras/layout/layout.cxx
index 51dd08159730..2f6f827b81a0 100644
--- a/sw/qa/extras/layout/layout.cxx
+++ b/sw/qa/extras/layout/layout.cxx
@@ -3564,7 +3564,7 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf137185)
     CPPUNIT_ASSERT_EQUAL(OUString("Align me!"), xTxt->getText()->getString());
 
     // Add a textbox to the shape
-    SwTextBoxHelper::create(pShape, true);
+    SwTextBoxHelper::create(pShape, pShape->FindRealSdrObject(), true);
 
     // Check if the text moved from the shape to the frame
     auto pFormat = SwTextBoxHelper::getOtherTextBoxFormat(getShape(1));
diff --git a/sw/qa/extras/uiwriter/uiwriter3.cxx 
b/sw/qa/extras/uiwriter/uiwriter3.cxx
index 7ea98b947995..590aa62bce8b 100644
--- a/sw/qa/extras/uiwriter/uiwriter3.cxx
+++ b/sw/qa/extras/uiwriter/uiwriter3.cxx
@@ -1824,7 +1824,7 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest3, testTdf130805)
     auto pShape = rFrmFormats.front();
     CPPUNIT_ASSERT(pShape);
 
-    SwTextBoxHelper::create(pShape);
+    SwTextBoxHelper::create(pShape, pShape->FindRealSdrObject());
     auto pTxBxFrm = SwTextBoxHelper::getOtherTextBoxFormat(getShape(1));
     CPPUNIT_ASSERT(pTxBxFrm);
 
@@ -1848,7 +1848,7 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest3, testTdf107893)
     CPPUNIT_ASSERT(pShape);
 
     //Add a textbox
-    SwTextBoxHelper::create(pShape);
+    SwTextBoxHelper::create(pShape, pShape->FindRealSdrObject());
     SwFrameFormat* pTxBxFrm = 
SwTextBoxHelper::getOtherTextBoxFormat(getShape(1));
     CPPUNIT_ASSERT(pTxBxFrm);
 
@@ -1856,7 +1856,7 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest3, testTdf107893)
     dispatchCommand(mxComponent, ".uno:Undo", {});
 
     //Add again
-    SwTextBoxHelper::create(pShape);
+    SwTextBoxHelper::create(pShape, pShape->FindRealSdrObject());
     pTxBxFrm = SwTextBoxHelper::getOtherTextBoxFormat(getShape(1));
 
     //This was nullptr because of unsuccessful re-adding
@@ -1903,7 +1903,7 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest3, 
TestTextBoxCrashAfterLineDel)
     CPPUNIT_ASSERT(pShape);
 
     // Add a textbox
-    SwTextBoxHelper::create(pShape);
+    SwTextBoxHelper::create(pShape, pShape->FindRealSdrObject());
     SwFrameFormat* pTxBxFrm = 
SwTextBoxHelper::getOtherTextBoxFormat(getShape(1));
     CPPUNIT_ASSERT(pTxBxFrm);
 
@@ -2454,7 +2454,7 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest3, AtPageTextBoxCrash)
     CPPUNIT_ASSERT(pShape);
 
     // Add a textbox to the shape
-    SwTextBoxHelper::create(pShape);
+    SwTextBoxHelper::create(pShape, pShape->FindRealSdrObject());
     auto pTxBxFrm = SwTextBoxHelper::getOtherTextBoxFormat(getShape(1));
     CPPUNIT_ASSERT(pTxBxFrm);
 
diff --git a/sw/qa/uitest/data/tdf143574.odt b/sw/qa/uitest/data/tdf143574.odt
new file mode 100644
index 000000000000..a2e961e11253
Binary files /dev/null and b/sw/qa/uitest/data/tdf143574.odt differ
diff --git a/sw/qa/uitest/writer_tests7/tdf143574.py 
b/sw/qa/uitest/writer_tests7/tdf143574.py
new file mode 100644
index 000000000000..08e59b7c682a
--- /dev/null
+++ b/sw/qa/uitest/writer_tests7/tdf143574.py
@@ -0,0 +1,39 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+from uitest.framework import UITestCase
+import org.libreoffice.unotest
+from uitest.uihelper.common import get_url_for_data_file
+from libreoffice.uno.propertyvalue import mkPropertyValues
+
+class tdf143574(UITestCase):
+    def test_tdf143574(self):
+        # load the sample file
+        with self.ui_test.load_file(get_url_for_data_file("tdf143574.odt")):
+            xWriterDoc = self.xUITest.getTopFocusWindow()
+            xWriterEdit = xWriterDoc.getChild("writer_edit")
+            document = self.ui_test.get_component()
+
+            # check the shape type.
+            self.assertEqual("com.sun.star.drawing.GroupShape", 
document.DrawPage.getByIndex(0).ShapeType)
+
+            # select the shape.
+            self.xUITest.executeCommand(".uno:JumpToNextFrame")
+            self.ui_test.wait_until_child_is_available('metricfield')
+
+            # go inside the group
+            self.xUITest.executeCommand(".uno:EnterGroup");
+
+            # select a shape in the group
+            xWriterEdit.executeAction("TYPE", mkPropertyValues({"KEYCODE": 
"TAB"}))
+
+            # At this point the Writer crashed here before the fix.
+            self.xUITest.executeCommand(".uno:AddTextBox");
+
+            #follow up commit will introduce:
+            #self.assertEqual(True, 
document.DrawPage.getByIndex(0).getByIndex(2).TextBox)
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/sw/source/core/doc/DocumentLayoutManager.cxx 
b/sw/source/core/doc/DocumentLayoutManager.cxx
index 1266e3ea719f..1e071ef3ac5e 100644
--- a/sw/source/core/doc/DocumentLayoutManager.cxx
+++ b/sw/source/core/doc/DocumentLayoutManager.cxx
@@ -490,8 +490,11 @@ SwFrameFormat *DocumentLayoutManager::CopyLayoutFormat(
         pDest->SetFormatAttr(aSet);
 
         // Link FLY and DRAW formats, so it becomes a text box
-        pDest->SetOtherTextBoxFormat(pDestTextBox);
-        pDestTextBox->SetOtherTextBoxFormat(pDest);
+        auto pTextBox = new SwTextBoxNode(pDest);
+        pTextBox->AddTextBox(pDest->FindRealSdrObject(), pDestTextBox);
+
+        pDest->SetOtherTextBoxFormat(pTextBox);
+        pDestTextBox->SetOtherTextBoxFormat(pTextBox);
     }
 
     if (pDest->GetName().isEmpty())
diff --git a/sw/source/core/doc/textboxhelper.cxx 
b/sw/source/core/doc/textboxhelper.cxx
index 410e2323e78d..2df4c625d1dc 100644
--- a/sw/source/core/doc/textboxhelper.cxx
+++ b/sw/source/core/doc/textboxhelper.cxx
@@ -28,11 +28,13 @@
 #include <fmtsrnd.hxx>
 #include <frmfmt.hxx>
 #include <frameformats.hxx>
+#include <dflyobj.hxx>
 
 #include <editeng/unoprnms.hxx>
 #include <editeng/memberids.h>
 #include <svx/svdoashp.hxx>
 #include <svx/svdpage.hxx>
+#include <svx/svdogrp.hxx>
 #include <svl/itemiter.hxx>
 #include <comphelper/sequenceashashmap.hxx>
 #include <sal/log.hxx>
@@ -58,10 +60,14 @@
 
 using namespace com::sun::star;
 
-void SwTextBoxHelper::create(SwFrameFormat* pShape, bool bCopyText)
+void SwTextBoxHelper::create(SwFrameFormat* pShape, SdrObject* pObject, bool 
bCopyText)
 {
+    if (dynamic_cast<SdrObjGroup*>(pObject->getParentSdrObjectFromSdrObject()))
+        // The GroupShape Textbox creation method call comes here.
+        return;
+
     // If TextBox wasn't enabled previously
-    if (pShape->GetAttrSet().HasItem(RES_CNTNT) && 
pShape->GetOtherTextBoxFormat())
+    if (pShape->GetOtherTextBoxFormat() && 
pShape->GetOtherTextBoxFormat()->GetTextBox(pObject))
         return;
 
     // Store the current text content of the shape
@@ -108,9 +114,20 @@ void SwTextBoxHelper::create(SwFrameFormat* pShape, bool 
bCopyText)
     assert(nullptr != dynamic_cast<SwDrawFrameFormat*>(pShape));
     assert(nullptr != dynamic_cast<SwFlyFrameFormat*>(pFormat));
 
-    pShape->SetOtherTextBoxFormat(pFormat);
-    pFormat->SetOtherTextBoxFormat(pShape);
-
+    if (!pShape->GetOtherTextBoxFormat())
+    {
+        auto* pTextBox = new SwTextBoxNode(pShape);
+        pTextBox->AddTextBox(pObject, pFormat);
+        pShape->SetOtherTextBoxFormat(pTextBox);
+        pFormat->SetOtherTextBoxFormat(pTextBox);
+    }
+    else
+    {
+        auto* pTextBox = pShape->GetOtherTextBoxFormat();
+        pTextBox->AddTextBox(pObject, pFormat);
+        pShape->SetOtherTextBoxFormat(pTextBox);
+        pFormat->SetOtherTextBoxFormat(pTextBox);
+    }
     // Initialize properties.
     uno::Reference<beans::XPropertySet> xPropertySet(xTextFrame, 
uno::UNO_QUERY);
     uno::Any aEmptyBorder = uno::makeAny(table::BorderLine2());
@@ -189,40 +206,45 @@ void SwTextBoxHelper::create(SwFrameFormat* pShape, bool 
bCopyText)
     }
 }
 
-void SwTextBoxHelper::destroy(SwFrameFormat* pShape)
+void SwTextBoxHelper::destroy(SwFrameFormat* pShape, SdrObject* pObject)
 {
     // If a TextBox was enabled previously
-    if (pShape->GetAttrSet().HasItem(RES_CNTNT))
+    auto pTextBox = pShape->GetOtherTextBoxFormat();
+    if (pTextBox && pTextBox->IsTextBoxActive(pObject))
     {
-        SwFrameFormat* pFormat = pShape->GetOtherTextBoxFormat();
-
         // Unlink the TextBox's text range from the original shape.
-        pShape->ResetFormatAttr(RES_CNTNT);
+        pTextBox->SetTextBoxInactive(pObject);
 
         // Delete the associated TextFrame.
-        if (pFormat)
-            
pShape->GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat(pFormat);
+        pTextBox->DelTextBox(pObject);
     }
 }
 
-bool SwTextBoxHelper::isTextBox(const SwFrameFormat* pFormat, sal_uInt16 nType)
+bool SwTextBoxHelper::isTextBox(const SwFrameFormat* pFormat, sal_uInt16 
nType, SdrObject* pObject)
 {
+    SolarMutexGuard aGuard;
     assert(nType == RES_FLYFRMFMT || nType == RES_DRAWFRMFMT);
-    if (!pFormat || pFormat->Which() != nType || 
!pFormat->GetAttrSet().HasItem(RES_CNTNT))
+    if (!pFormat || pFormat->Which() != nType)
         return false;
 
-    sal_uInt16 nOtherType = (pFormat->Which() == RES_FLYFRMFMT) ? 
sal_uInt16(RES_DRAWFRMFMT)
-                                                                : 
sal_uInt16(RES_FLYFRMFMT);
-    SwFrameFormat* pOtherFormat = pFormat->GetOtherTextBoxFormat();
-    if (!pOtherFormat)
+    auto pTextBox = pFormat->GetOtherTextBoxFormat();
+    if (!pTextBox)
         return false;
 
-    assert(pOtherFormat->Which() == nOtherType);
-    if (pOtherFormat->Which() != nOtherType)
-        return false;
+    if (nType == RES_DRAWFRMFMT)
+    {
+        if (pObject)
+            return pTextBox->GetTextBox(pObject);
+        if (auto pObj = pFormat->FindRealSdrObject())
+            return pTextBox->GetTextBox(pObj);
+    }
+
+    if (nType == RES_FLYFRMFMT)
+    {
+        return pTextBox->GetOwnerShape();
+    }
 
-    const SwFormatContent& rContent = pFormat->GetContent();
-    return pOtherFormat->GetAttrSet().HasItem(RES_CNTNT) && 
pOtherFormat->GetContent() == rContent;
+    return false;
 }
 
 bool SwTextBoxHelper::hasTextFrame(const SdrObject* pObj)
@@ -315,11 +337,25 @@ void SwTextBoxHelper::getShapeWrapThrough(const 
SwFrameFormat* pTextBox, bool& r
 }
 
 SwFrameFormat* SwTextBoxHelper::getOtherTextBoxFormat(const SwFrameFormat* 
pFormat,
-                                                      sal_uInt16 nType)
+                                                      sal_uInt16 nType, 
SdrObject* pObject)
 {
-    if (!isTextBox(pFormat, nType))
+    SolarMutexGuard aGuard;
+    if (!isTextBox(pFormat, nType, pObject))
+        return nullptr;
+
+    if (nType == RES_DRAWFRMFMT)
+    {
+        if (pObject)
+            return pFormat->GetOtherTextBoxFormat()->GetTextBox(pObject);
+        if (pFormat->FindRealSdrObject())
+            return 
pFormat->GetOtherTextBoxFormat()->GetTextBox(pFormat->FindRealSdrObject());
         return nullptr;
-    return pFormat->GetOtherTextBoxFormat();
+    }
+    if (nType == RES_FLYFRMFMT)
+    {
+        return pFormat->GetOtherTextBoxFormat()->GetOwnerShape();
+    }
+    return nullptr;
 }
 
 SwFrameFormat* 
SwTextBoxHelper::getOtherTextBoxFormat(uno::Reference<drawing::XShape> const& 
xShape)
@@ -341,7 +377,7 @@ 
SwTextBoxHelper::getUnoTextFrame(uno::Reference<drawing::XShape> const& xShape)
         if (pFrameFormat)
         {
             auto pSdrObj = pFrameFormat->FindSdrObject();
-            if (pSdrObj && pSdrObj->IsTextBox())
+            if (pSdrObj)
             {
                 return 
uno::Reference<css::text::XTextFrame>(pSdrObj->getUnoShape(),
                                                              uno::UNO_QUERY);
@@ -1323,4 +1359,134 @@ bool 
SwTextBoxHelper::DoTextBoxZOrderCorrection(SwFrameFormat* pShape)
     return false;
 }
 
+SwTextBoxNode::SwTextBoxNode(SwFrameFormat* pOwnerShape)
+{
+    assert(pOwnerShape);
+    assert(pOwnerShape->Which() == RES_DRAWFRMFMT);
+
+    m_pOwnerShapeFormat = pOwnerShape;
+    if (m_pTextBoxes.size())
+        m_pTextBoxes.clear();
+}
+
+SwTextBoxNode::~SwTextBoxNode()
+{
+    // This only happens if the shape or the doc is in dtor.
+    for (auto& rTextBoxEntry : m_pTextBoxes)
+    {
+        rTextBoxEntry.m_pDrawObject = nullptr;
+        
m_pOwnerShapeFormat->GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat(
+            rTextBoxEntry.m_pTextBoxFormat);
+    }
+    m_pOwnerShapeFormat->SetOtherTextBoxFormat(nullptr);
+}
+
+void SwTextBoxNode::AddTextBox(SdrObject* pDrawObject, SwFrameFormat* 
pNewTextBox)
+{
+    assert(pNewTextBox);
+    assert(pNewTextBox->Which() == RES_FLYFRMFMT);
+
+    assert(pDrawObject);
+
+    SwTextBoxElement aElem;
+    aElem.m_bIsActive = true;
+    aElem.m_pDrawObject = pDrawObject;
+    aElem.m_pTextBoxFormat = pNewTextBox;
+    SwFlyDrawObj* pSwFlyDraw = dynamic_cast<SwFlyDrawObj*>(pDrawObject);
+    if (pSwFlyDraw)
+    {
+        pSwFlyDraw->SetTextBox(true);
+    }
+    m_pTextBoxes.push_back(aElem);
+}
+
+void SwTextBoxNode::DelTextBox(SdrObject* pDrawObject)
+{
+    assert(pDrawObject);
+    if (m_pTextBoxes.size())
+    {
+        for (auto it = m_pTextBoxes.begin(); it != m_pTextBoxes.end(); it++)
+        {
+            if (it->m_pDrawObject == pDrawObject)
+            {
+                
m_pOwnerShapeFormat->GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat(
+                    it->m_pTextBoxFormat);
+                m_pTextBoxes.erase(it);
+                break;
+            }
+        }
+    }
+    if (!m_pTextBoxes.size())
+    {
+        m_pOwnerShapeFormat->SetOtherTextBoxFormat(nullptr);
+    }
+}
+
+SwFrameFormat* SwTextBoxNode::GetTextBox(const SdrObject* pDrawObject) const
+{
+    assert(pDrawObject);
+    if (m_pTextBoxes.size())
+    {
+        for (auto it = m_pTextBoxes.begin(); it != m_pTextBoxes.end(); it++)
+        {
+            if (it->m_pDrawObject == pDrawObject)
+            {
+                return it->m_pTextBoxFormat;
+            }
+        }
+    }
+    return nullptr;
+}
+
+bool SwTextBoxNode::IsTextBoxActive(const SdrObject* pDrawObject) const
+{
+    assert(pDrawObject);
+
+    if (m_pTextBoxes.size())
+    {
+        for (auto it = m_pTextBoxes.begin(); it != m_pTextBoxes.end(); it++)
+        {
+            if (it->m_pDrawObject == pDrawObject)
+            {
+                return it->m_bIsActive;
+            }
+        }
+    }
+    return false;
+}
+
+void SwTextBoxNode::SetTextBoxActive(SdrObject* pDrawObject)
+{
+    assert(pDrawObject);
+
+    if (m_pTextBoxes.size())
+    {
+        for (auto it = m_pTextBoxes.begin(); it != m_pTextBoxes.end(); it++)
+        {
+            if (it->m_pDrawObject == pDrawObject)
+            {
+                it->m_bIsActive = true;
+            }
+        }
+    }
+}
+
+void SwTextBoxNode::SetTextBoxInactive(SdrObject* pDrawObject)
+{
+    assert(pDrawObject);
+
+    if (m_pTextBoxes.size())
+    {
+        for (auto it = m_pTextBoxes.begin(); it != m_pTextBoxes.end(); it++)
+        {
+            if (it->m_pDrawObject == pDrawObject)
+            {
+                it->m_bIsActive = false;
+            }
+        }
+    }
+}
+
+bool SwTextBoxNode::IsGroupTextBox() const { return m_pTextBoxes.size() > 1; }
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/draw/dflyobj.cxx b/sw/source/core/draw/dflyobj.cxx
index 1d51efc07ce6..a0a0b1e58bee 100644
--- a/sw/source/core/draw/dflyobj.cxx
+++ b/sw/source/core/draw/dflyobj.cxx
@@ -1292,7 +1292,7 @@ SdrObject* SwVirtFlyDrawObj::CheckMacroHit( const 
SdrObjMacroHitRec& rRec ) cons
 
 bool SwVirtFlyDrawObj::IsTextBox() const
 {
-    return SwTextBoxHelper::isTextBox(GetFormat(), RES_FLYFRMFMT);
+    return SwTextBoxHelper::isTextBox(GetFormat(), RES_FLYFRMFMT, 
const_cast<SwVirtFlyDrawObj*>(this));
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/atrfrm.cxx b/sw/source/core/layout/atrfrm.cxx
index 932c3613ad3d..df55753f9b68 100644
--- a/sw/source/core/layout/atrfrm.cxx
+++ b/sw/source/core/layout/atrfrm.cxx
@@ -2552,7 +2552,18 @@ SwFrameFormat::~SwFrameFormat()
 
     if( nullptr != m_pOtherTextBoxFormat )
     {
-        m_pOtherTextBoxFormat->SetOtherTextBoxFormat( nullptr );
+        auto pObj = FindRealSdrObject();
+        if (Which() == RES_FLYFRMFMT && pObj)
+        {
+            // This is a fly-frame-format just del this
+            // textbox entry from the draw-frame-format.
+            m_pOtherTextBoxFormat->DelTextBox(pObj);
+
+            // delete format after deleting the last textbox
+            if (!m_pOtherTextBoxFormat->GetTextBoxCount())
+                delete m_pOtherTextBoxFormat;
+        }
+
         m_pOtherTextBoxFormat = nullptr;
     }
 }
@@ -2580,45 +2591,6 @@ void SwFrameFormat::SetName( const OUString& rNewName, 
bool bBroadcast )
         SwFormat::SetName( rNewName, bBroadcast );
 }
 
-void SwFrameFormat::SetOtherTextBoxFormat( SwFrameFormat *pFormat )
-{
-    if( nullptr != pFormat )
-    {
-        assert( (Which() == RES_DRAWFRMFMT && pFormat->Which() == 
RES_FLYFRMFMT)
-             || (Which() == RES_FLYFRMFMT && pFormat->Which() == 
RES_DRAWFRMFMT) );
-        assert( nullptr == m_pOtherTextBoxFormat );
-    }
-    else
-    {
-        assert( nullptr != m_pOtherTextBoxFormat );
-    }
-    bool bChanged = m_pOtherTextBoxFormat != pFormat;
-    m_pOtherTextBoxFormat = pFormat;
-
-    SdrObject* pObj = FindSdrObject();
-
-    if (pObj)
-    {
-        SwFlyDrawObj* pSwFlyDraw = dynamic_cast<SwFlyDrawObj*>(pObj);
-
-        if (pSwFlyDraw)
-            pSwFlyDraw->SetTextBox(true);
-    }
-
-    if (m_pOtherTextBoxFormat && bChanged && Which() == RES_DRAWFRMFMT)
-    {
-        // This is a shape of a shape+frame pair and my frame has changed. 
Make sure my content is
-        // in sync with the frame's content.
-        if (GetAttrSet().GetContent() != 
m_pOtherTextBoxFormat->GetAttrSet().GetContent())
-        {
-            SwAttrSet aSet(GetAttrSet());
-            SwFormatContent 
aContent(m_pOtherTextBoxFormat->GetAttrSet().GetContent());
-            aSet.Put(aContent);
-            SetFormatAttr(aSet);
-        }
-    }
-}
-
 bool SwFrameFormat::supportsFullDrawingLayerFillAttributeSet() const
 {
     return true;
diff --git a/sw/source/core/layout/flycnt.cxx b/sw/source/core/layout/flycnt.cxx
index 02c3d54bce4f..33b539949917 100644
--- a/sw/source/core/layout/flycnt.cxx
+++ b/sw/source/core/layout/flycnt.cxx
@@ -528,6 +528,8 @@ void SwFlyAtContentFrame::MakeAll(vcl::RenderContext* 
pRenderContext)
     // FIXME: According to tdf37153, ignore FollowTextFlow objs, because
     // wrong position will applied in that case. FollowTextFlow needs fix.
     if (pShapeFormat && !pShapeFormat->GetFollowTextFlow().GetValue() &&
+        SwTextBoxHelper::getProperty(pShapeFormat,
+            UNO_NAME_FRAME_ISAUTOMATIC_HEIGHT).hasValue() &&
         SwTextBoxHelper::getProperty(pShapeFormat,
             UNO_NAME_FRAME_ISAUTOMATIC_HEIGHT).get<bool>() )
     {
diff --git a/sw/source/core/undo/undobj1.cxx b/sw/source/core/undo/undobj1.cxx
index bbbfaaa5ef3e..54b031c8de59 100644
--- a/sw/source/core/undo/undobj1.cxx
+++ b/sw/source/core/undo/undobj1.cxx
@@ -142,11 +142,17 @@ void SwUndoFlyBase::InsFly(::sw::UndoRedoContext & 
rContext, bool bShowSelFrame)
     {
         // recklessly assume that this thing will live longer than the
         // SwUndoFlyBase - not sure what could be done if that isn't the 
case...
-        
m_pFrameFormat->GetOtherTextBoxFormat()->SetOtherTextBoxFormat(m_pFrameFormat);
+        
m_pFrameFormat->GetOtherTextBoxFormat()->GetOwnerShape()->SetOtherTextBoxFormat(
+            m_pFrameFormat->GetOtherTextBoxFormat());
 
-        if (m_pFrameFormat->GetOtherTextBoxFormat()->Which() == RES_DRAWFRMFMT)
+        SdrObject* pSdrObject
+            = 
m_pFrameFormat->GetOtherTextBoxFormat()->GetOwnerShape()->FindSdrObject();
+        if (pSdrObject && m_pFrameFormat->Which() == RES_FLYFRMFMT)
+            m_pFrameFormat->GetOtherTextBoxFormat()->AddTextBox(pSdrObject, 
m_pFrameFormat);
+
+        if (m_pFrameFormat->GetOtherTextBoxFormat()->GetOwnerShape()->Which() 
== RES_DRAWFRMFMT)
         {
-            SdrObject* pSdrObject = 
m_pFrameFormat->GetOtherTextBoxFormat()->FindSdrObject();
+
             if (pSdrObject)
             {
                 // Make sure the old UNO wrapper is no longer cached after 
changing the shape +
@@ -155,12 +161,10 @@ void SwUndoFlyBase::InsFly(::sw::UndoRedoContext & 
rContext, bool bShowSelFrame)
                 pSdrObject->setUnoShape(nullptr);
             }
         }
-        if (m_pFrameFormat->Which() == RES_DRAWFRMFMT)
+        if (m_pFrameFormat->Which() == RES_FLYFRMFMT)
         {
-            // This is a draw format and we just set the fly format's textbox 
pointer to this draw
-            // format.  Sync the draw format's content with the fly format's 
content.
-            SwFrameFormat* pFlyFormat = 
m_pFrameFormat->GetOtherTextBoxFormat();
-            m_pFrameFormat->SetFormatAttr(pFlyFormat->GetContent());
+            SwFrameFormat* pShapeFormat = 
m_pFrameFormat->GetOtherTextBoxFormat()->GetOwnerShape();
+            pShapeFormat->SetFormatAttr(m_pFrameFormat->GetContent());
         }
     }
 
@@ -205,7 +209,7 @@ void SwUndoFlyBase::DelFly( SwDoc* pDoc )
 
     if (m_pFrameFormat->GetOtherTextBoxFormat())
     {   // tdf#108867 clear that pointer
-        
m_pFrameFormat->GetOtherTextBoxFormat()->SetOtherTextBoxFormat(nullptr);
+        
m_pFrameFormat->GetOtherTextBoxFormat()->GetOwnerShape()->SetOtherTextBoxFormat(nullptr);
     }
 
     // all Uno objects should now log themselves off
diff --git a/sw/source/core/unocore/unodraw.cxx 
b/sw/source/core/unocore/unodraw.cxx
index 221f47fca29a..2d25547af82c 100644
--- a/sw/source/core/unocore/unodraw.cxx
+++ b/sw/source/core/unocore/unodraw.cxx
@@ -1166,9 +1166,9 @@ void SwXShape::setPropertyValue(const OUString& 
rPropertyName, const uno::Any& a
                 bool bValue(false);
                 aValue >>= bValue;
                 if (bValue)
-                    SwTextBoxHelper::create(pFormat);
+                    SwTextBoxHelper::create(pFormat, 
GetSvxShape()->GetSdrObject());
                 else
-                    SwTextBoxHelper::destroy(pFormat);
+                    SwTextBoxHelper::destroy(pFormat, 
GetSvxShape()->GetSdrObject());
 
             }
             else if (pEntry->nWID == RES_CHAIN)
diff --git a/sw/source/uibase/shells/drawsh.cxx 
b/sw/source/uibase/shells/drawsh.cxx
index 89d8658597dc..beb197c87fa9 100644
--- a/sw/source/uibase/shells/drawsh.cxx
+++ b/sw/source/uibase/shells/drawsh.cxx
@@ -372,7 +372,7 @@ void SwDrawShell::Execute(SfxRequest &rReq)
             {
                 SwFrameFormat* pFrameFormat = ::FindFrameFormat(pObj);
                 if (pFrameFormat)
-                    SwTextBoxHelper::create(pFrameFormat, pObj->HasText());
+                    SwTextBoxHelper::create(pFrameFormat, pObj, 
pObj->HasText());
             }
             break;
         }
@@ -382,7 +382,7 @@ void SwDrawShell::Execute(SfxRequest &rReq)
             {
                 SwFrameFormat* pFrameFormat = ::FindFrameFormat(pObj);
                 if (pFrameFormat)
-                    SwTextBoxHelper::destroy(pFrameFormat);
+                    SwTextBoxHelper::destroy(pFrameFormat, pObj);
             }
             break;
         }

Reply via email to