tdf#137802 tdf#84691 sw: sync anchoring of textbox with UNO

It was possible to anchor the textbox both to page and
paragraph, resulting crash during file saving.

Change-Id: I0c95a13c0d8d58cd7cc1fa86de1b80bf088ba782
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/105046
Tested-by: László Németh <nemeth@numbertext.org>
Reviewed-by: László Németh <nemeth@numbertext.org>
diff --git a/sw/qa/extras/uiwriter/data3/AtPageTextBoxCrash.odt b/sw/qa/extras/uiwriter/data3/AtPageTextBoxCrash.odt
new file mode 100644
index 0000000..20e58df
--- /dev/null
+++ b/sw/qa/extras/uiwriter/data3/AtPageTextBoxCrash.odt
Binary files differ
diff --git a/sw/qa/extras/uiwriter/uiwriter3.cxx b/sw/qa/extras/uiwriter/uiwriter3.cxx
index 09cad81..841b034 100644
--- a/sw/qa/extras/uiwriter/uiwriter3.cxx
+++ b/sw/qa/extras/uiwriter/uiwriter3.cxx
@@ -1856,6 +1856,45 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest3, testTdf127652)
    CPPUNIT_ASSERT_EQUAL_MESSAGE("We are on the wrong page!", assertPage, currentPage);
}

CPPUNIT_TEST_FIXTURE(SwUiWriterTest3, AtPageTextBoxCrash)
{
    // Load sample file
    load(DATA_DIRECTORY, "AtPageTextBoxCrash.odt");
    SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
    CPPUNIT_ASSERT(pTextDoc);

    // Get the Writer-Shell for later use
    SwWrtShell* pWrtSh = pTextDoc->GetDocShell()->GetWrtShell();
    CPPUNIT_ASSERT(pWrtSh);

    // Get the format of the shape
    const SwFrameFormats& rFrmFormats = *pWrtSh->GetDoc()->GetSpzFrameFormats();
    CPPUNIT_ASSERT(rFrmFormats.size() >= size_t(o3tl::make_unsigned(1)));
    auto pShape = rFrmFormats.front();
    CPPUNIT_ASSERT(pShape);

    // Add a textbox to the shape
    SwTextBoxHelper::create(pShape);
    auto pTxBxFrm = SwTextBoxHelper::getOtherTextBoxFormat(getShape(1));
    CPPUNIT_ASSERT(pTxBxFrm);

    // Change its anchor to page
    uno::Reference<beans::XPropertySet> xShpProps(getShape(1), uno::UNO_QUERY_THROW);
    xShpProps->setPropertyValue(
        "AnchorType", uno::makeAny(text::TextContentAnchorType::TextContentAnchorType_AT_PAGE));

    // The page anchored objects must not have content anchor
    // unless this will lead to crash later, for example on
    // removing the paragraph where it is achored to...
    CPPUNIT_ASSERT_EQUAL(RndStdIds::FLY_AT_PAGE, pTxBxFrm->GetAnchor().GetAnchorId());
    CPPUNIT_ASSERT(!pTxBxFrm->GetAnchor().GetContentAnchor());

    // Remove the paragraph where the textframe should be anchored
    // before. Now with the patch it must not crash...
    auto xPara = getParagraph(1);
    xPara->getText()->setString(OUString());
}

CPPUNIT_TEST_FIXTURE(SwUiWriterTest3, testTdf135661)
{
    load(DATA_DIRECTORY, "tdf135661.odt");
diff --git a/sw/source/core/doc/textboxhelper.cxx b/sw/source/core/doc/textboxhelper.cxx
index 82a1c9b..b62269f 100644
--- a/sw/source/core/doc/textboxhelper.cxx
+++ b/sw/source/core/doc/textboxhelper.cxx
@@ -130,6 +130,8 @@ void SwTextBoxHelper::create(SwFrameFormat* pShape)
    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));
    syncProperty(pShape, RES_ANCHOR, MID_ANCHOR_ANCHORTYPE,
                 xShapePropertySet->getPropertyValue(UNO_NAME_ANCHOR_TYPE));
    syncProperty(pShape, RES_HORI_ORIENT, MID_HORIORIENT_ORIENT,
                 xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT));
    syncProperty(pShape, RES_HORI_ORIENT, MID_HORIORIENT_RELATION,
@@ -160,15 +162,10 @@ void SwTextBoxHelper::create(SwFrameFormat* pShape)
        return;

    SfxItemSet aTxFrmSet(pFormat->GetDoc()->GetAttrPool(), aFrameFormatSetRange);
    SwFormatAnchor aNewAnch = pFormat->GetAnchor();

    if (pShape->GetAnchor().GetContentAnchor())
        aNewAnch.SetAnchor(pShape->GetAnchor().GetContentAnchor());
    if (pShape->GetAnchor().GetPageNum() > 0)
        aNewAnch.SetPageNum(pShape->GetAnchor().GetPageNum());

    aNewAnch.SetType(pShape->GetAnchor().GetAnchorId());
    aTxFrmSet.Put(aNewAnch);
    if (rAnch.GetAnchorId() == RndStdIds::FLY_AT_CHAR
        || rAnch.GetAnchorId() == RndStdIds::FLY_AT_PARA)
        aTxFrmSet.Put(rAnch);

    SwFormatVertOrient aVOri(pFormat->GetVertOrient());
    SwFormatHoriOrient aHOri(pFormat->GetHoriOrient());
@@ -626,17 +623,38 @@ void SwTextBoxHelper::syncProperty(SwFrameFormat* pShape, sal_uInt16 nWID, sal_u
            switch (nMemberID)
            {
                case MID_ANCHOR_ANCHORTYPE:
                {
                    uno::Reference<beans::XPropertySet> const xPropertySet(
                        SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat),
                        uno::UNO_QUERY);
                    // Surround (Wrap) has to be THROUGH always:
                    xPropertySet->setPropertyValue(UNO_NAME_SURROUND,
                                                   uno::makeAny(text::WrapTextMode_THROUGH));
                    // Use At_Char anchor instead of As_Char anchoring:
                    if (aValue.get<text::TextContentAnchorType>()
                        == text::TextContentAnchorType_AS_CHARACTER)
                        == text::TextContentAnchorType::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;
                        xPropertySet->setPropertyValue(
                            UNO_NAME_ANCHOR_TYPE,
                            uno::makeAny(
                                text::TextContentAnchorType::TextContentAnchorType_AT_CHARACTER));
                    }
                    break;
                    else // Otherwise copy the anchor type of the shape
                    {
                        xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_TYPE, aValue);
                    }
                    // After anchoring the position must be set as well:
                    if (aValue.get<text::TextContentAnchorType>()
                        == text::TextContentAnchorType::TextContentAnchorType_AT_PAGE)
                    {
                        xPropertySet->setPropertyValue(
                            UNO_NAME_ANCHOR_PAGE_NO,
                            uno::makeAny(pShape->GetAnchor().GetPageNum()));
                    }

                    return;
                }
                break;
            }
            break;
        case FN_TEXT_RANGE: