summaryrefslogtreecommitdiff
path: root/sw
diff options
context:
space:
mode:
authorLászló Németh <nemeth@numbertext.org>2021-10-18 16:55:04 +0200
committerLászló Németh <nemeth@numbertext.org>2021-10-20 12:37:06 +0200
commitec577f566fa3e6d2666069180f8ec8474054aea9 (patch)
treefe56d87d153122dcfb779bbb9936eaf92f27c34c /sw
parent378e8396223a80b96262d7b638a066eb83ba88d6 (diff)
tdf#145233 sw track changes: show moved text in green color
and with double strikethrough or underlines during change tracking to speed up reviewing, e.g. re-ordered list elements or changed paragraph or sentence order is more visible this way. Note: skip terminating white spaces of the changes during recognition of the text movement, as 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 changes without terminating spaces. See also commit bcdebc832b272662d28035007a4796e42d1305ae "tdf#104797 DOCX change tracking: handle moveFrom and moveTo". Change-Id: If2a16f1f43315ecab659b24425692ac14bcda619 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/123814 Tested-by: László Németh <nemeth@numbertext.org> Reviewed-by: László Németh <nemeth@numbertext.org>
Diffstat (limited to 'sw')
-rw-r--r--sw/inc/docary.hxx3
-rw-r--r--sw/qa/extras/layout/layout2.cxx35
-rw-r--r--sw/source/core/doc/docredln.cxx51
-rw-r--r--sw/source/core/text/redlnitr.cxx21
-rw-r--r--sw/source/core/text/txtfld.cxx25
5 files changed, 126 insertions, 9 deletions
diff --git a/sw/inc/docary.hxx b/sw/inc/docary.hxx
index 97642d2e3bbf..4905fc7a1e38 100644
--- a/sw/inc/docary.hxx
+++ b/sw/inc/docary.hxx
@@ -260,6 +260,9 @@ public:
@param next true: redline starts at position and ends after, false: redline starts before position and ends at or after
*/
const SwRangeRedline* FindAtPosition( const SwPosition& startPosition, size_type& tableIndex, bool next = true ) const;
+ // is there a redline with the same text content from the same author (near the redline),
+ // but with the opposite type (Insert or Delete). It's used to recognize tracked text moving.
+ bool isMoved(size_type tableIndex) const;
bool empty() const { return maVector.empty(); }
size_type size() const { return maVector.size(); }
diff --git a/sw/qa/extras/layout/layout2.cxx b/sw/qa/extras/layout/layout2.cxx
index fce384cd8f3e..a66baa7f4a81 100644
--- a/sw/qa/extras/layout/layout2.cxx
+++ b/sw/qa/extras/layout/layout2.cxx
@@ -26,6 +26,7 @@
#include <frameformats.hxx>
#include <unotextrange.hxx>
#include <fmtanchr.hxx>
+#include <editsh.hxx>
constexpr OUStringLiteral DATA_DIRECTORY = u"/sw/qa/extras/layout/data/";
@@ -290,14 +291,44 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter2, testRedlineNumberInNumbering)
CPPUNIT_ASSERT(pXmlDoc);
// Assert the tracked deletion of the number of joined list item and
- // the tracked insertion of the number after a split list item as not black elements
- assertXPath(pXmlDoc, "/metafile/push/push/push/textcolor[not(@color='#000000')]", 6);
+ // the tracked insertion of the number after a split list item as not black
+ // (and not COL_GREEN color of the tracked text movement, see testRedlineMoving) elements
+ assertXPath(
+ pXmlDoc,
+ "/metafile/push/push/push/textcolor[not(@color='#000000') and not(@color='#008000')]", 6);
// tdf#145068 numbering shows changes in the associated list item, not the next one
// This was 1 (black numbering of the first list item previously)
assertXPath(pXmlDoc, "/metafile/push/push/push/font[4][@color='#000000']", 0);
}
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter2, testRedlineMoving)
+{
+ SwDoc* pDoc = createSwDoc(DATA_DIRECTORY, "tdf42748.fodt");
+ SwDocShell* pShell = pDoc->GetDocShell();
+
+ // create a 3-element list without change tracking
+ SwEditShell* const pEditShell(pDoc->GetEditShell());
+ pEditShell->RejectRedline(0);
+ pEditShell->AcceptRedline(0);
+
+ // move down first list item with track changes
+ dispatchCommand(mxComponent, ".uno:GoToStartOfDoc", {});
+ dispatchCommand(mxComponent, ".uno:TrackChanges", {});
+ dispatchCommand(mxComponent, ".uno:MoveDown", {});
+
+ // 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 and numbering colors show moving of the list item
+ // These were 0 (other color, not COL_GREEN, color of the tracked text movement)
+ assertXPath(pXmlDoc, "/metafile/push/push/push/textcolor[@color='#008000']", 5);
+ assertXPath(pXmlDoc, "/metafile/push/push/push/font[@color='#008000']", 11);
+}
+
CPPUNIT_TEST_FIXTURE(SwLayoutWriter2, testTdf125300)
{
SwDoc* pDoc = createSwDoc(DATA_DIRECTORY, "tdf125300.docx");
diff --git a/sw/source/core/doc/docredln.cxx b/sw/source/core/doc/docredln.cxx
index 5e1cae4132be..3542693b815d 100644
--- a/sw/source/core/doc/docredln.cxx
+++ b/sw/source/core/doc/docredln.cxx
@@ -767,6 +767,57 @@ const SwRangeRedline* SwRedlineTable::FindAtPosition( const SwPosition& rSttPos,
return pFnd;
}
+bool SwRedlineTable::isMoved( size_type rPos ) const
+{
+ auto constexpr nLookahead = 20;
+ const SwRangeRedline* pRedline = (*this)[ rPos ];
+
+ // set redline type of the searched pair
+ RedlineType nPairType = pRedline->GetType();
+ if ( RedlineType::Delete == nPairType )
+ nPairType = RedlineType::Insert;
+ else if ( RedlineType::Insert == nPairType )
+ nPairType = RedlineType::Delete;
+ else
+ // 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();
+ if ( sTrimmed.isEmpty() )
+ 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 )
+ {
+ const SwRangeRedline* pPair = (*this)[ rPos ];
+ // TODO handle also Show Changes in Margin mode
+ if ( pPair->HasMark() && pPair->IsVisible() )
+ {
+ // 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() )
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
void SwRedlineTable::dumpAsXml(xmlTextWriterPtr pWriter) const
{
(void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwRedlineTable"));
diff --git a/sw/source/core/text/redlnitr.cxx b/sw/source/core/text/redlnitr.cxx
index 258b31b6d058..556bae8ebd67 100644
--- a/sw/source/core/text/redlnitr.cxx
+++ b/sw/source/core/text/redlnitr.cxx
@@ -47,6 +47,9 @@
#include <vcl/svapp.hxx>
#include "redlnitr.hxx"
#include <extinput.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/udlnitem.hxx>
using namespace ::com::sun::star;
@@ -703,17 +706,18 @@ short SwRedlineItr::Seek(SwFont& rFnt,
m_nStart = COMPLETE_STRING;
m_nEnd = COMPLETE_STRING;
+ const SwRedlineTable& rTable = m_rDoc.getIDocumentRedlineAccess().GetRedlineTable();
- for ( ; m_nAct < m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().size() ; ++m_nAct)
+ for ( ; m_nAct < rTable.size() ; ++m_nAct)
{
- m_rDoc.getIDocumentRedlineAccess().GetRedlineTable()[ m_nAct ]->CalcStartEnd(nNode, m_nStart, m_nEnd);
+ rTable[ m_nAct ]->CalcStartEnd(nNode, m_nStart, m_nEnd);
if (nNew < m_nEnd)
{
if (nNew >= m_nStart) // only possible candidate
{
m_bOn = true;
- const SwRangeRedline *pRed = m_rDoc.getIDocumentRedlineAccess().GetRedlineTable()[ m_nAct ];
+ const SwRangeRedline *pRed = rTable[ m_nAct ];
if (m_pSet)
m_pSet->ClearItem();
@@ -729,6 +733,17 @@ short SwRedlineItr::Seek(SwFont& rFnt,
FillHints( pRed->GetAuthor(), pRed->GetType() );
SfxWhichIter aIter( *m_pSet );
+
+ // moved text: dark green with double underline or strikethrough
+ if ( rTable.isMoved( m_nAct ) )
+ {
+ m_pSet->Put(SvxColorItem( COL_GREEN, RES_CHRATR_COLOR ));
+ if (SfxItemState::SET == m_pSet->GetItemState(RES_CHRATR_CROSSEDOUT, true))
+ m_pSet->Put(SvxCrossedOutItem( STRIKEOUT_DOUBLE, RES_CHRATR_CROSSEDOUT ));
+ else
+ m_pSet->Put(SvxUnderlineItem( LINESTYLE_DOUBLE, RES_CHRATR_UNDERLINE ));
+ }
+
sal_uInt16 nWhich = aIter.FirstWhich();
while( nWhich )
{
diff --git a/sw/source/core/text/txtfld.cxx b/sw/source/core/text/txtfld.cxx
index ef0de08fda59..a73b756de73b 100644
--- a/sw/source/core/text/txtfld.cxx
+++ b/sw/source/core/text/txtfld.cxx
@@ -535,7 +535,8 @@ static void checkApplyParagraphMarkFormatToNumbering(SwFont* pNumFnt, SwTextForm
pNumFnt->SetHighlightColor(aHighlight);
}
-static const SwRangeRedline* lcl_GetRedlineAtNodeInsertionOrDeletion( const SwTextNode& rTextNode )
+static const SwRangeRedline* lcl_GetRedlineAtNodeInsertionOrDeletion( const SwTextNode& rTextNode,
+ bool& bIsMoved )
{
const SwDoc& rDoc = rTextNode.GetDoc();
SwRedlineTable::size_type nRedlPos = rDoc.getIDocumentRedlineAccess().GetRedlinePos( rTextNode, RedlineType::Any );
@@ -543,15 +544,19 @@ static const SwRangeRedline* lcl_GetRedlineAtNodeInsertionOrDeletion( const SwTe
if( SwRedlineTable::npos != nRedlPos )
{
const sal_uLong nNdIdx = rTextNode.GetIndex();
- for( ; nRedlPos < rDoc.getIDocumentRedlineAccess().GetRedlineTable().size() ; ++nRedlPos )
+ const SwRedlineTable& rTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ for( ; nRedlPos < rTable.size() ; ++nRedlPos )
{
- const SwRangeRedline* pTmp = rDoc.getIDocumentRedlineAccess().GetRedlineTable()[ nRedlPos ];
+ const SwRangeRedline* pTmp = rTable[ nRedlPos ];
if( RedlineType::Delete == pTmp->GetType() ||
RedlineType::Insert == pTmp->GetType() )
{
const SwPosition *pRStt = pTmp->Start(), *pREnd = pTmp->End();
if( pRStt->nNode <= nNdIdx && pREnd->nNode > nNdIdx )
+ {
+ bIsMoved = rTable.isMoved(nRedlPos);
return pTmp;
+ }
}
}
}
@@ -563,10 +568,22 @@ static void lcl_setRedlineAttr( SwTextFormatInfo &rInf, const SwTextNode& rTextN
if ( rInf.GetVsh()->GetLayout()->IsHideRedlines() )
return;
- const SwRangeRedline* pRedlineNum = lcl_GetRedlineAtNodeInsertionOrDeletion( rTextNode );
+ bool bIsMoved;
+ const SwRangeRedline* pRedlineNum = lcl_GetRedlineAtNodeInsertionOrDeletion( rTextNode, bIsMoved );
if (!pRedlineNum)
return;
+ // moved text: dark green with double underline or strikethrough
+ if ( bIsMoved )
+ {
+ pNumFnt->SetColor(COL_GREEN);
+ if ( RedlineType::Delete == pRedlineNum->GetType() )
+ pNumFnt->SetStrikeout(STRIKEOUT_DOUBLE);
+ else
+ pNumFnt->SetUnderline(LINESTYLE_DOUBLE);
+ return;
+ }
+
SwAttrPool& rPool = rInf.GetVsh()->GetDoc()->GetAttrPool();
SfxItemSetFixed<RES_CHRATR_BEGIN, RES_CHRATR_END-1> aSet(rPool);