sw/inc/textboxhelper.hxx                            |   74 +--
 sw/inc/unomid.h                                     |    4 
 sw/inc/unoprnms.hxx                                 |    1 
 sw/qa/uitest/data/ComplexGroupShapeTest.odt         |binary
 sw/qa/uitest/writer_tests4/ComplexGroupShapeTest.py |  124 +++++
 sw/source/core/doc/DocumentLayoutManager.cxx        |   45 +
 sw/source/core/doc/docdraw.cxx                      |   55 +-
 sw/source/core/doc/docfly.cxx                       |   11 
 sw/source/core/doc/textboxhelper.cxx                |  492 +++++++++++++++-----
 sw/source/core/draw/dcontact.cxx                    |   11 
 sw/source/core/draw/dview.cxx                       |    8 
 sw/source/core/frmedt/feshview.cxx                  |    4 
 sw/source/core/layout/atrfrm.cxx                    |    5 
 sw/source/core/unocore/unodraw.cxx                  |   66 +-
 sw/source/core/unocore/unomap.cxx                   |    3 
 sw/source/core/unocore/unomap1.cxx                  |    1 
 16 files changed, 702 insertions(+), 202 deletions(-)

New commits:
commit eabcfb3f18a6944d9ad89cecd3eb3ca7a2259cf3
Author:     Attila Bakos (NISZ) <bakos.attilakar...@nisz.hu>
AuthorDate: Wed Nov 3 15:39:32 2021 +0100
Commit:     László Németh <nem...@numbertext.org>
CommitDate: Wed Nov 24 11:51:07 2021 +0100

    tdf#129183 sw: textboxes in group shapes - part 3
    
    Grouping/ungrouping nested groups works now.
    
    Manual test:
    
    1.  Insert Shape.
    2.  Right-click on selected shape, Add Text Box (and some text).
    3.  Insert a new shape.
    4.  Select and group the two shapes.
    3.  Insert a third shape.
    4.  Select and group the shape and the previously grouped shapes.
    
    The text box remains in the nested shape group.
    
    Details:
    
    1) tdf#144271 memory leak of SwTextBoxHelper, by replacing the
    textbox structure vector with std::unordered map, and rethinking
    of the ownership of the objects. If a SwFrameFormat dies, and that
    is a FLYFRMFMT, it will be deleted from the textbox node and the
    FrameFormat table in the doc too, and the drawing will be stay as
    it was before. If the dying format is a drawing, all the textboxes,
    and the node will be deleted.
    
    2) Introducing the new UNO property TextBoxContent, which is needed
    for writerfilter/xmloff later to set a new textbox for the shape
    via UNO.
    
    3) Missing parameters are present now for syncing the textbox
    parameters.
    
    4) Introducing a new function namely the handleGroupTextBox() to
    do the tasks simply with all textboxes in a group shape.
    This can handle nested groups as well (group in a group).
    
    Known issues: now copy of nested group objects is implemented
    but not enabled, because it causes an assert.
    
    Change-Id: I931886eda01c7a3db93098de10f5e5f48f2f217b
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/124657
    Tested-by: László Németh <nem...@numbertext.org>
    Reviewed-by: László Németh <nem...@numbertext.org>

diff --git a/sw/inc/textboxhelper.hxx b/sw/inc/textboxhelper.hxx
index 2e5b27cfccb0..10841ed8626c 100644
--- a/sw/inc/textboxhelper.hxx
+++ b/sw/inc/textboxhelper.hxx
@@ -11,6 +11,7 @@
 #define INCLUDED_SW_INC_TEXTBOXHELPER_HXX
 
 #include <map>
+#include <unordered_map>
 #include <optional>
 #include <set>
 #include <vector>
@@ -60,12 +61,16 @@ public:
     /// the original text in the shape will be copied to the frame
     /// The textbox is created for the shape given by the pObject parameter.
     static void create(SwFrameFormat* pShape, SdrObject* pObject, bool 
bCopyText = false);
+    /// Sets the given textframe as textbox for the given (group member) shape.
+    static void set(SwFrameFormat* pShape, SdrObject* pObject,
+                    css::uno::Reference<css::text::XTextFrame> xNew);
     /// 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(const SwFrameFormat* pShape, const 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);
+    static css::uno::Any queryInterface(const SwFrameFormat* pShape, const 
css::uno::Type& rType,
+                                        SdrObject* pObj = nullptr);
 
     /// Sync property of TextBox with the one of the shape.
     static void syncProperty(SwFrameFormat* pShape, sal_uInt16 nWID, sal_uInt8 
nMemberID,
@@ -90,7 +95,7 @@ public:
 
     /// Sets the surround to through for the textframe of the given shape,
     /// not to interfere with the layout. Returns true on success.
-    static bool setWrapThrough(SwFrameFormat* pShape);
+    static bool setWrapThrough(SwFrameFormat* pShape, SdrObject* pObj = 
nullptr);
 
     /// Sets the anchor of the associated textframe of the given shape, and
     /// returns true on success.
@@ -100,15 +105,20 @@ public:
     /// returns true on success.
     static bool doTextBoxPositioning(SwFrameFormat* pShape, SdrObject* pObj);
 
+    /// Sets the correct size of textframe depending on the given SdrObject.
+    static bool setTextBoxSize(const SwFrameFormat* pShape, SdrObject* pObj);
+
     /// Returns true if the anchor different for the  given shape, and the
     /// associated textframe of the given shape.
     /// Note: In case of AS_CHAR anchor the anchor type must be different,
     /// because if not, layout breaks, but this situation also handled by
     /// this function, and returns true in that case too.
-    static std::optional<bool> isAnchorTypeDifferent(const SwFrameFormat* 
pShape);
+    static std::optional<bool> isAnchorTypeDifferent(const SwFrameFormat* 
pShape,
+                                                     SdrObject* pObj = 
nullptr);
 
     /// Returns true if the given shape has a valid textframe.
-    static bool isTextBoxShapeHasValidTextFrame(const SwFrameFormat* pShape);
+    static bool isTextBoxShapeHasValidTextFrame(const SwFrameFormat* pShape,
+                                                SdrObject* pObj = nullptr);
 
     // Returns true on success. Synchronize z-order of the text frame of the 
given textbox
     // by setting it one level higher than the z-order of the shape of the 
textbox.
@@ -177,6 +187,26 @@ public:
     /// Undo the effect of saveLinks() + individual resetLink() calls.
     static void restoreLinks(std::set<ZSortFly>& rOld, 
std::vector<SwFrameFormat*>& rNew,
                              SavedLink& rSavedLinks);
+
+    /// The following actions are implemented for groupshapes with textboxes 
too.
+    /// The selected action will be done for all of the group member textboxes.
+    enum GroupTextBoxActionType
+    {
+        POSITION_SIZE_AND_ANCHOR_CHANGE,
+        DELETE,
+        Z_ORDER_CHANGE
+    };
+
+    /// Does the selected action with ALL textboxes in the group.
+    /// Parameters:
+    /// - pGroupShapeFormat: The frame format of the group shape where the 
textboxes belongs to.
+    /// - pGroupObject: The drawing object for the group.
+    /// - eActionType: The action what the function is supposed to do.
+    /// WARN: This function will run recursive! ALL textboxes of the group 
will be handled by
+    /// the desired action!
+    static void handleTextBoxGroup(SwFrameFormat* pGroupShapeFormat,
+                                   GroupTextBoxActionType eActionType,
+                                   SdrObject* pGroupObject = nullptr);
 };
 
 /// Textboxes are basically textframe + shape pairs. This means one shape has 
one frame.
@@ -185,27 +215,15 @@ public:
 /// 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 map stores the textboxes what belongs to this node
+    std::unordered_map<const SdrObject*, SwFrameFormat*> m_pTextBoxTable;
     // This is the pointer to the shape format, which has this node
     // (and the textboxes)
     SwFrameFormat* m_pOwnerShapeFormat;
-
-public:
     // Not needed.
     SwTextBoxNode() = delete;
 
+public:
     // ctor
     SwTextBoxNode(SwFrameFormat* pOwnerShapeFormat);
     // dtor
@@ -224,24 +242,24 @@ public:
     // Parameters:
     //     pDrawObject: The shape which have the textbox to be deleted.
     void DelTextBox(const SdrObject* pDrawObject);
+    void DelTextBox(SwFrameFormat* pTextBox);
 
     // 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(const SdrObject* pDrawObject);
-    void SetTextBoxActive(const SdrObject* pDrawObject);
-
     // If this is a group shape, that returns true.
-    bool IsGroupTextBox() const;
+    bool IsGroupTextBoxShape() 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(); };
+    size_t GetTextBoxCount() const { return m_pTextBoxTable.size(); };
+
+    // Gives a const reference to the text box table, useful for undo and 
grouping.
+    const std::unordered_map<const SdrObject*, SwFrameFormat*>& 
GetTextBoxTable() const
+    {
+        return m_pTextBoxTable;
+    };
 };
 
 #endif // INCLUDED_SW_INC_TEXTBOXHELPER_HXX
diff --git a/sw/inc/unomid.h b/sw/inc/unomid.h
index d249b32fc25a..5b6e8a0cb6cd 100644
--- a/sw/inc/unomid.h
+++ b/sw/inc/unomid.h
@@ -151,6 +151,10 @@
 // SwFormatFollowTextFlow
 #define MID_FOLLOW_TEXT_FLOW    0
 
+// TextBox
+#define MID_TEXTBOX             0
+#define MID_TEXTBOX_CONTENT     1
+
 #endif
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/inc/unoprnms.hxx b/sw/inc/unoprnms.hxx
index 7ef5d0a58cd7..1e16bc413d01 100644
--- a/sw/inc/unoprnms.hxx
+++ b/sw/inc/unoprnms.hxx
@@ -74,6 +74,7 @@
 #define UNO_NAME_FOOTER_RIGHT_MARGIN "FooterRightMargin"
 #define UNO_NAME_TEXT_RANGE "TextRange"
 #define UNO_NAME_TEXT_BOX "TextBox"
+#define UNO_NAME_TEXT_BOX_CONTENT "TextBoxContent"
 #define UNO_NAME_NAME "Name"
 #define UNO_NAME_CHAR_STYLE_NAME "CharStyleName"
 #define UNO_NAME_ANCHOR_CHAR_STYLE_NAME "AnchorCharStyleName"
diff --git a/sw/qa/uitest/data/ComplexGroupShapeTest.odt 
b/sw/qa/uitest/data/ComplexGroupShapeTest.odt
new file mode 100644
index 000000000000..8fe093203690
Binary files /dev/null and b/sw/qa/uitest/data/ComplexGroupShapeTest.odt differ
diff --git a/sw/qa/uitest/writer_tests4/ComplexGroupShapeTest.py 
b/sw/qa/uitest/writer_tests4/ComplexGroupShapeTest.py
new file mode 100644
index 000000000000..cdac088a32d7
--- /dev/null
+++ b/sw/qa/uitest/writer_tests4/ComplexGroupShapeTest.py
@@ -0,0 +1,124 @@
+# -*- 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
+from uitest.uihelper.common import get_state_as_dict
+from uitest.uihelper.common import select_pos
+from uitest.uihelper.common import get_url_for_data_file
+from libreoffice.uno.propertyvalue import mkPropertyValues
+import time
+
+class ComplexGroupShapeTest(UITestCase):
+    def test_ComplexGroupShape(self):
+        with 
self.ui_test.load_file(get_url_for_data_file("ComplexGroupShapeTest.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(1).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"}))
+
+            # add a textbox to this subshape
+            self.xUITest.executeCommand(".uno:AddTextBox")
+
+            # select the next shape in the group
+            xWriterEdit.executeAction("TYPE", mkPropertyValues({"KEYCODE": 
"TAB"}))
+
+            # add a textbox to this subshape
+            self.xUITest.executeCommand(".uno:AddTextBox")
+
+            # leave the groupshape
+            self.xUITest.executeCommand(".uno:LeaveGroup")
+
+            # select the other shape
+            self.xUITest.executeCommand(".uno:JumpToNextFrame")
+            self.ui_test.wait_until_child_is_available('metricfield')
+
+            # get the current selection
+            ShapeCollection = document.getCurrentSelection()
+
+            # extend the selection with the grouped shape
+            ShapeCollection.add(document.DrawPage.getByIndex(0))
+            ShapeCollection.add(document.DrawPage.getByIndex(1))
+
+            # select these shapes
+            document.getCurrentController().select(ShapeCollection)
+
+            # do ungroup
+            self.xUITest.executeCommand(".uno:FormatGroup")
+
+            # deselect
+            xWriterEdit.executeAction("TYPE", 
mkPropertyValues({"KEYCODE":"ESC"}))
+            time.sleep(0.1)
+
+            # select the group
+            self.xUITest.executeCommand(".uno:JumpToNextFrame")
+            self.ui_test.wait_until_child_is_available('metricfield')
+
+            # move it down
+            for i in range(1, 30):
+                xWriterEdit.executeAction("TYPE", 
mkPropertyValues({"KEYCODE":"DOWN"}))
+                time.sleep(0.1)
+
+            # select again
+            self.xUITest.executeCommand(".uno:JumpToNextFrame")
+            self.ui_test.wait_until_child_is_available('metricfield')
+
+            # do ungroup
+            self.xUITest.executeCommand(".uno:FormatUngroup")
+
+            # deselect everything
+            xWriterEdit.executeAction("TYPE", 
mkPropertyValues({"KEYCODE":"ESC"}))
+            time.sleep(0.1)
+
+            # select the first ex-group member shape
+            self.xUITest.executeCommand(".uno:JumpToNextFrame")
+            self.ui_test.wait_until_child_is_available('metricfield')
+            xWriterEdit.executeAction("TYPE", mkPropertyValues({"KEYCODE": 
"TAB"}))
+
+            # check if it is a textbox
+            
self.assertEqual(True,document.getCurrentSelection().getByIndex(0).TextBox)
+
+            # go to the other one
+            xWriterEdit.executeAction("TYPE", mkPropertyValues({"KEYCODE": 
"TAB"}))
+            xWriterEdit.executeAction("TYPE", mkPropertyValues({"KEYCODE": 
"TAB"}))
+
+            # this is still a group, so it cannot be a textbox
+            
self.assertEqual(False,document.getCurrentSelection().getByIndex(0).TextBox)
+
+            # do ungroup
+            self.xUITest.executeCommand(".uno:FormatUngroup")
+
+            # deselect
+            xWriterEdit.executeAction("TYPE", 
mkPropertyValues({"KEYCODE":"ESC"}))
+            time.sleep(0.1)
+
+            # select one shape of the last group
+            self.xUITest.executeCommand(".uno:JumpToNextFrame")
+            self.ui_test.wait_until_child_is_available('metricfield')
+            xWriterEdit.executeAction("TYPE", mkPropertyValues({"KEYCODE": 
"TAB"}))
+            xWriterEdit.executeAction("TYPE", mkPropertyValues({"KEYCODE": 
"TAB"}))
+            xWriterEdit.executeAction("TYPE", mkPropertyValues({"KEYCODE": 
"TAB"}))
+
+            # check if it is a textbox
+            
self.assertEqual(True,document.getCurrentSelection().getByIndex(0).TextBox)
+
+            # Without the fix in place, the following problems occurred during 
this test:
+            # - After the grouping old textbox frames detached from their 
shape before
+            # - Moving caused messed layout
+            # - After ungroup, the shapes in the embed group lost their 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 c67e9e05e9a6..ae49a77183c0 100644
--- a/sw/source/core/doc/DocumentLayoutManager.cxx
+++ b/sw/source/core/doc/DocumentLayoutManager.cxx
@@ -312,7 +312,39 @@ void DocumentLayoutManager::DelLayoutFormat( SwFrameFormat 
*pFormat )
     }
     m_rDoc.getIDocumentState().SetModified();
 }
-
+#if 0
+// TODO: Replace the textbox part of frameformat copying method to this.
+// Now disabled because cause asserts in sw (Node index is still registered in 
dtor).
+// Without this nested group textboxes are not handled correctly.
+static void lcl_CopyTextBoxes(SwDoc* pDoc, SwTextBoxNode* pSrcTextBoxes, const 
SdrObject* pSrcObj,
+                              SwTextBoxNode* pDestTextBoxes, SdrObject* 
pDestObj,
+                              bool bSetTextFlyAtt, bool bMakeFrames)
+{
+    auto pSrcChildren = pSrcObj->getChildrenOfSdrObject();
+    auto pDestChildren = pDestObj->getChildrenOfSdrObject();
+    if (pSrcChildren && pDestChildren)
+    {
+        if (pSrcChildren->GetObjCount() == pDestChildren->GetObjCount())
+            for (size_t i = 0; i < pSrcChildren->GetObjCount(); ++i)
+            {
+                lcl_CopyTextBoxes(pDoc, pSrcTextBoxes, 
pSrcChildren->GetObj(i), pDestTextBoxes,
+                                  pDestChildren->GetObj(i), bSetTextFlyAtt, 
bMakeFrames);
+            }
+    }
+    else
+    {
+        if (auto pSourceTextBoxFormat = pSrcTextBoxes->GetTextBox(pSrcObj))
+        {
+            assert(pSourceTextBoxFormat->GetAnchor().GetAnchorId() != 
RndStdIds::FLY_AS_CHAR);
+            auto pNewTextBoxFormat = 
pDoc->GetDocumentLayoutManager().CopyLayoutFormat(
+                *pSourceTextBoxFormat, pSourceTextBoxFormat->GetAnchor(), 
bSetTextFlyAtt,
+                bMakeFrames);
+            pDestTextBoxes->AddTextBox(pDestObj, pNewTextBoxFormat);
+            pNewTextBoxFormat->SetOtherTextBoxFormat(pDestTextBoxes);
+        }
+    }
+}
+#endif
 /** Copies the stated format (pSrc) to pDest and returns pDest.
 
     If there's no pDest, it is created.
@@ -469,7 +501,18 @@ SwFrameFormat *DocumentLayoutManager::CopyLayoutFormat(
         auto pObj = rSource.FindRealSdrObject();
         auto pTextBoxNd = new SwTextBoxNode(pDest);
         pDest->SetOtherTextBoxFormat(pTextBoxNd);
+#if 0
+        // TODO: Change the copy algorithm to this. See comment in 
lcl_CopyTextBoxes for details.
+        if (!bMakeFrames && rNewAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR)
+        {
+            // If the draw format is as-char, then it will be copied with 
bMakeFrames=false, but
+            // doing the same for the fly format would result in not making 
fly frames at all.
+            bMakeFrames = true;
+        }
 
+        lcl_CopyTextBoxes(&m_rDoc, rSource.GetOtherTextBoxFormat(), 
rSource.FindRealSdrObject(),
+                          pTextBoxNd, pDest->FindRealSdrObject(), 
bSetTextFlyAtt, bMakeFrames);
+#endif
         if (pObj)
         {
             const bool bIsGroupObj = pObj->getChildrenOfSdrObject();
diff --git a/sw/source/core/doc/docdraw.cxx b/sw/source/core/doc/docdraw.cxx
index 0aff4b8993ff..39adffb8b5a0 100644
--- a/sw/source/core/doc/docdraw.cxx
+++ b/sw/source/core/doc/docdraw.cxx
@@ -209,7 +209,7 @@ SwDrawContact* SwDoc::GroupSelection( SdrView& rDrawView )
             bGroupMembersNotPositioned = pAnchoredDrawObj->NotYetPositioned();
         }
 
-        std::vector<std::pair<SwFrameFormat*, SdrObject*>> vSavedTextBoxes;
+        std::unordered_map<const SdrObject*, SwFrameFormat*> vSavedTextBoxes;
         // Destroy ContactObjects and formats.
         for( size_t i = 0; i < rMrkList.GetMarkCount(); ++i )
         {
@@ -224,8 +224,13 @@ SwDrawContact* SwDoc::GroupSelection( SdrView& rDrawView )
                     "<SwDoc::GroupSelection(..)> - group members have 
different positioning status!" );
 #endif
             // Before the format will be killed, save its textbox for later 
use.
-            if (auto pTextBox = 
SwTextBoxHelper::getOtherTextBoxFormat(pContact->GetFormat(), RES_DRAWFRMFMT, 
pObj))
-                vSavedTextBoxes.push_back(std::pair<SwFrameFormat*, 
SdrObject*>(pTextBox, pObj));
+            if (auto pTxBxNd = pContact->GetFormat()->GetOtherTextBoxFormat())
+            {
+                for (auto& rElem : pTxBxNd->GetTextBoxTable())
+                {
+                    vSavedTextBoxes.emplace(rElem);
+                }
+            }
 
             pFormat = static_cast<SwDrawFrameFormat*>(pContact->GetFormat());
             // Deletes itself!
@@ -256,8 +261,8 @@ SwDrawContact* SwDoc::GroupSelection( SdrView& rDrawView )
         auto pTextBoxNode = new SwTextBoxNode(pFormat);
         for (auto& pTextBoxEntry : vSavedTextBoxes)
         {
-            pTextBoxNode->AddTextBox(pTextBoxEntry.second, 
pTextBoxEntry.first);
-            pTextBoxEntry.first->SetOtherTextBoxFormat(pTextBoxNode);
+            
pTextBoxNode->AddTextBox(const_cast<SdrObject*>(pTextBoxEntry.first), 
pTextBoxEntry.second);
+            pTextBoxEntry.second->SetOtherTextBoxFormat(pTextBoxNode);
         }
         pFormat->SetOtherTextBoxFormat(pTextBoxNode);
         vSavedTextBoxes.clear();
@@ -299,6 +304,27 @@ SwDrawContact* SwDoc::GroupSelection( SdrView& rDrawView )
     return pNewContact;
 }
 
+static void lcl_CollectTextBoxesForSubGroupObj(SwFrameFormat* pTargetFormat, 
SwTextBoxNode* pTextBoxNode,
+                                               SdrObject* pSourceObjs)
+{
+    if (auto pChildrenObjs = pSourceObjs->getChildrenOfSdrObject())
+        for (size_t i = 0; i < pChildrenObjs->GetObjCount(); ++i)
+            lcl_CollectTextBoxesForSubGroupObj(pTargetFormat, pTextBoxNode, 
pChildrenObjs->GetObj(i));
+    else
+    {
+        if (auto pTextBox = pTextBoxNode->GetTextBox(pSourceObjs))
+        {
+            if (!pTargetFormat->GetOtherTextBoxFormat())
+            {
+                pTargetFormat->SetOtherTextBoxFormat(new 
SwTextBoxNode(pTargetFormat));
+            }
+
+            pTargetFormat->GetOtherTextBoxFormat()->AddTextBox(pSourceObjs, 
pTextBox);
+            
pTextBox->SetOtherTextBoxFormat(pTargetFormat->GetOtherTextBoxFormat());
+        }
+    }
+}
+
 void SwDoc::UnGroupSelection( SdrView& rDrawView )
 {
     bool const bUndo = GetIDocumentUndoRedo().DoesUndo();
@@ -349,13 +375,22 @@ void SwDoc::UnGroupSelection( SdrView& rDrawView )
                         pFormat->SetFormatAttr( aAnch );
 
                         if (pTextBoxNode)
-                            if (auto pTextBoxFormat = 
pTextBoxNode->GetTextBox(pSubObj))
+                        {
+                            if (!pSubObj->getChildrenOfSdrObject())
+                            {
+                                if (auto pTextBoxFormat = 
pTextBoxNode->GetTextBox(pSubObj))
+                                {
+                                    auto pNewTextBoxNode = new 
SwTextBoxNode(pFormat);
+                                    pNewTextBoxNode->AddTextBox(pSubObj, 
pTextBoxFormat);
+                                    
pFormat->SetOtherTextBoxFormat(pNewTextBoxNode);
+                                    
pTextBoxFormat->SetOtherTextBoxFormat(pNewTextBoxNode);
+                                };
+                            }
+                            else
                             {
-                                auto pNewTextBoxNode = new 
SwTextBoxNode(pFormat);
-                                pNewTextBoxNode->AddTextBox(pSubObj, 
pTextBoxFormat);
-                                
pFormat->SetOtherTextBoxFormat(pNewTextBoxNode);
-                                
pTextBoxFormat->SetOtherTextBoxFormat(pNewTextBoxNode);
+                                lcl_CollectTextBoxesForSubGroupObj(pFormat, 
pTextBoxNode, pSubObj);
                             }
+                        }
 
                         // #i36010# - set layout direction of the position
                         pFormat->SetPositionLayoutDir(
diff --git a/sw/source/core/doc/docfly.cxx b/sw/source/core/doc/docfly.cxx
index 6b73f1f756b5..16365ebbbfec 100644
--- a/sw/source/core/doc/docfly.cxx
+++ b/sw/source/core/doc/docfly.cxx
@@ -577,8 +577,6 @@ bool SwDoc::SetFlyFrameAttr( SwFrameFormat& rFlyFormat, 
SfxItemSet& rSet )
 
     getIDocumentState().SetModified();
 
-    //SwTextBoxHelper::syncFlyFrameAttr(rFlyFormat, rSet);
-
     return bRet;
 }
 
@@ -920,12 +918,9 @@ bool SwDoc::ChgAnchor( const SdrMarkList& _rMrkList,
                     pNd->InsertItem( aFormat, aPos.nContent.GetIndex(), 0 );
 
                     // Has a textbox attached to the format? Sync it as well!
-                    if 
(SwTextBoxHelper::getOtherTextBoxFormat(pContact->GetFormat(),
-                                                               RES_DRAWFRMFMT))
-                    {
-                        
SwTextBoxHelper::syncFlyFrameAttr(*pContact->GetFormat(),
-                                                          
pContact->GetFormat()->GetAttrSet(), pObj);
-                    }
+                    SwTextBoxHelper::syncFlyFrameAttr(*pContact->GetFormat(),
+                                                      
pContact->GetFormat()->GetAttrSet(), pObj);
+
                 }
                 break;
             default:
diff --git a/sw/source/core/doc/textboxhelper.cxx 
b/sw/source/core/doc/textboxhelper.cxx
index 418c69927228..8bd107623b8a 100644
--- a/sw/source/core/doc/textboxhelper.cxx
+++ b/sw/source/core/doc/textboxhelper.cxx
@@ -187,7 +187,8 @@ void SwTextBoxHelper::create(SwFrameFormat* pShape, 
SdrObject* pObject, bool bCo
         syncProperty(pShape, RES_FRAMEDIR, 0, uno::makeAny(sal_Int16(eMode)), 
pObject);
 
     if (bIsGroupObj)
-        doTextBoxPositioning(pShape, pObject);
+        handleTextBoxGroup(
+            pShape, 
SwTextBoxHelper::GroupTextBoxActionType::POSITION_SIZE_AND_ANCHOR_CHANGE);
 
     // Check if the shape had text before and move it to the new textframe
     if (!bCopyText || sCopyableText.isEmpty())
@@ -207,18 +208,156 @@ void SwTextBoxHelper::create(SwFrameFormat* pShape, 
SdrObject* pObject, bool bCo
     }
 }
 
+void SwTextBoxHelper::set(SwFrameFormat* pShapeFormat, SdrObject* pObj,
+                          uno::Reference<text::XTextFrame> xNew)
+{
+    // Do not set invalid data
+    assert(pShapeFormat && pObj && xNew);
+    // Firstly find the format of the new textbox.
+    SwFrameFormat* pFormat = nullptr;
+    if (auto pTextFrame = dynamic_cast<SwXTextFrame*>(xNew.get()))
+        pFormat = pTextFrame->GetFrameFormat();
+    if (!pFormat)
+        return;
+    std::vector<std::pair<beans::Property, uno::Any>> aOldProps;
+    // If there is a format, check if the shape already has a textbox assigned 
to.
+    if (auto pTextBoxNode = pShapeFormat->GetOtherTextBoxFormat())
+    {
+        // If it has a texbox, destroy it.
+        if (pTextBoxNode->GetTextBox(pObj))
+        {
+            auto xOldFrame
+                = 
pObj->getUnoShape()->queryInterface(cppu::UnoType<text::XTextRange>::get());
+            if (xOldFrame.hasValue())
+            {
+                uno::Reference<beans::XPropertySet> xOldprops(xOldFrame, 
uno::UNO_QUERY);
+                uno::Reference<beans::XPropertyState> 
xOldPropStates(xOldFrame, uno::UNO_QUERY);
+                for (auto& rProp : 
xOldprops->getPropertySetInfo()->getProperties())
+                {
+                    try
+                    {
+                        if (xOldPropStates->getPropertyState(rProp.Name)
+                            == 
beans::PropertyState::PropertyState_DIRECT_VALUE)
+                            aOldProps.push_back(
+                                std::pair(rProp, 
xOldprops->getPropertyValue(rProp.Name)));
+                    }
+                    catch (...)
+                    {
+                    }
+                }
+            }
+            destroy(pShapeFormat, pObj);
+        }
+        // And set the new one.
+        pTextBoxNode->AddTextBox(pObj, pFormat);
+        pFormat->SetOtherTextBoxFormat(pTextBoxNode);
+    }
+    else
+    {
+        // If the shape do not have a texbox node and textbox,
+        // create that for the shape.
+        auto* pTextBox = new SwTextBoxNode(pShapeFormat);
+        pTextBox->AddTextBox(pObj, pFormat);
+        pShapeFormat->SetOtherTextBoxFormat(pTextBox);
+        pFormat->SetOtherTextBoxFormat(pTextBox);
+    }
+
+    // Initialize its properties
+    uno::Reference<beans::XPropertySet> xPropertySet(xNew, uno::UNO_QUERY);
+    uno::Any aEmptyBorder = uno::makeAny(table::BorderLine2());
+    xPropertySet->setPropertyValue(UNO_NAME_TOP_BORDER, aEmptyBorder);
+    xPropertySet->setPropertyValue(UNO_NAME_BOTTOM_BORDER, aEmptyBorder);
+    xPropertySet->setPropertyValue(UNO_NAME_LEFT_BORDER, aEmptyBorder);
+    xPropertySet->setPropertyValue(UNO_NAME_RIGHT_BORDER, aEmptyBorder);
+    xPropertySet->setPropertyValue(UNO_NAME_FILL_TRANSPARENCE, 
uno::makeAny(sal_Int32(100)));
+    xPropertySet->setPropertyValue(UNO_NAME_SIZE_TYPE, 
uno::makeAny(text::SizeType::FIX));
+    xPropertySet->setPropertyValue(UNO_NAME_SURROUND, 
uno::makeAny(text::WrapTextMode_THROUGH));
+    // Add a new name to it
+    uno::Reference<container::XNamed> xNamed(xNew, uno::UNO_QUERY);
+    xNamed->setName(pShapeFormat->GetDoc()->GetUniqueFrameName());
+    // And sync. properties.
+    uno::Reference<drawing::XShape> xShape(pObj->getUnoShape(), 
uno::UNO_QUERY);
+    syncProperty(pShapeFormat, RES_FRM_SIZE, MID_FRMSIZE_SIZE, 
uno::makeAny(xShape->getSize()),
+                 pObj);
+
+    uno::Reference<beans::XPropertySet> xShapePropertySet(xShape, 
uno::UNO_QUERY);
+    syncProperty(pShapeFormat, RES_ANCHOR, MID_ANCHOR_ANCHORTYPE,
+                 xShapePropertySet->getPropertyValue(UNO_NAME_ANCHOR_TYPE), 
pObj);
+    syncProperty(pShapeFormat, RES_HORI_ORIENT, MID_HORIORIENT_ORIENT,
+                 xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT), 
pObj);
+    syncProperty(pShapeFormat, RES_HORI_ORIENT, MID_HORIORIENT_RELATION,
+                 
xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT_RELATION), pObj);
+    syncProperty(pShapeFormat, RES_VERT_ORIENT, MID_VERTORIENT_ORIENT,
+                 xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT), 
pObj);
+    syncProperty(pShapeFormat, RES_VERT_ORIENT, MID_VERTORIENT_RELATION,
+                 
xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT_RELATION), pObj);
+    syncProperty(pShapeFormat, RES_HORI_ORIENT, MID_HORIORIENT_POSITION,
+                 
xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT_POSITION), pObj);
+    syncProperty(pShapeFormat, RES_VERT_ORIENT, MID_VERTORIENT_POSITION,
+                 
xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT_POSITION), pObj);
+    syncProperty(pShapeFormat, RES_FRM_SIZE, MID_FRMSIZE_IS_AUTO_HEIGHT,
+                 
xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_AUTOGROWHEIGHT), pObj);
+
+    drawing::TextVerticalAdjust aVertAdj = drawing::TextVerticalAdjust_CENTER;
+
+    if ((uno::Reference<beans::XPropertyState>(xShape, uno::UNO_QUERY_THROW))
+            ->getPropertyState(UNO_NAME_TEXT_VERT_ADJUST)
+        != beans::PropertyState::PropertyState_DEFAULT_VALUE)
+    {
+        aVertAdj = 
xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_VERT_ADJUST)
+                       .get<drawing::TextVerticalAdjust>();
+    }
+
+    xPropertySet->setPropertyValue(UNO_NAME_TEXT_VERT_ADJUST, 
uno::makeAny(aVertAdj));
+    text::WritingMode eMode;
+    if (xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_WRITINGMODE) >>= 
eMode)
+        syncProperty(pShapeFormat, RES_FRAMEDIR, 0, 
uno::makeAny(sal_Int16(eMode)), pObj);
+    if (aOldProps.size())
+    {
+        for (auto& rProp : aOldProps)
+        {
+            try
+            {
+                xPropertySet->setPropertyValue(rProp.first.Name, rProp.second);
+            }
+            catch (...)
+            {
+            }
+        }
+    }
+    if (pFormat->GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_PAGE
+        && pFormat->GetAnchor().GetPageNum() == 0)
+    {
+        pFormat->SetFormatAttr(SwFormatAnchor(RndStdIds::FLY_AT_PAGE, 1));
+    }
+    // Do sync for the new textframe.
+    handleTextBoxGroup(pShapeFormat, 
GroupTextBoxActionType::POSITION_SIZE_AND_ANCHOR_CHANGE);
+}
+
 void SwTextBoxHelper::destroy(const SwFrameFormat* pShape, const SdrObject* 
pObject)
 {
+    assert(pShape && pObject);
+
     // If a TextBox was enabled previously
-    auto pTextBox = pShape->GetOtherTextBoxFormat();
-    if (pTextBox && pTextBox->IsTextBoxActive(pObject))
+    auto pTextBoxNode = pShape->GetOtherTextBoxFormat();
+    if (!pTextBoxNode)
+    {
+        SAL_WARN("sw.core", "SwTextBoxHelper::destroy: No TextBoxNode!");
+        return;
+    }
+
+    if (auto pFormat = pTextBoxNode->GetTextBox(pObject))
     {
         // Unlink the TextBox's text range from the original shape.
-        pTextBox->SetTextBoxInactive(pObject);
+        pTextBoxNode->DelTextBox(pObject);
+        pFormat->SetOtherTextBoxFormat(nullptr);
 
         // Delete the associated TextFrame.
-        pTextBox->DelTextBox(pObject);
+        pFormat->GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat(pFormat);
+        return;
     }
+
+    SAL_WARN("sw.core", "SwTextBoxHelper::destroy: No TextBox to destroy!");
 }
 
 bool SwTextBoxHelper::isTextBox(const SwFrameFormat* pFormat, sal_uInt16 nType,
@@ -233,6 +372,9 @@ bool SwTextBoxHelper::isTextBox(const SwFrameFormat* 
pFormat, sal_uInt16 nType,
     if (!pTextBox)
         return false;
 
+    if (!pTextBox->GetTextBoxCount())
+        return false;
+
     if (nType == RES_DRAWFRMFMT)
     {
         if (pObject)
@@ -333,6 +475,12 @@ sal_Int32 SwTextBoxHelper::getOrdNum(const SdrObject* 
pObject)
 
 void SwTextBoxHelper::getShapeWrapThrough(const SwFrameFormat* pTextBox, bool& 
rWrapThrough)
 {
+    if (!pTextBox->GetOtherTextBoxFormat())
+    {
+        SAL_WARN("sw.core", "SwTextBoxHelper::getShapeWrapThrough: No 
TextBoxNode!");
+        return;
+    }
+
     SwFrameFormat* pShape = SwTextBoxHelper::getOtherTextBoxFormat(pTextBox, 
RES_FLYFRMFMT);
     if (pShape)
         rWrapThrough = pShape->GetSurround().GetSurround() == 
css::text::WrapTextMode_THROUGH;
@@ -367,7 +515,8 @@ SwFrameFormat* 
SwTextBoxHelper::getOtherTextBoxFormat(uno::Reference<drawing::XS
         return nullptr;
 
     SwFrameFormat* pFormat = pShape->GetFrameFormat();
-    return getOtherTextBoxFormat(pFormat, RES_DRAWFRMFMT);
+    return getOtherTextBoxFormat(pFormat, RES_DRAWFRMFMT,
+                                 SdrObject::getSdrObjectFromXShape(xShape));
 }
 
 uno::Reference<text::XTextFrame>
@@ -388,33 +537,49 @@ 
SwTextBoxHelper::getUnoTextFrame(uno::Reference<drawing::XShape> const& xShape)
     return {};
 }
 
-template <typename T> static void lcl_queryInterface(const SwFrameFormat* 
pShape, uno::Any& rAny)
+uno::Any SwTextBoxHelper::queryInterface(const SwFrameFormat* pShape, const 
uno::Type& rType,
+                                         SdrObject* pObj)
 {
-    if (SwFrameFormat* pFormat = 
SwTextBoxHelper::getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT))
+    if (!pShape)
+        return {};
+
+    if (!pShape->GetOtherTextBoxFormat())
     {
-        uno::Reference<T> const xInterface(
-            SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat), 
uno::UNO_QUERY);
-        rAny <<= xInterface;
+        SAL_WARN("sw.core", "SwTextBoxHelper::queryInterface: No 
TextBoxNode!");
+        return {};
+    }
+
+    SwFrameFormat* pFormat = SwTextBoxHelper::getOtherTextBoxFormat(pShape, 
RES_DRAWFRMFMT, pObj);
+
+    if (!pFormat)
+    {
+        SAL_WARN("sw.core", "SwTextBoxHelper::queryInterface: No TextBox!");
+        return {};
     }
-}
 
-uno::Any SwTextBoxHelper::queryInterface(const SwFrameFormat* pShape, const 
uno::Type& rType)
-{
     uno::Any aRet;
 
     if (rType == cppu::UnoType<css::text::XTextAppend>::get())
     {
-        lcl_queryInterface<text::XTextAppend>(pShape, aRet);
+        uno::Reference<css::text::XTextAppend> const xInterface(
+            SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat), 
uno::UNO_QUERY_THROW);
+        aRet <<= xInterface;
     }
     else if (rType == cppu::UnoType<css::text::XText>::get())
     {
-        lcl_queryInterface<text::XText>(pShape, aRet);
+        uno::Reference<css::text::XText> const xInterface(
+            SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat), 
uno::UNO_QUERY_THROW);
+        aRet <<= xInterface;
     }
     else if (rType == cppu::UnoType<css::text::XTextRange>::get())
     {
-        lcl_queryInterface<text::XTextRange>(pShape, aRet);
+        uno::Reference<css::text::XTextRange> const xInterface(
+            SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat), 
uno::UNO_QUERY_THROW);
+        aRet <<= xInterface;
     }
 
+    assert(aRet.hasValue());
+
     return aRet;
 }
 
@@ -908,6 +1073,51 @@ void SwTextBoxHelper::restoreLinks(std::set<ZSortFly>& 
rOld, std::vector<SwFrame
     }
 }
 
+void SwTextBoxHelper::handleTextBoxGroup(SwFrameFormat* pGroupShapeFormat,
+                                         GroupTextBoxActionType eActionType,
+                                         SdrObject* pGroupObject)
+{
+    if (!pGroupShapeFormat)
+        return;
+
+    SdrObject* pMasterObj = nullptr;
+    if (!pGroupObject)
+        pMasterObj = pGroupShapeFormat->FindRealSdrObject();
+    else
+        pMasterObj = pGroupObject;
+
+    if (auto pChildrenObjs = pMasterObj->getChildrenOfSdrObject())
+    {
+        for (size_t i = 0; i < pChildrenObjs->GetObjCount(); ++i)
+            handleTextBoxGroup(pGroupShapeFormat, eActionType, 
pChildrenObjs->GetObj(i));
+    }
+    else
+    {
+        switch (eActionType)
+        {
+            case GroupTextBoxActionType::POSITION_SIZE_AND_ANCHOR_CHANGE:
+            {
+                changeAnchor(pGroupShapeFormat, pMasterObj);
+                setTextBoxSize(pGroupShapeFormat, pMasterObj);
+                break;
+            }
+            case GroupTextBoxActionType::Z_ORDER_CHANGE:
+            {
+                DoTextBoxZOrderCorrection(pGroupShapeFormat, pMasterObj);
+                break;
+            }
+            case GroupTextBoxActionType::DELETE:
+            {
+                destroy(pGroupShapeFormat, pMasterObj);
+                break;
+            }
+            default:
+                SAL_WARN("sw.core", "SwTextBoxHelper::handleTextBoxGroup: 
Unknown Action!");
+                break;
+        }
+    }
+}
+
 text::TextContentAnchorType SwTextBoxHelper::mapAnchorType(const RndStdIds& 
rAnchorID)
 {
     text::TextContentAnchorType aAnchorType;
@@ -1066,7 +1276,6 @@ void SwTextBoxHelper::syncFlyFrameAttr(SwFrameFormat& 
rShape, SfxItemSet const&
 
     if (aTextBoxSet.Count())
         pFormat->SetFormatAttr(aTextBoxSet);
-    //pFormat->GetDoc()->SetFlyFrameAttr(*pFormat, aTextBoxSet);
 
     DoTextBoxZOrderCorrection(&rShape, pObj);
 }
@@ -1114,12 +1323,11 @@ void SwTextBoxHelper::updateTextBoxMargin(SdrObject* 
pObj)
     DoTextBoxZOrderCorrection(pParentFormat, pObj);
 }
 
-bool SwTextBoxHelper::setWrapThrough(SwFrameFormat* pShape)
+bool SwTextBoxHelper::setWrapThrough(SwFrameFormat* pShape, SdrObject* pObj)
 {
-    OUString sErrMsg;
-    if (isTextBoxShapeHasValidTextFrame(pShape))
+    if (isTextBoxShapeHasValidTextFrame(pShape, pObj))
     {
-        if (auto pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT))
+        if (auto pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj))
         {
             ::sw::UndoGuard const 
UndoGuard(pShape->GetDoc()->GetIDocumentUndoRedo());
             if (auto xFrame = 
SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat))
@@ -1132,53 +1340,77 @@ bool SwTextBoxHelper::setWrapThrough(SwFrameFormat* 
pShape)
                 }
                 catch (uno::Exception& e)
                 {
-                    sErrMsg = "Exception caught: " + e.Message;
+                    SAL_WARN("sw.core",
+                             "SwTextBoxHelper::setWrapThrough: Exception 
caught: " << e.Message);
                 }
             else
-                sErrMsg = "No XTextFrame!";
+                SAL_WARN("sw.core", "SwTextBoxHelper::setWrapThrough: No 
XTextFrame!");
         }
         else
-            sErrMsg = "No Other TextBox Format!";
+            SAL_WARN("sw.core", "SwTextBoxHelper::setWrapThrough: No Other 
TextBox Format!");
     }
     else
-        sErrMsg = "Not a Valid TextBox object!";
+        SAL_WARN("sw.core", "SwTextBoxHelper::setWrapThrough: Not a Valid 
TextBox object!");
 
-    SAL_WARN("sw.core", "SwTextBoxHelper::setWrapThrough: " << sErrMsg);
     return false;
 }
 
 bool SwTextBoxHelper::changeAnchor(SwFrameFormat* pShape, SdrObject* pObj)
 {
+    // Get the desired textbox from the shape
     if (auto pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj))
     {
+        // Collect the anchors
         const SwFormatAnchor& rOldAnch = pFormat->GetAnchor();
         const SwFormatAnchor& rNewAnch = pShape->GetAnchor();
 
+        // Collect the anchor positions (in the text)
         const auto pOldCnt = rOldAnch.GetContentAnchor();
         const auto pNewCnt = rNewAnch.GetContentAnchor();
 
+        // Collect the relative anchor points
         const uno::Any aShapeHorRelOrient
             = uno::makeAny(pShape->GetHoriOrient().GetRelationOrient());
+        const uno::Any aShapeVertRelOrient
+            = uno::makeAny(pShape->GetVertOrient().GetRelationOrient());
 
-        if (isAnchorTypeDifferent(pShape) || (pObj && pObj != 
pShape->FindRealSdrObject()))
+        // Get the new page number. If this is 0 incrase it to 1!
+        // (0 is invalid page)
+        const sal_uInt16 nPageNum = rNewAnch.GetPageNum() ? 
rNewAnch.GetPageNum() : 1;
+
+        // If anchor is different, sync needed
+        if (isAnchorTypeDifferent(pShape, pObj) || (pObj && pObj != 
pShape->FindRealSdrObject()))
         {
             try
             {
+                // Disable undo for the sync
                 ::sw::UndoGuard const 
UndoGuard(pShape->GetDoc()->GetIDocumentUndoRedo());
+                // Get the properties of the textframe
                 uno::Reference<beans::XPropertySet> const xPropertySet(
                     SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), 
pFormat), uno::UNO_QUERY);
+
+                // Anchoring situations:
+
+                // First:
+                // the old anchor is in the text (namely inline or 
at_char/para) and,
+                // the new one will be at_page
                 if (pOldCnt && rNewAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE
                     && rNewAnch.GetPageNum())
                 {
                     uno::Any aValue(text::TextContentAnchorType_AT_PAGE);
                     
xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION,
                                                    aShapeHorRelOrient);
+                    
xPropertySet->setPropertyValue(UNO_NAME_VERT_ORIENT_RELATION,
+                                                   aShapeVertRelOrient);
                     xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_TYPE, 
aValue);
-                    xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_PAGE_NO,
-                                                   
uno::Any(rNewAnch.GetPageNum()));
+                    xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_PAGE_NO, 
uno::Any(nPageNum));
                 }
+                // Second:
+                // The reverse of the previous one: Old one at page, new 
at-text.
                 else if (rOldAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE && 
pNewCnt)
                 {
+                    // If the new at-text anchor is inline, the textbox can 
not be inline too,
+                    // so change it to at_char and make its position according 
to the shape.
                     if (rNewAnch.GetAnchorId() == RndStdIds::FLY_AS_CHAR)
                     {
                         uno::Any 
aValue(text::TextContentAnchorType_AT_CHARACTER);
@@ -1191,17 +1423,28 @@ bool SwTextBoxHelper::changeAnchor(SwFrameFormat* 
pShape, SdrObject* pObj)
                         aPos.SetAnchor(pNewCnt);
                         pFormat->SetFormatAttr(aPos);
                     }
+                    // Else, copy the anchor of the shape to the frame. 
Correct pagenum if needed.
                     else
                     {
                         uno::Any aValue(mapAnchorType(rNewAnch.GetAnchorId()));
                         
xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION,
                                                        aShapeHorRelOrient);
+                        
xPropertySet->setPropertyValue(UNO_NAME_VERT_ORIENT_RELATION,
+                                                       aShapeVertRelOrient);
                         xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_TYPE, 
aValue);
+                        if (rNewAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE
+                            && !rNewAnch.GetPageNum())
+                            
xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_PAGE_NO,
+                                                           
uno::Any(sal_uInt16(1)));
+
                         pFormat->SetFormatAttr(rNewAnch);
                     }
                 }
+                // Third:
+                // At-text anchor changes to at-text. (Only the type changes)
                 else
                 {
+                    // Special treatment for inline case.
                     if (rNewAnch.GetAnchorId() == RndStdIds::FLY_AS_CHAR)
                     {
                         uno::Any 
aValue(text::TextContentAnchorType_AT_CHARACTER);
@@ -1214,10 +1457,19 @@ bool SwTextBoxHelper::changeAnchor(SwFrameFormat* 
pShape, SdrObject* pObj)
                         aPos.SetAnchor(pNewCnt);
                         pFormat->SetFormatAttr(aPos);
                     }
+                    // Else, just copy the anchor.
                     else
                     {
                         
xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION,
                                                        aShapeHorRelOrient);
+                        
xPropertySet->setPropertyValue(UNO_NAME_VERT_ORIENT_RELATION,
+                                                       aShapeVertRelOrient);
+
+                        if (rNewAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE
+                            && !rNewAnch.GetPageNum())
+                            
xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_PAGE_NO,
+                                                           
uno::Any(sal_uInt16(1)));
+
                         pFormat->SetFormatAttr(pShape->GetAnchor());
                     }
                 }
@@ -1253,6 +1505,12 @@ bool 
SwTextBoxHelper::doTextBoxPositioning(SwFrameFormat* pShape, SdrObject* pOb
             SwFormatVertOrient aNewVOri(pFormat->GetVertOrient());
             aNewVOri.SetPos(aRect.Top() + pShape->GetVertOrient().GetPos());
 
+            if (bIsGroupObj)
+            {
+                aNewHOri.SetPos(aNewHOri.GetPos() + 
pObj->GetRelativePos().getX());
+                aNewVOri.SetPos(aNewVOri.GetPos() + 
pObj->GetRelativePos().getY());
+            }
+
             // tdf#140598: Do not apply wrong rectangle position.
             if (aRect.TopLeft() != Point(0, 0))
             {
@@ -1272,11 +1530,11 @@ bool 
SwTextBoxHelper::doTextBoxPositioning(SwFrameFormat* pShape, SdrObject* pOb
             {
                 SwFormatHoriOrient aNewHOri(pShape->GetHoriOrient());
                 aNewHOri.SetPos(
-                    (bIsGroupObj && pObj ? pObj->GetRelativePos().getX() : 
aNewHOri.GetPos())
+                    ((bIsGroupObj && pObj) ? pObj->GetRelativePos().getX() : 
aNewHOri.GetPos())
                     + aRect.Left());
                 SwFormatVertOrient aNewVOri(pShape->GetVertOrient());
                 aNewVOri.SetPos(
-                    (bIsGroupObj && pObj ? pObj->GetRelativePos().getY() : 
aNewVOri.GetPos())
+                    ((bIsGroupObj && pObj) ? pObj->GetRelativePos().getY() : 
aNewVOri.GetPos())
                     + aRect.Top());
 
                 pFormat->SetFormatAttr(aNewHOri);
@@ -1291,12 +1549,31 @@ bool 
SwTextBoxHelper::doTextBoxPositioning(SwFrameFormat* pShape, SdrObject* pOb
     return false;
 }
 
-std::optional<bool> SwTextBoxHelper::isAnchorTypeDifferent(const 
SwFrameFormat* pShape)
+bool SwTextBoxHelper::setTextBoxSize(const SwFrameFormat* pShape, SdrObject* 
pObj)
+{
+    if (!pShape || !pObj)
+        return false;
+
+    if (auto pTBox = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj))
+    {
+        const auto& rSize = getTextRectangle(pObj, false).GetSize();
+        if (!rSize.IsEmpty())
+        {
+            SwFormatFrameSize aSizeFormatItem = pTBox->GetFrameSize();
+            aSizeFormatItem.SetSize(rSize);
+            return pTBox->SetFormatAttr(aSizeFormatItem);
+        }
+    }
+    return false;
+}
+
+std::optional<bool> SwTextBoxHelper::isAnchorTypeDifferent(const 
SwFrameFormat* pShape,
+                                                           SdrObject* pObj)
 {
     std::optional<bool> bRet;
-    if (isTextBoxShapeHasValidTextFrame(pShape))
+    if (isTextBoxShapeHasValidTextFrame(pShape, pObj))
     {
-        if (auto pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT))
+        if (auto pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj))
         {
             if (pShape->GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR)
                 bRet = (pFormat->GetAnchor().GetAnchorId() != 
RndStdIds::FLY_AT_CHAR
@@ -1308,20 +1585,21 @@ std::optional<bool> 
SwTextBoxHelper::isAnchorTypeDifferent(const SwFrameFormat*
     return bRet;
 }
 
-bool SwTextBoxHelper::isTextBoxShapeHasValidTextFrame(const SwFrameFormat* 
pShape)
+bool SwTextBoxHelper::isTextBoxShapeHasValidTextFrame(const SwFrameFormat* 
pShape, SdrObject* pObj)
 {
     if (pShape && pShape->Which() == RES_DRAWFRMFMT)
-        if (auto pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT))
+        if (auto pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj))
             if (pFormat && pFormat->Which() == RES_FLYFRMFMT)
                 return true;
             else
-                SAL_WARN("sw.core", 
"SwTextBoxHelper::isTextBoxShapeHasValidTextFrame: "
-                                    "Shape does not have valid textframe!");
+                SAL_WARN("sw.core", 
"SwTextBoxHelper::isTextBoxShapeHasValidTextFrame: Shape does "
+                                    "not have valid textframe!");
         else
-            SAL_WARN("sw.core", 
"SwTextBoxHelper::isTextBoxShapeHasValidTextFrame: "
-                                "Shape does not have associated frame!");
+            SAL_WARN("sw.core", 
"SwTextBoxHelper::isTextBoxShapeHasValidTextFrame: Shape does not "
+                                "have associated frame!");
     else
-        SAL_WARN("sw.core", "SwTextBoxHelper::isTextBoxShapeHasValidTextFrame: 
Not valid shape!");
+        SAL_WARN("sw.core", "SwTextBoxHelper::isTextBoxShapeHasValidTextFrame: 
No valid shape");
+
     return false;
 }
 
@@ -1329,9 +1607,6 @@ bool 
SwTextBoxHelper::DoTextBoxZOrderCorrection(SwFrameFormat* pShape, const Sdr
 {
     // TODO: do this with group shape textboxes.
     SdrObject* pShpObj = nullptr;
-    //if (pObj)
-    //    pShpObj = pObj;
-    //else
     pShpObj = pShape->FindRealSdrObject();
 
     if (pShpObj)
@@ -1365,39 +1640,53 @@ bool 
SwTextBoxHelper::DoTextBoxZOrderCorrection(SwFrameFormat* pShape, const Sdr
                             break;
                         ++nIterator;
                         if (nIterator > 300)
+                        {
                             break; // Do not run to infinity
+                        }
                     }
                 pPage->RecalcObjOrdNums();
                 return true; // Success
             }
-            SAL_WARN("sw.core", "SwTextBoxHelper::DoTextBoxZOrderCorrection(): 
"
-                                "No Valid Draw model for SdrObject for the 
shape!");
+            SAL_WARN("sw.core", "SwTextBoxHelper::DoTextBoxZOrderCorrection(): 
No Valid Draw model "
+                                "for SdrObject for the shape!");
         }
-        SAL_WARN("sw.core", "SwTextBoxHelper::DoTextBoxZOrderCorrection(): "
-                            "No Valid SdrObject for the frame!");
+        SAL_WARN("sw.core",
+                 "SwTextBoxHelper::DoTextBoxZOrderCorrection(): No Valid 
SdrObject for the frame!");
     }
-    SAL_WARN("sw.core", "SwTextBoxHelper::DoTextBoxZOrderCorrection(): "
-                        "No Valid SdrObject for the shape!");
+    SAL_WARN("sw.core",
+             "SwTextBoxHelper::DoTextBoxZOrderCorrection(): No Valid SdrObject 
for the shape!");
 
     return false;
 }
 
+// SwTextBoxNode class:
+
 SwTextBoxNode::SwTextBoxNode(SwFrameFormat* pOwnerShape)
 {
     assert(pOwnerShape);
     assert(pOwnerShape->Which() == RES_DRAWFRMFMT);
 
     m_pOwnerShapeFormat = pOwnerShape;
-    if (!m_pTextBoxes.empty())
-        m_pTextBoxes.clear();
+    if (!m_pTextBoxTable.empty())
+        m_pTextBoxTable.clear();
 }
 
 SwTextBoxNode::~SwTextBoxNode()
 {
-    m_pTextBoxes.clear();
+    if (m_pTextBoxTable.size())
+    {
+        for (auto& rTextBox : m_pTextBoxTable)
+        {
+            rTextBox.second->SetOtherTextBoxFormat(nullptr);
+            rTextBox.second = nullptr;
+        }
+        m_pTextBoxTable.clear();
+    }
 
     if (m_pOwnerShapeFormat && m_pOwnerShapeFormat->GetOtherTextBoxFormat())
         m_pOwnerShapeFormat->SetOtherTextBoxFormat(nullptr);
+
+    m_pOwnerShapeFormat = nullptr;
 }
 
 void SwTextBoxNode::AddTextBox(SdrObject* pDrawObject, SwFrameFormat* 
pNewTextBox)
@@ -1407,102 +1696,63 @@ void SwTextBoxNode::AddTextBox(SdrObject* pDrawObject, 
SwFrameFormat* pNewTextBo
 
     assert(pDrawObject);
 
-    SwTextBoxElement aElem;
-    aElem.m_bIsActive = true;
-    aElem.m_pDrawObject = pDrawObject;
-    aElem.m_pTextBoxFormat = pNewTextBox;
+    if (!m_pTextBoxTable.count(pDrawObject))
+    {
+        m_pTextBoxTable.emplace(pDrawObject, pNewTextBox);
+    }
+    else
+    {
+        m_pTextBoxTable[pDrawObject] = pNewTextBox;
+    }
     auto pSwFlyDraw = dynamic_cast<SwFlyDrawObj*>(pDrawObject);
     if (pSwFlyDraw)
     {
         pSwFlyDraw->SetTextBox(true);
     }
-    m_pTextBoxes.push_back(aElem);
 }
 
 void SwTextBoxNode::DelTextBox(const SdrObject* pDrawObject)
 {
     assert(pDrawObject);
-    if (m_pTextBoxes.empty())
-        return;
-
-    for (auto it = m_pTextBoxes.begin(); it != m_pTextBoxes.end();)
-    {
-        if (it->m_pDrawObject == pDrawObject)
-        {
-            
m_pOwnerShapeFormat->GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat(
-                it->m_pTextBoxFormat);
-            it = m_pTextBoxes.erase(it);
-            break;
-        }
-        ++it;
-    }
-}
+    auto pObj = const_cast<SdrObject*>(pDrawObject);
 
-SwFrameFormat* SwTextBoxNode::GetTextBox(const SdrObject* pDrawObject) const
-{
-    assert(pDrawObject);
-    if (!m_pTextBoxes.empty())
-    {
-        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_pTextBoxTable.empty())
+        return;
 
-    if (!m_pTextBoxes.empty())
+    if (m_pTextBoxTable.count(pObj))
     {
-        for (auto it = m_pTextBoxes.begin(); it != m_pTextBoxes.end(); it++)
-        {
-            if (it->m_pDrawObject == pDrawObject)
-            {
-                return it->m_bIsActive;
-            }
-        }
+        m_pTextBoxTable[pObj]->SetOtherTextBoxFormat(nullptr);
+        m_pTextBoxTable.erase(pObj);
     }
-    return false;
 }
 
-void SwTextBoxNode::SetTextBoxActive(const SdrObject* pDrawObject)
+void SwTextBoxNode::DelTextBox(SwFrameFormat* pTextBox)
 {
-    assert(pDrawObject);
+    if (!pTextBox || m_pTextBoxTable.empty())
+        return;
 
-    if (!m_pTextBoxes.empty())
-    {
-        for (auto it = m_pTextBoxes.begin(); it != m_pTextBoxes.end(); it++)
+    for (auto it = m_pTextBoxTable.begin(); it != m_pTextBoxTable.end(); ++it)
+        if (it->second == pTextBox)
         {
-            if (it->m_pDrawObject == pDrawObject)
-            {
-                it->m_bIsActive = true;
-            }
+            it->second->SetOtherTextBoxFormat(nullptr);
+            m_pTextBoxTable.erase(it->first);
+            break;
         }
-    }
 }
 
-void SwTextBoxNode::SetTextBoxInactive(const SdrObject* pDrawObject)
+SwFrameFormat* SwTextBoxNode::GetTextBox(const SdrObject* pDrawObject) const
 {
     assert(pDrawObject);
-
-    if (!m_pTextBoxes.empty())
+    if (!m_pTextBoxTable.empty())
     {
-        for (auto it = m_pTextBoxes.begin(); it != m_pTextBoxes.end(); it++)
+        if (m_pTextBoxTable.count(pDrawObject))
         {
-            if (it->m_pDrawObject == pDrawObject)
-            {
-                it->m_bIsActive = false;
-            }
+            return m_pTextBoxTable.at(pDrawObject);
         }
     }
+    return nullptr;
 }
 
-bool SwTextBoxNode::IsGroupTextBox() const { return m_pTextBoxes.size() > 1; }
+bool SwTextBoxNode::IsGroupTextBoxShape() const { return 
m_pTextBoxTable.size() > 1; }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/draw/dcontact.cxx b/sw/source/core/draw/dcontact.cxx
index cb9618c44e8b..f8aefc6acdd6 100644
--- a/sw/source/core/draw/dcontact.cxx
+++ b/sw/source/core/draw/dcontact.cxx
@@ -1250,10 +1250,9 @@ void SwDrawContact::Changed_( const SdrObject& rObj,
                     // use geometry of drawing object
                     aObjRect = pGroupObj->GetSnapRect();
 
-                    for (size_t i = 0; i < 
pGroupObj->getChildrenOfSdrObject()->GetObjCount(); ++i )
-                    {
-                        SwTextBoxHelper::doTextBoxPositioning(GetFormat(), 
pGroupObj->getChildrenOfSdrObject()->GetObj(i));
-                    }
+                    SwTextBoxHelper::handleTextBoxGroup(
+                        GetFormat(),
+                        
SwTextBoxHelper::GroupTextBoxActionType::POSITION_SIZE_AND_ANCHOR_CHANGE);
 
                 }
                 SwTwips nXPosDiff(0);
@@ -1370,7 +1369,9 @@ void SwDrawContact::Changed_( const SdrObject& rObj,
                     aSet.Put(aSyncSet);
                     aSet.Put(pSdrObj->GetMergedItem(RES_FRM_SIZE));
                     SwTextBoxHelper::syncFlyFrameAttr(*GetFormat(), aSet, 
pSdrObj);
-                    SwTextBoxHelper::changeAnchor(GetFormat(), pSdrObj);
+                    SwTextBoxHelper::handleTextBoxGroup(
+                        GetFormat(),
+                        
SwTextBoxHelper::GroupTextBoxActionType::POSITION_SIZE_AND_ANCHOR_CHANGE);
                 }
                 else
                     SwTextBoxHelper::syncFlyFrameAttr(*GetFormat(), aSyncSet, 
GetFormat()->FindRealSdrObject());
diff --git a/sw/source/core/draw/dview.cxx b/sw/source/core/draw/dview.cxx
index 510addf10a9c..6f7251698af8 100644
--- a/sw/source/core/draw/dview.cxx
+++ b/sw/source/core/draw/dview.cxx
@@ -967,12 +967,10 @@ void SwDrawView::DeleteMarked()
         SdrObject *pObject = rMarkList.GetMark(i)->GetMarkedSdrObj();
         SwContact* pContact = GetUserCall(pObject);
         SwFrameFormat* pFormat = pContact->GetFormat();
-        if (auto pChildren = pObject->getChildrenOfSdrObject())
+        if (pObject->getChildrenOfSdrObject())
         {
-            for (size_t it = 0; it < pChildren->GetObjCount(); ++it)
-                if (SwFrameFormat* pTextBox = 
SwTextBoxHelper::getOtherTextBoxFormat(
-                        pFormat, RES_DRAWFRMFMT, pChildren->GetObj(it)))
-                    aTextBoxesToDelete.push_back(pTextBox);
+            SwTextBoxHelper::handleTextBoxGroup(pFormat,
+                                                
SwTextBoxHelper::GroupTextBoxActionType::DELETE);
         }
         else
             if (SwFrameFormat* pTextBox = 
SwTextBoxHelper::getOtherTextBoxFormat(pFormat, RES_DRAWFRMFMT))
diff --git a/sw/source/core/frmedt/feshview.cxx 
b/sw/source/core/frmedt/feshview.cxx
index 1e3c8f2ff150..893926456d10 100644
--- a/sw/source/core/frmedt/feshview.cxx
+++ b/sw/source/core/frmedt/feshview.cxx
@@ -1064,7 +1064,7 @@ void SwFEShell::SelectionToTop( bool bTop )
             if (auto pFormat = FindFrameFormat(pObj))
             {
                 // If it has not textframe skip...
-                if (!SwTextBoxHelper::isTextBoxShapeHasValidTextFrame(pFormat))
+                if (!SwTextBoxHelper::isTextBoxShapeHasValidTextFrame(pFormat, 
pObj))
                     continue;
                 // If it has a textframe so it is a textbox, get its page
                 if (auto pDrwModel
@@ -1133,7 +1133,7 @@ void SwFEShell::SelectionToBottom( bool bBottom )
             if (auto pFormat = FindFrameFormat(pObj))
             {
                 // If the shape has not textframes skip.
-                if (!SwTextBoxHelper::isTextBoxShapeHasValidTextFrame(pFormat))
+                if (!SwTextBoxHelper::isTextBoxShapeHasValidTextFrame(pFormat, 
pObj))
                     continue;
                 // If has, move the shape to correct level with...
                 if (auto pDrwModel
diff --git a/sw/source/core/layout/atrfrm.cxx b/sw/source/core/layout/atrfrm.cxx
index 4791fda80169..9a2fb39ba680 100644
--- a/sw/source/core/layout/atrfrm.cxx
+++ b/sw/source/core/layout/atrfrm.cxx
@@ -2550,12 +2550,11 @@ SwFrameFormat::~SwFrameFormat()
     if( nullptr == m_pOtherTextBoxFormat )
         return;
 
-    auto pObj = FindRealSdrObject();
-    if (Which() == RES_FLYFRMFMT && pObj)
+    if (Which() == RES_FLYFRMFMT)
     {
         // This is a fly-frame-format just delete this
         // textbox entry from the draw-frame-format.
-        m_pOtherTextBoxFormat->DelTextBox(pObj);
+        m_pOtherTextBoxFormat->DelTextBox(this);
     }
 
     if (Which() == RES_DRAWFRMFMT)
diff --git a/sw/source/core/unocore/unodraw.cxx 
b/sw/source/core/unocore/unodraw.cxx
index 4d04857e7199..cbb0266c0af6 100644
--- a/sw/source/core/unocore/unodraw.cxx
+++ b/sw/source/core/unocore/unodraw.cxx
@@ -963,7 +963,13 @@ SwXShape::~SwXShape()
 
 uno::Any SwXShape::queryInterface( const uno::Type& aType )
 {
-    uno::Any aRet = SwTextBoxHelper::queryInterface(GetFrameFormat(), aType);
+    uno::Any aRet;
+    if (aType == cppu::UnoType<text::XText>::get()
+        || aType == cppu::UnoType<text::XTextAppend>::get()
+        || aType == cppu::UnoType<text::XTextRange>::get())
+        aRet = SwTextBoxHelper::queryInterface(GetFrameFormat(), aType,
+            SdrObject::getSdrObjectFromXShape(mxShape));
+
     if (aRet.hasValue())
         return aRet;
 
@@ -1151,18 +1157,28 @@ void SwXShape::setPropertyValue(const OUString& 
rPropertyName, const uno::Any& a
             }
             else if (pEntry->nWID == FN_TEXT_BOX)
             {
-                bool bValue(false);
-                aValue >>= bValue;
-                if (bValue)
-                    SwTextBoxHelper::create(pFormat, 
GetSvxShape()->GetSdrObject());
-                else
-                    SwTextBoxHelper::destroy(pFormat, 
GetSvxShape()->GetSdrObject());
-
+                if (pEntry->nMemberId == MID_TEXTBOX)
+                {
+                    bool bValue(false);
+                    aValue >>= bValue;
+                    if (bValue)
+                        SwTextBoxHelper::create(pFormat, 
GetSvxShape()->GetSdrObject());
+                    else
+                        SwTextBoxHelper::destroy(pFormat, 
GetSvxShape()->GetSdrObject());
+                }
+                else if (pEntry->nMemberId == MID_TEXTBOX_CONTENT)
+                {
+                    if (aValue.getValueType() == 
cppu::UnoType<uno::Reference<text::XTextFrame>>::get())
+                        SwTextBoxHelper::set(pFormat, 
GetSvxShape()->GetSdrObject(),
+                                             
aValue.get<uno::Reference<text::XTextFrame>>());
+                    else
+                        SAL_WARN( "sw.uno", "This is not a TextFrame!" );
+                }
             }
             else if (pEntry->nWID == RES_CHAIN)
             {
                 if (pEntry->nMemberId == MID_CHAIN_NEXTNAME || 
pEntry->nMemberId == MID_CHAIN_PREVNAME)
-                    SwTextBoxHelper::syncProperty(pFormat, pEntry->nWID, 
pEntry->nMemberId, aValue);
+                    SwTextBoxHelper::syncProperty(pFormat, pEntry->nWID, 
pEntry->nMemberId, aValue, SdrObject::getSdrObjectFromXShape(mxShape));
             }
             // #i28749#
             else if ( FN_SHAPE_POSITION_LAYOUT_DIR == pEntry->nWID )
@@ -1333,7 +1349,7 @@ void SwXShape::setPropertyValue(const OUString& 
rPropertyName, const uno::Any& a
                     pFormat->SetFormatAttr(aSet);
             }
             // We have a pFormat and a pEntry as well: try to sync TextBox 
property.
-            SwTextBoxHelper::syncProperty(pFormat, pEntry->nWID, 
pEntry->nMemberId, aValue);
+            SwTextBoxHelper::syncProperty(pFormat, pEntry->nWID, 
pEntry->nMemberId, aValue, SdrObject::getSdrObjectFromXShape(mxShape));
         }
         else
         {
@@ -1423,7 +1439,7 @@ void SwXShape::setPropertyValue(const OUString& 
rPropertyName, const uno::Any& a
         if (pFormat)
         {
             // We have a pFormat (but no pEntry): try to sync TextBox property.
-            SwTextBoxHelper::syncProperty(pFormat, rPropertyName, aValue);
+            SwTextBoxHelper::syncProperty(pFormat, rPropertyName, aValue, 
SdrObject::getSdrObjectFromXShape(mxShape));
         }
 
         // #i31698# - restore object position, if caption point is set.
@@ -1504,12 +1520,26 @@ uno::Any SwXShape::getPropertyValue(const OUString& 
rPropertyName)
                 }
                 else if (pEntry->nWID == FN_TEXT_BOX)
                 {
-                    auto pSvxShape = GetSvxShape();
-                    bool bValue = SwTextBoxHelper::isTextBox(
-                        pFormat, RES_DRAWFRMFMT,
-                        ((pSvxShape && pSvxShape->GetSdrObject()) ? 
pSvxShape->GetSdrObject()
-                                                                  : 
pFormat->FindRealSdrObject()));
-                    aRet <<= bValue;
+                    if (pEntry->nMemberId == MID_TEXTBOX)
+                    {
+                        auto pSvxShape = GetSvxShape();
+                        bool bValue = SwTextBoxHelper::isTextBox(
+                            pFormat, RES_DRAWFRMFMT,
+                            ((pSvxShape && pSvxShape->GetSdrObject()) ? 
pSvxShape->GetSdrObject()
+                                : pFormat->FindRealSdrObject()));
+                        aRet <<= bValue;
+                    }
+                    else if (pEntry->nMemberId == MID_TEXTBOX_CONTENT)
+                    {
+                        auto pObj = SdrObject::getSdrObjectFromXShape(mxShape);
+                        auto xRange = SwTextBoxHelper::queryInterface(
+                            pFormat, cppu::UnoType<text::XText>::get(),
+                            pObj ? pObj : pFormat->FindRealSdrObject());
+
+                        uno::Reference<text::XTextFrame> xFrame(xRange, 
uno::UNO_QUERY);
+                        if (xFrame.is())
+                            aRet <<= xFrame;
+                    }
                 }
                 else if (pEntry->nWID == RES_CHAIN)
                 {
@@ -1790,7 +1820,7 @@ uno::Sequence< beans::PropertyState > 
SwXShape::getPropertyStates(
             else if (pEntry->nWID == FN_TEXT_BOX)
             {
                 // The TextBox property is set, if we can find a textbox for 
this shape.
-                if (pFormat && SwTextBoxHelper::isTextBox(pFormat, 
RES_DRAWFRMFMT))
+                if (pFormat && SwTextBoxHelper::isTextBox(pFormat, 
RES_DRAWFRMFMT, SdrObject::getSdrObjectFromXShape(mxShape)))
                     pRet[nProperty] = beans::PropertyState_DIRECT_VALUE;
                 else
                     pRet[nProperty] = beans::PropertyState_DEFAULT_VALUE;
diff --git a/sw/source/core/unocore/unomap.cxx 
b/sw/source/core/unocore/unomap.cxx
index 4e373b6c511e..3b9f0d7d8944 100644
--- a/sw/source/core/unocore/unomap.cxx
+++ b/sw/source/core/unocore/unomap.cxx
@@ -291,7 +291,8 @@ const SfxItemPropertyMapEntry* 
SwUnoPropertyMapProvider::GetPropertyMapEntries(s
                     { u"" UNO_NAME_RELATIVE_HEIGHT_RELATION, RES_FRM_SIZE, 
cppu::UnoType<sal_Int16>::get(),      PROPERTY_NONE, 
MID_FRMSIZE_REL_HEIGHT_RELATION },
                     { u"" UNO_NAME_RELATIVE_WIDTH, RES_FRM_SIZE,      
cppu::UnoType<sal_Int16>::get()  ,         PROPERTY_NONE, MID_FRMSIZE_REL_WIDTH 
 },
                     { u"" UNO_NAME_RELATIVE_WIDTH_RELATION, RES_FRM_SIZE, 
cppu::UnoType<sal_Int16>::get(),       PROPERTY_NONE, 
MID_FRMSIZE_REL_WIDTH_RELATION },
-                    { u"" UNO_NAME_TEXT_BOX, FN_TEXT_BOX, 
cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+                    { u"" UNO_NAME_TEXT_BOX, FN_TEXT_BOX, 
cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_TEXTBOX},
+                    { u"" UNO_NAME_TEXT_BOX_CONTENT, FN_TEXT_BOX, 
cppu::UnoType<text::XTextFrame>::get(), PROPERTY_NONE, MID_TEXTBOX_CONTENT},
                     { u"" UNO_NAME_CHAIN_NEXT_NAME, RES_CHAIN,                
cppu::UnoType<OUString>::get(),            PropertyAttribute::MAYBEVOID 
,MID_CHAIN_NEXTNAME},
                     { u"" UNO_NAME_CHAIN_PREV_NAME, RES_CHAIN,                
cppu::UnoType<OUString>::get(),            PropertyAttribute::MAYBEVOID 
,MID_CHAIN_PREVNAME},
                     { u"" UNO_NAME_CHAIN_NAME,      RES_CHAIN,                
cppu::UnoType<OUString>::get(),            PropertyAttribute::MAYBEVOID 
,MID_CHAIN_NAME    },
diff --git a/sw/source/core/unocore/unomap1.cxx 
b/sw/source/core/unocore/unomap1.cxx
index 9a186b05d280..63b4f2d5e209 100644
--- a/sw/source/core/unocore/unomap1.cxx
+++ b/sw/source/core/unocore/unomap1.cxx
@@ -786,6 +786,7 @@ const SfxItemPropertyMapEntry*  
SwUnoPropertyMapProvider::GetFramePropertyMap()
         { u"" UNO_NAME_SIZE_TYPE, RES_FRM_SIZE,           
cppu::UnoType<sal_Int16>::get()  ,         PROPERTY_NONE,   
MID_FRMSIZE_SIZE_TYPE  },
         { u"" UNO_NAME_WIDTH_TYPE, RES_FRM_SIZE,          
cppu::UnoType<sal_Int16>::get()  ,         PROPERTY_NONE,   
MID_FRMSIZE_WIDTH_TYPE },
         { u"" UNO_NAME_WRITING_MODE, RES_FRAMEDIR, 
cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0 },
+        //{ u"" UNO_NAME_TEXT_BOX, FN_TEXT_BOX,  cppu::UnoType<bool>::get(), 
PROPERTY_NONE, MID_TEXTBOX},
 
         // added FillProperties for SW, same as FILL_PROPERTIES in svx
         // but need own defines in Writer due to later association of strings

Reply via email to