/* -*- 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 <ndtxt.hxx>
#include <docsh.hxx>
#include <unocoll.hxx>
#include <unoframe.hxx>
#include <unodraw.hxx>
#include <unotextrange.hxx>
#include <cmdid.h>
#include <unoprnms.hxx>
#include <mvsave.hxx>
#include <fmtsrnd.hxx>
#include <frmfmt.hxx>
 
#include <editeng/unoprnms.hxx>
#include <editeng/charrotateitem.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 <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/TextContentAnchorType.hpp>
#include <com/sun/star/text/WrapTextMode.hpp>
#include <com/sun/star/text/XTextDocument.hpp>
#include <com/sun/star/table/BorderLine2.hpp>
 
using namespace com::sun::star;
 
void SwTextBoxHelper::create(SwFrameFormat* pShape)
{
    // If TextBox wasn't enabled previously
    if (pShape->GetAttrSet().HasItem(RES_CNTNT))
        return;
 
    // 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));
 
    pShape->SetOtherTextBoxFormat(pFormat);
    pFormat->SetOtherTextBoxFormat(pShape);
 
    // Initialize properties.
    uno::Reference<beans::XPropertySet> xPropertySet(xTextFrame, uno::UNO_QUERY);
    uno::Any aEmptyBorder = uno::makeAny(table::BorderLine2());
    xPropertySet->setPropertyValue(UNO_NAME_TOP_BORDER, aEmptyBorder);
    xPropertySet->setPropertyValue(UNO_NAME_BOTTOM_BORDER, aEmptyBorder);
    xPropertySet->setPropertyValue(UNO_NAME_LEFT_BORDER, aEmptyBorder);
    xPropertySet->setPropertyValue(UNO_NAME_RIGHT_BORDER, aEmptyBorder);
 
    xPropertySet->setPropertyValue(UNO_NAME_FILL_TRANSPARENCE, uno::makeAny(sal_Int32(100)));
 
    xPropertySet->setPropertyValue(UNO_NAME_SIZE_TYPE, uno::makeAny(text::SizeType::FIX));
 
    xPropertySet->setPropertyValue(UNO_NAME_SURROUND, uno::makeAny(text::WrapTextMode_THROUGH));
 
    uno::Reference<container::XNamed> xNamed(xTextFrame, uno::UNO_QUERY);
    xNamed->setName(pShape->GetDoc()->GetUniqueFrameName());
 
    // 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);
    }
 
    // Also initialize the properties, which are not constant, but inherited from the shape's ones.
    uno::Reference<drawing::XShape> xShape(pShape->FindRealSdrObject()->getUnoShape(),
                                           uno::UNO_QUERY);
    syncProperty(pShape, RES_FRM_SIZE, MID_FRMSIZE_SIZE, uno::makeAny(xShape->getSize()));
 
    uno::Reference<beans::XPropertySet> xShapePropertySet(xShape, uno::UNO_QUERY);
    syncProperty(pShape, RES_HORI_ORIENT, MID_HORIORIENT_ORIENT,
                 xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT));
    syncProperty(pShape, RES_HORI_ORIENT, MID_HORIORIENT_RELATION,
                 xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT_RELATION));
    syncProperty(pShape, RES_VERT_ORIENT, MID_VERTORIENT_ORIENT,
                 xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT));
    syncProperty(pShape, RES_VERT_ORIENT, MID_VERTORIENT_RELATION,
                 xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT_RELATION));
    syncProperty(pShape, RES_HORI_ORIENT, MID_HORIORIENT_POSITION,
                 xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT_POSITION));
    syncProperty(pShape, RES_VERT_ORIENT, MID_VERTORIENT_POSITION,
                 xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT_POSITION));
    syncProperty(pShape, RES_FRM_SIZE, MID_FRMSIZE_IS_AUTO_HEIGHT,
                 xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_AUTOGROWHEIGHT));
    syncProperty(pShape, RES_TEXT_VERT_ADJUST, 0,
                 xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_VERT_ADJUST));
}
 
void SwTextBoxHelper::destroy(SwFrameFormat* pShape)
{
    // If a TextBox was enabled previously
    if (pShape->GetAttrSet().HasItem(RES_CNTNT))
    {
        SwFrameFormat* pFormat = pShape->GetOtherTextBoxFormat();
 
        // Unlink the TextBox's text range from the original shape.
        pShape->ResetFormatAttr(RES_CNTNT);
 
        // Delete the associated TextFrame.
        if (pFormat)
            pShape->GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat(pFormat);
    }
}
 
bool SwTextBoxHelper::isTextBox(const SwFrameFormat* pFormat, sal_uInt16 nType)
{
    assert(nType == RES_FLYFRMFMT || nType == RES_DRAWFRMFMT);
    if (!pFormat || pFormat->Which() != nType || !pFormat->GetAttrSet().HasItem(RES_CNTNT))
        return false;
 
    sal_uInt16 nOtherType = (pFormat->Which() == RES_FLYFRMFMT) ? sal_uInt16(RES_DRAWFRMFMT)
                                                                : sal_uInt16(RES_FLYFRMFMT);
    SwFrameFormat* pOtherFormat = pFormat->GetOtherTextBoxFormat();
    if (!pOtherFormat)
        return false;
 
    assert(pOtherFormat->Which() == nOtherType);
    if (pOtherFormat->Which() != nOtherType)
        return false;
 
    const SwFormatContent& rContent = pFormat->GetContent();
    return pOtherFormat->GetAttrSet().HasItem(RES_CNTNT) && pOtherFormat->GetContent() == rContent;
}
 
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* pDoc)
{
    sal_Int32 nRet = 0;
    const SwFrameFormats& rSpzFrameFormats = *pDoc->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::makeAny(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)
{
    if (!isTextBox(pFormat, nType))
        return nullptr;
    return pFormat->GetOtherTextBoxFormat();
}
 
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);
}
 
template <typename T> void lcl_queryInterface(SwFrameFormat* pShape, uno::Any& rAny)
{
    if (SwFrameFormat* pFormat = SwTextBoxHelper::getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT))
    {
        uno::Reference<T> const xInterface(
            SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat), uno::UNO_QUERY);
        rAny <<= xInterface;
    }
}
 
uno::Any SwTextBoxHelper::queryInterface(SwFrameFormat* pShape, const uno::Type& rType)
{
    uno::Any aRet;
 
    if (rType == cppu::UnoType<css::text::XTextAppend>::get())
    {
        lcl_queryInterface<text::XTextAppend>(pShape, aRet);
    }
    else if (rType == cppu::UnoType<css::text::XText>::get())
    {
        lcl_queryInterface<text::XText>(pShape, aRet);
    }
    else if (rType == cppu::UnoType<css::text::XTextRange>::get())
    {
        lcl_queryInterface<text::XTextRange>(pShape, aRet);
    }
 
    return aRet;
}
 
tools::Rectangle SwTextBoxHelper::getTextRectangle(SwFrameFormat* pShape, bool bAbsolute)
{
    tools::Rectangle aRet;
    aRet.SetEmpty();
    auto pCustomShape = dynamic_cast<SdrObjCustomShape*>(pShape->FindRealSdrObject());
    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);
    }
 
    if (!bAbsolute && pCustomShape)
    {
        // Relative, so count the logic (reference) rectangle, see the EnhancedCustomShape2d ctor.
        Point aPoint(pCustomShape->GetSnapRect().Center());
        Size aSize(pCustomShape->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, const OUString& rPropertyName,
                                   const css::uno::Any& rValue)
{
    if (rPropertyName == "CustomShapeGeometry")
    {
        // CustomShapeGeometry changes the textbox position offset and size, so adjust both.
        syncProperty(pShape, RES_FRM_SIZE, MID_FRMSIZE_SIZE, uno::Any());
 
        SdrObject* pObject = pShape->FindRealSdrObject();
        if (pObject)
        {
            tools::Rectangle aRectangle(pObject->GetSnapRect());
            syncProperty(
                pShape, RES_HORI_ORIENT, MID_HORIORIENT_POSITION,
                uno::makeAny(static_cast<sal_Int32>(convertTwipToMm100(aRectangle.Left()))));
            syncProperty(
                pShape, RES_VERT_ORIENT, MID_VERTORIENT_POSITION,
                uno::makeAny(static_cast<sal_Int32>(convertTwipToMm100(aRectangle.Top()))));
        }
 
        if (SwFrameFormat* pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT))
        {
            comphelper::SequenceAsHashMap aCustomShapeGeometry(rValue);
            // That would be the btLr text direction which we don't support at a frame level, so do it at a character level.
            if (aCustomShapeGeometry.find("TextPreRotateAngle") != aCustomShapeGeometry.end()
                && aCustomShapeGeometry["TextPreRotateAngle"].get<sal_Int32>() == -270)
            {
                if (const SwNodeIndex* pNodeIndex = pFormat->GetContent().GetContentIdx())
                {
                    SwPaM aPaM(*pFormat->GetDoc()->GetNodes()[pNodeIndex->GetIndex() + 1], 0);
                    aPaM.SetMark();
                    if (SwTextNode* pMark
                        = pFormat->GetDoc()
                              ->GetNodes()[pNodeIndex->GetNode().EndOfSectionIndex() - 1]
                              ->GetTextNode())
                    {
                        aPaM.GetMark()->nNode = *pMark;
                        aPaM.GetMark()->nContent.Assign(pMark, pMark->GetText().getLength());
                        SvxCharRotateItem aItem(900, false, RES_CHRATR_ROTATE);
                        pFormat->GetDoc()->getIDocumentContentOperations().InsertPoolItem(aPaM,
                                                                                          aItem);
                    }
                }
            }
        }
    }
    else if (rPropertyName == UNO_NAME_TEXT_VERT_ADJUST)
        syncProperty(pShape, RES_TEXT_VERT_ADJUST, 0, rValue);
    else if (rPropertyName == UNO_NAME_TEXT_AUTOGROWHEIGHT)
        syncProperty(pShape, RES_FRM_SIZE, MID_FRMSIZE_IS_AUTO_HEIGHT, rValue);
    else if (rPropertyName == UNO_NAME_TEXT_LEFTDIST)
        syncProperty(pShape, RES_BOX, LEFT_BORDER_DISTANCE, rValue);
    else if (rPropertyName == UNO_NAME_TEXT_RIGHTDIST)
        syncProperty(pShape, RES_BOX, RIGHT_BORDER_DISTANCE, rValue);
    else if (rPropertyName == UNO_NAME_TEXT_UPPERDIST)
        syncProperty(pShape, RES_BOX, TOP_BORDER_DISTANCE, rValue);
    else if (rPropertyName == UNO_NAME_TEXT_LOWERDIST)
        syncProperty(pShape, RES_BOX, BOTTOM_BORDER_DISTANCE, rValue);
}
 
void SwTextBoxHelper::getProperty(SwFrameFormat const* pShape, sal_uInt16 nWID, sal_uInt8 nMemberID,
                                  css::uno::Any& rValue)
{
    if (!pShape)
        return;
 
    nMemberID &= ~CONVERT_TWIPS;
 
    if (SwFrameFormat* pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT))
    {
        if (nWID == RES_CHAIN)
        {
            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;
            }
        }
    }
}
 
void SwTextBoxHelper::syncProperty(SwFrameFormat* pShape, sal_uInt16 nWID, sal_uInt8 nMemberID,
                                   const css::uno::Any& rValue)
{
    // No shape yet? Then nothing to do, initial properties are set by create().
    if (!pShape)
        return;
 
    uno::Any aValue(rValue);
    nMemberID &= ~CONVERT_TWIPS;
 
    if (SwFrameFormat* pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT))
    {
        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:
                        aPropertyName = UNO_NAME_HORI_ORIENT_RELATION;
                        break;
                    case MID_HORIORIENT_POSITION:
                        aPropertyName = UNO_NAME_HORI_ORIENT_POSITION;
                        bAdjustX = true;
                        break;
                }
                break;
            case RES_VERT_ORIENT:
                switch (nMemberID)
                {
                    case MID_VERTORIENT_ORIENT:
                        aPropertyName = UNO_NAME_VERT_ORIENT;
                        break;
                    case MID_VERTORIENT_RELATION:
                        aPropertyName = UNO_NAME_VERT_ORIENT_RELATION;
                        break;
                    case MID_VERTORIENT_POSITION:
                        aPropertyName = UNO_NAME_VERT_ORIENT_POSITION;
                        bAdjustY = true;
                        break;
                }
                break;
            case RES_FRM_SIZE:
                switch (nMemberID)
                {
                    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:
                        if (aValue.get<text::TextContentAnchorType>()
                            == text::TextContentAnchorType_AS_CHARACTER)
                        {
                            uno::Reference<beans::XPropertySet> const xPropertySet(
                                SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat),
                                uno::UNO_QUERY);
                            xPropertySet->setPropertyValue(
                                UNO_NAME_SURROUND, uno::makeAny(text::WrapTextMode_THROUGH));
                            return;
                        }
                        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;
                }
                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;
                }
                break;
            case RES_OPAQUE:
                aPropertyName = UNO_NAME_OPAQUE;
                break;
        }
 
        if (!aPropertyName.isEmpty())
        {
            // Position/size should be the text position/size, not the shape one as-is.
            if (bAdjustX || bAdjustY || bAdjustSize)
            {
                tools::Rectangle aRect = getTextRectangle(pShape, /*bAbsolute=*/false);
                if (!aRect.IsEmpty())
                {
                    if (bAdjustX || bAdjustY)
                    {
                        sal_Int32 nValue;
                        if (aValue >>= nValue)
                        {
                            if (bAdjustX)
                                nValue += TWIPS_TO_MM(aRect.getX());
                            else if (bAdjustY)
                                nValue += TWIPS_TO_MM(aRect.getY());
                            aValue <<= nValue;
                        }
                    }
                    else if (bAdjustSize)
                    {
                        awt::Size aSize(TWIPS_TO_MM(aRect.getWidth()),
                                        TWIPS_TO_MM(aRect.getHeight()));
                        aValue <<= aSize;
                    }
                }
            }
 
            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::resetLink(SwFrameFormat* pShape,
                                std::map<const SwFrameFormat*, SwFormatContent>& rOldContent)
{
    if (pShape->Which() == RES_DRAWFRMFMT)
    {
        if (pShape->GetContent().GetContentIdx())
            rOldContent.insert(std::make_pair(pShape, pShape->GetContent()));
        pShape->ResetFormatAttr(RES_CNTNT);
    }
}
 
void SwTextBoxHelper::restoreLinks(std::set<ZSortFly>& rOld, std::vector<SwFrameFormat*>& rNew,
                                   SavedLink& rSavedLinks, SavedContent& rResetContent)
{
    std::size_t i = 0;
    for (auto aSetIt = rOld.begin(); aSetIt != rOld.end(); ++aSetIt, ++i)
    {
        auto aTextBoxIt = rSavedLinks.find(aSetIt->GetFormat());
        if (aTextBoxIt != rSavedLinks.end())
        {
            std::size_t j = 0;
            for (auto aSetJt = rOld.begin(); aSetJt != rOld.end(); ++aSetJt, ++j)
            {
                if (aSetJt->GetFormat() == aTextBoxIt->second)
                    rNew[i]->SetFormatAttr(rNew[j]->GetContent());
            }
        }
        if (rResetContent.find(aSetIt->GetFormat()) != rResetContent.end())
            const_cast<SwFrameFormat*>(aSetIt->GetFormat())
                ->SetFormatAttr(rResetContent[aSetIt->GetFormat()]);
    }
}
 
void SwTextBoxHelper::syncFlyFrameAttr(SwFrameFormat& rShape, SfxItemSet const& rSet)
{
    if (SwFrameFormat* pFormat = getOtherTextBoxFormat(&rShape, RES_DRAWFRMFMT))
    {
        SfxItemSet aTextBoxSet(pFormat->GetDoc()->GetAttrPool(), aFrameFormatSetRange);
 
        SfxItemIter aIter(rSet);
        sal_uInt16 nWhich = aIter.GetCurItem()->Which();
        do
        {
            switch (nWhich)
            {
                case RES_VERT_ORIENT:
                {
                    auto& rOrient = static_cast<const SwFormatVertOrient&>(*aIter.GetCurItem());
                    SwFormatVertOrient aOrient(rOrient);
 
                    tools::Rectangle aRect = getTextRectangle(&rShape, /*bAbsolute=*/false);
                    if (!aRect.IsEmpty())
                        aOrient.SetPos(aOrient.GetPos() + aRect.getY());
 
                    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:
                {
                    auto& rOrient = static_cast<const SwFormatHoriOrient&>(*aIter.GetCurItem());
                    SwFormatHoriOrient aOrient(rOrient);
 
                    tools::Rectangle aRect = getTextRectangle(&rShape, /*bAbsolute=*/false);
                    if (!aRect.IsEmpty())
                        aOrient.SetPos(aOrient.GetPos() + aRect.getX());
 
                    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 = getTextRectangle(&rShape, /*bAbsolute=*/false);
                    if (!aRect.IsEmpty())
                    {
                        aVertOrient.SetPos(aVertOrient.GetPos() + aRect.getY());
                        aTextBoxSet.Put(aVertOrient);
 
                        aHoriOrient.SetPos(aHoriOrient.GetPos() + aRect.getX());
                        aTextBoxSet.Put(aHoriOrient);
 
                        aSize.SetWidth(aRect.getWidth());
                        aSize.SetHeight(aRect.getHeight());
                        aTextBoxSet.Put(aSize);
                    }
                }
                break;
                default:
                    SAL_WARN("sw.core",
                             "SwTextBoxHelper::syncFlyFrameAttr: unhandled which-id: " << nWhich);
                    break;
            }
 
            if (aIter.IsAtEnd())
                break;
        } while (0 != (nWhich = aIter.NextItem()->Which()));
 
        if (aTextBoxSet.Count())
            pFormat->GetDoc()->SetFlyFrameAttr(*pFormat, aTextBoxSet);
    }
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V522 There might be dereferencing of a potential null pointer 'pTextFrame'.