7e4f4a0ccd
This change adds loext:margin-left and loext:margin-right, which implement margins that support font-relative units. See tdf#36709 for additional details. Change-Id: I31b0dd2b6f98cb5b02fd4dca3608db6fdee4054c Reviewed-on: https://gerrit.libreoffice.org/c/core/+/177473 Tested-by: Jenkins Reviewed-by: Jonathan Clark <jonathan@libreoffice.org>
2055 lines
78 KiB
C++
2055 lines
78 KiB
C++
/* -*- 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 <dcontact.hxx>
|
|
#include <fmtcntnt.hxx>
|
|
#include <fmtanchr.hxx>
|
|
#include <fmtcnct.hxx>
|
|
#include <fmtornt.hxx>
|
|
#include <fmtfsize.hxx>
|
|
#include <doc.hxx>
|
|
#include <IDocumentLayoutAccess.hxx>
|
|
#include <IDocumentSettingAccess.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 <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>
|
|
#include <unotxdoc.hxx>
|
|
|
|
using namespace com::sun::star;
|
|
|
|
void SwTextBoxHelper::create(SwFrameFormat* pShape, SdrObject* pObject, bool bCopyText)
|
|
{
|
|
assert(pShape);
|
|
assert(pObject);
|
|
assert(pShape == ::FindFrameFormat(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::XTextRange> xAnchor;
|
|
uno::Reference<text::XTextContent> xAnchorProvider(pObject->getWeakUnoShape().get(),
|
|
uno::UNO_QUERY);
|
|
assert(xAnchorProvider.is());
|
|
if (xAnchorProvider.is())
|
|
xAnchor = xAnchorProvider->getAnchor();
|
|
|
|
uno::Reference<text::XTextContentAppend> xTextContentAppend;
|
|
if (xAnchor)
|
|
xTextContentAppend.set(xAnchor->getText(), uno::UNO_QUERY);
|
|
|
|
if (!xTextContentAppend)
|
|
{
|
|
if (SwDocShell* pShell = pShape->GetDoc()->GetDocShell())
|
|
{
|
|
rtl::Reference<SwXTextDocument> xTextDocument(pShell->GetBaseModel());
|
|
xTextContentAppend.set(xTextDocument->getText(), uno::UNO_QUERY_THROW);
|
|
}
|
|
}
|
|
|
|
if (xAnchor)
|
|
{
|
|
// insertTextContentWithProperties would fail if xAnchor is in a different XText
|
|
assert(xAnchor->getText() == xTextContentAppend);
|
|
xTextContentAppend->insertTextContentWithProperties(xTextFrame, {}, xAnchor);
|
|
}
|
|
else
|
|
{
|
|
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.GetPointNode().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);
|
|
// tdf#162075 shape word wrap to frame width type on shape creation.
|
|
bool bTextWordwrap = xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_WORDWRAP).get<bool>();
|
|
syncProperty(pShape, RES_FRM_SIZE, MID_FRMSIZE_WIDTH_TYPE,
|
|
uno::Any(bTextWordwrap ? text::SizeType::FIX : text::SizeType::MIN), 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 = DynCastSdrTextObj(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,
|
|
const 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)
|
|
{
|
|
DBG_TESTSOLARMUTEX();
|
|
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 (const rtl::Reference<SdrObject>& p : *pPage)
|
|
{
|
|
assert(p);
|
|
if (p->IsTextBox())
|
|
continue;
|
|
++nRet;
|
|
}
|
|
return nRet;
|
|
}
|
|
|
|
sal_Int32 SwTextBoxHelper::getCount(const SwDoc& rDoc)
|
|
{
|
|
sal_Int32 nRet = 0;
|
|
for (const sw::SpzFrameFormat* pFormat : *rDoc.GetSpzFrameFormats())
|
|
{
|
|
if (isTextBox(pFormat, RES_FLYFRMFMT))
|
|
++nRet;
|
|
}
|
|
return nRet;
|
|
}
|
|
|
|
uno::Any SwTextBoxHelper::getByIndex(SdrPage const* pPage, sal_Int32 nIndex)
|
|
{
|
|
if (nIndex < 0)
|
|
throw lang::IndexOutOfBoundsException();
|
|
|
|
sal_Int32 nCount = 0; // Current logical index.
|
|
for (const rtl::Reference<SdrObject>& p : *pPage)
|
|
{
|
|
assert(p);
|
|
if (p->IsTextBox())
|
|
continue;
|
|
if (nCount == nIndex)
|
|
return uno::Any(p->getUnoShape());
|
|
++nCount;
|
|
}
|
|
|
|
throw lang::IndexOutOfBoundsException();
|
|
}
|
|
|
|
sal_Int32 SwTextBoxHelper::getOrdNum(const SdrObject* pObject)
|
|
{
|
|
if (const SdrPage* pPage = pObject->getSdrPageFromSdrObject())
|
|
{
|
|
sal_Int32 nOrder = 0; // Current logical order.
|
|
for (const rtl::Reference<SdrObject>& p : *pPage)
|
|
{
|
|
assert(p);
|
|
if (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(
|
|
getXWeak(SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat).get()),
|
|
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;
|
|
|
|
// Older documents or documents in ODF strict do not have WritingMode, but have used the
|
|
// TextRotateAngle values -90 and -270 to emulate these text directions of frames.
|
|
// ToDo: Is TextPreRotateAngle needed for diagrams or can it be removed?
|
|
comphelper::SequenceAsHashMap aCustomShapeGeometry(rValue);
|
|
auto it = aCustomShapeGeometry.find(u"TextPreRotateAngle"_ustr);
|
|
if (it == aCustomShapeGeometry.end())
|
|
{
|
|
it = aCustomShapeGeometry.find(u"TextRotateAngle"_ustr);
|
|
}
|
|
|
|
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_RL90;
|
|
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 if (rPropertyName == u"WritingMode")
|
|
{
|
|
sal_Int16 eMode2;
|
|
if (rValue >>= eMode2)
|
|
syncProperty(pShape, RES_FRAMEDIR, 0, uno::Any(eMode2), pObj);
|
|
}
|
|
else if (rPropertyName == u"TextWordWrap")
|
|
{
|
|
// tdf#81567 shape word wrap to frame width type on shape update.
|
|
bool bTextWordwrap{};
|
|
if (rValue >>= bTextWordwrap)
|
|
{
|
|
syncProperty(pShape, RES_FRM_SIZE, MID_FRMSIZE_WIDTH_TYPE,
|
|
uno::Any(bTextWordwrap ? text::SizeType::FIX : text::SizeType::MIN), 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 {};
|
|
|
|
rtl::Reference<SwXTextFrame> xPropertySet
|
|
= SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat);
|
|
|
|
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.getOpenWidth()),
|
|
convertTwipToMm100(aRect.getOpenHeight()));
|
|
aValue <<= aSize;
|
|
}
|
|
}
|
|
}
|
|
auto aGuard = SwTextBoxLockGuard(*pShape->GetOtherTextBoxFormats());
|
|
rtl::Reference<SwXTextFrame> const xPropertySet
|
|
= SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat);
|
|
xPropertySet->setPropertyValue(aPropertyName, aValue);
|
|
}
|
|
|
|
void SwTextBoxHelper::saveLinks(const sw::FrameFormats<sw::SpzFrameFormat*>& 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().GetAnchorNode()
|
|
&& rShape.GetAnchor().GetAnchorNode()->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.getOpenHeight());
|
|
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.getOpenWidth());
|
|
aSize.SetHeight(aRect.getOpenHeight());
|
|
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());
|
|
rtl::Reference<SwXTextFrame> const xPropertySet
|
|
= SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat);
|
|
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());
|
|
|
|
// Set the textbox position at the X-axis:
|
|
SwFormatHoriOrient aNewHOri(pFormat->GetHoriOrient());
|
|
if (bIsGroupObj && aNewHOri.GetHoriOrient() != text::HoriOrientation::NONE)
|
|
aNewHOri.SetHoriOrient(text::HoriOrientation::NONE);
|
|
|
|
// tdf#152142: For RTL, positioning is relative to the right
|
|
if (pShape->GetLayoutDir() == SwFrameFormat::HORI_R2L)
|
|
{
|
|
auto nRightSpace = pShape->GetLRSpace().ResolveRight({});
|
|
|
|
const bool bMSOLayout = pFormat->getIDocumentSettingAccess().get(
|
|
DocumentSettingId::DO_NOT_MIRROR_RTL_DRAW_OBJS);
|
|
if (bMSOLayout)
|
|
{
|
|
aNewHOri.SetPos(-aRect.Right() + nRightSpace
|
|
+ (bIsGroupObj ? pObj->GetRelativePos().getX() : 0));
|
|
}
|
|
else
|
|
{
|
|
aNewHOri.SetPos(aRect.Right() + nRightSpace
|
|
+ (bIsGroupObj ? pObj->GetRelativePos().getX() : 0));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
auto nLeftSpace = pShape->GetLRSpace().ResolveLeft({});
|
|
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());
|
|
|
|
// 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().GetAnchorNode()
|
|
&& pShape->GetAnchor().GetAnchorNode()->FindTableNode())
|
|
{
|
|
// WARNING: It is highly likely that everything here is simplistic and incomplete.
|
|
|
|
// Microsoft allows WrapThrough shapes to be placed outside of the cell
|
|
// despite having specified layoutInCell.
|
|
// (Re-using existing, appropriately-named, compat flag to identify MSO formats.)
|
|
const bool bMSOLayout = pFormat->getIDocumentSettingAccess().get(
|
|
DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION);
|
|
|
|
// Table position
|
|
Point nTableOffset;
|
|
// Floating table
|
|
if (auto pFly
|
|
= pShape->GetAnchor().GetAnchorNode()->FindTableNode()->FindFlyStartNode())
|
|
{
|
|
if (auto pFlyFormat = pFly->GetFlyFormat())
|
|
{
|
|
nTableOffset.setX(pFlyFormat->GetHoriOrient().GetPos());
|
|
nTableOffset.setY(pFlyFormat->GetVertOrient().GetPos());
|
|
}
|
|
}
|
|
else
|
|
// Normal table
|
|
{
|
|
auto pTableNode = pShape->GetAnchor().GetAnchorNode()->FindTableNode();
|
|
if (auto pTableFormat = pTableNode->GetTable().GetFrameFormat())
|
|
{
|
|
nTableOffset.setX(pTableFormat->GetHoriOrient().GetPos());
|
|
nTableOffset.setY(pTableFormat->GetVertOrient().GetPos());
|
|
}
|
|
}
|
|
|
|
// stay within the cell limits (since following text flow)
|
|
// unless this is based on a Microsoft layout which has a through-wrap exception.
|
|
bool bWrapThrough = false;
|
|
getShapeWrapThrough(pShape, bWrapThrough);
|
|
sal_Int32 nPos = aNewHOri.GetPos();
|
|
if (nPos < 0 && (!bMSOLayout || !bWrapThrough))
|
|
nPos = 0;
|
|
// Add the table positions to the textbox
|
|
aNewHOri.SetPos(nPos + nTableOffset.getX());
|
|
|
|
if (pShape->GetVertOrient().GetRelationOrient() == text::RelOrientation::PAGE_FRAME
|
|
|| pShape->GetVertOrient().GetRelationOrient()
|
|
== text::RelOrientation::PAGE_PRINT_AREA)
|
|
{
|
|
nPos = aNewVOri.GetPos();
|
|
if (nPos < 0 && (!bMSOLayout || !bWrapThrough))
|
|
nPos = 0;
|
|
aNewVOri.SetPos(nPos + 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 aSize = getRelativeTextRectangle(pObj).GetSize();
|
|
if (!aSize.IsEmpty())
|
|
{
|
|
SwFormatFrameSize aFrameSize(pTextBox->GetFrameSize());
|
|
aFrameSize.SetSize(aSize);
|
|
return pTextBox->SetFormatAttr(aFrameSize);
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
SAL_WARN("sw.core", "SwTextBoxHelper::DoTextBoxZOrderCorrection(): "
|
|
"No Valid SdrObject for the shape!");
|
|
return false;
|
|
}
|
|
|
|
auto pTextBox = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj);
|
|
if (!pTextBox)
|
|
return false;
|
|
SdrObject* pFrmObj = pTextBox->FindRealSdrObject();
|
|
if (!pFrmObj)
|
|
{
|
|
if (SwFlyFrameFormat* pFlyFrameFormat = dynamic_cast<SwFlyFrameFormat*>(pTextBox))
|
|
{
|
|
// During loading there is no ready SdrObj for z-ordering, so create and cache it here
|
|
pFrmObj = SwXTextFrame::GetOrCreateSdrObject(*pFlyFrameFormat);
|
|
}
|
|
}
|
|
if (!pFrmObj)
|
|
{
|
|
SAL_WARN("sw.core", "SwTextBoxHelper::DoTextBoxZOrderCorrection(): "
|
|
"No Valid SdrObject for the frame!");
|
|
return false;
|
|
}
|
|
// Get the draw model from the doc
|
|
SwDrawModel* pDrawModel = pShape->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel();
|
|
if (!pDrawModel)
|
|
{
|
|
SAL_WARN("sw.core", "SwTextBoxHelper::DoTextBoxZOrderCorrection(): "
|
|
"No Valid Draw model for SdrObject for the shape!");
|
|
return false;
|
|
}
|
|
if (!pFrmObj->getParentSdrObjListFromSdrObject())
|
|
{
|
|
SAL_WARN("sw.core", "SwTextBoxHelper::DoTextBoxZOrderCorrection(): "
|
|
"Frame object is not inserted into any parent");
|
|
return false;
|
|
}
|
|
// Not really sure this will work on all pages, but it seems it will.
|
|
// 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.
|
|
pFrmObj->ensureSortedImmediatelyAfter(*pShpObj);
|
|
return true; // Success
|
|
}
|
|
|
|
void SwTextBoxHelper::synchronizeGroupTextBoxProperty(bool pFunc(SwFrameFormat*, SdrObject*),
|
|
SwFrameFormat* pFormat, SdrObject* pObj)
|
|
{
|
|
if (auto pChildren = pObj->getChildrenOfSdrObject())
|
|
{
|
|
for (const rtl::Reference<SdrObject>& pChildObj : *pChildren)
|
|
synchronizeGroupTextBoxProperty(pFunc, pFormat, pChildObj.get());
|
|
}
|
|
else
|
|
{
|
|
(*pFunc)(pFormat, pObj);
|
|
}
|
|
}
|
|
|
|
std::vector<SwFrameFormat*> SwTextBoxHelper::CollectTextBoxes(const SdrObject* pGroupObject,
|
|
SwFrameFormat* pFormat)
|
|
{
|
|
std::vector<SwFrameFormat*> vRet;
|
|
if (auto pChildren = pGroupObject->getChildrenOfSdrObject())
|
|
{
|
|
for (const rtl::Reference<SdrObject>& pObj : *pChildren)
|
|
{
|
|
auto pChildTextBoxes = CollectTextBoxes(pObj.get(), 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.GetAnchorNode() && rFrameAnchor.GetAnchorNode())
|
|
{
|
|
if (*rShapeAnchor.GetContentAnchor() != *rFrameAnchor.GetContentAnchor())
|
|
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.GetAnchorNode() && rFrameAnchor.GetAnchorNode())
|
|
{
|
|
if (*rShapeAnchor.GetContentAnchor() != *rFrameAnchor.GetContentAnchor())
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool SwTextBoxHelper::TextBoxIsFramePr(const SwFrameFormat& rFrameFormat)
|
|
{
|
|
SdrObject* pSdrObj = const_cast<SdrObject*>(rFrameFormat.FindRealSdrObject());
|
|
if (!pSdrObj)
|
|
return false;
|
|
|
|
uno::Reference<beans::XPropertySet> xPropertySet(pSdrObj->getUnoShape(), uno::UNO_QUERY);
|
|
if (!xPropertySet.is())
|
|
return false;
|
|
|
|
uno::Reference<beans::XPropertySetInfo> xPropSetInfo(xPropertySet->getPropertySetInfo());
|
|
if (!xPropSetInfo.is() || !xPropSetInfo->hasPropertyByName(u"FrameInteropGrabBag"_ustr))
|
|
return false;
|
|
|
|
bool bRet = false;
|
|
uno::Sequence<beans::PropertyValue> propList;
|
|
xPropertySet->getPropertyValue(u"FrameInteropGrabBag"_ustr) >>= propList;
|
|
auto pProp = std::find_if(
|
|
std::cbegin(propList), std::cend(propList),
|
|
[](const beans::PropertyValue& rProp) { return rProp.Name == "ParaFrameProperties"; });
|
|
if (pProp != std::cend(propList))
|
|
pProp->Value >>= bRet;
|
|
|
|
return bRet;
|
|
}
|
|
|
|
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.empty())
|
|
{
|
|
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!");
|
|
}
|
|
}
|
|
|
|
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 (auto itSrc = pSrcList->begin(), itDest = pDestList->begin(); itSrc != pSrcList->end();
|
|
++itSrc, ++itDest)
|
|
{
|
|
Clone_Impl(pDoc, rNewAnc, o_pTarget, itSrc->get(), itDest->get(), 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: */
|