tdf#144272 sw: track change of paragraph style

Modifying style of a paragraph, e.g. from "Standard"
or "Text body" to "Heading n" styles wasn't recorded
by change tracking.

Now it's possible to track and reject these changes
also after DOCX export/import.

Note: ODF track changes doesn't support format-only
changes, so likely it will need to export the paragraph
content two times, with the old and with the new
paragraph styles, too).

Note: selecting multiple paragraphs to modify their
styles at once doesn't record the changes, yet, because
multi-paragraph ParagraphFormat redline range hasn't
supported by AppendRedline().

Change-Id: I2d81fa23c59b7b75b3101dc3f2bb8c9eed8ab165
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/122707
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 1a9cbdc..4a273af 100644
--- a/sw/qa/extras/uiwriter/uiwriter2.cxx
+++ b/sw/qa/extras/uiwriter/uiwriter2.cxx
@@ -3194,6 +3194,58 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf50447_keep_hints)
    }
}

CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf144272)
{
    SwDoc* pDoc = createSwDoc(DATA_DIRECTORY, "tdf50447.fodt");

    SwWrtShell* const pWrtShell = pDoc->GetDocShell()->GetWrtShell();

    // turn on red-lining and show changes
    pDoc->getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::On | RedlineFlags::ShowDelete
                                                      | RedlineFlags::ShowInsert);
    CPPUNIT_ASSERT_MESSAGE("redlining should be on",
                           pDoc->getIDocumentRedlineAccess().IsRedlineOn());
    CPPUNIT_ASSERT_MESSAGE(
        "redlines should be visible",
        IDocumentRedlineAccess::IsShowChanges(pDoc->getIDocumentRedlineAccess().GetRedlineFlags()));

    // paragraph style of the first paragraph: Heading 1, second paragraph: Standard

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

    // modify with track changes: Standard and Heading 2

    uno::Sequence<beans::PropertyValue> aPropertyValues = comphelper::InitPropertySequence({
        { "Style", uno::makeAny(OUString("Standard")) },
        { "FamilyName", uno::makeAny(OUString("ParagraphStyles")) },
    });
    dispatchCommand(mxComponent, ".uno:StyleApply", aPropertyValues);
    pWrtShell->Down(/*bSelect=*/false);
    aPropertyValues = comphelper::InitPropertySequence({
        { "Style", uno::makeAny(OUString("Heading 2")) },
        { "FamilyName", uno::makeAny(OUString("ParagraphStyles")) },
    });
    dispatchCommand(mxComponent, ".uno:StyleApply", aPropertyValues);

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

    // reject tracked changes
    dispatchCommand(mxComponent, ".uno:RejectAllTrackedChanges", {});

    // This was Standard (missing reject)
    CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"),
                         getProperty<OUString>(getParagraph(1), "ParaStyleName"));
    // This was Heading 2 (missing reject)
    CPPUNIT_ASSERT_EQUAL(OUString("Standard"),
                         getProperty<OUString>(getParagraph(2), "ParaStyleName"));
}

CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf101873)
{
    SwDoc* pDoc = createSwDoc();
diff --git a/sw/source/core/edit/edfcol.cxx b/sw/source/core/edit/edfcol.cxx
index 6b76f14..dbed7a7 100644
--- a/sw/source/core/edit/edfcol.cxx
+++ b/sw/source/core/edit/edfcol.cxx
@@ -68,6 +68,8 @@
#include <vcl/weld.hxx>
#include <vcl/virdev.hxx>

#include <redline.hxx>
#include <poolfmt.hxx>
#include <hintids.hxx>
#include <doc.hxx>
#include <IDocumentUndoRedo.hxx>
@@ -2216,6 +2218,21 @@ void SwEditShell::SetTextFormatColl(SwTextFormatColl *pFormat,
                GetDoc()->getIDocumentRedlineAccess().SetRedlineFlags( eRedlMode );
            }

            // store previous paragraph style for track changes
            OUString sParaStyleName;
            sal_uInt16 nPoolId = USHRT_MAX;
            SwContentNode * pCnt = rPaM.Start()->nNode.GetNode().GetContentNode();
            if ( pCnt && pCnt->GetTextNode() && GetDoc()->getIDocumentRedlineAccess().IsRedlineOn() )
            {
                const SwTextFormatColl* pTextFormatColl = pCnt->GetTextNode()->GetTextColl();
                sal_uInt16 nStylePoolId = pTextFormatColl->GetPoolFormatId();
                // default paragraph style
                if ( nStylePoolId == RES_POOLCOLL_STANDARD )
                    nPoolId = nStylePoolId;
                else
                    sParaStyleName = pTextFormatColl->GetName();
            }

            // Change the paragraph style to pLocal and remove all direct paragraph formatting.
            GetDoc()->SetTextFormatColl(rPaM, pLocal, true, bResetListAttrs, GetLayout());

@@ -2227,6 +2244,27 @@ void SwEditShell::SetTextFormatColl(SwTextFormatColl *pFormat,
                aPaM.End()->nContent = pEndTextNode->GetText().getLength();
            }
            GetDoc()->RstTextAttrs(aPaM, /*bInclRefToxMark=*/false, /*bExactRange=*/true, GetLayout());

            // add redline tracking the previous paragraph style
            if ( GetDoc()->getIDocumentRedlineAccess().IsRedlineOn() &&
                // multi-paragraph ParagraphFormat redline ranges
                // haven't supported by AppendRedline(), yet
                // TODO handle multi-paragraph selections, too,
                // e.g. by breaking them to single paragraphs
                aPaM.Start()->nNode == aPaM.End()->nNode )
            {
                SwRangeRedline * pRedline = new SwRangeRedline( RedlineType::ParagraphFormat, aPaM );
                auto const result(GetDoc()->getIDocumentRedlineAccess().AppendRedline( pRedline, true));
                // store original paragraph style to reject formatting change
                if ( IDocumentRedlineAccess::AppendResult::IGNORED != result &&
                    ( nPoolId == RES_POOLCOLL_STANDARD || !sParaStyleName.isEmpty() ) )
                {
                    std::unique_ptr<SwRedlineExtraData_FormatColl> xExtra;
                    xExtra.reset(new SwRedlineExtraData_FormatColl(sParaStyleName, nPoolId, nullptr));
                    if (xExtra)
                       pRedline->SetExtraData( xExtra.get() );
                }
            }
        }

    }