summaryrefslogtreecommitdiffstats
path: root/sw/source/core/doc/textboxhelper.cxx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /sw/source/core/doc/textboxhelper.cxx
parentInitial commit. (diff)
downloadlibreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz
libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sw/source/core/doc/textboxhelper.cxx')
-rw-r--r--sw/source/core/doc/textboxhelper.cxx1970
1 files changed, 1970 insertions, 0 deletions
diff --git a/sw/source/core/doc/textboxhelper.cxx b/sw/source/core/doc/textboxhelper.cxx
new file mode 100644
index 000000000..358baaf7d
--- /dev/null
+++ b/sw/source/core/doc/textboxhelper.cxx
@@ -0,0 +1,1970 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * 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/.
+ */
+
+#include <textboxhelper.hxx>
+#include <fmtcntnt.hxx>
+#include <fmtanchr.hxx>
+#include <fmtcnct.hxx>
+#include <fmtornt.hxx>
+#include <fmtfsize.hxx>
+#include <doc.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentState.hxx>
+#include <docsh.hxx>
+#include <unocoll.hxx>
+#include <unoframe.hxx>
+#include <unodraw.hxx>
+#include <unotextrange.hxx>
+#include <cmdid.h>
+#include <unomid.h>
+#include <unoprnms.hxx>
+#include <mvsave.hxx>
+#include <fmtsrnd.hxx>
+#include <fmtfollowtextflow.hxx>
+#include <frmfmt.hxx>
+#include <frameformats.hxx>
+#include <dflyobj.hxx>
+#include <swtable.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>
+#include <tools/UnitConversion.hxx>
+#include <svx/swframetypes.hxx>
+#include <drawdoc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <frmatr.hxx>
+
+#include <com/sun/star/document/XActionLockable.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/text/SizeType.hpp>
+#include <com/sun/star/text/WrapTextMode.hpp>
+#include <com/sun/star/text/XTextDocument.hpp>
+#include <com/sun/star/text/XTextFrame.hpp>
+#include <com/sun/star/table/BorderLine2.hpp>
+#include <com/sun/star/text/WritingMode.hpp>
+#include <com/sun/star/text/WritingMode2.hpp>
+#include <com/sun/star/drawing/TextHorizontalAdjust.hpp>
+#include <com/sun/star/style/ParagraphAdjust.hpp>
+
+using namespace com::sun::star;
+
+void SwTextBoxHelper::create(SwFrameFormat* pShape, SdrObject* pObject, bool bCopyText)
+{
+ assert(pShape);
+ assert(pObject);
+
+ // If TextBox wasn't enabled previously
+ if (pShape->GetOtherTextBoxFormats() && pShape->GetOtherTextBoxFormats()->GetTextBox(pObject))
+ return;
+
+ // Store the current text content of the shape
+ OUString sCopyableText;
+
+ if (bCopyText)
+ {
+ if (pObject)
+ {
+ uno::Reference<text::XText> xSrcCnt(pObject->getWeakUnoShape().get(), uno::UNO_QUERY);
+ auto xCur = xSrcCnt->createTextCursor();
+ xCur->gotoStart(false);
+ xCur->gotoEnd(true);
+ sCopyableText = xCur->getText()->getString();
+ }
+ }
+
+ // Create the associated TextFrame and insert it into the document.
+ uno::Reference<text::XTextContent> xTextFrame(
+ SwXServiceProvider::MakeInstance(SwServiceType::TypeTextFrame, *pShape->GetDoc()),
+ uno::UNO_QUERY);
+ uno::Reference<text::XTextDocument> xTextDocument(
+ pShape->GetDoc()->GetDocShell()->GetBaseModel(), uno::UNO_QUERY);
+ uno::Reference<text::XTextContentAppend> xTextContentAppend(xTextDocument->getText(),
+ uno::UNO_QUERY);
+ xTextContentAppend->appendTextContent(xTextFrame, uno::Sequence<beans::PropertyValue>());
+
+ // Link FLY and DRAW formats, so it becomes a text box (needed for syncProperty calls).
+ uno::Reference<text::XTextFrame> xRealTextFrame(xTextFrame, uno::UNO_QUERY);
+ auto pTextFrame = dynamic_cast<SwXTextFrame*>(xRealTextFrame.get());
+ assert(nullptr != pTextFrame);
+ SwFrameFormat* pFormat = pTextFrame->GetFrameFormat();
+
+ assert(nullptr != dynamic_cast<SwDrawFrameFormat*>(pShape));
+ assert(nullptr != dynamic_cast<SwFlyFrameFormat*>(pFormat));
+
+ if (!pShape->GetOtherTextBoxFormats())
+ {
+ auto pTextBox = std::make_shared<SwTextBoxNode>(SwTextBoxNode(pShape));
+ pTextBox->AddTextBox(pObject, pFormat);
+ pShape->SetOtherTextBoxFormats(pTextBox);
+ pFormat->SetOtherTextBoxFormats(pTextBox);
+ }
+ else
+ {
+ auto& pTextBox = pShape->GetOtherTextBoxFormats();
+ pTextBox->AddTextBox(pObject, pFormat);
+ pFormat->SetOtherTextBoxFormats(pTextBox);
+ }
+ // Initialize properties.
+ uno::Reference<beans::XPropertySet> xPropertySet(xTextFrame, uno::UNO_QUERY);
+ uno::Any aEmptyBorder{ 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::Any(sal_Int32(100)));
+
+ xPropertySet->setPropertyValue(UNO_NAME_SIZE_TYPE, uno::Any(text::SizeType::FIX));
+
+ xPropertySet->setPropertyValue(UNO_NAME_SURROUND, uno::Any(text::WrapTextMode_THROUGH));
+
+ uno::Reference<container::XNamed> xNamed(xTextFrame, uno::UNO_QUERY);
+ assert(!xNamed->getName().isEmpty());
+ (void)xNamed;
+
+ // Link its text range to the original shape.
+ uno::Reference<text::XTextRange> xTextBox(xTextFrame, uno::UNO_QUERY_THROW);
+ SwUnoInternalPaM aInternalPaM(*pShape->GetDoc());
+ if (sw::XTextRangeToSwPaM(aInternalPaM, xTextBox))
+ {
+ SwAttrSet aSet(pShape->GetAttrSet());
+ SwFormatContent aContent(aInternalPaM.GetNode().StartOfSectionNode());
+ aSet.Put(aContent);
+ pShape->SetFormatAttr(aSet);
+ }
+
+ DoTextBoxZOrderCorrection(pShape, pObject);
+
+ // Also initialize the properties, which are not constant, but inherited from the shape's ones.
+ uno::Reference<drawing::XShape> xShape(pObject->getUnoShape(), uno::UNO_QUERY);
+ syncProperty(pShape, RES_FRM_SIZE, MID_FRMSIZE_SIZE, uno::Any(xShape->getSize()), pObject);
+
+ uno::Reference<beans::XPropertySet> xShapePropertySet(xShape, uno::UNO_QUERY);
+ syncProperty(pShape, RES_FOLLOW_TEXT_FLOW, MID_FOLLOW_TEXT_FLOW,
+ xShapePropertySet->getPropertyValue(UNO_NAME_IS_FOLLOWING_TEXT_FLOW), pObject);
+ syncProperty(pShape, RES_ANCHOR, MID_ANCHOR_ANCHORTYPE,
+ xShapePropertySet->getPropertyValue(UNO_NAME_ANCHOR_TYPE), pObject);
+ syncProperty(pShape, RES_HORI_ORIENT, MID_HORIORIENT_ORIENT,
+ xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT), pObject);
+ syncProperty(pShape, RES_HORI_ORIENT, MID_HORIORIENT_RELATION,
+ xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT_RELATION), pObject);
+ syncProperty(pShape, RES_VERT_ORIENT, MID_VERTORIENT_ORIENT,
+ xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT), pObject);
+ syncProperty(pShape, RES_VERT_ORIENT, MID_VERTORIENT_RELATION,
+ xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT_RELATION), pObject);
+ syncProperty(pShape, RES_HORI_ORIENT, MID_HORIORIENT_POSITION,
+ xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT_POSITION), pObject);
+ syncProperty(pShape, RES_VERT_ORIENT, MID_VERTORIENT_POSITION,
+ xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT_POSITION), pObject);
+ syncProperty(pShape, RES_FRM_SIZE, MID_FRMSIZE_IS_AUTO_HEIGHT,
+ xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_AUTOGROWHEIGHT), pObject);
+ syncProperty(pShape, RES_TEXT_VERT_ADJUST, 0,
+ xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_VERT_ADJUST), pObject);
+ text::WritingMode eMode;
+ if (xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_WRITINGMODE) >>= eMode)
+ syncProperty(pShape, RES_FRAMEDIR, 0, uno::Any(sal_Int16(eMode)), pObject);
+
+ changeAnchor(pShape, pObject);
+ syncTextBoxSize(pShape, pObject);
+
+ // Check if the shape had text before and move it to the new textframe
+ if (!bCopyText || sCopyableText.isEmpty())
+ return;
+
+ if (pObject)
+ {
+ auto pSourceText = dynamic_cast<SdrTextObj*>(pObject);
+ uno::Reference<text::XTextRange> xDestText(xRealTextFrame, uno::UNO_QUERY);
+
+ xDestText->setString(sCopyableText);
+
+ if (pSourceText)
+ pSourceText->SetText(OUString());
+
+ pShape->GetDoc()->getIDocumentState().SetModified();
+ }
+}
+
+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;
+
+ // If there is a format, check if the shape already has a textbox assigned to.
+ if (auto& pTextBoxNode = pShapeFormat->GetOtherTextBoxFormats())
+ {
+ // If it has a texbox, destroy it.
+ if (pTextBoxNode->GetTextBox(pObj))
+ pTextBoxNode->DelTextBox(pObj, true);
+ // And set the new one.
+ pTextBoxNode->AddTextBox(pObj, pFormat);
+ pFormat->SetOtherTextBoxFormats(pTextBoxNode);
+ }
+ else
+ {
+ // If the shape do not have a texbox node and textbox,
+ // create that for the shape.
+ auto pTextBox = std::make_shared<SwTextBoxNode>(SwTextBoxNode(pShapeFormat));
+ pTextBox->AddTextBox(pObj, pFormat);
+ pShapeFormat->SetOtherTextBoxFormats(pTextBox);
+ pFormat->SetOtherTextBoxFormats(pTextBox);
+ }
+ // Initialize its properties
+ uno::Reference<beans::XPropertySet> xPropertySet(xNew, uno::UNO_QUERY);
+ uno::Any aEmptyBorder{ 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::Any(sal_Int32(100)));
+ xPropertySet->setPropertyValue(UNO_NAME_SIZE_TYPE, uno::Any(text::SizeType::FIX));
+ xPropertySet->setPropertyValue(UNO_NAME_SURROUND, uno::Any(text::WrapTextMode_THROUGH));
+ // Add a new name to it
+ uno::Reference<container::XNamed> xNamed(xNew, uno::UNO_QUERY);
+ assert(!xNamed->getName().isEmpty());
+ (void)xNamed;
+ // And sync. properties.
+ uno::Reference<drawing::XShape> xShape(pObj->getUnoShape(), uno::UNO_QUERY);
+ syncProperty(pShapeFormat, RES_FRM_SIZE, MID_FRMSIZE_SIZE, uno::Any(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::Any(aVertAdj));
+ text::WritingMode eMode;
+ if (xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_WRITINGMODE) >>= eMode)
+ syncProperty(pShapeFormat, RES_FRAMEDIR, 0, uno::Any(sal_Int16(eMode)), pObj);
+
+ // Do sync for the new textframe.
+ synchronizeGroupTextBoxProperty(&changeAnchor, pShapeFormat, pObj);
+ synchronizeGroupTextBoxProperty(&syncTextBoxSize, pShapeFormat, pObj);
+
+ updateTextBoxMargin(pObj);
+}
+
+void SwTextBoxHelper::destroy(const SwFrameFormat* pShape, const SdrObject* pObject)
+{
+ // If a TextBox was enabled previously
+ auto& pTextBox = pShape->GetOtherTextBoxFormats();
+ if (pTextBox)
+ {
+ // Unlink the TextBox's text range from the original shape.
+ // Delete the associated TextFrame.
+ pTextBox->DelTextBox(pObject, true);
+ }
+}
+
+bool SwTextBoxHelper::isTextBox(const SwFrameFormat* pFormat, sal_uInt16 nType,
+ const SdrObject* pObject)
+{
+ SolarMutexGuard aGuard;
+ assert(nType == RES_FLYFRMFMT || nType == RES_DRAWFRMFMT);
+ if (!pFormat || pFormat->Which() != nType)
+ return false;
+
+ auto& pTextBox = pFormat->GetOtherTextBoxFormats();
+ if (!pTextBox)
+ 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();
+ }
+
+ return false;
+}
+
+bool SwTextBoxHelper::hasTextFrame(const SdrObject* pObj)
+{
+ if (!pObj)
+ return false;
+
+ uno::Reference<drawing::XShape> xShape(pObj->getWeakUnoShape().get(), uno::UNO_QUERY);
+ if (!xShape)
+ return false;
+ return SwTextBoxHelper::getOtherTextBoxFormat(xShape);
+}
+
+sal_Int32 SwTextBoxHelper::getCount(SdrPage const* pPage)
+{
+ sal_Int32 nRet = 0;
+ for (std::size_t i = 0; i < pPage->GetObjCount(); ++i)
+ {
+ SdrObject* p = pPage->GetObj(i);
+ if (p && p->IsTextBox())
+ continue;
+ ++nRet;
+ }
+ return nRet;
+}
+
+sal_Int32 SwTextBoxHelper::getCount(const SwDoc& rDoc)
+{
+ sal_Int32 nRet = 0;
+ const SwFrameFormats& rSpzFrameFormats = *rDoc.GetSpzFrameFormats();
+ for (const auto pFormat : rSpzFrameFormats)
+ {
+ if (isTextBox(pFormat, RES_FLYFRMFMT))
+ ++nRet;
+ }
+ return nRet;
+}
+
+uno::Any SwTextBoxHelper::getByIndex(SdrPage const* pPage, sal_Int32 nIndex)
+{
+ if (nIndex < 0)
+ throw lang::IndexOutOfBoundsException();
+
+ SdrObject* pRet = nullptr;
+ sal_Int32 nCount = 0; // Current logical index.
+ for (std::size_t i = 0; i < pPage->GetObjCount(); ++i)
+ {
+ SdrObject* p = pPage->GetObj(i);
+ if (p && p->IsTextBox())
+ continue;
+ if (nCount == nIndex)
+ {
+ pRet = p;
+ break;
+ }
+ ++nCount;
+ }
+
+ if (!pRet)
+ throw lang::IndexOutOfBoundsException();
+
+ return uno::Any(uno::Reference<drawing::XShape>(pRet->getUnoShape(), uno::UNO_QUERY));
+}
+
+sal_Int32 SwTextBoxHelper::getOrdNum(const SdrObject* pObject)
+{
+ if (const SdrPage* pPage = pObject->getSdrPageFromSdrObject())
+ {
+ sal_Int32 nOrder = 0; // Current logical order.
+ for (std::size_t i = 0; i < pPage->GetObjCount(); ++i)
+ {
+ SdrObject* p = pPage->GetObj(i);
+ if (p && p->IsTextBox())
+ continue;
+ if (p == pObject)
+ return nOrder;
+ ++nOrder;
+ }
+ }
+
+ SAL_WARN("sw.core", "SwTextBoxHelper::getOrdNum: no page or page doesn't contain the object");
+ return pObject->GetOrdNum();
+}
+
+void SwTextBoxHelper::getShapeWrapThrough(const SwFrameFormat* pTextBox, bool& rWrapThrough)
+{
+ SwFrameFormat* pShape = SwTextBoxHelper::getOtherTextBoxFormat(pTextBox, RES_FLYFRMFMT);
+ if (pShape)
+ rWrapThrough = pShape->GetSurround().GetSurround() == css::text::WrapTextMode_THROUGH;
+}
+
+SwFrameFormat* SwTextBoxHelper::getOtherTextBoxFormat(const SwFrameFormat* pFormat,
+ sal_uInt16 nType, const SdrObject* pObject)
+{
+ SolarMutexGuard aGuard;
+ if (!isTextBox(pFormat, nType, pObject))
+ return nullptr;
+
+ if (nType == RES_DRAWFRMFMT)
+ {
+ if (pObject)
+ return pFormat->GetOtherTextBoxFormats()->GetTextBox(pObject);
+ if (pFormat->FindRealSdrObject())
+ return pFormat->GetOtherTextBoxFormats()->GetTextBox(pFormat->FindRealSdrObject());
+ return nullptr;
+ }
+ if (nType == RES_FLYFRMFMT)
+ {
+ return pFormat->GetOtherTextBoxFormats()->GetOwnerShape();
+ }
+ return nullptr;
+}
+
+SwFrameFormat* SwTextBoxHelper::getOtherTextBoxFormat(uno::Reference<drawing::XShape> const& xShape)
+{
+ auto pShape = dynamic_cast<SwXShape*>(xShape.get());
+ if (!pShape)
+ return nullptr;
+
+ SwFrameFormat* pFormat = pShape->GetFrameFormat();
+ return getOtherTextBoxFormat(pFormat, RES_DRAWFRMFMT,
+ SdrObject::getSdrObjectFromXShape(xShape));
+}
+
+uno::Reference<text::XTextFrame>
+SwTextBoxHelper::getUnoTextFrame(uno::Reference<drawing::XShape> const& xShape)
+{
+ if (xShape)
+ {
+ auto pFrameFormat = SwTextBoxHelper::getOtherTextBoxFormat(xShape);
+ if (pFrameFormat)
+ {
+ auto pSdrObj = pFrameFormat->FindSdrObject();
+ if (pSdrObj)
+ {
+ return { pSdrObj->getUnoShape(), uno::UNO_QUERY };
+ }
+ }
+ }
+ return {};
+}
+
+template <typename T>
+static void lcl_queryInterface(const SwFrameFormat* pShape, uno::Any& rAny, SdrObject* pObj)
+{
+ if (SwFrameFormat* pFormat
+ = SwTextBoxHelper::getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj))
+ {
+ uno::Reference<T> const xInterface(
+ SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat), uno::UNO_QUERY);
+ rAny <<= xInterface;
+ }
+}
+
+uno::Any SwTextBoxHelper::queryInterface(const SwFrameFormat* pShape, const uno::Type& rType,
+ SdrObject* pObj)
+{
+ uno::Any aRet;
+
+ if (rType == cppu::UnoType<css::text::XTextAppend>::get())
+ {
+ lcl_queryInterface<text::XTextAppend>(pShape, aRet, pObj);
+ }
+ else if (rType == cppu::UnoType<css::text::XText>::get())
+ {
+ lcl_queryInterface<text::XText>(pShape, aRet, pObj);
+ }
+ else if (rType == cppu::UnoType<css::text::XTextRange>::get())
+ {
+ lcl_queryInterface<text::XTextRange>(pShape, aRet, pObj);
+ }
+
+ return aRet;
+}
+
+tools::Rectangle SwTextBoxHelper::getRelativeTextRectangle(SdrObject* pShape)
+{
+ tools::Rectangle aRet;
+ aRet.SetEmpty();
+
+ assert(pShape);
+
+ auto pCustomShape = dynamic_cast<SdrObjCustomShape*>(pShape);
+ if (pCustomShape)
+ {
+ // Need to temporarily release the lock acquired in
+ // SdXMLShapeContext::AddShape(), otherwise we get an empty rectangle,
+ // see EnhancedCustomShapeEngine::getTextBounds().
+ uno::Reference<document::XActionLockable> xLockable(pCustomShape->getUnoShape(),
+ uno::UNO_QUERY);
+ sal_Int16 nLocks = 0;
+ if (xLockable.is())
+ nLocks = xLockable->resetActionLocks();
+ pCustomShape->GetTextBounds(aRet);
+ if (nLocks)
+ xLockable->setActionLocks(nLocks);
+ }
+ else if (pShape)
+ {
+ // fallback - get *any* bound rect we can possibly get hold of
+ aRet = pShape->GetCurrentBoundRect();
+ }
+
+ if (pShape)
+ {
+ // Relative, so count the logic (reference) rectangle, see the EnhancedCustomShape2d ctor.
+ Point aPoint(pShape->GetSnapRect().Center());
+ Size aSize(pShape->GetLogicRect().GetSize());
+ aPoint.AdjustX(-(aSize.Width() / 2));
+ aPoint.AdjustY(-(aSize.Height() / 2));
+ tools::Rectangle aLogicRect(aPoint, aSize);
+ aRet.Move(-1 * aLogicRect.Left(), -1 * aLogicRect.Top());
+ }
+
+ return aRet;
+}
+
+void SwTextBoxHelper::syncProperty(SwFrameFormat* pShape, std::u16string_view rPropertyName,
+ const css::uno::Any& rValue, SdrObject* pObj)
+{
+ // Textframes does not have valid horizontal adjust property, so map it to paragraph adjust property
+ if (rPropertyName == UNO_NAME_TEXT_HORZADJUST)
+ {
+ SwFrameFormat* pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj);
+ if (!pFormat)
+ return;
+
+ auto xTextFrame = SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat);
+ uno::Reference<text::XTextCursor> xCursor = xTextFrame->getText()->createTextCursor();
+
+ // Select all paragraphs in the textframe
+ xCursor->gotoStart(false);
+ xCursor->gotoEnd(true);
+ uno::Reference<beans::XPropertySet> xFrameParaProps(xCursor, uno::UNO_QUERY);
+
+ // And simply map the property
+ const auto eValue = rValue.get<drawing::TextHorizontalAdjust>();
+ switch (eValue)
+ {
+ case drawing::TextHorizontalAdjust::TextHorizontalAdjust_CENTER:
+ xFrameParaProps->setPropertyValue(
+ UNO_NAME_PARA_ADJUST,
+ uno::Any(style::ParagraphAdjust::ParagraphAdjust_CENTER)); //3
+ break;
+ case drawing::TextHorizontalAdjust::TextHorizontalAdjust_LEFT:
+ xFrameParaProps->setPropertyValue(
+ UNO_NAME_PARA_ADJUST,
+ uno::Any(style::ParagraphAdjust::ParagraphAdjust_LEFT)); //0
+ break;
+ case drawing::TextHorizontalAdjust::TextHorizontalAdjust_RIGHT:
+ xFrameParaProps->setPropertyValue(
+ UNO_NAME_PARA_ADJUST,
+ uno::Any(style::ParagraphAdjust::ParagraphAdjust_RIGHT)); //1
+ break;
+ default:
+ SAL_WARN("sw.core",
+ "SwTextBoxHelper::syncProperty: unhandled TextHorizontalAdjust: "
+ << static_cast<sal_Int32>(eValue));
+ break;
+ }
+ return;
+ }
+
+ if (rPropertyName == u"CustomShapeGeometry")
+ {
+ // CustomShapeGeometry changes the textbox position offset and size, so adjust both.
+ syncProperty(pShape, RES_FRM_SIZE, MID_FRMSIZE_SIZE, uno::Any());
+
+ SdrObject* pObject = pObj ? pObj : pShape->FindRealSdrObject();
+ if (pObject)
+ {
+ tools::Rectangle aRectangle(pObject->GetSnapRect());
+ syncProperty(pShape, RES_HORI_ORIENT, MID_HORIORIENT_POSITION,
+ uno::Any(static_cast<sal_Int32>(convertTwipToMm100(aRectangle.Left()))));
+ syncProperty(pShape, RES_VERT_ORIENT, MID_VERTORIENT_POSITION,
+ uno::Any(static_cast<sal_Int32>(convertTwipToMm100(aRectangle.Top()))));
+ }
+
+ SwFrameFormat* pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj);
+ if (!pFormat)
+ return;
+
+ comphelper::SequenceAsHashMap aCustomShapeGeometry(rValue);
+ auto it = aCustomShapeGeometry.find("TextPreRotateAngle");
+ if (it == aCustomShapeGeometry.end())
+ {
+ it = aCustomShapeGeometry.find("TextRotateAngle");
+ }
+
+ if (it != aCustomShapeGeometry.end())
+ {
+ auto nAngle = it->second.has<sal_Int32>() ? it->second.get<sal_Int32>() : 0;
+ if (nAngle == 0)
+ {
+ nAngle = it->second.has<double>() ? it->second.get<double>() : 0;
+ }
+
+ sal_Int16 nDirection = 0;
+ switch (nAngle)
+ {
+ case -90:
+ nDirection = text::WritingMode2::TB_RL;
+ break;
+ case -270:
+ nDirection = text::WritingMode2::BT_LR;
+ break;
+ default:
+ SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled property value: "
+ "CustomShapeGeometry:TextPreRotateAngle: "
+ << nAngle);
+ break;
+ }
+
+ if (nDirection)
+ {
+ syncProperty(pShape, RES_FRAMEDIR, 0, uno::Any(nDirection), pObj);
+ }
+ }
+ }
+ else if (rPropertyName == UNO_NAME_TEXT_VERT_ADJUST)
+ syncProperty(pShape, RES_TEXT_VERT_ADJUST, 0, rValue, pObj);
+ else if (rPropertyName == UNO_NAME_TEXT_AUTOGROWHEIGHT)
+ syncProperty(pShape, RES_FRM_SIZE, MID_FRMSIZE_IS_AUTO_HEIGHT, rValue, pObj);
+ else if (rPropertyName == UNO_NAME_TEXT_LEFTDIST)
+ syncProperty(pShape, RES_BOX, LEFT_BORDER_DISTANCE, rValue, pObj);
+ else if (rPropertyName == UNO_NAME_TEXT_RIGHTDIST)
+ syncProperty(pShape, RES_BOX, RIGHT_BORDER_DISTANCE, rValue, pObj);
+ else if (rPropertyName == UNO_NAME_TEXT_UPPERDIST)
+ syncProperty(pShape, RES_BOX, TOP_BORDER_DISTANCE, rValue, pObj);
+ else if (rPropertyName == UNO_NAME_TEXT_LOWERDIST)
+ syncProperty(pShape, RES_BOX, BOTTOM_BORDER_DISTANCE, rValue, pObj);
+ else if (rPropertyName == UNO_NAME_TEXT_WRITINGMODE)
+ {
+ text::WritingMode eMode;
+ sal_Int16 eMode2;
+ if (rValue >>= eMode)
+ syncProperty(pShape, RES_FRAMEDIR, 0, uno::Any(sal_Int16(eMode)), pObj);
+ else if (rValue >>= eMode2)
+ syncProperty(pShape, RES_FRAMEDIR, 0, uno::Any(eMode2), pObj);
+ }
+ else
+ SAL_INFO("sw.core", "SwTextBoxHelper::syncProperty: unhandled property: "
+ << static_cast<OUString>(rPropertyName));
+}
+
+void SwTextBoxHelper::getProperty(SwFrameFormat const* pShape, sal_uInt16 nWID, sal_uInt8 nMemberID,
+ css::uno::Any& rValue)
+{
+ if (!pShape)
+ return;
+
+ nMemberID &= ~CONVERT_TWIPS;
+
+ SwFrameFormat* pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT);
+ if (!pFormat)
+ return;
+
+ if (nWID != RES_CHAIN)
+ return;
+
+ switch (nMemberID)
+ {
+ case MID_CHAIN_PREVNAME:
+ case MID_CHAIN_NEXTNAME:
+ {
+ const SwFormatChain& rChain = pFormat->GetChain();
+ rChain.QueryValue(rValue, nMemberID);
+ }
+ break;
+ case MID_CHAIN_NAME:
+ rValue <<= pFormat->GetName();
+ break;
+ default:
+ SAL_WARN("sw.core", "SwTextBoxHelper::getProperty: unhandled member-id: "
+ << o3tl::narrowing<sal_uInt16>(nMemberID));
+ break;
+ }
+}
+
+css::uno::Any SwTextBoxHelper::getProperty(SwFrameFormat const* pShape, const OUString& rPropName)
+{
+ if (!pShape)
+ return {};
+
+ SwFrameFormat* pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT);
+ if (!pFormat)
+ return {};
+
+ uno::Reference<beans::XPropertySet> const xPropertySet(
+ SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat), uno::UNO_QUERY);
+
+ return xPropertySet->getPropertyValue(rPropName);
+}
+
+void SwTextBoxHelper::syncProperty(SwFrameFormat* pShape, sal_uInt16 nWID, sal_uInt8 nMemberID,
+ const css::uno::Any& rValue, SdrObject* pObj)
+{
+ // No shape yet? Then nothing to do, initial properties are set by create().
+ if (!pShape)
+ return;
+
+ uno::Any aValue(rValue);
+ nMemberID &= ~CONVERT_TWIPS;
+
+ SwFrameFormat* pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj);
+ if (!pFormat)
+ return;
+
+ OUString aPropertyName;
+ bool bAdjustX = false;
+ bool bAdjustY = false;
+ bool bAdjustSize = false;
+ switch (nWID)
+ {
+ case RES_HORI_ORIENT:
+ switch (nMemberID)
+ {
+ case MID_HORIORIENT_ORIENT:
+ aPropertyName = UNO_NAME_HORI_ORIENT;
+ break;
+ case MID_HORIORIENT_RELATION:
+ if (pShape->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
+ aPropertyName = UNO_NAME_HORI_ORIENT_RELATION;
+ else
+ return;
+ break;
+ case MID_HORIORIENT_POSITION:
+ aPropertyName = UNO_NAME_HORI_ORIENT_POSITION;
+ bAdjustX = true;
+ break;
+ default:
+ SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
+ << o3tl::narrowing<sal_uInt16>(nMemberID)
+ << " (which-id: " << nWID << ")");
+ break;
+ }
+ break;
+ case RES_LR_SPACE:
+ {
+ switch (nMemberID)
+ {
+ case MID_L_MARGIN:
+ aPropertyName = UNO_NAME_LEFT_MARGIN;
+ break;
+ case MID_R_MARGIN:
+ aPropertyName = UNO_NAME_RIGHT_MARGIN;
+ break;
+ default:
+ SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
+ << o3tl::narrowing<sal_uInt16>(nMemberID)
+ << " (which-id: " << nWID << ")");
+ break;
+ }
+ break;
+ }
+ case RES_VERT_ORIENT:
+ switch (nMemberID)
+ {
+ case MID_VERTORIENT_ORIENT:
+ aPropertyName = UNO_NAME_VERT_ORIENT;
+ break;
+ case MID_VERTORIENT_RELATION:
+ if (pShape->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
+ aPropertyName = UNO_NAME_VERT_ORIENT_RELATION;
+ else
+ return;
+ break;
+ case MID_VERTORIENT_POSITION:
+ aPropertyName = UNO_NAME_VERT_ORIENT_POSITION;
+ bAdjustY = true;
+ break;
+ default:
+ SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
+ << o3tl::narrowing<sal_uInt16>(nMemberID)
+ << " (which-id: " << nWID << ")");
+ break;
+ }
+ break;
+ case RES_FRM_SIZE:
+ switch (nMemberID)
+ {
+ case MID_FRMSIZE_WIDTH_TYPE:
+ aPropertyName = UNO_NAME_WIDTH_TYPE;
+ break;
+ case MID_FRMSIZE_IS_AUTO_HEIGHT:
+ aPropertyName = UNO_NAME_FRAME_ISAUTOMATIC_HEIGHT;
+ break;
+ case MID_FRMSIZE_REL_HEIGHT_RELATION:
+ aPropertyName = UNO_NAME_RELATIVE_HEIGHT_RELATION;
+ break;
+ case MID_FRMSIZE_REL_WIDTH_RELATION:
+ aPropertyName = UNO_NAME_RELATIVE_WIDTH_RELATION;
+ break;
+ default:
+ aPropertyName = UNO_NAME_SIZE;
+ bAdjustSize = true;
+ break;
+ }
+ break;
+ case RES_ANCHOR:
+ switch (nMemberID)
+ {
+ case MID_ANCHOR_ANCHORTYPE:
+ {
+ changeAnchor(pShape, pObj);
+ return;
+ }
+ break;
+ default:
+ SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
+ << o3tl::narrowing<sal_uInt16>(nMemberID)
+ << " (which-id: " << nWID << ")");
+ break;
+ }
+ break;
+ case FN_TEXT_RANGE:
+ {
+ uno::Reference<text::XTextRange> xRange;
+ rValue >>= xRange;
+ SwUnoInternalPaM aInternalPaM(*pFormat->GetDoc());
+ if (sw::XTextRangeToSwPaM(aInternalPaM, xRange))
+ {
+ SwFormatAnchor aAnchor(pFormat->GetAnchor());
+ aAnchor.SetAnchor(aInternalPaM.Start());
+ pFormat->SetFormatAttr(aAnchor);
+ }
+ }
+ break;
+ case RES_CHAIN:
+ switch (nMemberID)
+ {
+ case MID_CHAIN_PREVNAME:
+ aPropertyName = UNO_NAME_CHAIN_PREV_NAME;
+ break;
+ case MID_CHAIN_NEXTNAME:
+ aPropertyName = UNO_NAME_CHAIN_NEXT_NAME;
+ break;
+ default:
+ SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
+ << o3tl::narrowing<sal_uInt16>(nMemberID)
+ << " (which-id: " << nWID << ")");
+ break;
+ }
+ break;
+ case RES_TEXT_VERT_ADJUST:
+ aPropertyName = UNO_NAME_TEXT_VERT_ADJUST;
+ break;
+ case RES_BOX:
+ switch (nMemberID)
+ {
+ case LEFT_BORDER_DISTANCE:
+ aPropertyName = UNO_NAME_LEFT_BORDER_DISTANCE;
+ break;
+ case RIGHT_BORDER_DISTANCE:
+ aPropertyName = UNO_NAME_RIGHT_BORDER_DISTANCE;
+ break;
+ case TOP_BORDER_DISTANCE:
+ aPropertyName = UNO_NAME_TOP_BORDER_DISTANCE;
+ break;
+ case BOTTOM_BORDER_DISTANCE:
+ aPropertyName = UNO_NAME_BOTTOM_BORDER_DISTANCE;
+ break;
+ default:
+ SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
+ << o3tl::narrowing<sal_uInt16>(nMemberID)
+ << " (which-id: " << nWID << ")");
+ break;
+ }
+ break;
+ case RES_OPAQUE:
+ aPropertyName = UNO_NAME_OPAQUE;
+ break;
+ case RES_FRAMEDIR:
+ aPropertyName = UNO_NAME_WRITING_MODE;
+ break;
+ case RES_WRAP_INFLUENCE_ON_OBJPOS:
+ switch (nMemberID)
+ {
+ case MID_ALLOW_OVERLAP:
+ aPropertyName = UNO_NAME_ALLOW_OVERLAP;
+ break;
+ default:
+ SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
+ << o3tl::narrowing<sal_uInt16>(nMemberID)
+ << " (which-id: " << nWID << ")");
+ break;
+ }
+ break;
+ default:
+ SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled which-id: "
+ << nWID << " (member-id: "
+ << o3tl::narrowing<sal_uInt16>(nMemberID) << ")");
+ break;
+ }
+
+ if (aPropertyName.isEmpty())
+ return;
+
+ // Position/size should be the text position/size, not the shape one as-is.
+ if (bAdjustX || bAdjustY || bAdjustSize)
+ {
+ changeAnchor(pShape, pObj);
+ tools::Rectangle aRect
+ = getRelativeTextRectangle(pObj ? pObj : pShape->FindRealSdrObject());
+ if (!aRect.IsEmpty())
+ {
+ if (bAdjustX || bAdjustY)
+ {
+ sal_Int32 nValue;
+ if (aValue >>= nValue)
+ {
+ nValue += convertTwipToMm100(bAdjustX ? aRect.Left() : aRect.Top());
+ aValue <<= nValue;
+ }
+ }
+ else if (bAdjustSize)
+ {
+ awt::Size aSize(convertTwipToMm100(aRect.getWidth()),
+ convertTwipToMm100(aRect.getHeight()));
+ aValue <<= aSize;
+ }
+ }
+ }
+ auto aGuard = SwTextBoxLockGuard(*pShape->GetOtherTextBoxFormats());
+ uno::Reference<beans::XPropertySet> const xPropertySet(
+ SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat), uno::UNO_QUERY);
+ xPropertySet->setPropertyValue(aPropertyName, aValue);
+}
+
+void SwTextBoxHelper::saveLinks(const SwFrameFormats& rFormats,
+ std::map<const SwFrameFormat*, const SwFrameFormat*>& rLinks)
+{
+ for (const auto pFormat : rFormats)
+ {
+ if (SwFrameFormat* pTextBox = getOtherTextBoxFormat(pFormat, RES_DRAWFRMFMT))
+ rLinks[pFormat] = pTextBox;
+ }
+}
+
+void SwTextBoxHelper::restoreLinks(std::set<ZSortFly>& rOld, std::vector<SwFrameFormat*>& rNew,
+ SavedLink& rSavedLinks)
+{
+ std::size_t i = 0;
+ for (const auto& rIt : rOld)
+ {
+ auto aTextBoxIt = rSavedLinks.find(rIt.GetFormat());
+ if (aTextBoxIt != rSavedLinks.end())
+ {
+ std::size_t j = 0;
+ for (const auto& rJt : rOld)
+ {
+ if (rJt.GetFormat() == aTextBoxIt->second)
+ rNew[i]->SetFormatAttr(rNew[j]->GetContent());
+ ++j;
+ }
+ }
+ ++i;
+ }
+}
+
+text::TextContentAnchorType SwTextBoxHelper::mapAnchorType(const RndStdIds& rAnchorID)
+{
+ text::TextContentAnchorType aAnchorType;
+ switch (rAnchorID)
+ {
+ case RndStdIds::FLY_AS_CHAR:
+ aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AS_CHARACTER;
+ break;
+ case RndStdIds::FLY_AT_CHAR:
+ aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AT_CHARACTER;
+ break;
+ case RndStdIds::FLY_AT_PARA:
+ aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AT_PARAGRAPH;
+ break;
+ case RndStdIds::FLY_AT_PAGE:
+ aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AT_PAGE;
+ break;
+ case RndStdIds::FLY_AT_FLY:
+ aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AT_FRAME;
+ break;
+ default:
+ aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AT_PARAGRAPH;
+ SAL_WARN("sw.core", "SwTextBoxHelper::mapAnchorType: Unknown AnchorType!");
+ break;
+ }
+ return aAnchorType;
+}
+
+void SwTextBoxHelper::syncFlyFrameAttr(SwFrameFormat& rShape, SfxItemSet const& rSet,
+ SdrObject* pObj)
+{
+ SwFrameFormat* pFormat = getOtherTextBoxFormat(&rShape, RES_DRAWFRMFMT, pObj);
+ if (!pFormat)
+ return;
+
+ const bool bInlineAnchored = rShape.GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR;
+ const bool bLayoutInCell
+ = rShape.GetFollowTextFlow().GetValue() && rShape.GetAnchor().GetContentAnchor()
+ && rShape.GetAnchor().GetContentAnchor()->nNode.GetNode().FindTableNode();
+ SfxItemSet aTextBoxSet(pFormat->GetDoc()->GetAttrPool(), aFrameFormatSetRange);
+
+ SfxItemIter aIter(rSet);
+ const SfxPoolItem* pItem = aIter.GetCurItem();
+
+ do
+ {
+ switch (pItem->Which())
+ {
+ case RES_VERT_ORIENT:
+ {
+ // The new position can be with anchor changing so sync it!
+ const text::TextContentAnchorType aNewAnchorType
+ = mapAnchorType(rShape.GetAnchor().GetAnchorId());
+ syncProperty(&rShape, RES_ANCHOR, MID_ANCHOR_ANCHORTYPE, uno::Any(aNewAnchorType),
+ pObj);
+ if (bInlineAnchored || bLayoutInCell)
+ return;
+ SwFormatVertOrient aOrient(pItem->StaticWhichCast(RES_VERT_ORIENT));
+
+ tools::Rectangle aRect
+ = getRelativeTextRectangle(pObj ? pObj : rShape.FindRealSdrObject());
+ if (!aRect.IsEmpty())
+ aOrient.SetPos(aOrient.GetPos() + aRect.Top());
+
+ if (rShape.GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_PAGE
+ && rShape.GetAnchor().GetPageNum() != 0)
+ aOrient.SetRelationOrient(rShape.GetVertOrient().GetRelationOrient());
+ aTextBoxSet.Put(aOrient);
+
+ // restore height (shrunk for extending beyond the page bottom - tdf#91260)
+ SwFormatFrameSize aSize(pFormat->GetFrameSize());
+ if (!aRect.IsEmpty())
+ {
+ aSize.SetHeight(aRect.getHeight());
+ aTextBoxSet.Put(aSize);
+ }
+ }
+ break;
+ case RES_HORI_ORIENT:
+ {
+ // The new position can be with anchor changing so sync it!
+ const text::TextContentAnchorType aNewAnchorType
+ = mapAnchorType(rShape.GetAnchor().GetAnchorId());
+ syncProperty(&rShape, RES_ANCHOR, MID_ANCHOR_ANCHORTYPE, uno::Any(aNewAnchorType),
+ pObj);
+ if (bInlineAnchored || bLayoutInCell)
+ return;
+ SwFormatHoriOrient aOrient(pItem->StaticWhichCast(RES_HORI_ORIENT));
+
+ tools::Rectangle aRect
+ = getRelativeTextRectangle(pObj ? pObj : rShape.FindRealSdrObject());
+ if (!aRect.IsEmpty())
+ aOrient.SetPos(aOrient.GetPos() + aRect.Left());
+
+ if (rShape.GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_PAGE
+ && rShape.GetAnchor().GetPageNum() != 0)
+ aOrient.SetRelationOrient(rShape.GetHoriOrient().GetRelationOrient());
+ aTextBoxSet.Put(aOrient);
+ }
+ break;
+ case RES_FRM_SIZE:
+ {
+ // In case the shape got resized, then we need to adjust both
+ // the position and the size of the textbox (e.g. larger
+ // rounded edges of a rectangle -> need to push right/down the
+ // textbox).
+ SwFormatVertOrient aVertOrient(rShape.GetVertOrient());
+ SwFormatHoriOrient aHoriOrient(rShape.GetHoriOrient());
+ SwFormatFrameSize aSize(pFormat->GetFrameSize());
+
+ tools::Rectangle aRect
+ = getRelativeTextRectangle(pObj ? pObj : rShape.FindRealSdrObject());
+ if (!aRect.IsEmpty())
+ {
+ if (!bInlineAnchored)
+ {
+ aVertOrient.SetPos(
+ (pObj ? pObj->GetRelativePos().getX() : aVertOrient.GetPos())
+ + aRect.Top());
+ aHoriOrient.SetPos(
+ (pObj ? pObj->GetRelativePos().getY() : aHoriOrient.GetPos())
+ + aRect.Left());
+
+ aTextBoxSet.Put(aVertOrient);
+ aTextBoxSet.Put(aHoriOrient);
+ }
+
+ aSize.SetWidth(aRect.getWidth());
+ aSize.SetHeight(aRect.getHeight());
+ aTextBoxSet.Put(aSize);
+ }
+ }
+ break;
+ case RES_ANCHOR:
+ {
+ if (pItem->StaticWhichCast(RES_ANCHOR) == rShape.GetAnchor())
+ // the anchor have to be synced
+ {
+ const text::TextContentAnchorType aNewAnchorType
+ = mapAnchorType(rShape.GetAnchor().GetAnchorId());
+ syncProperty(&rShape, RES_ANCHOR, MID_ANCHOR_ANCHORTYPE,
+ uno::Any(aNewAnchorType), pObj);
+ }
+ else
+ {
+ SAL_WARN("sw.core", "SwTextBoxHelper::syncFlyFrameAttr: The anchor of the "
+ "shape different from the textframe!");
+ }
+ }
+ break;
+ default:
+ SAL_WARN("sw.core", "SwTextBoxHelper::syncFlyFrameAttr: unhandled which-id: "
+ << pItem->Which());
+ break;
+ }
+
+ pItem = aIter.NextItem();
+ } while (pItem && (0 != pItem->Which()));
+
+ if (aTextBoxSet.Count())
+ {
+ auto aGuard = SwTextBoxLockGuard(*rShape.GetOtherTextBoxFormats());
+ pFormat->SetFormatAttr(aTextBoxSet);
+ }
+ DoTextBoxZOrderCorrection(&rShape, pObj);
+}
+
+void SwTextBoxHelper::updateTextBoxMargin(SdrObject* pObj)
+{
+ if (!pObj)
+ return;
+ uno::Reference<drawing::XShape> xShape(pObj->getUnoShape(), uno::UNO_QUERY);
+ if (!xShape)
+ return;
+ uno::Reference<beans::XPropertySet> const xPropertySet(xShape, uno::UNO_QUERY);
+
+ auto pParentFormat = getOtherTextBoxFormat(getOtherTextBoxFormat(xShape), RES_FLYFRMFMT);
+ if (!pParentFormat)
+ return;
+
+ // Sync the padding
+ syncProperty(pParentFormat, UNO_NAME_TEXT_LEFTDIST,
+ xPropertySet->getPropertyValue(UNO_NAME_TEXT_LEFTDIST), pObj);
+ syncProperty(pParentFormat, UNO_NAME_TEXT_RIGHTDIST,
+ xPropertySet->getPropertyValue(UNO_NAME_TEXT_RIGHTDIST), pObj);
+ syncProperty(pParentFormat, UNO_NAME_TEXT_UPPERDIST,
+ xPropertySet->getPropertyValue(UNO_NAME_TEXT_UPPERDIST), pObj);
+ syncProperty(pParentFormat, UNO_NAME_TEXT_LOWERDIST,
+ xPropertySet->getPropertyValue(UNO_NAME_TEXT_LOWERDIST), pObj);
+
+ // Sync the text aligning
+ syncProperty(pParentFormat, UNO_NAME_TEXT_VERTADJUST,
+ xPropertySet->getPropertyValue(UNO_NAME_TEXT_VERTADJUST), pObj);
+ syncProperty(pParentFormat, UNO_NAME_TEXT_HORZADJUST,
+ xPropertySet->getPropertyValue(UNO_NAME_TEXT_HORZADJUST), pObj);
+
+ // tdf137803: Sync autogrow:
+ const bool bIsAutoGrow
+ = xPropertySet->getPropertyValue(UNO_NAME_TEXT_AUTOGROWHEIGHT).get<bool>();
+ const bool bIsAutoWrap = xPropertySet->getPropertyValue(UNO_NAME_TEXT_WORDWRAP).get<bool>();
+
+ syncProperty(pParentFormat, RES_FRM_SIZE, MID_FRMSIZE_IS_AUTO_HEIGHT, uno::Any(bIsAutoGrow),
+ pObj);
+
+ syncProperty(pParentFormat, RES_FRM_SIZE, MID_FRMSIZE_WIDTH_TYPE,
+ uno::Any(bIsAutoWrap ? text::SizeType::FIX : text::SizeType::MIN), pObj);
+
+ changeAnchor(pParentFormat, pObj);
+ DoTextBoxZOrderCorrection(pParentFormat, pObj);
+}
+
+bool SwTextBoxHelper::changeAnchor(SwFrameFormat* pShape, SdrObject* pObj)
+{
+ if (auto pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj))
+ {
+ if (!isAnchorSyncNeeded(pShape, pFormat))
+ {
+ doTextBoxPositioning(pShape, pObj);
+ DoTextBoxZOrderCorrection(pShape, pObj);
+ if (pShape->GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR
+ && pFormat->GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_CHAR
+ && pFormat->GetVertOrient().GetRelationOrient() != text::RelOrientation::PRINT_AREA)
+ {
+ SwFormatVertOrient aTmp = pFormat->GetVertOrient();
+ aTmp.SetRelationOrient(text::RelOrientation::PRINT_AREA);
+ pFormat->SetFormatAttr(aTmp);
+ }
+
+ return false;
+ }
+
+ const SwFormatAnchor& rOldAnch = pFormat->GetAnchor();
+ const SwFormatAnchor& rNewAnch = pShape->GetAnchor();
+
+ const auto pOldCnt = rOldAnch.GetContentAnchor();
+ const auto pNewCnt = rNewAnch.GetContentAnchor();
+
+ const uno::Any aShapeHorRelOrient(pShape->GetHoriOrient().GetRelationOrient());
+
+ try
+ {
+ auto aGuard = SwTextBoxLockGuard(*pShape->GetOtherTextBoxFormats());
+ ::sw::UndoGuard const UndoGuard(pShape->GetDoc()->GetIDocumentUndoRedo());
+ uno::Reference<beans::XPropertySet> const xPropertySet(
+ SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat), uno::UNO_QUERY);
+ 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_ANCHOR_TYPE, aValue);
+ xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_PAGE_NO,
+ uno::Any(rNewAnch.GetPageNum()));
+ }
+ else if (rOldAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE && pNewCnt)
+ {
+ if (rNewAnch.GetAnchorId() == RndStdIds::FLY_AS_CHAR)
+ {
+ assert(pNewCnt);
+ uno::Any aValue(text::TextContentAnchorType_AT_CHARACTER);
+ xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_TYPE, aValue);
+ xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION,
+ uno::Any(text::RelOrientation::CHAR));
+ xPropertySet->setPropertyValue(UNO_NAME_VERT_ORIENT_RELATION,
+ uno::Any(text::RelOrientation::PRINT_AREA));
+ SwFormatAnchor aPos(pFormat->GetAnchor());
+ aPos.SetAnchor(pNewCnt);
+ pFormat->SetFormatAttr(aPos);
+ }
+ else
+ {
+ uno::Any aValue(mapAnchorType(rNewAnch.GetAnchorId()));
+ xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION,
+ aShapeHorRelOrient);
+ xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_TYPE, aValue);
+ pFormat->SetFormatAttr(rNewAnch);
+ }
+ }
+ else
+ {
+ if (rNewAnch.GetAnchorId() == RndStdIds::FLY_AS_CHAR)
+ {
+ assert(pNewCnt);
+ uno::Any aValue(text::TextContentAnchorType_AT_CHARACTER);
+ xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_TYPE, aValue);
+ xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION,
+ uno::Any(text::RelOrientation::CHAR));
+ xPropertySet->setPropertyValue(UNO_NAME_VERT_ORIENT_RELATION,
+ uno::Any(text::RelOrientation::PRINT_AREA));
+ SwFormatAnchor aPos(pFormat->GetAnchor());
+ aPos.SetAnchor(pNewCnt);
+ pFormat->SetFormatAttr(aPos);
+ }
+ else
+ {
+ xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION,
+ aShapeHorRelOrient);
+ if (rNewAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE
+ && rNewAnch.GetPageNum() == 0)
+ {
+ pFormat->SetFormatAttr(SwFormatAnchor(RndStdIds::FLY_AT_PAGE, 1));
+ }
+ else
+ pFormat->SetFormatAttr(pShape->GetAnchor());
+ }
+ }
+ }
+ catch (uno::Exception& e)
+ {
+ SAL_WARN("sw.core", "SwTextBoxHelper::changeAnchor(): " << e.Message);
+ }
+
+ doTextBoxPositioning(pShape, pObj);
+ DoTextBoxZOrderCorrection(pShape, pObj);
+ return true;
+ }
+
+ return false;
+}
+
+bool SwTextBoxHelper::doTextBoxPositioning(SwFrameFormat* pShape, SdrObject* pObj)
+{
+ // Set the position of the textboxes according to the position of its shape-pair
+ const bool bIsGroupObj = (pObj != pShape->FindRealSdrObject()) && pObj;
+ if (auto pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj))
+ {
+ // Do not create undo entry for the positioning
+ ::sw::UndoGuard const UndoGuard(pShape->GetDoc()->GetIDocumentUndoRedo());
+ auto aGuard = SwTextBoxLockGuard(*pShape->GetOtherTextBoxFormats());
+ // Special treatment for AS_CHAR textboxes:
+ if (pShape->GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR)
+ {
+ // Get the text area of the shape
+ tools::Rectangle aRect
+ = getRelativeTextRectangle(pObj ? pObj : pShape->FindRealSdrObject());
+
+ // Get the left spacing of the text area of the shape
+ auto nLeftSpace = pShape->GetLRSpace().GetLeft();
+
+ // Set the textbox position at the X-axis:
+ SwFormatHoriOrient aNewHOri(pFormat->GetHoriOrient());
+ if (bIsGroupObj && aNewHOri.GetHoriOrient() != text::HoriOrientation::NONE)
+ aNewHOri.SetHoriOrient(text::HoriOrientation::NONE);
+ aNewHOri.SetPos(aRect.Left() + nLeftSpace
+ + (bIsGroupObj ? pObj->GetRelativePos().getX() : 0));
+ SwFormatVertOrient aNewVOri(pFormat->GetVertOrient());
+
+ // Special handling of group textboxes
+ if (bIsGroupObj)
+ {
+ // There are the following cases:
+ // case 1: The textbox should be in that position where the shape is.
+ // case 2: The shape has negative offset so that have to be subtracted
+ // case 3: The shape and its parent shape also has negative offset, so subtract
+ aNewVOri.SetPos(
+ ((pObj->GetRelativePos().getY()) > 0
+ ? (pShape->GetVertOrient().GetPos() > 0
+ ? pObj->GetRelativePos().getY()
+ : pObj->GetRelativePos().getY() - pShape->GetVertOrient().GetPos())
+ : (pShape->GetVertOrient().GetPos() > 0
+ ? 0 // Is this can be a variation?
+ : pObj->GetRelativePos().getY() - pShape->GetVertOrient().GetPos()))
+ + aRect.Top());
+ }
+ else
+ {
+ // Simple textboxes: vertical position equals to the vertical offset of the shape
+ aNewVOri.SetPos(
+ ((pShape->GetVertOrient().GetPos()) > 0 ? pShape->GetVertOrient().GetPos() : 0)
+ + aRect.Top());
+ }
+
+ // Special cases when the shape is aligned to the line
+ if (pShape->GetVertOrient().GetVertOrient() != text::VertOrientation::NONE)
+ {
+ aNewVOri.SetVertOrient(text::VertOrientation::NONE);
+ switch (pShape->GetVertOrient().GetVertOrient())
+ {
+ // Top aligned shape
+ case text::VertOrientation::TOP:
+ case text::VertOrientation::CHAR_TOP:
+ case text::VertOrientation::LINE_TOP:
+ {
+ aNewVOri.SetPos(aNewVOri.GetPos() - pShape->GetFrameSize().GetHeight());
+ break;
+ }
+ // Bottom aligned shape
+ case text::VertOrientation::BOTTOM:
+ case text::VertOrientation::CHAR_BOTTOM:
+ case text::VertOrientation::LINE_BOTTOM:
+ {
+ aNewVOri.SetPos(aNewVOri.GetPos() + pShape->GetFrameSize().GetHeight());
+ break;
+ }
+ // Center aligned shape
+ case text::VertOrientation::CENTER:
+ case text::VertOrientation::CHAR_CENTER:
+ case text::VertOrientation::LINE_CENTER:
+ {
+ aNewVOri.SetPos(aNewVOri.GetPos()
+ + std::lroundf(pShape->GetFrameSize().GetHeight() / 2));
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ pFormat->SetFormatAttr(aNewHOri);
+ pFormat->SetFormatAttr(aNewVOri);
+ }
+ // Other cases when the shape has different anchor from AS_CHAR
+ else
+ {
+ // Text area of the shape
+ tools::Rectangle aRect
+ = getRelativeTextRectangle(pObj ? pObj : pShape->FindRealSdrObject());
+
+ // X Offset of the shape spacing
+ auto nLeftSpace = pShape->GetLRSpace().GetLeft();
+
+ // Set the same position as the (child) shape has
+ SwFormatHoriOrient aNewHOri(pShape->GetHoriOrient());
+ if (bIsGroupObj && aNewHOri.GetHoriOrient() != text::HoriOrientation::NONE)
+ aNewHOri.SetHoriOrient(text::HoriOrientation::NONE);
+
+ aNewHOri.SetPos(
+ (bIsGroupObj && pObj ? pObj->GetRelativePos().getX() : aNewHOri.GetPos())
+ + aRect.Left());
+ SwFormatVertOrient aNewVOri(pShape->GetVertOrient());
+ aNewVOri.SetPos(
+ (bIsGroupObj && pObj ? pObj->GetRelativePos().getY() : aNewVOri.GetPos())
+ + aRect.Top());
+
+ // Get the distance of the child shape inside its parent
+ const auto& nInshapePos
+ = pObj ? pObj->GetRelativePos() - pShape->FindRealSdrObject()->GetRelativePos()
+ : Point();
+
+ // Special case: the shape has relative position from the page
+ if (pShape->GetHoriOrient().GetRelationOrient() == text::RelOrientation::PAGE_FRAME
+ && pShape->GetAnchor().GetAnchorId() != RndStdIds::FLY_AT_PAGE)
+ {
+ aNewHOri.SetRelationOrient(text::RelOrientation::PAGE_FRAME);
+ aNewHOri.SetPos(pShape->GetHoriOrient().GetPos() + nInshapePos.getX()
+ + aRect.Left());
+ }
+
+ if (pShape->GetVertOrient().GetRelationOrient() == text::RelOrientation::PAGE_FRAME
+ && pShape->GetAnchor().GetAnchorId() != RndStdIds::FLY_AT_PAGE)
+ {
+ aNewVOri.SetRelationOrient(text::RelOrientation::PAGE_FRAME);
+ aNewVOri.SetPos(pShape->GetVertOrient().GetPos() + nInshapePos.getY()
+ + aRect.Top());
+ }
+
+ // Other special case: shape is inside a table or floating table following the text flow
+ if (pShape->GetFollowTextFlow().GetValue() && pShape->GetAnchor().GetContentAnchor()
+ && pShape->GetAnchor().GetContentAnchor()->nNode.GetNode().FindTableNode())
+ {
+ // Table position
+ Point nTableOffset;
+ // Floating table
+ if (auto pFly = pShape->GetAnchor()
+ .GetContentAnchor()
+ ->nNode.GetNode()
+ .FindTableNode()
+ ->FindFlyStartNode())
+ {
+ if (auto pFlyFormat = pFly->GetFlyFormat())
+ {
+ nTableOffset.setX(pFlyFormat->GetHoriOrient().GetPos());
+ nTableOffset.setY(pFlyFormat->GetVertOrient().GetPos());
+ }
+ }
+ else
+ // Normal table
+ {
+ auto pTableNode
+ = pShape->GetAnchor().GetContentAnchor()->nNode.GetNode().FindTableNode();
+ if (auto pTableFormat = pTableNode->GetTable().GetFrameFormat())
+ {
+ nTableOffset.setX(pTableFormat->GetHoriOrient().GetPos());
+ nTableOffset.setY(pTableFormat->GetVertOrient().GetPos());
+ }
+ }
+
+ // Add the table positions to the textbox.
+ aNewHOri.SetPos(aNewHOri.GetPos() + nTableOffset.getX() + nLeftSpace);
+ if (pShape->GetVertOrient().GetRelationOrient() == text::RelOrientation::PAGE_FRAME
+ || pShape->GetVertOrient().GetRelationOrient()
+ == text::RelOrientation::PAGE_PRINT_AREA)
+ aNewVOri.SetPos(aNewVOri.GetPos() + nTableOffset.getY());
+ }
+
+ pFormat->SetFormatAttr(aNewHOri);
+ pFormat->SetFormatAttr(aNewVOri);
+ }
+ return true;
+ }
+
+ return false;
+}
+
+bool SwTextBoxHelper::syncTextBoxSize(SwFrameFormat* pShape, SdrObject* pObj)
+{
+ if (!pShape || !pObj)
+ return false;
+
+ if (auto pTextBox = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj))
+ {
+ auto aGuard = SwTextBoxLockGuard(*pShape->GetOtherTextBoxFormats());
+ const auto& rSize = getRelativeTextRectangle(pObj).GetSize();
+ if (!rSize.IsEmpty())
+ {
+ SwFormatFrameSize aSize(pTextBox->GetFrameSize());
+ aSize.SetSize(rSize);
+ return pTextBox->SetFormatAttr(aSize);
+ }
+ }
+
+ return false;
+}
+
+bool SwTextBoxHelper::DoTextBoxZOrderCorrection(SwFrameFormat* pShape, const SdrObject* pObj)
+{
+ // TODO: do this with group shape textboxes.
+ SdrObject* pShpObj = nullptr;
+
+ pShpObj = pShape->FindRealSdrObject();
+
+ if (pShpObj)
+ {
+ auto pTextBox = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj);
+ if (!pTextBox)
+ return false;
+ SdrObject* pFrmObj = pTextBox->FindRealSdrObject();
+ if (!pFrmObj)
+ {
+ // During loading there is no ready SdrObj for z-ordering, so create and cache it here
+ pFrmObj
+ = SwXTextFrame::GetOrCreateSdrObject(*dynamic_cast<SwFlyFrameFormat*>(pTextBox));
+ }
+ if (pFrmObj)
+ {
+ // Get the draw model from the doc
+ SwDrawModel* pDrawModel
+ = pShape->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel();
+ if (pDrawModel)
+ {
+ // Not really sure this will work on all pages, but it seems it will.
+ auto pPage = pDrawModel->GetPage(0);
+ // Recalc all Z-orders
+ pPage->RecalcObjOrdNums();
+ // Here is a counter avoiding running to in infinity:
+ sal_uInt16 nIterator = 0;
+ // If the shape is behind the frame, is good, but if there are some objects
+ // between of them that is wrong so put the frame exactly one level higher
+ // than the shape.
+ if (pFrmObj->GetOrdNum() > pShpObj->GetOrdNum())
+ pPage->SetObjectOrdNum(pFrmObj->GetOrdNum(), pShpObj->GetOrdNum() + 1);
+ else
+ // Else, if the frame is behind the shape, bring to the front of it.
+ while (pFrmObj->GetOrdNum() <= pShpObj->GetOrdNum())
+ {
+ pPage->SetObjectOrdNum(pFrmObj->GetOrdNum(), pFrmObj->GetOrdNum() + 1);
+ // If there is any problem with the indexes, do not run over the infinity
+ if (pPage->GetObjCount() == pFrmObj->GetOrdNum())
+ 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 SdrObject for the frame!");
+ }
+ SAL_WARN("sw.core", "SwTextBoxHelper::DoTextBoxZOrderCorrection(): "
+ "No Valid SdrObject for the shape!");
+
+ return false;
+}
+
+void SwTextBoxHelper::synchronizeGroupTextBoxProperty(bool pFunc(SwFrameFormat*, SdrObject*),
+ SwFrameFormat* pFormat, SdrObject* pObj)
+{
+ if (auto pChildren = pObj->getChildrenOfSdrObject())
+ {
+ for (size_t i = 0; i < pChildren->GetObjCount(); ++i)
+ synchronizeGroupTextBoxProperty(pFunc, pFormat, pChildren->GetObj(i));
+ }
+ else
+ {
+ (*pFunc)(pFormat, pObj);
+ }
+}
+
+std::vector<SwFrameFormat*> SwTextBoxHelper::CollectTextBoxes(const SdrObject* pGroupObject,
+ SwFrameFormat* pFormat)
+{
+ std::vector<SwFrameFormat*> vRet;
+ if (auto pChildren = pGroupObject->getChildrenOfSdrObject())
+ {
+ for (size_t i = 0; i < pChildren->GetObjCount(); ++i)
+ {
+ auto pChildTextBoxes = CollectTextBoxes(pChildren->GetObj(i), pFormat);
+ for (auto& rChildTextBox : pChildTextBoxes)
+ vRet.push_back(rChildTextBox);
+ }
+ }
+ else
+ {
+ if (isTextBox(pFormat, RES_DRAWFRMFMT, pGroupObject))
+ vRet.push_back(getOtherTextBoxFormat(pFormat, RES_DRAWFRMFMT, pGroupObject));
+ }
+ return vRet;
+}
+
+bool SwTextBoxHelper::isAnchorSyncNeeded(const SwFrameFormat* pFirst, const SwFrameFormat* pSecond)
+{
+ if (!pFirst)
+ return false;
+
+ if (!pSecond)
+ return false;
+
+ if (pFirst == pSecond)
+ return false;
+
+ if (!pFirst->GetOtherTextBoxFormats())
+ return false;
+
+ if (!pSecond->GetOtherTextBoxFormats())
+ return false;
+
+ if (pFirst->GetOtherTextBoxFormats() != pSecond->GetOtherTextBoxFormats())
+ return false;
+
+ if (pFirst->GetOtherTextBoxFormats()->GetOwnerShape() == pSecond
+ || pFirst == pSecond->GetOtherTextBoxFormats()->GetOwnerShape())
+ {
+ const auto& rShapeAnchor
+ = pFirst->Which() == RES_DRAWFRMFMT ? pFirst->GetAnchor() : pSecond->GetAnchor();
+ const auto& rFrameAnchor
+ = pFirst->Which() == RES_FLYFRMFMT ? pFirst->GetAnchor() : pSecond->GetAnchor();
+
+ if (rShapeAnchor.GetAnchorId() == rFrameAnchor.GetAnchorId())
+ {
+ if (rShapeAnchor.GetContentAnchor() && rFrameAnchor.GetContentAnchor())
+ {
+ if (rShapeAnchor.GetContentAnchor()->nContent
+ != rFrameAnchor.GetContentAnchor()->nContent)
+ return true;
+
+ if (rShapeAnchor.GetContentAnchor()->nNode
+ != rFrameAnchor.GetContentAnchor()->nNode)
+ return true;
+
+ return false;
+ }
+
+ if (rShapeAnchor.GetAnchorId() == RndStdIds::FLY_AT_PAGE
+ && rFrameAnchor.GetAnchorId() == RndStdIds::FLY_AT_PAGE)
+ {
+ if (rShapeAnchor.GetPageNum() == rFrameAnchor.GetPageNum())
+ return false;
+ else
+ return true;
+ }
+
+ return true;
+ }
+
+ if (rShapeAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR
+ && rFrameAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR)
+ {
+ if (rShapeAnchor.GetContentAnchor() && rFrameAnchor.GetContentAnchor())
+ {
+ if (rShapeAnchor.GetContentAnchor()->nContent
+ != rFrameAnchor.GetContentAnchor()->nContent)
+ return true;
+
+ if (rShapeAnchor.GetContentAnchor()->nNode
+ != rFrameAnchor.GetContentAnchor()->nNode)
+ return true;
+
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+SwTextBoxNode::SwTextBoxNode(SwFrameFormat* pOwnerShape)
+{
+ assert(pOwnerShape);
+ assert(pOwnerShape->Which() == RES_DRAWFRMFMT);
+
+ m_bIsCloningInProgress = false;
+ m_bLock = false;
+
+ m_pOwnerShapeFormat = pOwnerShape;
+ if (!m_pTextBoxes.empty())
+ m_pTextBoxes.clear();
+}
+
+SwTextBoxNode::~SwTextBoxNode()
+{
+ if (m_pTextBoxes.size() != 0)
+ {
+ SAL_WARN("sw.core", "SwTextBoxNode::~SwTextBoxNode(): Text-Box-Vector still not empty!");
+ assert(false);
+ }
+}
+
+void SwTextBoxNode::AddTextBox(SdrObject* pDrawObject, SwFrameFormat* pNewTextBox)
+{
+ assert(pNewTextBox);
+ assert(pNewTextBox->Which() == RES_FLYFRMFMT);
+
+ assert(pDrawObject);
+
+ SwTextBoxElement aElem;
+ aElem.m_pDrawObject = pDrawObject;
+ aElem.m_pTextBoxFormat = pNewTextBox;
+
+ for (const auto& rE : m_pTextBoxes)
+ {
+ if (rE.m_pDrawObject == pDrawObject || rE.m_pTextBoxFormat == pNewTextBox)
+ {
+ SAL_WARN("sw.core", "SwTextBoxNode::AddTextBox(): Already exist!");
+ return;
+ }
+ }
+
+ auto pSwFlyDraw = dynamic_cast<SwFlyDrawObj*>(pDrawObject);
+ if (pSwFlyDraw)
+ {
+ pSwFlyDraw->SetTextBox(true);
+ }
+ m_pTextBoxes.push_back(aElem);
+}
+
+void SwTextBoxNode::DelTextBox(const SdrObject* pDrawObject, bool bDelFromDoc)
+{
+ assert(pDrawObject);
+ if (m_pTextBoxes.empty())
+ return;
+
+ for (auto it = m_pTextBoxes.begin(); it != m_pTextBoxes.end();)
+ {
+ if (it->m_pDrawObject == pDrawObject)
+ {
+ if (bDelFromDoc)
+ {
+ it->m_pTextBoxFormat->GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat(
+ it->m_pTextBoxFormat);
+ // What about m_pTextBoxes? So, when the DelLayoutFormat() removes the format
+ // then the ~SwFrameFormat() will call this method again to remove the entry.
+ return;
+ }
+ else
+ {
+ it = m_pTextBoxes.erase(it);
+ return;
+ }
+ }
+ ++it;
+ }
+
+ SAL_WARN("sw.core", "SwTextBoxNode::DelTextBox(): Not found!");
+}
+
+void SwTextBoxNode::DelTextBox(const SwFrameFormat* pTextBox, bool bDelFromDoc)
+{
+ if (m_pTextBoxes.empty())
+ return;
+
+ for (auto it = m_pTextBoxes.begin(); it != m_pTextBoxes.end();)
+ {
+ if (it->m_pTextBoxFormat == pTextBox)
+ {
+ if (bDelFromDoc)
+ {
+ it->m_pTextBoxFormat->GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat(
+ it->m_pTextBoxFormat);
+ // What about m_pTextBoxes? So, when the DelLayoutFormat() removes the format
+ // then the ~SwFrameFormat() will call this method again to remove the entry.
+ return;
+ }
+ else
+ {
+ it = m_pTextBoxes.erase(it);
+ return;
+ }
+ }
+ ++it;
+ }
+
+ SAL_WARN("sw.core", "SwTextBoxNode::DelTextBox(): Not found!");
+}
+
+SwFrameFormat* SwTextBoxNode::GetTextBox(const SdrObject* pDrawObject) const
+{
+ assert(pDrawObject);
+ assert(m_pOwnerShapeFormat);
+
+ if (auto& pTextBoxes = m_pOwnerShapeFormat->GetOtherTextBoxFormats())
+ {
+ if (size_t(pTextBoxes.use_count()) != pTextBoxes->GetTextBoxCount() + size_t(1))
+ {
+ SAL_WARN("sw.core", "SwTextBoxNode::GetTextBox(): RefCount and TexBox count mismatch!");
+ assert(false);
+ }
+ }
+
+ if (m_bLock)
+ return nullptr;
+
+ if (!m_pTextBoxes.empty())
+ {
+ for (auto it = m_pTextBoxes.begin(); it != m_pTextBoxes.end(); it++)
+ {
+ if (it->m_pDrawObject == pDrawObject)
+ {
+ return it->m_pTextBoxFormat;
+ }
+ }
+ SAL_WARN("sw.core", "SwTextBoxNode::GetTextBox(): Not found!");
+ }
+
+ return nullptr;
+}
+
+void SwTextBoxNode::ClearAll()
+{
+ // If this called from ~SwDoc(), then only the address entries
+ // have to be removed, the format will be deleted by the
+ // the mpSpzFrameFormatTable->DeleteAndDestroyAll() in ~SwDoc()!
+ if (m_pOwnerShapeFormat->GetDoc()->IsInDtor())
+ {
+ m_pTextBoxes.clear();
+ return;
+ }
+
+ // For loop control
+ sal_uInt16 nLoopCount = 0;
+
+ // Reference not enough, copy needed.
+ const size_t nTextBoxCount = m_pTextBoxes.size();
+
+ // For loop has problems: When one entry deleted, the iterator has
+ // to be refreshed according to the new situation. So using While() instead.
+ while (!m_pTextBoxes.empty())
+ {
+ // Delete the last textbox of the vector from the doc
+ // (what will call deregister in ~SwFrameFormat()
+ m_pOwnerShapeFormat->GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat(
+ m_pTextBoxes.back().m_pTextBoxFormat);
+
+ // Check if we are looping
+ if (nLoopCount > (nTextBoxCount + 1))
+ {
+ SAL_WARN("sw.core", "SwTextBoxNode::ClearAll(): Maximum loop count reached!");
+ break;
+ }
+ else
+ {
+ nLoopCount++;
+ }
+ }
+
+ // Ensure the vector is empty.
+ if (!m_pTextBoxes.empty())
+ {
+ SAL_WARN("sw.core", "SwTextBoxNode::ClearAll(): Text-Box-Vector still not empty!");
+ assert(false);
+ }
+}
+
+bool SwTextBoxNode::IsGroupTextBox() const { return m_pTextBoxes.size() > 1; }
+
+std::map<SdrObject*, SwFrameFormat*> SwTextBoxNode::GetAllTextBoxes() const
+{
+ std::map<SdrObject*, SwFrameFormat*> aRet;
+ for (auto& rElem : m_pTextBoxes)
+ {
+ aRet.emplace(rElem.m_pDrawObject, rElem.m_pTextBoxFormat);
+ }
+ return aRet;
+}
+
+void SwTextBoxNode::Clone(SwDoc* pDoc, const SwFormatAnchor& rNewAnc, SwFrameFormat* o_pTarget,
+ bool bSetAttr, bool bMakeFrame) const
+{
+ if (!o_pTarget || !pDoc)
+ return;
+
+ if (o_pTarget->Which() != RES_DRAWFRMFMT)
+ return;
+
+ if (m_bIsCloningInProgress)
+ return;
+
+ m_bIsCloningInProgress = true;
+
+ Clone_Impl(pDoc, rNewAnc, o_pTarget, m_pOwnerShapeFormat->FindSdrObject(),
+ o_pTarget->FindSdrObject(), bSetAttr, bMakeFrame);
+
+ m_bIsCloningInProgress = false;
+
+ for (auto& rElem : m_pTextBoxes)
+ {
+ SwTextBoxHelper::changeAnchor(m_pOwnerShapeFormat, rElem.m_pDrawObject);
+ SwTextBoxHelper::doTextBoxPositioning(m_pOwnerShapeFormat, rElem.m_pDrawObject);
+ SwTextBoxHelper::DoTextBoxZOrderCorrection(m_pOwnerShapeFormat, rElem.m_pDrawObject);
+ SwTextBoxHelper::syncTextBoxSize(m_pOwnerShapeFormat, rElem.m_pDrawObject);
+ }
+}
+
+void SwTextBoxNode::Clone_Impl(SwDoc* pDoc, const SwFormatAnchor& rNewAnc, SwFrameFormat* o_pTarget,
+ const SdrObject* pSrcObj, SdrObject* pDestObj, bool bSetAttr,
+ bool bMakeFrame) const
+{
+ if (!pSrcObj || !pDestObj)
+ return;
+
+ auto pSrcList = pSrcObj->getChildrenOfSdrObject();
+ auto pDestList = pDestObj->getChildrenOfSdrObject();
+
+ if (pSrcList && pDestList)
+ {
+ if (pSrcList->GetObjCount() != pDestList->GetObjCount())
+ {
+ SAL_WARN("sw.core", "SwTextBoxNode::Clone_Impl(): Difference between the shapes!");
+ return;
+ }
+
+ for (size_t i = 0; i < pSrcList->GetObjCount(); ++i)
+ {
+ Clone_Impl(pDoc, rNewAnc, o_pTarget, pSrcList->GetObj(i), pDestList->GetObj(i),
+ bSetAttr, bMakeFrame);
+ }
+ return;
+ }
+
+ if (!pSrcList && !pDestList)
+ {
+ if (auto pSrcFormat = GetTextBox(pSrcObj))
+ {
+ SwFormatAnchor aNewAnchor(rNewAnc);
+ if (aNewAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR)
+ {
+ aNewAnchor.SetType(RndStdIds::FLY_AT_CHAR);
+
+ if (!bMakeFrame)
+ bMakeFrame = true;
+ }
+
+ if (auto pTargetFormat = pDoc->getIDocumentLayoutAccess().CopyLayoutFormat(
+ *pSrcFormat, aNewAnchor, bSetAttr, bMakeFrame))
+ {
+ if (!o_pTarget->GetOtherTextBoxFormats())
+ {
+ auto pNewTextBoxes = std::make_shared<SwTextBoxNode>(SwTextBoxNode(o_pTarget));
+ o_pTarget->SetOtherTextBoxFormats(pNewTextBoxes);
+ pNewTextBoxes->AddTextBox(pDestObj, pTargetFormat);
+ pTargetFormat->SetOtherTextBoxFormats(pNewTextBoxes);
+ }
+ else
+ {
+ o_pTarget->GetOtherTextBoxFormats()->AddTextBox(pDestObj, pTargetFormat);
+ pTargetFormat->SetOtherTextBoxFormats(o_pTarget->GetOtherTextBoxFormats());
+ }
+ o_pTarget->SetFormatAttr(pTargetFormat->GetContent());
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */