summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAttila Bakos (NISZ) <bakos.attilakaroly@nisz.hu>2021-08-13 14:11:24 +0200
committerLászló Németh <nemeth@numbertext.org>2021-09-01 10:09:37 +0200
commit504d78acb866495fd954fcd6db22ea68f174a5ab (patch)
tree454c012ca959cf144c3380a7e7b58561fac3704e
parent9c9ff9e89fd50223b28f327d3a7e416ab0ae831f (diff)
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 <nemeth@numbertext.org> Reviewed-by: László Németh <nemeth@numbertext.org>
-rw-r--r--sw/inc/frmfmt.hxx10
-rw-r--r--sw/inc/textboxhelper.hxx94
-rw-r--r--sw/qa/extras/layout/layout.cxx2
-rw-r--r--sw/qa/extras/uiwriter/uiwriter3.cxx10
-rw-r--r--sw/qa/uitest/data/tdf143574.odtbin0 -> 10275 bytes
-rw-r--r--sw/qa/uitest/writer_tests7/tdf143574.py39
-rw-r--r--sw/source/core/doc/DocumentLayoutManager.cxx7
-rw-r--r--sw/source/core/doc/textboxhelper.cxx220
-rw-r--r--sw/source/core/draw/dflyobj.cxx2
-rw-r--r--sw/source/core/layout/atrfrm.cxx52
-rw-r--r--sw/source/core/layout/flycnt.cxx2
-rw-r--r--sw/source/core/undo/undobj1.cxx22
-rw-r--r--sw/source/core/unocore/unodraw.cxx4
-rw-r--r--sw/source/uibase/shells/drawsh.cxx4
14 files changed, 368 insertions, 100 deletions
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
--- /dev/null
+++ b/sw/qa/uitest/data/tdf143574.odt
Binary files 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
@@ -529,6 +529,8 @@ void SwFlyAtContentFrame::MakeAll(vcl::RenderContext* pRenderContext)
// 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>() )
{
// get the text area of the shape
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;
}