summaryrefslogtreecommitdiffstats
path: root/sw/source/core/doc/docredln.cxx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /sw/source/core/doc/docredln.cxx
parentInitial commit. (diff)
downloadlibreoffice-upstream.tar.xz
libreoffice-upstream.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sw/source/core/doc/docredln.cxx')
-rw-r--r--sw/source/core/doc/docredln.cxx2136
1 files changed, 2136 insertions, 0 deletions
diff --git a/sw/source/core/doc/docredln.cxx b/sw/source/core/doc/docredln.cxx
new file mode 100644
index 000000000..002184cd6
--- /dev/null
+++ b/sw/source/core/doc/docredln.cxx
@@ -0,0 +1,2136 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <libxml/xmlwriter.h>
+#include <boost/property_tree/json_parser.hpp>
+
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <tools/datetimeutils.hxx>
+#include <hintids.hxx>
+#include <svl/itemiter.hxx>
+#include <comphelper/lok.hxx>
+#include <comphelper/string.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <unotools/datetime.hxx>
+#include <sfx2/viewsh.hxx>
+#include <o3tl/string_view.hxx>
+#include <swmodule.hxx>
+#include <doc.hxx>
+#include <docredln.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <DocumentContentOperationsManager.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <docary.hxx>
+#include <ndtxt.hxx>
+#include <redline.hxx>
+#include <UndoCore.hxx>
+#include <hints.hxx>
+#include <pamtyp.hxx>
+#include <poolfmt.hxx>
+#include <algorithm>
+#include <limits>
+#include <view.hxx>
+#include <viewopt.hxx>
+#include <usrpref.hxx>
+#include <viewsh.hxx>
+#include <viscrs.hxx>
+#include <rootfrm.hxx>
+#include <strings.hrc>
+#include <swtypes.hxx>
+#include <wrtsh.hxx>
+#include <txtfld.hxx>
+
+#include <flowfrm.hxx>
+#include <txtfrm.hxx>
+
+using namespace com::sun::star;
+
+#ifdef DBG_UTIL
+
+ void sw_DebugRedline( const SwDoc* pDoc )
+ {
+ static SwRedlineTable::size_type nWatch = 0; // loplugin:constvars:ignore
+ const SwRedlineTable& rTable = pDoc->getIDocumentRedlineAccess().GetRedlineTable();
+ for( SwRedlineTable::size_type n = 0; n < rTable.size(); ++n )
+ {
+ volatile SwRedlineTable::size_type nDummy = 0;
+ const SwRangeRedline* pCurrent = rTable[ n ];
+ const SwRangeRedline* pNext = n+1 < rTable.size() ? rTable[ n+1 ] : nullptr;
+ if( pCurrent == pNext )
+ (void) nDummy;
+ if( n == nWatch )
+ (void) nDummy; // Possible debugger breakpoint
+ }
+ }
+
+#endif
+
+
+SwExtraRedlineTable::~SwExtraRedlineTable()
+{
+ DeleteAndDestroyAll();
+}
+
+void SwExtraRedlineTable::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwExtraRedlineTable"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+
+ for (sal_uInt16 nCurExtraRedlinePos = 0; nCurExtraRedlinePos < GetSize(); ++nCurExtraRedlinePos)
+ {
+ const SwExtraRedline* pExtraRedline = GetRedline(nCurExtraRedlinePos);
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwExtraRedline"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("symbol"), "%s", BAD_CAST(typeid(*pExtraRedline).name()));
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+#if OSL_DEBUG_LEVEL > 0
+static bool CheckPosition( const SwPosition* pStt, const SwPosition* pEnd )
+{
+ int nError = 0;
+ SwNode* pSttNode = &pStt->nNode.GetNode();
+ SwNode* pEndNode = &pEnd->nNode.GetNode();
+ SwNode* pSttTab = pSttNode->StartOfSectionNode()->FindTableNode();
+ SwNode* pEndTab = pEndNode->StartOfSectionNode()->FindTableNode();
+ SwNode* pSttStart = pSttNode;
+ while( pSttStart && (!pSttStart->IsStartNode() || pSttStart->IsSectionNode() ||
+ pSttStart->IsTableNode() ) )
+ pSttStart = pSttStart->StartOfSectionNode();
+ SwNode* pEndStart = pEndNode;
+ while( pEndStart && (!pEndStart->IsStartNode() || pEndStart->IsSectionNode() ||
+ pEndStart->IsTableNode() ) )
+ pEndStart = pEndStart->StartOfSectionNode();
+ assert(pSttTab == pEndTab);
+ if( pSttTab != pEndTab )
+ nError = 1;
+ assert(pSttTab || pSttStart == pEndStart);
+ if( !pSttTab && pSttStart != pEndStart )
+ nError |= 2;
+ if( nError )
+ nError += 10;
+ return nError != 0;
+}
+#endif
+
+bool SwExtraRedlineTable::DeleteAllTableRedlines( SwDoc& rDoc, const SwTable& rTable, bool bSaveInUndo, RedlineType nRedlineTypeToDelete )
+{
+ bool bChg = false;
+
+ if (bSaveInUndo && rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ // #TODO - Add 'Undo' support for deleting 'Table Cell' redlines
+ /*
+ SwUndoRedline* pUndo = new SwUndoRedline( SwUndoId::REDLINE, rRange );
+ if( pUndo->GetRedlSaveCount() )
+ {
+ GetIDocumentUndoRedo().AppendUndo(pUndo);
+ }
+ else
+ delete pUndo;
+ */
+ }
+
+ for (sal_uInt16 nCurRedlinePos = 0; nCurRedlinePos < GetSize(); )
+ {
+ SwExtraRedline* pExtraRedline = GetRedline(nCurRedlinePos);
+ const SwTableCellRedline* pTableCellRedline = dynamic_cast<const SwTableCellRedline*>(pExtraRedline);
+ if (pTableCellRedline)
+ {
+ const SwTableBox *pRedTabBox = &pTableCellRedline->GetTableBox();
+ const SwTable& rRedTable = pRedTabBox->GetSttNd()->FindTableNode()->GetTable();
+ if ( &rRedTable == &rTable )
+ {
+ // Redline for this table
+ const SwRedlineData& aRedlineData = pTableCellRedline->GetRedlineData();
+ const RedlineType nRedlineType = aRedlineData.GetType();
+
+ // Check if this redline object type should be deleted
+ if (RedlineType::Any == nRedlineTypeToDelete || nRedlineTypeToDelete == nRedlineType)
+ {
+
+ DeleteAndDestroy( nCurRedlinePos );
+ bChg = true;
+ continue; // don't increment position after delete
+ }
+ }
+ }
+ ++nCurRedlinePos;
+ }
+
+ if( bChg )
+ rDoc.getIDocumentState().SetModified();
+
+ return bChg;
+}
+
+bool SwExtraRedlineTable::DeleteTableRowRedline( SwDoc* pDoc, const SwTableLine& rTableLine, bool bSaveInUndo, RedlineType nRedlineTypeToDelete )
+{
+ bool bChg = false;
+
+ if (bSaveInUndo && pDoc->GetIDocumentUndoRedo().DoesUndo())
+ {
+ // #TODO - Add 'Undo' support for deleting 'Table Cell' redlines
+ /*
+ SwUndoRedline* pUndo = new SwUndoRedline( SwUndoId::REDLINE, rRange );
+ if( pUndo->GetRedlSaveCount() )
+ {
+ GetIDocumentUndoRedo().AppendUndo(pUndo);
+ }
+ else
+ delete pUndo;
+ */
+ }
+
+ for(sal_uInt16 nCurRedlinePos = 0; nCurRedlinePos < GetSize(); ++nCurRedlinePos )
+ {
+ SwExtraRedline* pExtraRedline = GetRedline(nCurRedlinePos);
+ const SwTableRowRedline* pTableRowRedline = dynamic_cast<const SwTableRowRedline*>(pExtraRedline);
+ const SwTableLine *pRedTabLine = pTableRowRedline ? &pTableRowRedline->GetTableLine() : nullptr;
+ if ( pRedTabLine == &rTableLine )
+ {
+ // Redline for this table row
+ const SwRedlineData& aRedlineData = pTableRowRedline->GetRedlineData();
+ const RedlineType nRedlineType = aRedlineData.GetType();
+
+ // Check if this redline object type should be deleted
+ if( RedlineType::Any != nRedlineTypeToDelete && nRedlineTypeToDelete != nRedlineType )
+ continue;
+
+ DeleteAndDestroy( nCurRedlinePos );
+ bChg = true;
+ }
+ }
+
+ if( bChg )
+ pDoc->getIDocumentState().SetModified();
+
+ return bChg;
+}
+
+bool SwExtraRedlineTable::DeleteTableCellRedline( SwDoc* pDoc, const SwTableBox& rTableBox, bool bSaveInUndo, RedlineType nRedlineTypeToDelete )
+{
+ bool bChg = false;
+
+ if (bSaveInUndo && pDoc->GetIDocumentUndoRedo().DoesUndo())
+ {
+ // #TODO - Add 'Undo' support for deleting 'Table Cell' redlines
+ /*
+ SwUndoRedline* pUndo = new SwUndoRedline( SwUndoId::REDLINE, rRange );
+ if( pUndo->GetRedlSaveCount() )
+ {
+ GetIDocumentUndoRedo().AppendUndo(pUndo);
+ }
+ else
+ delete pUndo;
+ */
+ }
+
+ for(sal_uInt16 nCurRedlinePos = 0; nCurRedlinePos < GetSize(); ++nCurRedlinePos )
+ {
+ SwExtraRedline* pExtraRedline = GetRedline(nCurRedlinePos);
+ const SwTableCellRedline* pTableCellRedline = dynamic_cast<const SwTableCellRedline*>(pExtraRedline);
+ const SwTableBox *pRedTabBox = pTableCellRedline ? &pTableCellRedline->GetTableBox() : nullptr;
+ if ( pRedTabBox == &rTableBox )
+ {
+ // Redline for this table cell
+ const SwRedlineData& aRedlineData = pTableCellRedline->GetRedlineData();
+ const RedlineType nRedlineType = aRedlineData.GetType();
+
+ // Check if this redline object type should be deleted
+ if( RedlineType::Any != nRedlineTypeToDelete && nRedlineTypeToDelete != nRedlineType )
+ continue;
+
+ DeleteAndDestroy( nCurRedlinePos );
+ bChg = true;
+ }
+ }
+
+ if( bChg )
+ pDoc->getIDocumentState().SetModified();
+
+ return bChg;
+}
+
+namespace
+{
+
+void lcl_LOKInvalidateFrames(const sw::BroadcastingModify& rMod, const SwRootFrame* pLayout,
+ SwFrameType const nFrameType, const Point* pPoint)
+{
+ SwIterator<SwFrame, sw::BroadcastingModify, sw::IteratorMode::UnwrapMulti> aIter(rMod);
+
+ for (SwFrame* pTmpFrame = aIter.First(); pTmpFrame; pTmpFrame = aIter.Next() )
+ {
+ if ((pTmpFrame->GetType() & nFrameType) &&
+ (!pLayout || pLayout == pTmpFrame->getRootFrame()) &&
+ (!pTmpFrame->IsFlowFrame() || !SwFlowFrame::CastFlowFrame( pTmpFrame )->IsFollow()))
+ {
+ if (pPoint)
+ {
+ pTmpFrame->InvalidateSize();
+
+ // Also empty the text portion cache, so it gets rebuilt, taking the new redlines
+ // into account.
+ if (pTmpFrame->IsTextFrame())
+ {
+ auto pTextFrame = static_cast<SwTextFrame*>(pTmpFrame);
+ pTextFrame->ClearPara();
+ }
+ }
+ }
+ }
+}
+
+void lcl_LOKInvalidateStartEndFrames(SwShellCursor& rCursor)
+{
+ if (!(rCursor.HasMark() &&
+ rCursor.GetPoint()->nNode.GetNode().IsContentNode() &&
+ rCursor.GetPoint()->nNode.GetNode().GetContentNode()->getLayoutFrame(rCursor.GetShell()->GetLayout()) &&
+ (rCursor.GetMark()->nNode == rCursor.GetPoint()->nNode ||
+ (rCursor.GetMark()->nNode.GetNode().IsContentNode() &&
+ rCursor.GetMark()->nNode.GetNode().GetContentNode()->getLayoutFrame(rCursor.GetShell()->GetLayout())))))
+ {
+ return;
+ }
+
+
+ SwPosition *pStartPos = rCursor.Start(),
+ *pEndPos = rCursor.End();
+
+
+ lcl_LOKInvalidateFrames(*(pStartPos->nNode.GetNode().GetContentNode()),
+ rCursor.GetShell()->GetLayout(),
+ FRM_CNTNT, &rCursor.GetSttPos());
+
+ lcl_LOKInvalidateFrames(*(pEndPos->nNode.GetNode().GetContentNode()),
+ rCursor.GetShell()->GetLayout(),
+ FRM_CNTNT, &rCursor.GetEndPos());
+}
+
+bool lcl_LOKRedlineNotificationEnabled()
+{
+ static bool bDisableRedlineComments = getenv("DISABLE_REDLINE") != nullptr;
+ if (comphelper::LibreOfficeKit::isActive() && !bDisableRedlineComments)
+ return true;
+
+ return false;
+}
+
+} // anonymous namespace
+
+/// Emits LOK notification about one addition / removal of a redline item.
+void SwRedlineTable::LOKRedlineNotification(RedlineNotification nType, SwRangeRedline* pRedline)
+{
+ // Disable since usability is very low beyond some small number of changes.
+ if (!lcl_LOKRedlineNotificationEnabled())
+ return;
+
+ boost::property_tree::ptree aRedline;
+ aRedline.put("action", (nType == RedlineNotification::Add ? "Add" :
+ (nType == RedlineNotification::Remove ? "Remove" :
+ (nType == RedlineNotification::Modify ? "Modify" : "???"))));
+ aRedline.put("index", pRedline->GetId());
+ aRedline.put("author", pRedline->GetAuthorString(1).toUtf8().getStr());
+ aRedline.put("type", SwRedlineTypeToOUString(pRedline->GetRedlineData().GetType()).toUtf8().getStr());
+ aRedline.put("comment", pRedline->GetRedlineData().GetComment().toUtf8().getStr());
+ aRedline.put("description", pRedline->GetDescr().toUtf8().getStr());
+ OUString sDateTime = utl::toISO8601(pRedline->GetRedlineData().GetTimeStamp().GetUNODateTime());
+ aRedline.put("dateTime", sDateTime.toUtf8().getStr());
+
+ SwPosition* pStartPos = pRedline->Start();
+ SwPosition* pEndPos = pRedline->End();
+ SwContentNode* pContentNd = pRedline->GetContentNode();
+ SwView* pView = dynamic_cast<SwView*>(SfxViewShell::Current());
+ if (pView && pContentNd)
+ {
+ SwShellCursor aCursor(pView->GetWrtShell(), *pStartPos);
+ aCursor.SetMark();
+ aCursor.GetMark()->nNode = pEndPos->nNode;
+ aCursor.GetMark()->nContent = pEndPos->nContent;
+
+ aCursor.FillRects();
+
+ SwRects* pRects(&aCursor);
+ std::vector<OString> aRects;
+ for(const SwRect& rNextRect : *pRects)
+ aRects.push_back(rNextRect.SVRect().toString());
+
+ const OString sRects = comphelper::string::join("; ", aRects);
+ aRedline.put("textRange", sRects.getStr());
+
+ lcl_LOKInvalidateStartEndFrames(aCursor);
+
+ // When this notify method is called text invalidation is not done yet
+ // Calling FillRects updates the text area so invalidation will not run on the correct rects
+ // So we need to do an own invalidation here. It invalidates text frames containing the redlining
+ SwDoc& rDoc = pRedline->GetDoc();
+ SwViewShell* pSh;
+ if( !rDoc.IsInDtor() )
+ {
+ pSh = rDoc.getIDocumentLayoutAccess().GetCurrentViewShell();
+ if( pSh )
+ for(SwNodeIndex nIdx = pStartPos->nNode; nIdx <= pEndPos->nNode; ++nIdx)
+ {
+ SwContentNode* pContentNode = nIdx.GetNode().GetContentNode();
+ if (pContentNode)
+ pSh->InvalidateWindows(pContentNode->FindLayoutRect());
+ }
+ }
+ }
+
+ boost::property_tree::ptree aTree;
+ aTree.add_child("redline", aRedline);
+ std::stringstream aStream;
+ boost::property_tree::write_json(aStream, aTree);
+ std::string aPayload = aStream.str();
+
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ if (pView && pView->GetDocId() == pViewShell->GetDocId())
+ pViewShell->libreOfficeKitViewCallback(nType == RedlineNotification::Modify ? LOK_CALLBACK_REDLINE_TABLE_ENTRY_MODIFIED : LOK_CALLBACK_REDLINE_TABLE_SIZE_CHANGED, aPayload.c_str());
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+}
+
+bool SwRedlineTable::Insert(SwRangeRedline*& p)
+{
+ if( p->HasValidRange() )
+ {
+ std::pair<vector_type::const_iterator, bool> rv = maVector.insert( p );
+ size_type nP = rv.first - begin();
+ LOKRedlineNotification(RedlineNotification::Add, p);
+
+ // detect text moving by checking nearby redlines, except during Undo
+ // (apply isMoved() during OpenDocument and DOCX import, too, to fix
+ // missing text moving handling in ODF and e.g. web version of MSO)
+ if ( p->GetDoc().GetIDocumentUndoRedo().DoesUndo() ||
+ p->GetDoc().IsInWriterfilterImport() ||
+ p->GetDoc().IsInXMLImport() )
+ {
+ isMoved(nP);
+ }
+
+ p->CallDisplayFunc(nP);
+ if (rv.second)
+ CheckOverlapping(rv.first);
+ return rv.second;
+ }
+ return InsertWithValidRanges( p );
+}
+
+void SwRedlineTable::CheckOverlapping(vector_type::const_iterator it)
+{
+ if (m_bHasOverlappingElements)
+ return;
+ if (maVector.size() <= 1) // a single element cannot be overlapping
+ return;
+ auto pCurr = *it;
+ auto itNext = it + 1;
+ if (itNext != maVector.end())
+ {
+ auto pNext = *itNext;
+ if (pCurr->End()->nNode.GetIndex() >= pNext->Start()->nNode.GetIndex())
+ {
+ m_bHasOverlappingElements = true;
+ return;
+ }
+ }
+ if (it != maVector.begin())
+ {
+ auto pPrev = *(it - 1);
+ if (pPrev->End()->nNode.GetIndex() >= pCurr->Start()->nNode.GetIndex())
+ m_bHasOverlappingElements = true;
+ }
+}
+
+bool SwRedlineTable::Insert(SwRangeRedline*& p, size_type& rP)
+{
+ if( p->HasValidRange() )
+ {
+ std::pair<vector_type::const_iterator, bool> rv = maVector.insert( p );
+ rP = rv.first - begin();
+ p->CallDisplayFunc(rP);
+ if (rv.second)
+ CheckOverlapping(rv.first);
+ return rv.second;
+ }
+ return InsertWithValidRanges( p, &rP );
+}
+
+namespace sw {
+
+std::vector<SwRangeRedline*> GetAllValidRanges(std::unique_ptr<SwRangeRedline> p)
+{
+ std::vector<SwRangeRedline*> ret;
+ // Create valid "sub-ranges" from the Selection
+ SwPosition* pStt = p->Start(),
+ * pEnd = p->End();
+ SwPosition aNewStt( *pStt );
+ SwNodes& rNds = aNewStt.nNode.GetNodes();
+ SwContentNode* pC;
+
+ if( !aNewStt.nNode.GetNode().IsContentNode() )
+ {
+ pC = rNds.GoNext( &aNewStt.nNode );
+ if( pC )
+ aNewStt.nContent.Assign( pC, 0 );
+ else
+ aNewStt.nNode = rNds.GetEndOfContent();
+ }
+
+ SwRangeRedline* pNew = nullptr;
+
+ if( aNewStt < *pEnd )
+ do {
+ if( !pNew )
+ pNew = new SwRangeRedline( p->GetRedlineData(), aNewStt );
+ else
+ {
+ pNew->DeleteMark();
+ *pNew->GetPoint() = aNewStt;
+ }
+
+ pNew->SetMark();
+ GoEndSection( pNew->GetPoint() );
+ // i60396: If the redlines starts before a table but the table is the last member
+ // of the section, the GoEndSection will end inside the table.
+ // This will result in an incorrect redline, so we've to go back
+ SwNode* pTab = pNew->GetPoint()->nNode.GetNode().StartOfSectionNode()->FindTableNode();
+ // We end in a table when pTab != 0
+ if( pTab && !pNew->GetMark()->nNode.GetNode().StartOfSectionNode()->FindTableNode() )
+ { // but our Mark was outside the table => Correction
+ do
+ {
+ // We want to be before the table
+ *pNew->GetPoint() = SwPosition(*pTab);
+ pC = GoPreviousNds( &pNew->GetPoint()->nNode, false ); // here we are.
+ if( pC )
+ pNew->GetPoint()->nContent.Assign( pC, 0 );
+ pTab = pNew->GetPoint()->nNode.GetNode().StartOfSectionNode()->FindTableNode();
+ } while( pTab ); // If there is another table we have to repeat our step backwards
+ }
+
+ if( *pNew->GetPoint() > *pEnd )
+ {
+ pC = nullptr;
+ if( aNewStt.nNode != pEnd->nNode )
+ do {
+ SwNode& rCurNd = aNewStt.nNode.GetNode();
+ if( rCurNd.IsStartNode() )
+ {
+ if( rCurNd.EndOfSectionIndex() < pEnd->nNode.GetIndex() )
+ aNewStt.nNode = *rCurNd.EndOfSectionNode();
+ else
+ break;
+ }
+ else if( rCurNd.IsContentNode() )
+ pC = rCurNd.GetContentNode();
+ ++aNewStt.nNode;
+ } while( aNewStt.nNode.GetIndex() < pEnd->nNode.GetIndex() );
+
+ if( aNewStt.nNode == pEnd->nNode )
+ aNewStt.nContent = pEnd->nContent;
+ else if( pC )
+ {
+ aNewStt.nNode = *pC;
+ aNewStt.nContent.Assign( pC, pC->Len() );
+ }
+
+ if( aNewStt <= *pEnd )
+ *pNew->GetPoint() = aNewStt;
+ }
+ else
+ aNewStt = *pNew->GetPoint();
+#if OSL_DEBUG_LEVEL > 0
+ CheckPosition( pNew->GetPoint(), pNew->GetMark() );
+#endif
+
+ if( *pNew->GetPoint() != *pNew->GetMark() &&
+ pNew->HasValidRange())
+ {
+ ret.push_back(pNew);
+ pNew = nullptr;
+ }
+
+ if( aNewStt >= *pEnd )
+ break;
+ pC = rNds.GoNext( &aNewStt.nNode );
+ if( !pC )
+ break;
+
+ aNewStt.nContent.Assign( pC, 0 );
+
+ } while( aNewStt < *pEnd );
+
+ delete pNew;
+ p.reset();
+ return ret;
+}
+
+} // namespace sw
+
+bool SwRedlineTable::InsertWithValidRanges(SwRangeRedline*& p, size_type* pInsPos)
+{
+ bool bAnyIns = false;
+ std::vector<SwRangeRedline*> const redlines(
+ GetAllValidRanges(std::unique_ptr<SwRangeRedline>(p)));
+ for (SwRangeRedline * pRedline : redlines)
+ {
+ assert(pRedline->HasValidRange());
+ size_type nInsPos;
+ if (Insert(pRedline, nInsPos))
+ {
+ pRedline->CallDisplayFunc(nInsPos);
+ bAnyIns = true;
+ if (pInsPos && *pInsPos < nInsPos)
+ {
+ *pInsPos = nInsPos;
+ }
+ }
+ }
+ p = nullptr;
+ return bAnyIns;
+}
+
+bool CompareSwRedlineTable::operator()(SwRangeRedline* const &lhs, SwRangeRedline* const &rhs) const
+{
+ return *lhs < *rhs;
+}
+
+SwRedlineTable::~SwRedlineTable()
+{
+ maVector.DeleteAndDestroyAll();
+}
+
+SwRedlineTable::size_type SwRedlineTable::GetPos(const SwRangeRedline* p) const
+{
+ vector_type::const_iterator it = maVector.find(const_cast<SwRangeRedline*>(p));
+ if( it == maVector.end() )
+ return npos;
+ return it - maVector.begin();
+}
+
+void SwRedlineTable::Remove( const SwRangeRedline* p )
+{
+ const size_type nPos = GetPos(p);
+ if (nPos == npos)
+ return;
+ Remove(nPos);
+}
+
+void SwRedlineTable::Remove( size_type nP )
+{
+ LOKRedlineNotification(RedlineNotification::Remove, maVector[nP]);
+ SwDoc* pDoc = nullptr;
+ if( !nP && 1 == size() )
+ pDoc = &maVector.front()->GetDoc();
+
+ maVector.erase( maVector.begin() + nP );
+
+ if( pDoc && !pDoc->IsInDtor() )
+ {
+ SwViewShell* pSh = pDoc->getIDocumentLayoutAccess().GetCurrentViewShell();
+ if( pSh )
+ pSh->InvalidateWindows( SwRect( 0, 0, SAL_MAX_INT32, SAL_MAX_INT32 ) );
+ }
+}
+
+void SwRedlineTable::DeleteAndDestroyAll()
+{
+ while (!maVector.empty())
+ {
+ auto const pRedline = maVector.back();
+ maVector.erase_at(maVector.size() - 1);
+ LOKRedlineNotification(RedlineNotification::Remove, pRedline);
+ delete pRedline;
+ }
+ m_bHasOverlappingElements = false;
+}
+
+void SwRedlineTable::DeleteAndDestroy(size_type const nP)
+{
+ auto const pRedline = maVector[nP];
+ maVector.erase(maVector.begin() + nP);
+ LOKRedlineNotification(RedlineNotification::Remove, pRedline);
+ delete pRedline;
+}
+
+SwRedlineTable::size_type SwRedlineTable::FindNextOfSeqNo( size_type nSttPos ) const
+{
+ return nSttPos + 1 < size()
+ ? FindNextSeqNo( operator[]( nSttPos )->GetSeqNo(), nSttPos+1 )
+ : npos;
+}
+
+SwRedlineTable::size_type SwRedlineTable::FindPrevOfSeqNo( size_type nSttPos ) const
+{
+ return nSttPos ? FindPrevSeqNo( operator[]( nSttPos )->GetSeqNo(), nSttPos-1 )
+ : npos;
+}
+
+/// Find the next or preceding Redline with the same seq.no.
+/// We can limit the search using look ahead (0 searches the whole array).
+SwRedlineTable::size_type SwRedlineTable::FindNextSeqNo( sal_uInt16 nSeqNo, size_type nSttPos ) const
+{
+ auto constexpr nLookahead = 20;
+ size_type nRet = npos;
+ if( nSeqNo && nSttPos < size() )
+ {
+ size_type nEnd = size();
+ const size_type nTmp = nSttPos + nLookahead;
+ if (nTmp < nEnd)
+ {
+ nEnd = nTmp;
+ }
+
+ for( ; nSttPos < nEnd; ++nSttPos )
+ if( nSeqNo == operator[]( nSttPos )->GetSeqNo() )
+ {
+ nRet = nSttPos;
+ break;
+ }
+ }
+ return nRet;
+}
+
+SwRedlineTable::size_type SwRedlineTable::FindPrevSeqNo( sal_uInt16 nSeqNo, size_type nSttPos ) const
+{
+ auto constexpr nLookahead = 20;
+ size_type nRet = npos;
+ if( nSeqNo && nSttPos < size() )
+ {
+ size_type nEnd = 0;
+ if( nSttPos > nLookahead )
+ nEnd = nSttPos - nLookahead;
+
+ ++nSttPos;
+ while( nSttPos > nEnd )
+ if( nSeqNo == operator[]( --nSttPos )->GetSeqNo() )
+ {
+ nRet = nSttPos;
+ break;
+ }
+ }
+ return nRet;
+}
+
+const SwRangeRedline* SwRedlineTable::FindAtPosition( const SwPosition& rSttPos,
+ size_type& rPos,
+ bool bNext ) const
+{
+ const SwRangeRedline* pFnd = nullptr;
+ for( ; rPos < maVector.size() ; ++rPos )
+ {
+ const SwRangeRedline* pTmp = (*this)[ rPos ];
+ if( pTmp->HasMark() && pTmp->IsVisible() )
+ {
+ const SwPosition* pRStt = pTmp->Start(),
+ * pREnd = pTmp->End();
+ if( bNext ? *pRStt <= rSttPos : *pRStt < rSttPos )
+ {
+ if( bNext ? *pREnd > rSttPos : *pREnd >= rSttPos )
+ {
+ pFnd = pTmp;
+ break;
+ }
+ }
+ else
+ break;
+ }
+ }
+ return pFnd;
+}
+
+bool SwRedlineTable::isMoved( size_type rPos ) const
+{
+ bool bRet = false;
+ auto constexpr nLookahead = 20;
+ 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;
+
+ 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();
+ // detection of move needs at least 6 characters with an inner
+ // space after stripping white spaces of the redline to skip
+ // frequent deleted and inserted articles or other common
+ // word parts, e.g. 'the' and 'of a' to detect as text moving
+ if ( sTrimmed.getLength() < 6 || sTrimmed.indexOf(' ') == -1 )
+ {
+ 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 && !bRet ; ++rPos )
+ {
+ SwRangeRedline* pPair = (*this)[ rPos ];
+
+ // redline must be the requested type and from the same author
+ if ( nPairType != pPair->GetType() ||
+ pRedline->GetAuthor() != pPair->GetAuthor() )
+ {
+ 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 trailing white spaces
+ if ( abs(pPaM->GetText().getLength() - pPairPaM->GetText().getLength()) <= 2 &&
+ sTrimmed == o3tl::trim(pPairPaM->GetText()) )
+ {
+ pRedline->SetMoved();
+ pPair->SetMoved();
+ pPair->InvalidateRange(SwRangeRedline::Invalidation::Add);
+ bRet = true;
+ }
+
+ if ( bDeletePairPaM )
+ delete pPairPaM;
+ }
+
+ if ( bDeletePaM )
+ delete pPaM;
+
+ return bRet;
+}
+
+void SwRedlineTable::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwRedlineTable"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+
+ for (SwRedlineTable::size_type nCurRedlinePos = 0; nCurRedlinePos < size(); ++nCurRedlinePos)
+ operator[](nCurRedlinePos)->dumpAsXml(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+SwRedlineExtraData::~SwRedlineExtraData()
+{
+}
+
+void SwRedlineExtraData::Reject( SwPaM& ) const
+{
+}
+
+bool SwRedlineExtraData::operator == ( const SwRedlineExtraData& ) const
+{
+ return false;
+}
+
+SwRedlineExtraData_FormatColl::SwRedlineExtraData_FormatColl( const OUString& rColl,
+ sal_uInt16 nPoolFormatId,
+ const SfxItemSet* pItemSet,
+ bool bFormatAll )
+ : m_sFormatNm(rColl), m_nPoolId(nPoolFormatId), m_bFormatAll(bFormatAll)
+{
+ if( pItemSet && pItemSet->Count() )
+ m_pSet.reset( new SfxItemSet( *pItemSet ) );
+}
+
+SwRedlineExtraData_FormatColl::~SwRedlineExtraData_FormatColl()
+{
+}
+
+SwRedlineExtraData* SwRedlineExtraData_FormatColl::CreateNew() const
+{
+ return new SwRedlineExtraData_FormatColl( m_sFormatNm, m_nPoolId, m_pSet.get(), m_bFormatAll );
+}
+
+void SwRedlineExtraData_FormatColl::Reject( SwPaM& rPam ) const
+{
+ SwDoc& rDoc = rPam.GetDoc();
+
+ // What about Undo? Is it turned off?
+ SwTextFormatColl* pColl = USHRT_MAX == m_nPoolId
+ ? rDoc.FindTextFormatCollByName( m_sFormatNm )
+ : rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( m_nPoolId );
+
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore));
+
+ SwPaM aPam( *rPam.GetMark(), *rPam.GetPoint() );
+
+ const SwPosition* pEnd = rPam.End();
+
+ if ( !m_bFormatAll || pEnd->nContent == 0 )
+ {
+ // don't reject the format of the next paragraph (that is handled by the next redline)
+ if (aPam.GetPoint()->nNode > aPam.GetMark()->nNode)
+ {
+ aPam.GetPoint()->nNode--;
+ SwContentNode* pNode = aPam.GetPoint()->nNode.GetNode().GetContentNode();
+ if ( pNode )
+ aPam.GetPoint()->nContent.Assign( pNode, pNode->Len() );
+ else
+ // tdf#147507 set it back to a content node to avoid of crashing
+ aPam.GetPoint()->nNode++;
+ }
+ else if (aPam.GetPoint()->nNode < aPam.GetMark()->nNode)
+ {
+ aPam.GetMark()->nNode--;
+ SwContentNode* pNode = aPam.GetMark()->nNode.GetNode().GetContentNode();
+ aPam.GetMark()->nContent.Assign( pNode, pNode->Len() );
+ }
+ }
+
+ if( pColl )
+ rDoc.SetTextFormatColl( aPam, pColl, false );
+
+ if( m_pSet )
+ rDoc.getIDocumentContentOperations().InsertItemSet( aPam, *m_pSet );
+
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+}
+
+bool SwRedlineExtraData_FormatColl::operator == ( const SwRedlineExtraData& r) const
+{
+ const SwRedlineExtraData_FormatColl& rCmp = static_cast<const SwRedlineExtraData_FormatColl&>(r);
+ return m_sFormatNm == rCmp.m_sFormatNm && m_nPoolId == rCmp.m_nPoolId &&
+ m_bFormatAll == rCmp.m_bFormatAll &&
+ ( ( !m_pSet && !rCmp.m_pSet ) ||
+ ( m_pSet && rCmp.m_pSet && *m_pSet == *rCmp.m_pSet ) );
+}
+
+void SwRedlineExtraData_FormatColl::SetItemSet( const SfxItemSet& rSet )
+{
+ if( rSet.Count() )
+ m_pSet.reset( new SfxItemSet( rSet ) );
+ else
+ m_pSet.reset();
+}
+
+SwRedlineExtraData_Format::SwRedlineExtraData_Format( const SfxItemSet& rSet )
+{
+ SfxItemIter aIter( rSet );
+ for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ {
+ m_aWhichIds.push_back( pItem->Which() );
+ }
+}
+
+SwRedlineExtraData_Format::SwRedlineExtraData_Format(
+ const SwRedlineExtraData_Format& rCpy )
+ : SwRedlineExtraData()
+{
+ m_aWhichIds.insert( m_aWhichIds.begin(), rCpy.m_aWhichIds.begin(), rCpy.m_aWhichIds.end() );
+}
+
+SwRedlineExtraData_Format::~SwRedlineExtraData_Format()
+{
+}
+
+SwRedlineExtraData* SwRedlineExtraData_Format::CreateNew() const
+{
+ return new SwRedlineExtraData_Format( *this );
+}
+
+void SwRedlineExtraData_Format::Reject( SwPaM& rPam ) const
+{
+ SwDoc& rDoc = rPam.GetDoc();
+
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore));
+
+ // Actually we need to reset the Attribute here!
+ for( const auto& rWhichId : m_aWhichIds )
+ {
+ rDoc.getIDocumentContentOperations().InsertPoolItem( rPam, *GetDfltAttr( rWhichId ),
+ SetAttrMode::DONTEXPAND );
+ }
+
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+}
+
+bool SwRedlineExtraData_Format::operator == ( const SwRedlineExtraData& rCmp ) const
+{
+ const size_t nEnd = m_aWhichIds.size();
+ if( nEnd != static_cast<const SwRedlineExtraData_Format&>(rCmp).m_aWhichIds.size() )
+ return false;
+
+ for( size_t n = 0; n < nEnd; ++n )
+ {
+ if( static_cast<const SwRedlineExtraData_Format&>(rCmp).m_aWhichIds[n] != m_aWhichIds[n])
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+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_bMoved(false)
+{
+ m_aStamp.SetNanoSec( 0 );
+}
+
+SwRedlineData::SwRedlineData(
+ const SwRedlineData& rCpy,
+ bool bCpyNext )
+ : m_pNext( ( bCpyNext && rCpy.m_pNext ) ? new SwRedlineData( *rCpy.m_pNext ) : nullptr )
+ , m_pExtraData( rCpy.m_pExtraData ? rCpy.m_pExtraData->CreateNew() : nullptr )
+ , m_sComment( rCpy.m_sComment )
+ , m_aStamp( rCpy.m_aStamp )
+ , m_nAuthor( rCpy.m_nAuthor )
+ , m_eType( rCpy.m_eType )
+ , m_nSeqNo( rCpy.m_nSeqNo )
+ , m_bAutoFormat(false)
+ , m_bMoved( rCpy.m_bMoved )
+{
+}
+
+// For sw3io: We now own pNext!
+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_bMoved(false)
+{
+}
+
+SwRedlineData::~SwRedlineData()
+{
+ delete m_pExtraData;
+ delete m_pNext;
+}
+
+// Check whether the absolute difference between the two dates is no larger than one minute (can
+// give inaccurate results if at least one of the dates is not valid/normalized):
+static bool deltaOneMinute(DateTime const & t1, DateTime const & t2) {
+ auto const & [min, max] = std::minmax(t1, t2);
+ // Avoid overflow of `min + tools::Time(0, 1)` below when min is close to the maximum valid
+ // DateTime:
+ if (min >= DateTime({31, 12, std::numeric_limits<sal_Int16>::max()}, {23, 59})) {
+ return true;
+ }
+ return max <= min + tools::Time(0, 1);
+}
+
+bool SwRedlineData::CanCombine(const SwRedlineData& rCmp) const
+{
+ return m_nAuthor == rCmp.m_nAuthor &&
+ m_eType == rCmp.m_eType &&
+ m_sComment == rCmp.m_sComment &&
+ deltaOneMinute(GetTimeStamp(), rCmp.GetTimeStamp()) &&
+ m_bMoved == rCmp.m_bMoved &&
+ (( !m_pNext && !rCmp.m_pNext ) ||
+ ( m_pNext && rCmp.m_pNext &&
+ m_pNext->CanCombine( *rCmp.m_pNext ))) &&
+ (( !m_pExtraData && !rCmp.m_pExtraData ) ||
+ ( m_pExtraData && rCmp.m_pExtraData &&
+ *m_pExtraData == *rCmp.m_pExtraData ));
+}
+
+/// ExtraData is copied. The Pointer's ownership is thus NOT transferred
+/// to the Redline Object!
+void SwRedlineData::SetExtraData( const SwRedlineExtraData* pData )
+{
+ delete m_pExtraData;
+
+ // Check if there is data - and if so - delete it
+ if( pData )
+ m_pExtraData = pData->CreateNew();
+ else
+ m_pExtraData = nullptr;
+}
+
+const TranslateId STR_REDLINE_ARY[] =
+{
+ STR_UNDO_REDLINE_INSERT,
+ STR_UNDO_REDLINE_DELETE,
+ STR_UNDO_REDLINE_FORMAT,
+ STR_UNDO_REDLINE_TABLE,
+ STR_UNDO_REDLINE_FMTCOLL,
+ STR_UNDO_REDLINE_PARAGRAPH_FORMAT,
+ STR_UNDO_REDLINE_TABLE_ROW_INSERT,
+ STR_UNDO_REDLINE_TABLE_ROW_DELETE,
+ STR_UNDO_REDLINE_TABLE_CELL_INSERT,
+ STR_UNDO_REDLINE_TABLE_CELL_DELETE
+};
+
+OUString SwRedlineData::GetDescr() const
+{
+ return SwResId(STR_REDLINE_ARY[static_cast<int>(GetType())]);
+}
+
+sal_uInt32 SwRangeRedline::s_nLastId = 1;
+
+SwRangeRedline::SwRangeRedline(RedlineType eTyp, const SwPaM& rPam )
+ : SwPaM( *rPam.GetMark(), *rPam.GetPoint() ),
+ m_pRedlineData( new SwRedlineData( eTyp, GetDoc().getIDocumentRedlineAccess().GetRedlineAuthor() ) ),
+ m_pContentSect( nullptr ),
+ m_nId( s_nLastId++ )
+{
+ m_bDelLastPara = false;
+ m_bIsVisible = true;
+ if( !rPam.HasMark() )
+ DeleteMark();
+
+ // set default comment for single annotations added or deleted
+ if ( IsAnnotation() )
+ {
+ SetComment( RedlineType::Delete == eTyp
+ ? SwResId(STR_REDLINE_COMMENT_DELETED)
+ : SwResId(STR_REDLINE_COMMENT_ADDED) );
+ }
+}
+
+SwRangeRedline::SwRangeRedline( const SwRedlineData& rData, const SwPaM& rPam )
+ : SwPaM( *rPam.GetMark(), *rPam.GetPoint() ),
+ m_pRedlineData( new SwRedlineData( rData )),
+ m_pContentSect( nullptr ),
+ m_nId( s_nLastId++ )
+{
+ m_bDelLastPara = false;
+ m_bIsVisible = true;
+ if( !rPam.HasMark() )
+ DeleteMark();
+}
+
+SwRangeRedline::SwRangeRedline( const SwRedlineData& rData, const SwPosition& rPos )
+ : SwPaM( rPos ),
+ m_pRedlineData( new SwRedlineData( rData )),
+ m_pContentSect( nullptr ),
+ m_nId( s_nLastId++ )
+{
+ m_bDelLastPara = false;
+ m_bIsVisible = true;
+}
+
+SwRangeRedline::SwRangeRedline( const SwRangeRedline& rCpy )
+ : SwPaM( *rCpy.GetMark(), *rCpy.GetPoint() ),
+ m_pRedlineData( new SwRedlineData( *rCpy.m_pRedlineData )),
+ m_pContentSect( nullptr ),
+ m_nId( s_nLastId++ )
+{
+ m_bDelLastPara = false;
+ m_bIsVisible = true;
+ if( !rCpy.HasMark() )
+ DeleteMark();
+}
+
+SwRangeRedline::~SwRangeRedline()
+{
+ if( m_pContentSect )
+ {
+ // delete the ContentSection
+ if( !GetDoc().IsInDtor() )
+ GetDoc().getIDocumentContentOperations().DeleteSection( &m_pContentSect->GetNode() );
+ delete m_pContentSect;
+ }
+ delete m_pRedlineData;
+}
+
+void MaybeNotifyRedlineModification(SwRangeRedline& rRedline, SwDoc& rDoc)
+{
+ if (!lcl_LOKRedlineNotificationEnabled())
+ return;
+
+ const SwRedlineTable& rRedTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ for (SwRedlineTable::size_type i = 0; i < rRedTable.size(); ++i)
+ {
+ if (rRedTable[i] == &rRedline)
+ {
+ SwRedlineTable::LOKRedlineNotification(RedlineNotification::Modify, &rRedline);
+ break;
+ }
+ }
+}
+
+void SwRangeRedline::MaybeNotifyRedlinePositionModification(tools::Long nTop)
+{
+ if (!lcl_LOKRedlineNotificationEnabled())
+ return;
+
+ if(!m_oLOKLastNodeTop || *m_oLOKLastNodeTop != nTop)
+ {
+ m_oLOKLastNodeTop = nTop;
+ SwRedlineTable::LOKRedlineNotification(RedlineNotification::Modify, this);
+ }
+}
+
+void SwRangeRedline::SetStart( const SwPosition& rPos, SwPosition* pSttPtr )
+{
+ if( !pSttPtr ) pSttPtr = Start();
+ *pSttPtr = rPos;
+
+ MaybeNotifyRedlineModification(*this, GetDoc());
+}
+
+void SwRangeRedline::SetEnd( const SwPosition& rPos, SwPosition* pEndPtr )
+{
+ if( !pEndPtr ) pEndPtr = End();
+ *pEndPtr = rPos;
+
+ MaybeNotifyRedlineModification(*this, GetDoc());
+}
+
+/// Do we have a valid Selection?
+bool SwRangeRedline::HasValidRange() const
+{
+ const SwNode* pPtNd = &GetPoint()->nNode.GetNode(),
+ * pMkNd = &GetMark()->nNode.GetNode();
+ if( pPtNd->StartOfSectionNode() == pMkNd->StartOfSectionNode() &&
+ !pPtNd->StartOfSectionNode()->IsTableNode() &&
+ // invalid if points on the end of content
+ // end-of-content only invalid if no content index exists
+ ( pPtNd != pMkNd || GetContentIdx() != nullptr ||
+ pPtNd != &pPtNd->GetNodes().GetEndOfContent() )
+ )
+ return true;
+ return false;
+}
+
+void SwRangeRedline::CallDisplayFunc(size_t nMyPos)
+{
+ RedlineFlags eShow = RedlineFlags::ShowMask & GetDoc().getIDocumentRedlineAccess().GetRedlineFlags();
+ if (eShow == (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete))
+ Show(0, nMyPos);
+ else if (eShow == RedlineFlags::ShowInsert)
+ Hide(0, nMyPos);
+ else if (eShow == RedlineFlags::ShowDelete)
+ ShowOriginal(0, nMyPos);
+}
+
+void SwRangeRedline::Show(sal_uInt16 nLoop, size_t nMyPos, bool bForced)
+{
+ SwDoc& rDoc = GetDoc();
+
+ bool bIsShowChangesInMargin = false;
+ if ( !bForced )
+ {
+ SwViewShell* pSh = rDoc.getIDocumentLayoutAccess().GetCurrentViewShell();
+ if (pSh)
+ bIsShowChangesInMargin = pSh->GetViewOptions()->IsShowChangesInMargin();
+ else
+ bIsShowChangesInMargin = SW_MOD()->GetUsrPref(false)->IsShowChangesInMargin();
+ }
+
+ if( 1 > nLoop && !bIsShowChangesInMargin )
+ return;
+
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld | RedlineFlags::Ignore);
+ ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+
+ switch( GetType() )
+ {
+ case RedlineType::Insert: // Content has been inserted
+ m_bIsVisible = true;
+ MoveFromSection(nMyPos);
+ break;
+
+ case RedlineType::Delete: // Content has been deleted
+ m_bIsVisible = !bIsShowChangesInMargin;
+
+ if (m_bIsVisible)
+ MoveFromSection(nMyPos);
+ else
+ {
+ switch( nLoop )
+ {
+ case 0: MoveToSection(); break;
+ case 1: CopyToSection(); break;
+ case 2: DelCopyOfSection(nMyPos); break;
+ }
+ }
+ break;
+
+ case RedlineType::Format: // Attributes have been applied
+ case RedlineType::Table: // Table structure has been modified
+ InvalidateRange(Invalidation::Add);
+ break;
+ default:
+ break;
+ }
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+}
+
+void SwRangeRedline::Hide(sal_uInt16 nLoop, size_t nMyPos, bool /*bForced*/)
+{
+ SwDoc& rDoc = GetDoc();
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld | RedlineFlags::Ignore);
+ ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+
+ switch( GetType() )
+ {
+ case RedlineType::Insert: // Content has been inserted
+ m_bIsVisible = true;
+ if( 1 <= nLoop )
+ MoveFromSection(nMyPos);
+ break;
+
+ case RedlineType::Delete: // Content has been deleted
+ m_bIsVisible = false;
+ switch( nLoop )
+ {
+ case 0: MoveToSection(); break;
+ case 1: CopyToSection(); break;
+ case 2: DelCopyOfSection(nMyPos); break;
+ }
+ break;
+
+ case RedlineType::Format: // Attributes have been applied
+ case RedlineType::Table: // Table structure has been modified
+ if( 1 <= nLoop )
+ InvalidateRange(Invalidation::Remove);
+ break;
+ default:
+ break;
+ }
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+}
+
+void SwRangeRedline::ShowOriginal(sal_uInt16 nLoop, size_t nMyPos, bool /*bForced*/)
+{
+ SwDoc& rDoc = GetDoc();
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ SwRedlineData* pCur;
+
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld | RedlineFlags::Ignore);
+ ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+
+ // Determine the Type, it's the first on Stack
+ for( pCur = m_pRedlineData; pCur->m_pNext; )
+ pCur = pCur->m_pNext;
+
+ switch( pCur->m_eType )
+ {
+ case RedlineType::Insert: // Content has been inserted
+ m_bIsVisible = false;
+ switch( nLoop )
+ {
+ case 0: MoveToSection(); break;
+ case 1: CopyToSection(); break;
+ case 2: DelCopyOfSection(nMyPos); break;
+ }
+ break;
+
+ case RedlineType::Delete: // Content has been deleted
+ m_bIsVisible = true;
+ if( 1 <= nLoop )
+ MoveFromSection(nMyPos);
+ break;
+
+ case RedlineType::Format: // Attributes have been applied
+ case RedlineType::Table: // Table structure has been modified
+ if( 1 <= nLoop )
+ InvalidateRange(Invalidation::Remove);
+ break;
+ default:
+ break;
+ }
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+}
+
+// trigger the Layout
+void SwRangeRedline::InvalidateRange(Invalidation const eWhy)
+{
+ SwNodeOffset nSttNd = Start()->nNode.GetIndex(),
+ nEndNd = End()->nNode.GetIndex();
+ sal_Int32 nSttCnt = Start()->nContent.GetIndex();
+ sal_Int32 nEndCnt = End()->nContent.GetIndex();
+
+ SwNodes& rNds = GetDoc().GetNodes();
+ for (SwNodeOffset n(nSttNd); n <= nEndNd; ++n)
+ {
+ SwNode* pNode = rNds[n];
+
+ if (pNode && pNode->IsTextNode())
+ {
+ SwTextNode* pNd = pNode->GetTextNode();
+
+ SwUpdateAttr aHt(
+ n == nSttNd ? nSttCnt : 0,
+ n == nEndNd ? nEndCnt : pNd->GetText().getLength(),
+ RES_FMT_CHG);
+
+ pNd->TriggerNodeUpdate(sw::LegacyModifyHint(&aHt, &aHt));
+
+ // SwUpdateAttr must be handled first, otherwise indexes are off
+ if (GetType() == RedlineType::Delete)
+ {
+ sal_Int32 const nStart(n == nSttNd ? nSttCnt : 0);
+ sal_Int32 const nLen((n == nEndNd ? nEndCnt : pNd->GetText().getLength()) - nStart);
+ if (eWhy == Invalidation::Add)
+ {
+ sw::RedlineDelText const hint(nStart, nLen);
+ pNd->CallSwClientNotify(hint);
+ }
+ else
+ {
+ sw::RedlineUnDelText const hint(nStart, nLen);
+ pNd->CallSwClientNotify(hint);
+ }
+ }
+ }
+ }
+}
+
+/** Calculates the start and end position of the intersection rTmp and
+ text node nNdIdx */
+void SwRangeRedline::CalcStartEnd( SwNodeOffset nNdIdx, sal_Int32& rStart, sal_Int32& rEnd ) const
+{
+ const SwPosition *pRStt = Start(), *pREnd = End();
+ if( pRStt->nNode < nNdIdx )
+ {
+ if( pREnd->nNode > nNdIdx )
+ {
+ rStart = 0; // Paragraph is completely enclosed
+ rEnd = COMPLETE_STRING;
+ }
+ else if (pREnd->nNode == nNdIdx)
+ {
+ rStart = 0; // Paragraph is overlapped in the beginning
+ rEnd = pREnd->nContent.GetIndex();
+ }
+ else // redline ends before paragraph
+ {
+ rStart = COMPLETE_STRING;
+ rEnd = COMPLETE_STRING;
+ }
+ }
+ else if( pRStt->nNode == nNdIdx )
+ {
+ rStart = pRStt->nContent.GetIndex();
+ if( pREnd->nNode == nNdIdx )
+ rEnd = pREnd->nContent.GetIndex(); // Within the Paragraph
+ else
+ rEnd = COMPLETE_STRING; // Paragraph is overlapped in the end
+ }
+ else
+ {
+ rStart = COMPLETE_STRING;
+ rEnd = COMPLETE_STRING;
+ }
+}
+
+static void lcl_storeAnnotationMarks(SwDoc& rDoc, const SwPosition* pStt, const SwPosition* pEnd)
+{
+ // tdf#115815 keep original start position of collapsed annotation ranges
+ // as temporary bookmarks (removed after file saving and file loading)
+ IDocumentMarkAccess& rDMA(*rDoc.getIDocumentMarkAccess());
+ for (auto iter = rDMA.getAnnotationMarksBegin();
+ iter != rDMA.getAnnotationMarksEnd(); )
+ {
+ SwPosition const& rStartPos((**iter).GetMarkStart());
+ if ( *pStt <= rStartPos && rStartPos < *pEnd )
+ {
+ IDocumentMarkAccess::const_iterator_t pOldMark =
+ rDMA.findAnnotationBookmark((**iter).GetName());
+ if ( pOldMark == rDMA.getBookmarksEnd() )
+ {
+ // at start of redlines use a 1-character length bookmark range
+ // instead of a 0-character length bookmark position to avoid its losing
+ sal_Int32 nLen = (*pStt == rStartPos) ? 1 : 0;
+ SwPaM aPam( rStartPos.nNode, rStartPos.nContent.GetIndex(),
+ rStartPos.nNode, rStartPos.nContent.GetIndex() + nLen);
+ ::sw::mark::IMark* pMark = rDMA.makeAnnotationBookmark(
+ aPam,
+ (**iter).GetName(),
+ IDocumentMarkAccess::MarkType::BOOKMARK, sw::mark::InsertMode::New);
+ ::sw::mark::IBookmark* pBookmark = dynamic_cast< ::sw::mark::IBookmark* >(pMark);
+ if (pBookmark)
+ {
+ pBookmark->SetKeyCode(vcl::KeyCode());
+ pBookmark->SetShortName(OUString());
+ }
+ }
+ }
+ ++iter;
+ }
+}
+
+void SwRangeRedline::MoveToSection()
+{
+ if( !m_pContentSect )
+ {
+ const SwPosition* pStt = Start(),
+ * pEnd = End();
+
+ SwDoc& rDoc = GetDoc();
+ SwPaM aPam( *pStt, *pEnd );
+ SwContentNode* pCSttNd = pStt->nNode.GetNode().GetContentNode();
+ SwContentNode* pCEndNd = pEnd->nNode.GetNode().GetContentNode();
+
+ if( !pCSttNd )
+ {
+ // In order to not move other Redlines' indices, we set them
+ // to the end (is exclusive)
+ const SwRedlineTable& rTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ for(SwRangeRedline* pRedl : rTable)
+ {
+ if( pRedl->GetBound() == *pStt )
+ pRedl->GetBound() = *pEnd;
+ if( pRedl->GetBound(false) == *pStt )
+ pRedl->GetBound(false) = *pEnd;
+ }
+ }
+
+ SwStartNode* pSttNd;
+ SwNodes& rNds = rDoc.GetNodes();
+ if( pCSttNd || pCEndNd )
+ {
+ SwTextFormatColl* pColl = (pCSttNd && pCSttNd->IsTextNode() )
+ ? pCSttNd->GetTextNode()->GetTextColl()
+ : (pCEndNd && pCEndNd->IsTextNode() )
+ ? pCEndNd->GetTextNode()->GetTextColl()
+ : rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD);
+
+ pSttNd = rNds.MakeTextSection( SwNodeIndex( rNds.GetEndOfRedlines() ),
+ SwNormalStartNode, pColl );
+ SwTextNode* pTextNd = rNds[ pSttNd->GetIndex() + 1 ]->GetTextNode();
+
+ SwNodeIndex aNdIdx( *pTextNd );
+ SwPosition aPos( aNdIdx, SwIndex( pTextNd ));
+ if( pCSttNd && pCEndNd )
+ {
+ // tdf#140982 keep annotation ranges in deletions in margin mode
+ lcl_storeAnnotationMarks( rDoc, pStt, pEnd );
+ rDoc.getIDocumentContentOperations().MoveAndJoin( aPam, aPos );
+ }
+ else
+ {
+ if( pCSttNd && !pCEndNd )
+ m_bDelLastPara = true;
+ rDoc.getIDocumentContentOperations().MoveRange( aPam, aPos,
+ SwMoveFlags::DEFAULT );
+ }
+ }
+ else
+ {
+ pSttNd = SwNodes::MakeEmptySection( SwNodeIndex( rNds.GetEndOfRedlines() ) );
+
+ SwPosition aPos( *pSttNd->EndOfSectionNode() );
+ rDoc.getIDocumentContentOperations().MoveRange( aPam, aPos,
+ SwMoveFlags::DEFAULT );
+ }
+ m_pContentSect = new SwNodeIndex( *pSttNd );
+
+ if( pStt == GetPoint() )
+ Exchange();
+
+ DeleteMark();
+ }
+ else
+ InvalidateRange(Invalidation::Remove);
+}
+
+void SwRangeRedline::CopyToSection()
+{
+ if( m_pContentSect )
+ return;
+
+ const SwPosition* pStt = Start(),
+ * pEnd = End();
+
+ SwContentNode* pCSttNd = pStt->nNode.GetNode().GetContentNode();
+ SwContentNode* pCEndNd = pEnd->nNode.GetNode().GetContentNode();
+
+ SwStartNode* pSttNd;
+ SwDoc& rDoc = GetDoc();
+ SwNodes& rNds = rDoc.GetNodes();
+
+ bool bSaveCopyFlag = rDoc.IsCopyIsMove(),
+ bSaveRdlMoveFlg = rDoc.getIDocumentRedlineAccess().IsRedlineMove();
+ rDoc.SetCopyIsMove( true );
+
+ // The IsRedlineMove() flag causes the behaviour of the
+ // DocumentContentOperationsManager::CopyFlyInFlyImpl() method to change,
+ // which will eventually be called by the CopyRange() below.
+ rDoc.getIDocumentRedlineAccess().SetRedlineMove(true);
+
+ if( pCSttNd )
+ {
+ SwTextFormatColl* pColl = pCSttNd->IsTextNode()
+ ? pCSttNd->GetTextNode()->GetTextColl()
+ : rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD);
+
+ pSttNd = rNds.MakeTextSection( SwNodeIndex( rNds.GetEndOfRedlines() ),
+ SwNormalStartNode, pColl );
+
+ SwNodeIndex aNdIdx( *pSttNd, 1 );
+ SwTextNode* pTextNd = aNdIdx.GetNode().GetTextNode();
+ SwPosition aPos( aNdIdx, SwIndex( pTextNd ));
+
+ // tdf#115815 keep original start position of collapsed annotation ranges
+ // as temporary bookmarks (removed after file saving and file loading)
+ lcl_storeAnnotationMarks( rDoc, pStt, pEnd );
+ rDoc.getIDocumentContentOperations().CopyRange(*this, aPos, SwCopyFlags::CheckPosInFly);
+
+ // Take over the style from the EndNode if needed
+ // We don't want this in Doc::Copy
+ if( pCEndNd && pCEndNd != pCSttNd )
+ {
+ SwContentNode* pDestNd = aPos.nNode.GetNode().GetContentNode();
+ if( pDestNd )
+ {
+ if( pDestNd->IsTextNode() && pCEndNd->IsTextNode() )
+ pCEndNd->GetTextNode()->CopyCollFormat(*pDestNd->GetTextNode());
+ else
+ pDestNd->ChgFormatColl( pCEndNd->GetFormatColl() );
+ }
+ }
+ }
+ else
+ {
+ pSttNd = SwNodes::MakeEmptySection( SwNodeIndex( rNds.GetEndOfRedlines() ) );
+
+ if( pCEndNd )
+ {
+ SwPosition aPos( *pSttNd->EndOfSectionNode() );
+ rDoc.getIDocumentContentOperations().CopyRange(*this, aPos, SwCopyFlags::CheckPosInFly);
+ }
+ else
+ {
+ SwNodeIndex aInsPos( *pSttNd->EndOfSectionNode() );
+ SwNodeRange aRg( pStt->nNode, SwNodeOffset(0), pEnd->nNode, SwNodeOffset(1) );
+ rDoc.GetDocumentContentOperationsManager().CopyWithFlyInFly(aRg, aInsPos);
+ }
+ }
+ m_pContentSect = new SwNodeIndex( *pSttNd );
+
+ rDoc.SetCopyIsMove( bSaveCopyFlag );
+ rDoc.getIDocumentRedlineAccess().SetRedlineMove( bSaveRdlMoveFlg );
+}
+
+void SwRangeRedline::DelCopyOfSection(size_t nMyPos)
+{
+ if( !m_pContentSect )
+ return;
+
+ const SwPosition* pStt = Start(),
+ * pEnd = End();
+
+ SwDoc& rDoc = GetDoc();
+ SwPaM aPam( *pStt, *pEnd );
+ SwContentNode* pCSttNd = pStt->nNode.GetNode().GetContentNode();
+ SwContentNode* pCEndNd = pEnd->nNode.GetNode().GetContentNode();
+
+ if( !pCSttNd )
+ {
+ // In order to not move other Redlines' indices, we set them
+ // to the end (is exclusive)
+ const SwRedlineTable& rTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ for(SwRangeRedline* pRedl : rTable)
+ {
+ if( pRedl->GetBound() == *pStt )
+ pRedl->GetBound() = *pEnd;
+ if( pRedl->GetBound(false) == *pStt )
+ pRedl->GetBound(false) = *pEnd;
+ }
+ }
+
+ if( pCSttNd && pCEndNd )
+ {
+ // #i100466# - force a <join next> on <delete and join> operation
+ // tdf#125319 - rather not?
+ rDoc.getIDocumentContentOperations().DeleteAndJoin(aPam/*, true*/);
+ }
+ else if( pCSttNd || pCEndNd )
+ {
+ if( pCSttNd && !pCEndNd )
+ m_bDelLastPara = true;
+ rDoc.getIDocumentContentOperations().DeleteRange( aPam );
+
+ if( m_bDelLastPara )
+ {
+ // To prevent dangling references to the paragraph to
+ // be deleted, redline that point into this paragraph should be
+ // moved to the new end position. Since redlines in the redline
+ // table are sorted and the pEnd position is an endnode (see
+ // bDelLastPara condition above), only redlines before the
+ // current ones can be affected.
+ const SwRedlineTable& rTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ size_t n = nMyPos;
+ for( bool bBreak = false; !bBreak && n > 0; )
+ {
+ --n;
+ bBreak = true;
+ if( rTable[ n ]->GetBound() == *aPam.GetPoint() )
+ {
+ rTable[ n ]->GetBound() = *pEnd;
+ bBreak = false;
+ }
+ if( rTable[ n ]->GetBound(false) == *aPam.GetPoint() )
+ {
+ rTable[ n ]->GetBound(false) = *pEnd;
+ bBreak = false;
+ }
+ }
+
+ *GetPoint() = *pEnd;
+ *GetMark() = *pEnd;
+ DeleteMark();
+
+ aPam.GetBound().nContent.Assign( nullptr, 0 );
+ aPam.GetBound( false ).nContent.Assign( nullptr, 0 );
+ aPam.DeleteMark();
+ rDoc.getIDocumentContentOperations().DelFullPara( aPam );
+ }
+ }
+ else
+ {
+ rDoc.getIDocumentContentOperations().DeleteRange( aPam );
+ }
+
+ if( pStt == GetPoint() )
+ Exchange();
+
+ DeleteMark();
+}
+
+void SwRangeRedline::MoveFromSection(size_t nMyPos)
+{
+ if( m_pContentSect )
+ {
+ SwDoc& rDoc = GetDoc();
+ const SwRedlineTable& rTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ std::vector<SwPosition*> aBeforeArr, aBehindArr;
+ bool bBreak = false;
+ SwRedlineTable::size_type n;
+
+ for( n = nMyPos+1; !bBreak && n < rTable.size(); ++n )
+ {
+ bBreak = true;
+ if( rTable[ n ]->GetBound() == *GetPoint() )
+ {
+ SwRangeRedline* pRedl = rTable[n];
+ aBehindArr.push_back(&pRedl->GetBound());
+ bBreak = false;
+ }
+ if( rTable[ n ]->GetBound(false) == *GetPoint() )
+ {
+ SwRangeRedline* pRedl = rTable[n];
+ aBehindArr.push_back(&pRedl->GetBound(false));
+ bBreak = false;
+ }
+ }
+ for( bBreak = false, n = nMyPos; !bBreak && n ; )
+ {
+ --n;
+ bBreak = true;
+ if( rTable[ n ]->GetBound() == *GetPoint() )
+ {
+ SwRangeRedline* pRedl = rTable[n];
+ aBeforeArr.push_back(&pRedl->GetBound());
+ bBreak = false;
+ }
+ if( rTable[ n ]->GetBound(false) == *GetPoint() )
+ {
+ SwRangeRedline* pRedl = rTable[n];
+ aBeforeArr.push_back(&pRedl->GetBound(false));
+ bBreak = false;
+ }
+ }
+
+ const SwNode* pKeptContentSectNode( &m_pContentSect->GetNode() ); // #i95711#
+ {
+ SwPaM aPam( m_pContentSect->GetNode(),
+ *m_pContentSect->GetNode().EndOfSectionNode(), SwNodeOffset(1),
+ SwNodeOffset( m_bDelLastPara ? -2 : -1 ) );
+ SwContentNode* pCNd = aPam.GetContentNode();
+ if( pCNd )
+ aPam.GetPoint()->nContent.Assign( pCNd, pCNd->Len() );
+ else
+ ++aPam.GetPoint()->nNode;
+
+ SwFormatColl* pColl = pCNd && pCNd->Len() && aPam.GetPoint()->nNode !=
+ aPam.GetMark()->nNode
+ ? pCNd->GetFormatColl() : nullptr;
+
+ SwNodeIndex aNdIdx( GetPoint()->nNode, -1 );
+ const sal_Int32 nPos = GetPoint()->nContent.GetIndex();
+
+ SwPosition aPos( *GetPoint() );
+ if( m_bDelLastPara && *aPam.GetPoint() == *aPam.GetMark() )
+ {
+ --aPos.nNode;
+
+ rDoc.getIDocumentContentOperations().AppendTextNode( aPos );
+ }
+ else
+ {
+ rDoc.getIDocumentContentOperations().MoveRange( aPam, aPos,
+ SwMoveFlags::ALLFLYS );
+ }
+
+ SetMark();
+ *GetPoint() = aPos;
+ GetMark()->nNode = aNdIdx.GetIndex() + 1;
+ pCNd = GetMark()->nNode.GetNode().GetContentNode();
+ GetMark()->nContent.Assign( pCNd, nPos );
+
+ if( m_bDelLastPara )
+ {
+ ++GetPoint()->nNode;
+ pCNd = GetContentNode();
+ GetPoint()->nContent.Assign( pCNd, 0 );
+ m_bDelLastPara = false;
+ }
+ else if( pColl )
+ pCNd = GetContentNode();
+
+ if( pColl && pCNd )
+ pCNd->ChgFormatColl( pColl );
+ }
+
+ // #i95771#
+ // Under certain conditions the previous <SwDoc::Move(..)> has already
+ // removed the change tracking section of this <SwRangeRedline> instance from
+ // the change tracking nodes area.
+ // Thus, check if <pContentSect> still points to the change tracking section
+ // by comparing it with the "indexed" <SwNode> instance copied before
+ // perform the intrinsic move.
+ // Note: Such condition is e.g. a "delete" change tracking only containing a table.
+ if ( &m_pContentSect->GetNode() == pKeptContentSectNode )
+ {
+ rDoc.getIDocumentContentOperations().DeleteSection( &m_pContentSect->GetNode() );
+ }
+ delete m_pContentSect;
+ m_pContentSect = nullptr;
+
+ // adjustment of redline table positions must take start and
+ // end into account, not point and mark.
+ for( auto& pItem : aBeforeArr )
+ *pItem = *Start();
+ for( auto& pItem : aBehindArr )
+ *pItem = *End();
+ }
+ else
+ InvalidateRange(Invalidation::Add);
+}
+
+// for Undo
+void SwRangeRedline::SetContentIdx( const SwNodeIndex* pIdx )
+{
+ if( pIdx && !m_pContentSect )
+ {
+ m_pContentSect = new SwNodeIndex( *pIdx );
+ m_bIsVisible = false;
+ }
+ else if( !pIdx && m_pContentSect )
+ {
+ delete m_pContentSect;
+ m_pContentSect = nullptr;
+ m_bIsVisible = false;
+ }
+ else
+ {
+ OSL_FAIL("SwRangeRedline::SetContentIdx: invalid state");
+ }
+}
+
+bool SwRangeRedline::CanCombine( const SwRangeRedline& rRedl ) const
+{
+ return IsVisible() && rRedl.IsVisible() &&
+ m_pRedlineData->CanCombine( *rRedl.m_pRedlineData );
+}
+
+void SwRangeRedline::PushData( const SwRangeRedline& rRedl, bool bOwnAsNext )
+{
+ SwRedlineData* pNew = new SwRedlineData( *rRedl.m_pRedlineData, false );
+ if( bOwnAsNext )
+ {
+ pNew->m_pNext = m_pRedlineData;
+ m_pRedlineData = pNew;
+ }
+ else
+ {
+ pNew->m_pNext = m_pRedlineData->m_pNext;
+ m_pRedlineData->m_pNext = pNew;
+ }
+}
+
+bool SwRangeRedline::PopData()
+{
+ if( !m_pRedlineData->m_pNext )
+ return false;
+ SwRedlineData* pCur = m_pRedlineData;
+ m_pRedlineData = pCur->m_pNext;
+ pCur->m_pNext = nullptr;
+ delete pCur;
+ return true;
+}
+
+sal_uInt16 SwRangeRedline::GetStackCount() const
+{
+ sal_uInt16 nRet = 1;
+ for( SwRedlineData* pCur = m_pRedlineData; pCur->m_pNext; pCur = pCur->m_pNext )
+ ++nRet;
+ return nRet;
+}
+
+std::size_t SwRangeRedline::GetAuthor( sal_uInt16 nPos ) const
+{
+ return GetRedlineData(nPos).m_nAuthor;
+}
+
+OUString const & SwRangeRedline::GetAuthorString( sal_uInt16 nPos ) const
+{
+ return SW_MOD()->GetRedlineAuthor(GetRedlineData(nPos).m_nAuthor);
+}
+
+const DateTime& SwRangeRedline::GetTimeStamp( sal_uInt16 nPos ) const
+{
+ return GetRedlineData(nPos).m_aStamp;
+}
+
+RedlineType SwRangeRedline::GetType( sal_uInt16 nPos ) const
+{
+ return GetRedlineData(nPos).m_eType;
+}
+
+bool SwRangeRedline::IsAnnotation() const
+{
+ return GetText().getLength() == 1 && GetText()[0] == CH_TXTATR_INWORD;
+}
+
+const OUString& SwRangeRedline::GetComment( sal_uInt16 nPos ) const
+{
+ return GetRedlineData(nPos).m_sComment;
+}
+
+bool SwRangeRedline::operator<( const SwRangeRedline& rCmp ) const
+{
+ if (*Start() < *rCmp.Start())
+ return true;
+
+ return *Start() == *rCmp.Start() && *End() < *rCmp.End();
+}
+
+const SwRedlineData & SwRangeRedline::GetRedlineData(const sal_uInt16 nPos) const
+{
+ SwRedlineData * pCur = m_pRedlineData;
+
+ sal_uInt16 nP = nPos;
+
+ while (nP > 0 && nullptr != pCur->m_pNext)
+ {
+ pCur = pCur->m_pNext;
+
+ nP--;
+ }
+
+ SAL_WARN_IF( nP != 0, "sw.core", "Pos " << nPos << " is " << nP << " too big");
+
+ return *pCur;
+}
+
+OUString SwRangeRedline::GetDescr(bool bSimplified)
+{
+ // get description of redline data (e.g.: "insert $1")
+ OUString aResult = GetRedlineData().GetDescr();
+
+ SwPaM * pPaM = nullptr;
+ bool bDeletePaM = false;
+
+ // if this redline is visible the content is in this PaM
+ if (nullptr == m_pContentSect)
+ {
+ pPaM = this;
+ }
+ else // otherwise it is saved in pContentSect
+ {
+ SwNodeIndex aTmpIdx( *m_pContentSect->GetNode().EndOfSectionNode() );
+ pPaM = new SwPaM(*m_pContentSect, aTmpIdx );
+ bDeletePaM = true;
+ }
+
+ OUString sDescr = DenoteSpecialCharacters(pPaM->GetText().replace('\n', ' '), /*bQuoted=*/!bSimplified);
+ if (const SwTextNode *pTextNode = pPaM->GetNode().GetTextNode())
+ {
+ if (const SwTextAttr* pTextAttr = pTextNode->GetFieldTextAttrAt(pPaM->GetPoint()->nContent.GetIndex() - 1, true ))
+ {
+ sDescr = ( bSimplified ? "" : SwResId(STR_START_QUOTE) )
+ + pTextAttr->GetFormatField().GetField()->GetFieldName()
+ + ( bSimplified ? "" : SwResId(STR_END_QUOTE) );
+ }
+ }
+
+ // replace $1 in description by description of the redlines text
+ const OUString aTmpStr = ShortenString(sDescr, nUndoStringLength, SwResId(STR_LDOTS));
+
+ if (!bSimplified)
+ {
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, aTmpStr);
+
+ aResult = aRewriter.Apply(aResult);
+ }
+ else
+ {
+ aResult = aTmpStr;
+ // more shortening
+ sal_Int32 nPos = aTmpStr.indexOf(SwResId(STR_LDOTS));
+ if (nPos > 5)
+ aResult = aTmpStr.copy(0, nPos + SwResId(STR_LDOTS).getLength());
+ }
+
+ if (bDeletePaM)
+ delete pPaM;
+
+ return aResult;
+}
+
+void SwRangeRedline::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwRangeRedline"));
+
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("id"), BAD_CAST(OString::number(GetSeqNo()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("author"), BAD_CAST(SW_MOD()->GetRedlineAuthor(GetAuthor()).toUtf8().getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("date"), BAD_CAST(DateTimeToOString(GetTimeStamp()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("descr"), BAD_CAST(const_cast<SwRangeRedline*>(this)->GetDescr().toUtf8().getStr()));
+
+ OString sRedlineType;
+ switch (GetType())
+ {
+ case RedlineType::Insert:
+ sRedlineType = "REDLINE_INSERT";
+ break;
+ case RedlineType::Delete:
+ sRedlineType = "REDLINE_DELETE";
+ break;
+ case RedlineType::Format:
+ sRedlineType = "REDLINE_FORMAT";
+ break;
+ default:
+ sRedlineType = "UNKNOWN";
+ break;
+ }
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("type"), BAD_CAST(sRedlineType.getStr()));
+
+ SwPaM::dumpAsXml(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+void SwExtraRedlineTable::Insert( SwExtraRedline* p )
+{
+ m_aExtraRedlines.push_back( p );
+ //p->CallDisplayFunc();
+}
+
+void SwExtraRedlineTable::DeleteAndDestroy(sal_uInt16 const nPos)
+{
+ /*
+ SwDoc* pDoc = 0;
+ if( !nP && nL && nL == size() )
+ pDoc = front()->GetDoc();
+ */
+
+ delete m_aExtraRedlines[nPos];
+ m_aExtraRedlines.erase(m_aExtraRedlines.begin() + nPos);
+
+ /*
+ SwViewShell* pSh;
+ if( pDoc && !pDoc->IsInDtor() &&
+ 0 != ( pSh = pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() ) )
+ pSh->InvalidateWindows( SwRect( 0, 0, SAL_MAX_INT32, SAL_MAX_INT32 ) );
+ */
+}
+
+void SwExtraRedlineTable::DeleteAndDestroyAll()
+{
+ while (!m_aExtraRedlines.empty())
+ {
+ auto const pRedline = m_aExtraRedlines.back();
+ m_aExtraRedlines.pop_back();
+ delete pRedline;
+ }
+}
+
+SwExtraRedline::~SwExtraRedline()
+{
+}
+
+SwTableRowRedline::SwTableRowRedline(const SwRedlineData& rData, const SwTableLine& rTableLine)
+ : m_aRedlineData(rData)
+ , m_rTableLine(rTableLine)
+{
+}
+
+SwTableRowRedline::~SwTableRowRedline()
+{
+}
+
+SwTableCellRedline::SwTableCellRedline(const SwRedlineData& rData, const SwTableBox& rTableBox)
+ : m_aRedlineData(rData)
+ , m_rTableBox(rTableBox)
+{
+}
+
+SwTableCellRedline::~SwTableCellRedline()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */