tdf#119571 fix style & numbering at tracked deletion

and direct paragraph formattings after partially deleted
paragraphs. Clean-up and extension of the previous workaround,
now with Undo.

See also commit b69c518df68ce673b28d589da6626bd3d860f309
"tdf#54819 keep style & numbering at tracked deletion".

Change-Id: Icc4d21e3fd0496442329c65e379522f4b7fdc6b4
Reviewed-on: https://gerrit.libreoffice.org/74633
Tested-by: Jenkins
Reviewed-by: László Németh <nemeth@numbertext.org>
diff --git a/sw/qa/extras/uiwriter/uiwriter2.cxx b/sw/qa/extras/uiwriter/uiwriter2.cxx
index e00bf02..4a5a014 100644
--- a/sw/qa/extras/uiwriter/uiwriter2.cxx
+++ b/sw/qa/extras/uiwriter/uiwriter2.cxx
@@ -368,6 +368,92 @@
    CPPUNIT_ASSERT_MESSAGE("Not a bulleted list item", sNumName != "Outline");
}

CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf119571_keep_numbering_with_Undo)
{
    // as the previous test, but with partial paragraph deletion
    load(DATA_DIRECTORY, "tdf54819b.odt");

    SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
    CPPUNIT_ASSERT(pTextDoc);

    // heading

    CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"),
                         getProperty<OUString>(getParagraph(2), "ParaStyleName"));
    CPPUNIT_ASSERT_EQUAL(OUString("Outline"),
                         getProperty<OUString>(getParagraph(2), "NumberingStyleName"));

    // next paragraph: bulleted list item

    CPPUNIT_ASSERT_EQUAL(OUString("Standard"),
                         getProperty<OUString>(getParagraph(3), "ParaStyleName"));
    OUString sNumName = getProperty<OUString>(getParagraph(3), "NumberingStyleName");
    CPPUNIT_ASSERT_MESSAGE("Missing numbering style", !sNumName.isEmpty());
    CPPUNIT_ASSERT_MESSAGE("Not a bulleted list item", sNumName != "Outline");

    //turn on red-lining and show changes
    SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc();
    pDoc->getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::On | RedlineFlags::ShowDelete
                                                      | RedlineFlags::ShowInsert);
    pDoc->getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::On);
    CPPUNIT_ASSERT_MESSAGE("redlining should be on",
                           pDoc->getIDocumentRedlineAccess().IsRedlineOn());
    CPPUNIT_ASSERT_MESSAGE("redlines shouldn't be visible",
                           !IDocumentRedlineAccess::IsShowChanges(
                               pDoc->getIDocumentRedlineAccess().GetRedlineFlags()));

    // remove only end part of the heading with paragraph break
    SwWrtShell* pWrtShell = pTextDoc->GetDocShell()->GetWrtShell();

    pWrtShell->Down(/*bSelect=*/false);
    pWrtShell->Down(/*bSelect=*/false);
    pWrtShell->Down(/*bSelect=*/false);
    pWrtShell->Down(/*bSelect=*/false);
    pWrtShell->Down(/*bSelect=*/false);
    pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, /*bBasicCall=*/false);
    pWrtShell->EndPara(/*bSelect=*/true);
    pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/true, 1, /*bBasicCall=*/false);
    rtl::Reference<SwTransferable> pTransfer = new SwTransferable(*pWrtShell);
    pTransfer->Cut();

    // solved problem: changing paragraph style after deletion
    CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"),
                         getProperty<OUString>(getParagraph(2), "ParaStyleName"));

    // solved problem: apply numbering
    CPPUNIT_ASSERT_EQUAL(OUString("Outline"),
                         getProperty<OUString>(getParagraph(2), "NumberingStyleName"));

    // accept deletion, remaining (now second) paragraph: it is still heading
    IDocumentRedlineAccess& rIDRA(pDoc->getIDocumentRedlineAccess());
    rIDRA.AcceptAllRedline(true);

    CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"),
                         getProperty<OUString>(getParagraph(2), "ParaStyleName"));
    CPPUNIT_ASSERT_EQUAL(OUString("Outline"),
                         getProperty<OUString>(getParagraph(2), "NumberingStyleName"));

    // solved problem: Undo with the workaround
    sw::UndoManager& rUndoManager = pDoc->GetUndoManager();
    rUndoManager.Undo();
    rUndoManager.Undo();

    // heading

    CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"),
                         getProperty<OUString>(getParagraph(2), "ParaStyleName"));
    CPPUNIT_ASSERT_EQUAL(OUString("Outline"),
                         getProperty<OUString>(getParagraph(2), "NumberingStyleName"));

    // next paragraph: bulleted list item

    CPPUNIT_ASSERT_EQUAL(OUString("Standard"),
                         getProperty<OUString>(getParagraph(3), "ParaStyleName"));
    sNumName = getProperty<OUString>(getParagraph(3), "NumberingStyleName");
    CPPUNIT_ASSERT_MESSAGE("Missing numbering style", !sNumName.isEmpty());
    CPPUNIT_ASSERT_MESSAGE("Not a bulleted list item", sNumName != "Outline");
}

CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf109376_redline)
{
    SwDoc* pDoc = createDoc();
diff --git a/sw/source/core/doc/DocumentRedlineManager.cxx b/sw/source/core/doc/DocumentRedlineManager.cxx
index e327ff5..30b6b09 100644
--- a/sw/source/core/doc/DocumentRedlineManager.cxx
+++ b/sw/source/core/doc/DocumentRedlineManager.cxx
@@ -774,6 +774,47 @@
        }
    }

    void lcl_CopyStyle( const SwPosition & rFrom, const SwPosition & rTo )
    {
        SwTextNode* pToNode = rTo.nNode.GetNode().GetTextNode();
        SwTextNode* pFromNode = rFrom.nNode.GetNode().GetTextNode();
        if (pToNode != nullptr && pFromNode != nullptr && pToNode != pFromNode)
        {
            const SwPaM aPam(*pToNode);
            SwDoc* pDoc = aPam.GetDoc();
            // using Undo, copy paragraph style
            pDoc->SetTextFormatColl(aPam, pFromNode->GetTextColl());

            // using Undo, remove direct paragraph formatting of the "To" paragraph,
            // and apply here direct paragraph formatting of the "From" paragraph
            SfxItemSet aTmp(
                pDoc->GetAttrPool(),
                svl::Items<
                    RES_PARATR_LINESPACING, RES_PARATR_OUTLINELEVEL,
                    RES_PARATR_LIST_BEGIN, RES_PARATR_LIST_END - 1>{});

            SfxItemSet aTmp2(
                pDoc->GetAttrPool(),
                svl::Items<
                    RES_PARATR_LINESPACING, RES_PARATR_OUTLINELEVEL,
                    RES_PARATR_LIST_BEGIN, RES_PARATR_LIST_END - 1>{});

            pToNode->GetParaAttr(aTmp, 0, 0);
            pFromNode->GetParaAttr(aTmp2, 0, 0);

            for( sal_uInt16 nItem = 0; nItem < aTmp.TotalCount(); ++nItem)
            {
                sal_uInt16 nWhich = aTmp.GetWhichByPos(nItem);
                if( SfxItemState::SET == aTmp.GetItemState( nWhich, false ) &&
                    SfxItemState::SET != aTmp2.GetItemState( nWhich, false ) )
                        aTmp2.Put( aTmp.GetPool()->GetDefaultItem(nWhich), nWhich );
            }

            if (aTmp2.Count())
                pDoc->getIDocumentContentOperations().InsertItemSet(aPam, aTmp2);
        }
    }

    /// in case some text is deleted, ensure that the not-yet-inserted
    /// SwRangeRedline has its positions corrected not to point to deleted node
    class TemporaryRedlineUpdater
@@ -1931,64 +1972,15 @@
                        // after the fully deleted paragraphs (normal behaviour
                        // of editing without change tracking), we copy its style
                        // to the first removed paragraph.
                        SwTextNode* pDelNode = pStt->nNode.GetNode().GetTextNode();
                        SwTextNode* pTextNode = pEnd->nNode.GetNode().GetTextNode();
                        if (pDelNode != nullptr && pTextNode != nullptr && pDelNode != pTextNode)
                        {
                            const SwPaM aPam(*pDelNode);
                            // using Undo, apply paragraph style
                            m_rDoc.SetTextFormatColl(aPam, pTextNode->GetTextColl());

                            // using Undo, remove direct paragraph formatting of the first deleted paragraph,
                            // and apply direct paragraph formatting of the next remaining paragraph
                            SfxItemSet aTmp(
                                m_rDoc.GetAttrPool(),
                                svl::Items<
                                    RES_PARATR_LINESPACING, RES_PARATR_OUTLINELEVEL,
                                    RES_PARATR_LIST_BEGIN, RES_PARATR_LIST_END - 1>{});

                            SfxItemSet aTmp2(
                                m_rDoc.GetAttrPool(),
                                svl::Items<
                                    RES_PARATR_LINESPACING, RES_PARATR_OUTLINELEVEL,
                                    RES_PARATR_LIST_BEGIN, RES_PARATR_LIST_END - 1>{});

                            pDelNode->GetParaAttr(aTmp, 0, 0);
                            pTextNode->GetParaAttr(aTmp2, 0, 0);

                            for( sal_uInt16 nItem = 0; nItem < aTmp.TotalCount(); ++nItem)
                            {
                                sal_uInt16 nWhich = aTmp.GetWhichByPos(nItem);
                                if( SfxItemState::SET == aTmp.GetItemState( nWhich, false ) &&
                                    SfxItemState::SET != aTmp2.GetItemState( nWhich, false ) )
                                        aTmp2.Put( aTmp.GetPool()->GetDefaultItem(nWhich), nWhich );
                            }

                            if (aTmp2.Count())
                                m_rDoc.getIDocumentContentOperations().InsertItemSet(aPam, aTmp2);
                        }
                        lcl_CopyStyle(*pEnd, *pStt);
                    }
                    else
                    {
                        // tdf#119571 update the style of the joined paragraph
                        // after a partially deleted paragraph to show its correct style
                        // in "Show changes" mode, too. All removed paragraphs
                        // get the style of the first (partially deleted) paragraph
                        // to avoid text insertion with bad style in the deleted
                        // area later.
                        SwContentNode* pDelNd = pStt->nNode.GetNode().GetContentNode();
                        SwContentNode* pTextNd = pEnd->nNode.GetNode().GetContentNode();
                        SwTextNode* pDelNode = pStt->nNode.GetNode().GetTextNode();
                        SwTextNode* pTextNode;
                        SwNodeIndex aIdx( pEnd->nNode.GetNode() );

                        while (pDelNode != nullptr && pTextNd != nullptr && pDelNd->GetIndex() < pTextNd->GetIndex())
                        {
                            pTextNode = pTextNd->GetTextNode();
                            if (pTextNode && pDelNode != pTextNode )
                                pTextNode->ChgFormatColl( pDelNode->GetTextColl() );
                            pTextNd = SwNodes::GoPrevious( &aIdx );
                        }
                        // in "Show changes" mode, too. The paragraph after the deletion
                        // gets the style of the first (partially deleted) paragraph.
                        lcl_CopyStyle(*pStt, *pEnd);
                    }
                }
                bool const ret = mpRedlineTable->Insert( pNewRedl );