/* -*- 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 .
*/
// Check if this redline object type should be deleted if( RedlineType::Any != nRedlineTypeToDelete && nRedlineTypeToDelete != nRedlineType ) continue;
// Check if this redline object type should be deleted if( RedlineType::Any != nRedlineTypeToDelete && nRedlineTypeToDelete != nRedlineType ) continue;
// 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();
}
}
}
}
}
/// 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;
// 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->GetNode()); nIdx <= pEndPos->GetNode(); ++nIdx)
{
SwContentNode* pContentNode = nIdx.GetNode().GetContentNode(); if (pContentNode)
pSh->InvalidateWindows(pContentNode->FindLayoutRect());
}
}
}
// 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); if (!mpMaxEndPos || (*(*rv.first)->End()) > *mpMaxEndPos->End())
mpMaxEndPos = *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()->GetNodeIndex() >= pNext->Start()->GetNodeIndex())
{
m_bHasOverlappingElements = true; return;
}
} if (it != maVector.begin())
{ auto pPrev = *(it - 1); if (pPrev->End()->GetNodeIndex() >= pCurr->Start()->GetNodeIndex())
m_bHasOverlappingElements = true;
}
}
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()->GetNode().StartOfSectionNode()->FindTableNode(); // We end in a table when pTab != 0 if( pTab && !pNew->GetMark()->GetNode().StartOfSectionNode()->FindTableNode() )
{ // but our Mark was outside the table => Correction do
{ // We want to be before the table
pNew->GetPoint()->Assign(*pTab);
pC = GoPreviousPos( pNew->GetPoint(), false ); // here we are. if( pC )
pNew->GetPoint()->SetContent( 0 );
pTab = pNew->GetPoint()->GetNode().StartOfSectionNode()->FindTableNode();
} while( pTab ); // If there is another table we have to repeat our step backwards
}
// insert dummy character to the empty table rows to keep their changes
SwNode& rBoxNode = pNew->GetMark()->GetNode(); if ( rBoxNode.GetDoc().GetIDocumentUndoRedo().DoesUndo() && rBoxNode.GetTableBox() &&
rBoxNode.GetTableBox()->GetUpper()->IsEmpty() && rBoxNode.GetTextNode() )
{
::sw::UndoGuard const undoGuard(rBoxNode.GetDoc().GetIDocumentUndoRedo());
rBoxNode.GetTextNode()->InsertDummy();
pNew->GetMark()->SetContent( 1 );
}
// tdf#147180 set table change tracking in the empty row with text insertion if ( bInsert )
lcl_setRowNotTracked(*pSttNode);
for (std::unique_ptr<SwRangeRedline> & pRedline : redlines)
{
assert(pRedline->HasValidRange());
size_type nInsPos; auto pTmpRedline = pRedline.release(); if (Insert(pTmpRedline, nInsPos))
{ // tdf#147180 set table tracking to the table row
lcl_setRowNotTracked(pTmpRedline->GetPointNode());
if (bReverseDir)
{ if (*(pOther->End()) != *(pActual->Start())) returnfalse;
} else
{ if (*(pActual->End()) != *(pOther->Start())) returnfalse;
}
if (!pOrigin->GetRedlineData(0).CanCombineForAcceptReject(pOther->GetRedlineData(0)))
{ if (!bCheckChilds)
{ returnfalse;
}
// See if pOrigin and pOther can be combined because one redline data can combine with the // underlying redline data of the other redline. bool bChildCanCombine = false; if (pOther->GetStackCount() > 1
&& pOrigin->GetRedlineData(0).CanCombineForAcceptReject(pOther->GetRedlineData(1)))
{
bChildCanCombine = true;
}
if (!bChildCanCombine)
{ returnfalse;
}
} if (pOther->Start()->GetNode().StartOfSectionNode()
!= pActual->Start()->GetNode().StartOfSectionNode()) returnfalse;
returntrue;
}
}
const SwPosition& SwRedlineTable::GetMaxEndPos() const
{
assert(!empty() && "cannot call this when the redline table is empty"); if (mpMaxEndPos) return *mpMaxEndPos->End(); for (const SwRangeRedline* i : maVector)
{ if (!mpMaxEndPos || *i->End() > *mpMaxEndPos->End())
mpMaxEndPos = i;
}
assert(mpMaxEndPos); return *mpMaxEndPos->End();
}
void SwRedlineTable::getConnectedArea(size_type nPosOrigin, size_type& rPosStart,
size_type& rPosEnd, bool bCheckChilds) const
{ // Keep the original redline .. else we should memorize which children was checked // at the last combined redline.
SwRangeRedline* pOrigin = (*this)[nPosOrigin];
rPosStart = nPosOrigin;
rPosEnd = nPosOrigin;
SwRangeRedline* pRedline = pOrigin;
SwRangeRedline* pOther;
// connection info is already here..only the actual text is missing at import time // so no need to check Redline->GetContentIdx() here yet. while (rPosStart > 0 && (pOther = (*this)[rPosStart - 1])
&& lcl_CanCombineWithRange(pOrigin, pRedline, pOther, true, bCheckChilds))
{
rPosStart--;
pRedline = pOther;
}
pRedline = pOrigin; while (rPosEnd + 1 < size() && (pOther = (*this)[rPosEnd + 1])
&& lcl_CanCombineWithRange(pOrigin, pRedline, pOther, false, bCheckChilds))
{
rPosEnd++;
pRedline = pOther;
}
}
OUString SwRedlineTable::getTextOfArea(size_type rPosStart, size_type rPosEnd) const
{ // Normally a SwPaM::GetText() would be enough with rPosStart-start and rPosEnd-end // But at import time some text is not present there yet // we have to collect them 1 by 1
if (nullptr == pRedline->GetContentIdx())
{
pRedline->AppendTextTo(sRet);
} else// otherwise it is saved in pContentSect, e.g. during ODT import
{
SwPaM aTmpPaM(pRedline->GetContentIdx()->GetNode(),
*pRedline->GetContentIdx()->GetNode().EndOfSectionNode()); if (!aTmpPaM.Start()->nNode.GetNode().GetTextNode())
{
OUString sNew = aTmpPaM.GetText(); if (sNew[0] == CH_TXTATR_NEWLINE)
sRet.append(sNew.subView(1)); else
sRet.append(sNew);
} else
aTmpPaM.AppendTextTo(sRet); // append contents of aTmpPaM to sRet
}
}
return sRet.makeStringAndClear();
}
bool SwRedlineTable::isMoved(size_type rPos) const
{ // If it is already a part of a movement, then don't check it. if ((*this)[rPos]->GetMoved() != 0) returnfalse; // First try with single redline. then try with combined redlines if (isMovedImpl(rPos, false)) returntrue; else return isMovedImpl(rPos, true);
}
// set redline type of the searched pair
RedlineType nPairType = pRedline->GetType(); if ( RedlineType::Delete == nPairType )
nPairType = RedlineType::Insert; elseif ( RedlineType::Insert == nPairType )
nPairType = RedlineType::Delete; else // only deleted or inserted text can be moved returnfalse;
if (bTryCombined)
{
getConnectedArea(rPos, nPosStart, nPosEnd, false); if (nPosStart != nPosEnd)
sTrimmed = getTextOfArea(nPosStart, nPosEnd).trim();
}
if (sTrimmed.isEmpty())
{ // if this redline is visible the content is in this PaM if (nullptr == pRedline->GetContentIdx())
{
sTrimmed = pRedline->GetText().trim();
} else// otherwise it is saved in pContentSect, e.g. during ODT import
{
SwPaM aTmpPaM(pRedline->GetContentIdx()->GetNode(),
*pRedline->GetContentIdx()->GetNode().EndOfSectionNode());
sTrimmed = aTmpPaM.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)
{ returnfalse;
}
// Todo: lessen the previous condition..: // if the source / destination is a whole node change then maybe space is not needed
// search pair around the actual redline
size_type nEnd = rPos + nLookahead < size()
? rPos + nLookahead
: size();
size_type nStart = rPos > nLookahead ? rPos - nLookahead : 0; // first, try to compare to single redlines // next, try to compare to combined redlines for (int nPass = 0; nPass < 2 && !bRet; nPass++)
{ for (size_type nPosAct = nStart; nPosAct < nEnd && !bRet; ++nPosAct)
{
SwRangeRedline* pPair = (*this)[nPosAct];
// redline must be the requested type and from the same author if (nPairType != pPair->GetType() || pRedline->GetAuthor() != pPair->GetAuthor())
{ continue;
}
if (nPass == 0)
{ // if this redline is visible the content is in this PaM if (nullptr == pPair->GetContentIdx())
{
sPairTrimmed = o3tl::trim(pPair->GetText());
} else// otherwise it is saved in pContentSect, e.g. during ODT import
{ // saved in pContentSect, e.g. during ODT import
SwPaM aPairPaM(pPair->GetContentIdx()->GetNode(),
*pPair->GetContentIdx()->GetNode().EndOfSectionNode());
sPairTrimmed = o3tl::trim(aPairPaM.GetText());
}
} else
{
getConnectedArea(nPosAct, nPairStart, nPairEnd, false); if (nPairStart != nPairEnd)
sPairTrimmed = getTextOfArea(nPairStart, nPairEnd).trim();
}
// pair at tracked moving: same text by trimming trailing white spaces if (abs(sTrimmed.getLength() - sPairTrimmed.getLength()) <= 2
&& sTrimmed == sPairTrimmed)
{
sal_uInt32 nMID = getNewMovedID(); if (nPosStart != nPosEnd)
{ for (size_type nIdx = nPosStart; nIdx <= nPosEnd; ++nIdx)
{
(*this)[nIdx]->SetMoved(nMID); if (nIdx != rPos)
(*this)[nIdx]->InvalidateRange(SwRangeRedline::Invalidation::Add);
}
} else
pRedline->SetMoved(nMID);
//in (nPass == 0) it will only call once .. as nPairStart == nPairEnd == nPosAct for (size_type nIdx = nPairStart; nIdx <= nPairEnd; ++nIdx)
{
(*this)[nIdx]->SetMoved(nMID);
(*this)[nIdx]->InvalidateRange(SwRangeRedline::Invalidation::Add);
}
bRet = true;
}
//we can skip the combined redlines if (nPass == 1)
nPosAct = nPairEnd;
}
}
if ( !m_bFormatAll || pEnd->GetContentIndex() == 0 )
{ // don't reject the format of the next paragraph (that is handled by the next redline) if (aPam.GetPoint()->GetNode() > aPam.GetMark()->GetNode())
{
aPam.GetPoint()->Adjust(SwNodeOffset(-1));
SwContentNode* pNode = aPam.GetPoint()->GetNode().GetContentNode(); if ( pNode )
aPam.GetPoint()->SetContent( pNode->Len() ); else // tdf#147507 set it back to a content node to avoid of crashing
aPam.GetPoint()->Adjust(SwNodeOffset(+1));
} elseif (aPam.GetPoint()->GetNode() < aPam.GetMark()->GetNode())
{
aPam.GetMark()->Adjust(SwNodeOffset(-1));
SwContentNode* pNode = aPam.GetMark()->GetNode().GetContentNode();
aPam.GetMark()->SetContent( pNode->Len() );
}
}
// 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): staticbool deltaOneMinute(DateTime const & t1, DateTime const & t2) { autoconst [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})) { returntrue;
} return max <= min + tools::Time(0, 1);
}
// Check if we could/should accept/reject the 2 redlineData at the same time. // No need to check its children equality bool SwRedlineData::CanCombineForAcceptReject(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_nMovedID == rCmp.m_nMovedID &&
(( !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;
}
// 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) );
// set default comment for single annotations added or deleted if ( IsAnnotation() )
{
SetComment( RedlineType::Delete == rData.m_eType
? SwResId(STR_REDLINE_COMMENT_DELETED)
: SwResId(STR_REDLINE_COMMENT_ADDED) );
/// Do we have a valid Selection? bool SwRangeRedline::HasValidRange() const
{ const SwNode* pPtNd = &GetPoint()->GetNode(),
* pMkNd = &GetMark()->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() )
) returntrue; returnfalse;
}
if (eType == RedlineType::Format && rRedline.GetStackCount() > 1
&& rRedline.GetType(1) == RedlineType::Delete)
{ // Consider format-on-delete the same as simple delete, so the range gets moved to the // "Deleted Change Tracking content" toplevel section from body content during file save.
eType = RedlineType::Delete;
}
switch (GetRedlineTypeIgnoringAdditonalFormat(*this))
{ 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 );
}
switch (GetRedlineTypeIgnoringAdditonalFormat(*this))
{ 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 );
}
// 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 );
}
/** Calculates the start and end position of the intersection rTmp and
text node nNdIdx */ bool SwRangeRedline::CalcStartEnd(SwNodeOffset const nNdIdx,
sal_Int32 & rStart, sal_Int32 & rEnd) const
{ auto [pRStt, pREnd] = StartEnd(); // SwPosition* if( pRStt->GetNodeIndex() < nNdIdx )
{ if( pREnd->GetNodeIndex() > nNdIdx )
{
rStart = 0; // Paragraph is completely enclosed
rEnd = COMPLETE_STRING;
} elseif (pREnd->GetNodeIndex() == nNdIdx)
{
rStart = 0; // Paragraph is overlapped in the beginning
rEnd = pREnd->GetContentIndex();
} else// redline ends before paragraph
{
rStart = COMPLETE_STRING;
rEnd = COMPLETE_STRING; returntrue;
}
} elseif( pRStt->GetNodeIndex() == nNdIdx )
{
rStart = pRStt->GetContentIndex(); if( pREnd->GetNodeIndex() == nNdIdx )
rEnd = pREnd->GetContentIndex(); // Within the Paragraph else
rEnd = COMPLETE_STRING; // Paragraph is overlapped in the end
} else
{
rStart = COMPLETE_STRING;
rEnd = COMPLETE_STRING;
} returnfalse;
}
staticvoid lcl_storeAnnotationMarks(SwDoc& rDoc, const SwPosition* pStart, 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.findFirstAnnotationMarkNotStartsBefore(*pStart);
iter != rDMA.getAnnotationMarksEnd(); ++iter)
{
SwPosition const& rStartPos((**iter).GetMarkStart()); // vector is sorted by start pos, so we can exit early if ( rStartPos > *pEnd ) break; if ( *pStart <= rStartPos && rStartPos < *pEnd )
{ auto 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 = (*pStart == rStartPos) ? 1 : 0;
SwPaM aPam( rStartPos.GetNode(), rStartPos.GetContentIndex(),
rStartPos.GetNode(), rStartPos.GetContentIndex() + nLen);
::sw::mark::Bookmark* pBookmark = rDMA.makeAnnotationBookmark(
aPam,
(**iter).GetName(),
sw::mark::InsertMode::New); if (pBookmark)
{
pBookmark->SetKeyCode(vcl::KeyCode());
pBookmark->SetShortName(OUString());
}
}
}
}
}
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() == *pStart )
pRedl->GetBound() = *pEnd; if( pRedl->GetBound(false) == *pStart )
pRedl->GetBound(false) = *pEnd;
}
}
SwStartNode* pSttNd;
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Dauer der Verarbeitung: 0.24 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.