tdf#148897 sw: layout: force invalidate background when moving fly off page

The fly 15 moves forward in SwLayAction::FormatContent(), then back
again later, without being positioned on the next page, and it happens
to go to the same position that it originally had.

Then SwFlyNotify sees that the position is the same, and doesn't do any
invalidation of the background, so the fly overlaps a text frame.

When the fly is moved by the object positioning code (before the below
commit), it first gets a new position on the next page, and then moves
back, so all invalidations happen.

Force the invalidation of the background in this case.

(regression from commit eb85de8e6b61fb3fcb6c03ae0145f7fe5478bccf
 "sw: layout: if fly's anchor moves forward, move fly off SwPageFrame")

Change-Id: Ifdc9460defe7b1f37cbb05310906146919e8fb6a
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/152245
Tested-by: Michael Stahl <michael.stahl@allotropia.de>
Reviewed-by: Michael Stahl <michael.stahl@allotropia.de>
diff --git a/sw/inc/anchoredobject.hxx b/sw/inc/anchoredobject.hxx
index ea81048..19cd2e7 100644
--- a/sw/inc/anchoredobject.hxx
+++ b/sw/inc/anchoredobject.hxx
@@ -109,6 +109,8 @@ class SW_DLLPUBLIC SwAnchoredObject
        // page frame starts.
        bool mbTmpConsiderWrapInfluence;

        bool mbForceNotifyNewBackground = false;

        mutable SwRect maObjRectWithSpaces;
        mutable bool mbObjRectWithSpacesValid;
        mutable SwRect maLastObjRect;
@@ -423,6 +425,9 @@ class SW_DLLPUBLIC SwAnchoredObject
        bool IsTmpConsiderWrapInfluence() const { return mbTmpConsiderWrapInfluence;}
        void ClearTmpConsiderWrapInfluence();

        bool IsForceNotifyNewBackground() { return mbForceNotifyNewBackground; }
        void SetForceNotifyNewBackground(bool const b) { mbForceNotifyNewBackground = b; }

        /** method to determine, if the anchored object is overlapping with a
            previous column

diff --git a/sw/qa/extras/layout/data/tdf148897.odt b/sw/qa/extras/layout/data/tdf148897.odt
new file mode 100644
index 0000000..d5d1555c
--- /dev/null
+++ b/sw/qa/extras/layout/data/tdf148897.odt
Binary files differ
diff --git a/sw/qa/extras/layout/layout2.cxx b/sw/qa/extras/layout/layout2.cxx
index 3f80458..b4b0049 100644
--- a/sw/qa/extras/layout/layout2.cxx
+++ b/sw/qa/extras/layout/layout2.cxx
@@ -12,6 +12,7 @@
#include <com/sun/star/text/XTextFrame.hpp>
#include <com/sun/star/text/XTextTable.hpp>
#include <com/sun/star/linguistic2/XHyphenator.hpp>
#include <com/sun/star/view/XSelectionSupplier.hpp>

#include <comphelper/scopeguard.hxx>
#include <comphelper/sequence.hxx>
@@ -144,6 +145,87 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter2, testTdf100680_as_char_wrap)
    // If the third line missing that assert will fire, as was before the fix.
}

CPPUNIT_TEST_FIXTURE(SwLayoutWriter2, testTdf148897)
{
    createSwDoc("tdf148897.odt");

    xmlDocUniquePtr pXmlDoc = parseLayoutDump();

    assertXPath(pXmlDoc, "/root/page[1]/sorted_objs/fly", 1);
    assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/anchored/fly", 1);
    assertXPath(pXmlDoc, "/root/page[2]/sorted_objs/fly", 1);
    assertXPath(pXmlDoc, "/root/page[2]/body/txt[3]/anchored/fly", 1);
    assertXPath(pXmlDoc, "/root/page[3]/sorted_objs/fly", 0);
    assertXPath(pXmlDoc, "/root/page[3]/body/txt/anchored/fly", 0);
    assertXPath(pXmlDoc, "/root/page[4]/sorted_objs/fly", 1);
    assertXPath(pXmlDoc, "/root/page[4]/body/txt[1]/anchored/fly", 1);
    // fly portion exists, no overlapping text
    assertXPath(pXmlDoc, "/root/page[4]/body/txt[1]/SwParaPortion/SwLineLayout[1]/SwFixPortion",
                "height", "5797");
    assertXPath(pXmlDoc, "/root/page[5]/sorted_objs/fly", 0);
    assertXPath(pXmlDoc, "/root/page", 5);

    auto xModel = mxComponent.queryThrow<frame::XModel>();
    uno::Reference<drawing::XShape> xShape(getShapeByName(u"Image3"));
    uno::Reference<view::XSelectionSupplier> xCtrl(xModel->getCurrentController(), uno::UNO_QUERY);
    xCtrl->select(uno::Any(xShape));

    dispatchCommand(mxComponent, ".uno:Delete", {});

    discardDumpedLayout();
    pXmlDoc = parseLayoutDump();

    assertXPath(pXmlDoc, "/root/page[1]/sorted_objs/fly", 0);
    assertXPath(pXmlDoc, "/root/page[1]/body/txt/anchored/fly", 0);
    assertXPath(pXmlDoc, "/root/page[2]/sorted_objs/fly", 1);
    assertXPath(pXmlDoc, "/root/page[2]/body/txt[1]/anchored/fly", 1);
    assertXPath(pXmlDoc, "/root/page[3]/sorted_objs/fly", 1);
    assertXPath(pXmlDoc, "/root/page[3]/body/txt[2]/anchored/fly", 1);
    // fly portion exists, no overlapping text
    assertXPath(pXmlDoc, "/root/page[3]/body/txt[1]/SwParaPortion/SwLineLayout[1]/SwFixPortion",
                "height", "5797");
    assertXPath(pXmlDoc, "/root/page[4]/sorted_objs/fly", 0);
    assertXPath(pXmlDoc, "/root/page[4]/body/txt/anchored/fly", 0);
    assertXPath(pXmlDoc, "/root/page", 4);

    dispatchCommand(mxComponent, ".uno:Undo", {});

    discardDumpedLayout();
    pXmlDoc = parseLayoutDump();

    assertXPath(pXmlDoc, "/root/page[1]/sorted_objs/fly", 1);
    assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/anchored/fly", 1);
    assertXPath(pXmlDoc, "/root/page[2]/sorted_objs/fly", 1);
    assertXPath(pXmlDoc, "/root/page[2]/body/txt[3]/anchored/fly", 1);
    assertXPath(pXmlDoc, "/root/page[3]/sorted_objs/fly", 0);
    assertXPath(pXmlDoc, "/root/page[3]/body/txt/anchored/fly", 0);
    assertXPath(pXmlDoc, "/root/page[4]/sorted_objs/fly", 1);
    assertXPath(pXmlDoc, "/root/page[4]/body/txt[1]/anchored/fly", 1);
    // fly portion exists, no overlapping text
    assertXPath(pXmlDoc, "/root/page[4]/body/txt[1]/SwParaPortion/SwLineLayout[1]/SwFixPortion",
                "height", "5797");
    assertXPath(pXmlDoc, "/root/page[5]/sorted_objs/fly", 0);
    assertXPath(pXmlDoc, "/root/page", 5);

    dispatchCommand(mxComponent, ".uno:Redo", {});

    discardDumpedLayout();
    pXmlDoc = parseLayoutDump();

    assertXPath(pXmlDoc, "/root/page[1]/sorted_objs/fly", 0);
    assertXPath(pXmlDoc, "/root/page[1]/body/txt/anchored/fly", 0);
    assertXPath(pXmlDoc, "/root/page[2]/sorted_objs/fly", 1);
    assertXPath(pXmlDoc, "/root/page[2]/body/txt[1]/anchored/fly", 1);
    assertXPath(pXmlDoc, "/root/page[3]/sorted_objs/fly", 1);
    assertXPath(pXmlDoc, "/root/page[3]/body/txt[2]/anchored/fly", 1);
    // fly portion exists, no overlapping text
    assertXPath(pXmlDoc, "/root/page[3]/body/txt[1]/SwParaPortion/SwLineLayout[1]/SwFixPortion",
                "height", "5797");
    assertXPath(pXmlDoc, "/root/page[4]/sorted_objs/fly", 0);
    assertXPath(pXmlDoc, "/root/page[4]/body/txt/anchored/fly", 0);
    assertXPath(pXmlDoc, "/root/page", 4);
}

CPPUNIT_TEST_FIXTURE(SwLayoutWriter2, testRedlineCharAttributes)
{
    createSwDoc("redline_charatr.fodt");
diff --git a/sw/source/core/layout/flylay.cxx b/sw/source/core/layout/flylay.cxx
index b1bfe32..40b8547 100644
--- a/sw/source/core/layout/flylay.cxx
+++ b/sw/source/core/layout/flylay.cxx
@@ -220,7 +220,7 @@ void SwFlyFreeFrame::MakeAll(vcl::RenderContext* /*pRenderContext*/)
            else
                // #i26791# - use new method <MakeObjPos()>
                MakeObjPos();
            if( aOldPos == aRectFnSet.GetPos(getFrameArea()) )
            if (!IsForceNotifyNewBackground() && aOldPos == aRectFnSet.GetPos(getFrameArea()))
            {
                if( !isFrameAreaPositionValid() && GetAnchorFrame()->IsInSct() &&
                    !GetAnchorFrame()->FindSctFrame()->isFrameAreaDefinitionValid() )
diff --git a/sw/source/core/layout/frmtool.cxx b/sw/source/core/layout/frmtool.cxx
index 193478da..bc0231e 100644
--- a/sw/source/core/layout/frmtool.cxx
+++ b/sw/source/core/layout/frmtool.cxx
@@ -691,6 +691,11 @@ void SwFlyNotify::ImplDestroy()
        }
        pFly->ResetNotifyBack();
    }
    if (pFly->IsForceNotifyNewBackground())
    {
        pFly->NotifyBackground(pFly->FindPageFrame(), pFly->GetObjRectWithSpaces(), PrepareHint::FlyFrameArrive);
        pFly->SetForceNotifyNewBackground(false);
    }

    //Have the size or the position changed,
    //so should the view know this.
diff --git a/sw/source/core/layout/layact.cxx b/sw/source/core/layout/layact.cxx
index f0ac091..1996f8e 100644
--- a/sw/source/core/layout/layact.cxx
+++ b/sw/source/core/layout/layact.cxx
@@ -1682,6 +1682,12 @@ bool SwLayAction::FormatContent(SwPageFrame *const pPage)
                pObj->InvalidateObjPos();
                ::Notify_Background(pObj->GetDrawObj(), pPage,
                    pObj->GetObjRect(), PrepareHint::FlyFrameLeave, false);
                // tdf#148897 in case the fly moves back to this page before
                // being positioned again, the SwFlyNotify / ::Notify() could
                // conclude that it didn't move at all and not call
                // NotifyBackground(); note: pObj->SetObjTop(FAR_AWAY) results
                // in wrong positions, use different approach!
                pObj->SetForceNotifyNewBackground(true);
            }
            if (!moved.empty())
            {