sw/inc/redline.hxx                     |   11 ++-
 sw/qa/extras/layout/data/tdf145719.odt |binary
 sw/qa/extras/layout/layout2.cxx        |   16 +++++
 sw/source/core/doc/docredln.cxx        |  100 +++++++++++++++++++++------------
 sw/source/core/inc/UndoCore.hxx        |    1 
 sw/source/core/undo/undobj.cxx         |    4 -
 6 files changed, 93 insertions(+), 39 deletions(-)

New commits:
commit 7bc57e698910e24495605bd197a6d3ab5e0be5b8
Author:     László Németh <nem...@numbertext.org>
AuthorDate: Fri Nov 19 12:57:23 2021 +0100
Commit:     László Németh <nem...@numbertext.org>
CommitDate: Tue Nov 23 11:54:13 2021 +0100

    tdf#145719 sw: track moved text in import and ChangesInMargin
    
    Recognize moved text by accessing to the hidden redline content
    pContentSect during ODT import (in the case of Delete redlines)
    and ChangesInMargin mode (Delete or Insert redlines depending on
    Deletion in Margin or Insertion in Margin modes).
    
    Fix Undo and redline stack handling by moving IsMoved bit to
    SwRedlineData from SwRangeRedline.
    
    Note: .fodt format is applicable for the unit test document,
    because it's not affected by the problem.
    
    Change-Id: Ifd4f993520bec4b845d978a844c465509ea87b50
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/125552
    Tested-by: László Németh <nem...@numbertext.org>
    Reviewed-by: László Németh <nem...@numbertext.org>

diff --git a/sw/inc/redline.hxx b/sw/inc/redline.hxx
index 8d17948205fa..5e01cfa8349f 100644
--- a/sw/inc/redline.hxx
+++ b/sw/inc/redline.hxx
@@ -95,6 +95,7 @@ class SW_DLLPUBLIC SwRedlineData
     RedlineType m_eType;
     sal_uInt16 m_nSeqNo;
     bool m_bAutoFormat;
+    bool m_bMoved;
 
 public:
     SwRedlineData( RedlineType eT, std::size_t nAut );
@@ -111,6 +112,7 @@ public:
             return m_nAuthor == rCmp.m_nAuthor &&
                     m_eType == rCmp.m_eType &&
                     m_bAutoFormat == rCmp.m_bAutoFormat &&
+                    m_bMoved == rCmp.m_bMoved &&
                     m_sComment == rCmp.m_sComment &&
                     (( !m_pNext && !rCmp.m_pNext ) ||
                         ( m_pNext && rCmp.m_pNext && *m_pNext == *rCmp.m_pNext 
)) &&
@@ -133,6 +135,8 @@ public:
 
     void SetAutoFormat() { m_bAutoFormat = true; }
     bool IsAutoFormat() const { return m_bAutoFormat; }
+    void SetMoved() { m_bMoved = true; }
+    bool IsMoved() const { return m_bMoved; }
     bool CanCombine( const SwRedlineData& rCmp ) const;
 
     // ExtraData gets copied, the pointer is therefore not taken over by
@@ -155,7 +159,6 @@ class SW_DLLPUBLIC SwRangeRedline final : public SwPaM
     SwNodeIndex* m_pContentSect;
     bool m_bDelLastPara : 1;
     bool m_bIsVisible : 1;
-    bool m_bIsMoved : 1;
     sal_uInt32 m_nId;
 
     std::optional<tools::Long> m_oLOKLastNodeTop;
@@ -175,7 +178,7 @@ public:
     SwRangeRedline(SwRedlineData* pData, const SwPosition& rPos,
                bool bDelLP) :
         SwPaM( rPos ), m_pRedlineData( pData ), m_pContentSect( nullptr ),
-        m_bDelLastPara( bDelLP ), m_bIsVisible( true ), m_bIsMoved( false ), 
m_nId( s_nLastId++ )
+        m_bDelLastPara( bDelLP ), m_bIsVisible( true ), m_nId( s_nLastId++ )
     {}
     SwRangeRedline( const SwRangeRedline& );
     virtual ~SwRangeRedline() override;
@@ -263,8 +266,8 @@ public:
 
     void MaybeNotifyRedlinePositionModification(tools::Long nTop);
 
-    void SetMoved() { m_bIsMoved = true; }
-    bool IsMoved() const { return m_bIsMoved; }
+    void SetMoved() {  m_pRedlineData->SetMoved(); }
+    bool IsMoved() const { return m_pRedlineData->IsMoved(); }
 };
 
 void MaybeNotifyRedlineModification(SwRangeRedline& rRedline, SwDoc& rDoc);
diff --git a/sw/qa/extras/layout/data/tdf145719.odt 
b/sw/qa/extras/layout/data/tdf145719.odt
new file mode 100644
index 000000000000..62e4cc4a73e4
Binary files /dev/null and b/sw/qa/extras/layout/data/tdf145719.odt differ
diff --git a/sw/qa/extras/layout/layout2.cxx b/sw/qa/extras/layout/layout2.cxx
index 9eee8228ea8d..3ca1abc2747a 100644
--- a/sw/qa/extras/layout/layout2.cxx
+++ b/sw/qa/extras/layout/layout2.cxx
@@ -350,6 +350,22 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter2, 
testRedlineMovingDOCX)
     assertXPath(pXmlDoc, 
"/metafile/push/push/push/textcolor[@color='#008000']", 6);
 }
 
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter2, testTdf145719)
+{
+    SwDoc* pDoc = createSwDoc(DATA_DIRECTORY, "tdf145719.odt");
+    SwDocShell* pShell = pDoc->GetDocShell();
+
+    // Dump the rendering of the first page as an XML file.
+    std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
+    MetafileXmlDump dumper;
+    xmlDocUniquePtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
+    CPPUNIT_ASSERT(pXmlDoc);
+
+    // text colors show moved text
+    // This was 0 (other color, not COL_GREEN, color of the tracked text 
movement)
+    assertXPath(pXmlDoc, 
"/metafile/push/push/push/textcolor[@color='#008000']", 4);
+}
+
 CPPUNIT_TEST_FIXTURE(SwLayoutWriter2, 
testTdf145225_RedlineMovingWithBadInsertion)
 {
     SwDoc* pDoc = createSwDoc(DATA_DIRECTORY, "tdf42748.fodt");
diff --git a/sw/source/core/doc/docredln.cxx b/sw/source/core/doc/docredln.cxx
index 732d92a40246..d911ecb85d53 100644
--- a/sw/source/core/doc/docredln.cxx
+++ b/sw/source/core/doc/docredln.cxx
@@ -772,6 +772,7 @@ const SwRangeRedline* SwRedlineTable::FindAtPosition( const 
SwPosition& rSttPos,
 
 bool SwRedlineTable::isMoved( size_type rPos ) const
 {
+    bool bRet = false;
     auto constexpr nLookahead = 20;
     SwRangeRedline* pRedline = (*this)[ rPos ];
 
@@ -785,43 +786,79 @@ bool SwRedlineTable::isMoved( size_type rPos ) const
         // only deleted or inserted text can be moved
         return false;
 
-    // Skip terminating white spaces of the redline, a workaround
-    // for a typical difference resulted by e.g. Writer UX:
-    // selecting a sentence or a word, and moving it with
-    // the mouse, Writer removes a space at the deletion
-    // to avoid double spaces, also inserts a space at
-    // the insertion point automatically. Because the result
-    // can be different (space before and space after the
-    // moved text), compare the redlines without terminating spaces
-    const OUString sTrimmed = pRedline->GetText().trim();
+    bool bDeletePaM = false;
+    SwPaM* pPaM;
+
+    // if this redline is visible the content is in this PaM
+    if ( nullptr == pRedline->GetContentIdx() )
+    {
+        pPaM = pRedline;
+    }
+    else // otherwise it is saved in pContentSect, e.g. during ODT import
+    {
+        SwNodeIndex aTmpIdx( 
*pRedline->GetContentIdx()->GetNode().EndOfSectionNode() );
+        pPaM = new SwPaM(*pRedline->GetContentIdx(), aTmpIdx );
+        bDeletePaM = true;
+    }
+
+    const OUString sTrimmed = pPaM->GetText().trim();
     if ( sTrimmed.isEmpty() )
+    {
+        if ( bDeletePaM )
+            delete pPaM;
         return false;
+    }
 
     // search pair around the actual redline
     size_type nEnd = rPos + nLookahead < size()
         ? rPos + nLookahead
         : size();
     rPos = rPos > nLookahead ? rPos - nLookahead : 0;
-    for ( ; rPos < nEnd ; ++rPos )
+    for ( ; rPos < nEnd && !bRet ; ++rPos )
     {
         SwRangeRedline* pPair = (*this)[ rPos ];
-        // TODO handle also Show Changes in Margin mode
-        if ( pPair->HasMark() && pPair->IsVisible() )
+
+        // redline must be the requested type and from the same author
+        if ( nPairType != pPair->GetType() ||
+             pRedline->GetAuthor() != pPair->GetAuthor() )
         {
-            // pair at tracked moving: same text from the same author, but 
with opposite type
-            if ( nPairType == pPair->GetType() &&
-                pRedline->GetAuthor() == pPair->GetAuthor() &&
-                abs(pRedline->GetText().getLength() - 
pPair->GetText().getLength()) <= 2 &&
-                sTrimmed == pPair->GetText().trim() )
-            {
-                pRedline->SetMoved();
-                pPair->SetMoved();
-                pPair->InvalidateRange(SwRangeRedline::Invalidation::Remove);
-                return true;
-            }
+            continue;
         }
+
+        bool bDeletePairPaM = false;
+        SwPaM* pPairPaM;
+
+        // if this redline is visible the content is in this PaM
+        if ( nullptr == pPair->GetContentIdx() )
+        {
+            pPairPaM = pPair;
+        }
+        else // otherwise it is saved in pContentSect, e.g. during ODT import
+        {
+            // saved in pContentSect, e.g. during ODT import
+            SwNodeIndex aTmpIdx( 
*pPair->GetContentIdx()->GetNode().EndOfSectionNode() );
+            pPairPaM = new SwPaM(*pPair->GetContentIdx(), aTmpIdx );
+            bDeletePairPaM = true;
+        }
+
+        // pair at tracked moving: same text by trimming terminatin white 
spaces
+        if ( abs(pPaM->GetText().getLength() - 
pPairPaM->GetText().getLength()) <= 2 &&
+            sTrimmed == pPairPaM->GetText().trim() )
+        {
+            pRedline->SetMoved();
+            pPair->SetMoved();
+            pPair->InvalidateRange(SwRangeRedline::Invalidation::Remove);
+            bRet = true;
+        }
+
+        if ( bDeletePairPaM )
+            delete pPairPaM;
     }
-    return false;
+
+    if ( bDeletePaM )
+        delete pPaM;
+
+    return bRet;
 }
 
 void SwRedlineTable::dumpAsXml(xmlTextWriterPtr pWriter) const
@@ -987,7 +1024,7 @@ bool SwRedlineExtraData_Format::operator == ( const 
SwRedlineExtraData& rCmp ) c
 SwRedlineData::SwRedlineData( RedlineType eT, std::size_t nAut )
     : m_pNext( nullptr ), m_pExtraData( nullptr ),
     m_aStamp( DateTime::SYSTEM ),
-    m_nAuthor( nAut ), m_eType( eT ), m_nSeqNo( 0 ), m_bAutoFormat(false)
+    m_nAuthor( nAut ), m_eType( eT ), m_nSeqNo( 0 ), m_bAutoFormat(false), 
m_bMoved(false)
 {
     m_aStamp.SetNanoSec( 0 );
 }
@@ -1003,6 +1040,7 @@ SwRedlineData::SwRedlineData(
     , m_eType( rCpy.m_eType )
     , m_nSeqNo( rCpy.m_nSeqNo )
     , m_bAutoFormat(false)
+    , m_bMoved( rCpy.m_bMoved )
 {
 }
 
@@ -1010,7 +1048,7 @@ SwRedlineData::SwRedlineData(
 SwRedlineData::SwRedlineData(RedlineType eT, std::size_t nAut, const DateTime& 
rDT,
     const OUString& rCmnt, SwRedlineData *pNxt)
     : m_pNext(pNxt), m_pExtraData(nullptr), m_sComment(rCmnt), m_aStamp(rDT),
-    m_nAuthor(nAut), m_eType(eT), m_nSeqNo(0), m_bAutoFormat(false)
+    m_nAuthor(nAut), m_eType(eT), m_nSeqNo(0), m_bAutoFormat(false), 
m_bMoved(false)
 {
 }
 
@@ -1030,6 +1068,7 @@ bool SwRedlineData::CanCombine(const SwRedlineData& rCmp) 
const
             m_eType == rCmp.m_eType &&
             m_sComment == rCmp.m_sComment &&
             aTime == aCompareTime &&
+            m_bMoved == rCmp.m_bMoved &&
             (( !m_pNext && !rCmp.m_pNext ) ||
                 ( m_pNext && rCmp.m_pNext &&
                 m_pNext->CanCombine( *rCmp.m_pNext ))) &&
@@ -1080,7 +1119,6 @@ SwRangeRedline::SwRangeRedline(RedlineType eTyp, const 
SwPaM& rPam )
 {
     m_bDelLastPara = false;
     m_bIsVisible = true;
-    m_bIsMoved = false;
     if( !rPam.HasMark() )
         DeleteMark();
 }
@@ -1093,7 +1131,6 @@ SwRangeRedline::SwRangeRedline( const SwRedlineData& 
rData, const SwPaM& rPam )
 {
     m_bDelLastPara = false;
     m_bIsVisible = true;
-    m_bIsMoved = false;
     if( !rPam.HasMark() )
         DeleteMark();
 }
@@ -1106,7 +1143,6 @@ SwRangeRedline::SwRangeRedline( const SwRedlineData& 
rData, const SwPosition& rP
 {
     m_bDelLastPara = false;
     m_bIsVisible = true;
-    m_bIsMoved = false;
 }
 
 SwRangeRedline::SwRangeRedline( const SwRangeRedline& rCpy )
@@ -1117,9 +1153,6 @@ SwRangeRedline::SwRangeRedline( const SwRangeRedline& 
rCpy )
 {
     m_bDelLastPara = false;
     m_bIsVisible = true;
-    // inserting characters within a moved text must keep
-    // IsMoved bit in the second part of the split redline
-    m_bIsMoved = rCpy.IsMoved();
     if( !rCpy.HasMark() )
         DeleteMark();
 }
@@ -1840,8 +1873,7 @@ void SwRangeRedline::SetContentIdx( const SwNodeIndex* 
pIdx )
 bool SwRangeRedline::CanCombine( const SwRangeRedline& rRedl ) const
 {
     return  IsVisible() && rRedl.IsVisible() &&
-            m_pRedlineData->CanCombine( *rRedl.m_pRedlineData ) &&
-            !IsMoved() && !rRedl.IsMoved();
+            m_pRedlineData->CanCombine( *rRedl.m_pRedlineData );
 }
 
 void SwRangeRedline::PushData( const SwRangeRedline& rRedl, bool bOwnAsNext )
diff --git a/sw/source/core/inc/UndoCore.hxx b/sw/source/core/inc/UndoCore.hxx
index 7e670c9c6ba4..6efaf23c0b61 100644
--- a/sw/source/core/inc/UndoCore.hxx
+++ b/sw/source/core/inc/UndoCore.hxx
@@ -62,6 +62,7 @@ public:
 
 #if OSL_DEBUG_LEVEL > 0
     sal_uInt16 m_nRedlineCount;
+    bool m_bRedlineMoved;
 #endif
 };
 
diff --git a/sw/source/core/undo/undobj.cxx b/sw/source/core/undo/undobj.cxx
index b03a3e1daded..64faed602dae 100644
--- a/sw/source/core/undo/undobj.cxx
+++ b/sw/source/core/undo/undobj.cxx
@@ -1367,6 +1367,7 @@ SwRedlineSaveData::SwRedlineSaveData(
 
 #if OSL_DEBUG_LEVEL > 0
     m_nRedlineCount = 
rSttPos.nNode.GetNode().GetDoc().getIDocumentRedlineAccess().GetRedlineTable().size();
+    m_bRedlineMoved = rRedl.IsMoved();
 #endif
 }
 
@@ -1481,7 +1482,8 @@ void SwUndo::SetSaveData( SwDoc& rDoc, 
SwRedlineSaveDatas& rSData )
 
 #if OSL_DEBUG_LEVEL > 0
     // check redline count against count saved in RedlineSaveData object
-    assert(rSData.empty() ||
+    // except in the case of moved redlines
+    assert(rSData.empty() || rSData[0].m_bRedlineMoved ||
            (rSData[0].m_nRedlineCount == 
rDoc.getIDocumentRedlineAccess().GetRedlineTable().size()));
             // "redline count not restored properly"
 #endif

Reply via email to