/* -*- 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 <DocumentContentOperationsManager.hxx>
#include <doc.hxx>
#include <IDocumentUndoRedo.hxx>
#include <IDocumentMarkAccess.hxx>
#include <DocumentRedlineManager.hxx>
#include <IDocumentState.hxx>
#include <IDocumentLayoutAccess.hxx>
#include <IDocumentStylePoolAccess.hxx>
#include <UndoManager.hxx>
#include <docary.hxx>
#include <textboxhelper.hxx>
#include <dcontact.hxx>
#include <grfatr.hxx>
#include <numrule.hxx>
#include <charfmt.hxx>
#include <ndgrf.hxx>
#include <ndnotxt.hxx>
#include <ndole.hxx>
#include <fmtcol.hxx>
#include <breakit.hxx>
#include <frmfmt.hxx>
#include <fmtanchr.hxx>
#include <fmtcntnt.hxx>
#include <fmtinfmt.hxx>
#include <fmtpdsc.hxx>
#include <fmtcnct.hxx>
#include <SwStyleNameMapper.hxx>
#include <redline.hxx>
#include <unocrsr.hxx>
#include <mvsave.hxx>
#include <ndtxt.hxx>
#include <poolfmt.hxx>
#include <paratr.hxx>
#include <txatbase.hxx>
#include <UndoRedline.hxx>
#include <undobj.hxx>
#include <UndoBookmark.hxx>
#include <UndoDelete.hxx>
#include <UndoSplitMove.hxx>
#include <UndoOverwrite.hxx>
#include <UndoInsert.hxx>
#include <UndoAttribute.hxx>
#include <rolbck.hxx>
#include <acorrect.hxx>
#include <ftnidx.hxx>
#include <txtftn.hxx>
#include <hints.hxx>
#include <crsrsh.hxx>
#include <fmtflcnt.hxx>
#include <docedt.hxx>
#include <sal/log.hxx>
#include <unotools/charclass.hxx>
#include <unotools/configmgr.hxx>
#include <sfx2/Metadatable.hxx>
#include <svl/stritem.hxx>
#include <svl/itemiter.hxx>
#include <svx/svdobj.hxx>
#include <svx/svdouno.hxx>
#include <tools/globname.hxx>
#include <editeng/formatbreakitem.hxx>
#include <o3tl/make_unique.hxx>
#include <com/sun/star/i18n/Boundary.hpp>
#include <com/sun/star/i18n/XBreakIterator.hpp>
#include <memory>
using namespace ::com::sun::star::i18n;
namespace
{
// Copy method from SwDoc
// Prevent copying in Flys that are anchored in the area
bool lcl_ChkFlyFly( SwDoc* pDoc, sal_uLong nSttNd, sal_uLong nEndNd,
sal_uLong nInsNd )
{
const SwFrameFormats& rFrameFormatTable = *pDoc->GetSpzFrameFormats();
for( size_t n = 0; n < rFrameFormatTable.size(); ++n )
{
SwFrameFormat const*const pFormat = rFrameFormatTable[n];
SwFormatAnchor const*const pAnchor = &pFormat->GetAnchor();
SwPosition const*const pAPos = pAnchor->GetContentAnchor();
if (pAPos &&
((RndStdIds::FLY_AS_CHAR == pAnchor->GetAnchorId()) ||
(RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId()) ||
(RndStdIds::FLY_AT_FLY == pAnchor->GetAnchorId()) ||
(RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId())) &&
nSttNd <= pAPos->nNode.GetIndex() &&
pAPos->nNode.GetIndex() < nEndNd )
{
const SwFormatContent& rContent = pFormat->GetContent();
SwStartNode* pSNd;
if( !rContent.GetContentIdx() ||
nullptr == ( pSNd = rContent.GetContentIdx()->GetNode().GetStartNode() ))
continue;
if( pSNd->GetIndex() < nInsNd &&
nInsNd < pSNd->EndOfSectionIndex() )
// Do not copy !
return true;
if( lcl_ChkFlyFly( pDoc, pSNd->GetIndex(),
pSNd->EndOfSectionIndex(), nInsNd ) )
// Do not copy !
return true;
}
}
return false;
}
SwNodeIndex InitDelCount(SwPaM const& rSourcePaM, sal_uLong & rDelCount)
{
SwNodeIndex const& rStart(rSourcePaM.Start()->nNode);
// Special handling for SwDoc::AppendDoc
if (rSourcePaM.GetDoc()->GetNodes().GetEndOfExtras().GetIndex() + 1
== rStart.GetIndex())
{
rDelCount = 1;
return SwNodeIndex(rStart, +1);
}
else
{
rDelCount = 0;
return rStart;
}
}
/*
The lcl_CopyBookmarks function has to copy bookmarks from the source to the destination nodes
array. It is called after a call of the CopyNodes(..) function. But this function does not copy
every node (at least at the moment: 2/08/2006 ), section start and end nodes will not be copied
if the corresponding end/start node is outside the copied pam.
The lcl_NonCopyCount function counts the number of these nodes, given the copied pam and a node
index inside the pam.
rPam is the original source pam, rLastIdx is the last calculated position, rDelCount the number
of "non-copy" nodes between rPam.Start() and rLastIdx.
nNewIdx is the new position of interest.
*/
void lcl_NonCopyCount( const SwPaM& rPam, SwNodeIndex& rLastIdx, const sal_uLong nNewIdx, sal_uLong& rDelCount )
{
sal_uLong nStart = rPam.Start()->nNode.GetIndex();
sal_uLong nEnd = rPam.End()->nNode.GetIndex();
if( rLastIdx.GetIndex() < nNewIdx ) // Moving forward?
{
// We never copy the StartOfContent node
do // count "non-copy" nodes
{
SwNode& rNode = rLastIdx.GetNode();
if( ( rNode.IsSectionNode() && rNode.EndOfSectionIndex() >= nEnd )
|| ( rNode.IsEndNode() && rNode.StartOfSectionNode()->GetIndex() < nStart ) )
{
++rDelCount;
}
++rLastIdx;
}
while( rLastIdx.GetIndex() < nNewIdx );
}
else if( rDelCount ) // optimization: if there are no "non-copy" nodes until now,
// no move backward needed
{
while( rLastIdx.GetIndex() > nNewIdx )
{
SwNode& rNode = rLastIdx.GetNode();
if( ( rNode.IsSectionNode() && rNode.EndOfSectionIndex() >= nEnd )
|| ( rNode.IsEndNode() && rNode.StartOfSectionNode()->GetIndex() < nStart ) )
{
--rDelCount;
}
rLastIdx--;
}
}
}
void lcl_SetCpyPos( const SwPosition& rOrigPos,
const SwPosition& rOrigStt,
const SwPosition& rCpyStt,
SwPosition& rChgPos,
sal_uLong nDelCount )
{
sal_uLong nNdOff = rOrigPos.nNode.GetIndex();
nNdOff -= rOrigStt.nNode.GetIndex();
nNdOff -= nDelCount;
sal_Int32 nContentPos = rOrigPos.nContent.GetIndex();
// Always adjust <nNode> at to be changed <SwPosition> instance <rChgPos>
rChgPos.nNode = nNdOff + rCpyStt.nNode.GetIndex();
if( !nNdOff )
{
// just adapt the content index
if( nContentPos > rOrigStt.nContent.GetIndex() )
nContentPos -= rOrigStt.nContent.GetIndex();
else
nContentPos = 0;
nContentPos += rCpyStt.nContent.GetIndex();
}
rChgPos.nContent.Assign( rChgPos.nNode.GetNode().GetContentNode(), nContentPos );
}
// TODO: use SaveBookmark (from DelBookmarks)
void lcl_CopyBookmarks(
const SwPaM& rPam,
SwPaM& rCpyPam )
{
const SwDoc* pSrcDoc = rPam.GetDoc();
SwDoc* pDestDoc = rCpyPam.GetDoc();
const IDocumentMarkAccess* const pSrcMarkAccess = pSrcDoc->getIDocumentMarkAccess();
::sw::UndoGuard const undoGuard(pDestDoc->GetIDocumentUndoRedo());
const SwPosition &rStt = *rPam.Start(), &rEnd = *rPam.End();
SwPosition* pCpyStt = rCpyPam.Start();
typedef std::vector< const ::sw::mark::IMark* > mark_vector_t;
mark_vector_t vMarksToCopy;
for ( IDocumentMarkAccess::const_iterator_t ppMark = pSrcMarkAccess->getAllMarksBegin();
ppMark != pSrcMarkAccess->getAllMarksEnd();
++ppMark )
{
const ::sw::mark::IMark* const pMark = ppMark->get();
const SwPosition& rMarkStart = pMark->GetMarkStart();
const SwPosition& rMarkEnd = pMark->GetMarkEnd();
// only include marks that are in the range and not touching both start and end
// - not for annotation or checkbox marks.
const bool bIsNotOnBoundary =
pMark->IsExpanded()
? (rMarkStart != rStt || rMarkEnd != rEnd) // rMarkStart != rMarkEnd
: (rMarkStart != rStt && rMarkEnd != rEnd); // rMarkStart == rMarkEnd
const IDocumentMarkAccess::MarkType aMarkType = IDocumentMarkAccess::GetType(*pMark);
if ( rMarkStart >= rStt && rMarkEnd <= rEnd
&& ( bIsNotOnBoundary
|| aMarkType == IDocumentMarkAccess::MarkType::ANNOTATIONMARK
|| aMarkType == IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK ) )
{
vMarksToCopy.push_back(pMark);
}
}
// We have to count the "non-copied" nodes..
sal_uLong nDelCount;
SwNodeIndex aCorrIdx(InitDelCount(rPam, nDelCount));
for(mark_vector_t::const_iterator ppMark = vMarksToCopy.begin();
ppMark != vMarksToCopy.end();
++ppMark)
{
const ::sw::mark::IMark* const pMark = *ppMark;
SwPaM aTmpPam(*pCpyStt);
lcl_NonCopyCount(rPam, aCorrIdx, pMark->GetMarkPos().nNode.GetIndex(), nDelCount);
lcl_SetCpyPos( pMark->GetMarkPos(), rStt, *pCpyStt, *aTmpPam.GetPoint(), nDelCount);
if(pMark->IsExpanded())
{
aTmpPam.SetMark();
lcl_NonCopyCount(rPam, aCorrIdx, pMark->GetOtherMarkPos().nNode.GetIndex(), nDelCount);
lcl_SetCpyPos(pMark->GetOtherMarkPos(), rStt, *pCpyStt, *aTmpPam.GetMark(), nDelCount);
}
::sw::mark::IMark* const pNewMark = pDestDoc->getIDocumentMarkAccess()->makeMark(
aTmpPam,
pMark->GetName(),
IDocumentMarkAccess::GetType(*pMark),
::sw::mark::InsertMode::CopyText);
// Explicitly try to get exactly the same name as in the source
// because NavigatorReminders, DdeBookmarks etc. ignore the proposed name
pDestDoc->getIDocumentMarkAccess()->renameMark(pNewMark, pMark->GetName());
// copying additional attributes for bookmarks or fieldmarks
::sw::mark::IBookmark* const pNewBookmark =
dynamic_cast< ::sw::mark::IBookmark* const >(pNewMark);
const ::sw::mark::IBookmark* const pOldBookmark =
dynamic_cast< const ::sw::mark::IBookmark* >(pMark);
if (pNewBookmark && pOldBookmark)
{
pNewBookmark->SetKeyCode(pOldBookmark->GetKeyCode());
pNewBookmark->SetShortName(pOldBookmark->GetShortName());
}
::sw::mark::IFieldmark* const pNewFieldmark =
dynamic_cast< ::sw::mark::IFieldmark* const >(pNewMark);
const ::sw::mark::IFieldmark* const pOldFieldmark =
dynamic_cast< const ::sw::mark::IFieldmark* >(pMark);
if (pNewFieldmark && pOldFieldmark)
{
pNewFieldmark->SetFieldname(pOldFieldmark->GetFieldname());
pNewFieldmark->SetFieldHelptext(pOldFieldmark->GetFieldHelptext());
::sw::mark::IFieldmark::parameter_map_t* pNewParams = pNewFieldmark->GetParameters();
const ::sw::mark::IFieldmark::parameter_map_t* pOldParams = pOldFieldmark->GetParameters();
::sw::mark::IFieldmark::parameter_map_t::const_iterator pIt = pOldParams->begin();
for (; pIt != pOldParams->end(); ++pIt )
{
pNewParams->insert( *pIt );
}
}
::sfx2::Metadatable const*const pMetadatable(
dynamic_cast< ::sfx2::Metadatable const* >(pMark));
::sfx2::Metadatable *const pNewMetadatable(
dynamic_cast< ::sfx2::Metadatable * >(pNewMark));
if (pMetadatable && pNewMetadatable)
{
pNewMetadatable->RegisterAsCopyOf(*pMetadatable);
}
}
}
void lcl_DeleteRedlines( const SwPaM& rPam, SwPaM& rCpyPam )
{
const SwDoc* pSrcDoc = rPam.GetDoc();
const SwRedlineTable& rTable = pSrcDoc->getIDocumentRedlineAccess().GetRedlineTable();
if( !rTable.empty() )
{
SwDoc* pDestDoc = rCpyPam.GetDoc();
SwPosition* pCpyStt = rCpyPam.Start(), *pCpyEnd = rCpyPam.End();
std::unique_ptr<SwPaM> pDelPam;
const SwPosition *pStt = rPam.Start(), *pEnd = rPam.End();
// We have to count the "non-copied" nodes
sal_uLong nDelCount;
SwNodeIndex aCorrIdx(InitDelCount(rPam, nDelCount));
SwRedlineTable::size_type n = 0;
pSrcDoc->getIDocumentRedlineAccess().GetRedline( *pStt, &n );
for( ; n < rTable.size(); ++n )
{
const SwRangeRedline* pRedl = rTable[ n ];
if( nsRedlineType_t::REDLINE_DELETE == pRedl->GetType() && pRedl->IsVisible() )
{
const SwPosition *pRStt = pRedl->Start(), *pREnd = pRedl->End();
SwComparePosition eCmpPos = ComparePosition( *pStt, *pEnd, *pRStt, *pREnd );
switch( eCmpPos )
{
case SwComparePosition::CollideEnd:
case SwComparePosition::Before:
// Pos1 is before Pos2
break;
case SwComparePosition::CollideStart:
case SwComparePosition::Behind:
// Pos1 is after Pos2
n = rTable.size();
break;
default:
{
pDelPam.reset(new SwPaM( *pCpyStt, pDelPam.release() ));
if( *pStt < *pRStt )
{
lcl_NonCopyCount( rPam, aCorrIdx, pRStt->nNode.GetIndex(), nDelCount );
lcl_SetCpyPos( *pRStt, *pStt, *pCpyStt,
*pDelPam->GetPoint(), nDelCount );
}
pDelPam->SetMark();
if( *pEnd < *pREnd )
*pDelPam->GetPoint() = *pCpyEnd;
else
{
lcl_NonCopyCount( rPam, aCorrIdx, pREnd->nNode.GetIndex(), nDelCount );
lcl_SetCpyPos( *pREnd, *pStt, *pCpyStt,
*pDelPam->GetPoint(), nDelCount );
}
}
}
}
}
if( pDelPam )
{
RedlineFlags eOld = pDestDoc->getIDocumentRedlineAccess().GetRedlineFlags();
pDestDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld | RedlineFlags::Ignore );
::sw::UndoGuard const undoGuard(pDestDoc->GetIDocumentUndoRedo());
do {
pDestDoc->getIDocumentContentOperations().DeleteAndJoin( *pDelPam->GetNext() );
if( !pDelPam->IsMultiSelection() )
break;
delete pDelPam->GetNext();
} while( true );
pDestDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
}
}
}
void lcl_DeleteRedlines( const SwNodeRange& rRg, SwNodeRange const & rCpyRg )
{
SwDoc* pSrcDoc = rRg.aStart.GetNode().GetDoc();
if( !pSrcDoc->getIDocumentRedlineAccess().GetRedlineTable().empty() )
{
SwPaM aRgTmp( rRg.aStart, rRg.aEnd );
SwPaM aCpyTmp( rCpyRg.aStart, rCpyRg.aEnd );
lcl_DeleteRedlines( aRgTmp, aCpyTmp );
}
}
void lcl_ChainFormats( SwFlyFrameFormat *pSrc, SwFlyFrameFormat *pDest )
{
SwFormatChain aSrc( pSrc->GetChain() );
if ( !aSrc.GetNext() )
{
aSrc.SetNext( pDest );
pSrc->SetFormatAttr( aSrc );
}
SwFormatChain aDest( pDest->GetChain() );
if ( !aDest.GetPrev() )
{
aDest.SetPrev( pSrc );
pDest->SetFormatAttr( aDest );
}
}
// #i86492#
bool lcl_ContainsOnlyParagraphsInList( const SwPaM& rPam )
{
bool bRet = false;
const SwTextNode* pTextNd = rPam.Start()->nNode.GetNode().GetTextNode();
const SwTextNode* pEndTextNd = rPam.End()->nNode.GetNode().GetTextNode();
if ( pTextNd && pTextNd->IsInList() &&
pEndTextNd && pEndTextNd->IsInList() )
{
bRet = true;
SwNodeIndex aIdx(rPam.Start()->nNode);
do
{
++aIdx;
pTextNd = aIdx.GetNode().GetTextNode();
if ( !pTextNd || !pTextNd->IsInList() )
{
bRet = false;
break;
}
} while ( pTextNd && pTextNd != pEndTextNd );
}
return bRet;
}
bool lcl_MarksWholeNode(const SwPaM & rPam)
{
bool bResult = false;
const SwPosition* pStt = rPam.Start();
const SwPosition* pEnd = rPam.End();
if (nullptr != pStt && nullptr != pEnd)
{
const SwTextNode* pSttNd = pStt->nNode.GetNode().GetTextNode();
const SwTextNode* pEndNd = pEnd->nNode.GetNode().GetTextNode();
if (nullptr != pSttNd && nullptr != pEndNd &&
pStt->nContent.GetIndex() == 0 &&
pEnd->nContent.GetIndex() == pEndNd->Len())
{
bResult = true;
}
}
return bResult;
}
}
//local functions originally from sw/source/core/doc/docedt.cxx
namespace
{
void
lcl_CalcBreaks( std::vector<sal_Int32> & rBreaks, SwPaM const & rPam )
{
SwTextNode const * const pTextNode(
rPam.End()->nNode.GetNode().GetTextNode() );
if (!pTextNode)
return; // left-overlap only possible at end of selection...
const sal_Int32 nStart(rPam.Start()->nContent.GetIndex());
const sal_Int32 nEnd (rPam.End ()->nContent.GetIndex());
if (nEnd == pTextNode->Len())
return; // paragraph selected until the end
for (sal_Int32 i = nStart; i < nEnd; ++i)
{
const sal_Unicode c(pTextNode->GetText()[i]);
if ((CH_TXTATR_INWORD == c) || (CH_TXTATR_BREAKWORD == c))
{
SwTextAttr const * const pAttr( pTextNode->GetTextAttrForCharAt(i) );
if (pAttr && pAttr->End() && (*pAttr->End() > nEnd))
{
OSL_ENSURE(pAttr->HasDummyChar(), "GetTextAttrForCharAt broken?");
rBreaks.push_back(i);
}
}
}
}
bool lcl_DoWithBreaks(::sw::DocumentContentOperationsManager & rDocumentContentOperations, SwPaM & rPam,
bool (::sw::DocumentContentOperationsManager::*pFunc)(SwPaM&, bool), const bool bForceJoinNext = false)
{
std::vector<sal_Int32> Breaks;
lcl_CalcBreaks(Breaks, rPam);
if (Breaks.empty())
{
return (rDocumentContentOperations.*pFunc)(rPam, bForceJoinNext);
}
// Deletion must be split into several parts if the text node
// contains a text attribute with end and with dummy character
// and the selection does not contain the text attribute completely,
// but overlaps its start (left), where the dummy character is.
SwPosition const & rSelectionEnd( *rPam.End() );
bool bRet( true );
// iterate from end to start, to avoid invalidating the offsets!
std::vector<sal_Int32>::reverse_iterator iter( Breaks.rbegin() );
SwPaM aPam( rSelectionEnd, rSelectionEnd ); // end node!
SwPosition & rEnd( *aPam.End() );
SwPosition & rStart( *aPam.Start() );
while (iter != Breaks.rend())
{
rStart.nContent = *iter + 1;
if (rEnd.nContent > rStart.nContent) // check if part is empty
{
bRet &= (rDocumentContentOperations.*pFunc)(aPam, bForceJoinNext);
}
rEnd.nContent = *iter;
++iter;
}
rStart = *rPam.Start(); // set to original start
if (rEnd.nContent > rStart.nContent) // check if part is empty
{
bRet &= (rDocumentContentOperations.*pFunc)(aPam, bForceJoinNext);
}
return bRet;
}
bool lcl_StrLenOverflow( const SwPaM& rPam )
{
// If we try to merge two paragraphs we have to test if afterwards
// the string doesn't exceed the allowed string length
if( rPam.GetPoint()->nNode != rPam.GetMark()->nNode )
{
const SwPosition* pStt = rPam.Start(), *pEnd = rPam.End();
const SwTextNode* pEndNd = pEnd->nNode.GetNode().GetTextNode();
if( (nullptr != pEndNd) && pStt->nNode.GetNode().IsTextNode() )
{
const sal_uInt64 nSum = pStt->nContent.GetIndex() +
pEndNd->GetText().getLength() - pEnd->nContent.GetIndex();
return nSum > static_cast<sal_uInt64>(SAL_MAX_INT32);
}
}
return false;
}
struct SaveRedline
{
SwRangeRedline* pRedl;
sal_uInt32 nStt, nEnd;
sal_Int32 nSttCnt;
sal_Int32 nEndCnt;
SaveRedline( SwRangeRedline* pR, const SwNodeIndex& rSttIdx )
: pRedl(pR)
, nEnd(0)
, nEndCnt(0)
{
const SwPosition* pStt = pR->Start(),
* pEnd = pR->GetMark() == pStt ? pR->GetPoint() : pR->GetMark();
sal_uInt32 nSttIdx = rSttIdx.GetIndex();
nStt = pStt->nNode.GetIndex() - nSttIdx;
nSttCnt = pStt->nContent.GetIndex();
if( pR->HasMark() )
{
nEnd = pEnd->nNode.GetIndex() - nSttIdx;
nEndCnt = pEnd->nContent.GetIndex();
}
pRedl->GetPoint()->nNode = 0;
pRedl->GetPoint()->nContent.Assign( nullptr, 0 );
pRedl->GetMark()->nNode = 0;
pRedl->GetMark()->nContent.Assign( nullptr, 0 );
}
SaveRedline( SwRangeRedline* pR, const SwPosition& rPos )
: pRedl(pR)
, nEnd(0)
, nEndCnt(0)
{
const SwPosition* pStt = pR->Start(),
* pEnd = pR->GetMark() == pStt ? pR->GetPoint() : pR->GetMark();
sal_uInt32 nSttIdx = rPos.nNode.GetIndex();
nStt = pStt->nNode.GetIndex() - nSttIdx;
nSttCnt = pStt->nContent.GetIndex();
if( nStt == 0 )
nSttCnt = nSttCnt - rPos.nContent.GetIndex();
if( pR->HasMark() )
{
nEnd = pEnd->nNode.GetIndex() - nSttIdx;
nEndCnt = pEnd->nContent.GetIndex();
if( nEnd == 0 )
nEndCnt = nEndCnt - rPos.nContent.GetIndex();
}
pRedl->GetPoint()->nNode = 0;
pRedl->GetPoint()->nContent.Assign( nullptr, 0 );
pRedl->GetMark()->nNode = 0;
pRedl->GetMark()->nContent.Assign( nullptr, 0 );
}
void SetPos( sal_uInt32 nInsPos )
{
pRedl->GetPoint()->nNode = nInsPos + nStt;
pRedl->GetPoint()->nContent.Assign( pRedl->GetContentNode(), nSttCnt );
if( pRedl->HasMark() )
{
pRedl->GetMark()->nNode = nInsPos + nEnd;
pRedl->GetMark()->nContent.Assign( pRedl->GetContentNode(false), nEndCnt );
}
}
void SetPos( const SwPosition& aPos )
{
pRedl->GetPoint()->nNode = aPos.nNode.GetIndex() + nStt;
pRedl->GetPoint()->nContent.Assign( pRedl->GetContentNode(), nSttCnt + ( nStt == 0 ? aPos.nContent.GetIndex() : 0 ) );
if( pRedl->HasMark() )
{
pRedl->GetMark()->nNode = aPos.nNode.GetIndex() + nEnd;
pRedl->GetMark()->nContent.Assign( pRedl->GetContentNode(false), nEndCnt + ( nEnd == 0 ? aPos.nContent.GetIndex() : 0 ) );
}
}
};
typedef std::vector< SaveRedline > SaveRedlines_t;
void lcl_SaveRedlines(const SwPaM& aPam, SaveRedlines_t& rArr)
{
SwDoc* pDoc = aPam.GetNode().GetDoc();
const SwPosition* pStart = aPam.Start();
const SwPosition* pEnd = aPam.End();
// get first relevant redline
SwRedlineTable::size_type nCurrentRedline;
pDoc->getIDocumentRedlineAccess().GetRedline( *pStart, &nCurrentRedline );
if( nCurrentRedline > 0)
nCurrentRedline--;
// redline mode RedlineFlags::Ignore|RedlineFlags::On; save old mode
RedlineFlags eOld = pDoc->getIDocumentRedlineAccess().GetRedlineFlags();
pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld & ~RedlineFlags::Ignore) | RedlineFlags::On );
// iterate over relevant redlines and decide for each whether it should
// be saved, or split + saved
SwRedlineTable& rRedlineTable = pDoc->getIDocumentRedlineAccess().GetRedlineTable();
for( ; nCurrentRedline < rRedlineTable.size(); nCurrentRedline++ )
{
SwRangeRedline* pCurrent = rRedlineTable[ nCurrentRedline ];
SwComparePosition eCompare =
ComparePosition( *pCurrent->Start(), *pCurrent->End(),
*pStart, *pEnd);
// we must save this redline if it overlaps aPam
// (we may have to split it, too)
if( eCompare == SwComparePosition::OverlapBehind ||
eCompare == SwComparePosition::OverlapBefore ||
eCompare == SwComparePosition::Outside ||
eCompare == SwComparePosition::Inside ||
eCompare == SwComparePosition::Equal )
{
rRedlineTable.Remove( nCurrentRedline-- );
// split beginning, if necessary
if( eCompare == SwComparePosition::OverlapBefore ||
eCompare == SwComparePosition::Outside )
{
SwRangeRedline* pNewRedline = new SwRangeRedline( *pCurrent );
*pNewRedline->End() = *pStart;
*pCurrent->Start() = *pStart;
pDoc->getIDocumentRedlineAccess().AppendRedline( pNewRedline, true );
}
// split end, if necessary
if( eCompare == SwComparePosition::OverlapBehind ||
eCompare == SwComparePosition::Outside )
{
SwRangeRedline* pNewRedline = new SwRangeRedline( *pCurrent );
*pNewRedline->Start() = *pEnd;
*pCurrent->End() = *pEnd;
pDoc->getIDocumentRedlineAccess().AppendRedline( pNewRedline, true );
}
// save the current redline
rArr.emplace_back( pCurrent, *pStart );
}
}
// restore old redline mode
pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
}
void lcl_RestoreRedlines(SwDoc* pDoc, const SwPosition& rPos, SaveRedlines_t& rArr)
{
RedlineFlags eOld = pDoc->getIDocumentRedlineAccess().GetRedlineFlags();
pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld & ~RedlineFlags::Ignore) | RedlineFlags::On );
for(SaveRedline & rSvRedLine : rArr)
{
rSvRedLine.SetPos( rPos );
pDoc->getIDocumentRedlineAccess().AppendRedline( rSvRedLine.pRedl, true );
}
pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
}
void lcl_SaveRedlines(const SwNodeRange& rRg, SaveRedlines_t& rArr)
{
SwDoc* pDoc = rRg.aStart.GetNode().GetDoc();
SwRedlineTable::size_type nRedlPos;
SwPosition aSrchPos( rRg.aStart ); aSrchPos.nNode--;
aSrchPos.nContent.Assign( aSrchPos.nNode.GetNode().GetContentNode(), 0 );
if( pDoc->getIDocumentRedlineAccess().GetRedline( aSrchPos, &nRedlPos ) && nRedlPos )
--nRedlPos;
else if( nRedlPos >= pDoc->getIDocumentRedlineAccess().GetRedlineTable().size() )
return ;
RedlineFlags eOld = pDoc->getIDocumentRedlineAccess().GetRedlineFlags();
pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld & ~RedlineFlags::Ignore) | RedlineFlags::On );
SwRedlineTable& rRedlTable = pDoc->getIDocumentRedlineAccess().GetRedlineTable();
do {
SwRangeRedline* pTmp = rRedlTable[ nRedlPos ];
const SwPosition* pRStt = pTmp->Start(),
* pREnd = pTmp->GetMark() == pRStt
? pTmp->GetPoint() : pTmp->GetMark();
if( pRStt->nNode < rRg.aStart )
{
if( pREnd->nNode > rRg.aStart && pREnd->nNode < rRg.aEnd )
{
// Create a copy and set the end of the original to the end of the MoveArea.
// The copy is moved too.
SwRangeRedline* pNewRedl = new SwRangeRedline( *pTmp );
SwPosition* pTmpPos = pNewRedl->Start();
pTmpPos->nNode = rRg.aStart;
pTmpPos->nContent.Assign(
pTmpPos->nNode.GetNode().GetContentNode(), 0 );
rArr.emplace_back(pNewRedl, rRg.aStart);
pTmpPos = pTmp->End();
pTmpPos->nNode = rRg.aEnd;
pTmpPos->nContent.Assign(
pTmpPos->nNode.GetNode().GetContentNode(), 0 );
}
else if( pREnd->nNode == rRg.aStart )
{
SwPosition* pTmpPos = pTmp->End();
pTmpPos->nNode = rRg.aEnd;
pTmpPos->nContent.Assign(
pTmpPos->nNode.GetNode().GetContentNode(), 0 );
}
}
else if( pRStt->nNode < rRg.aEnd )
{
rRedlTable.Remove( nRedlPos-- );
if( pREnd->nNode < rRg.aEnd ||
( pREnd->nNode == rRg.aEnd && !pREnd->nContent.GetIndex()) )
{
// move everything
rArr.emplace_back( pTmp, rRg.aStart );
}
else
{
// split
SwRangeRedline* pNewRedl = new SwRangeRedline( *pTmp );
SwPosition* pTmpPos = pNewRedl->End();
pTmpPos->nNode = rRg.aEnd;
pTmpPos->nContent.Assign(
pTmpPos->nNode.GetNode().GetContentNode(), 0 );
rArr.emplace_back( pNewRedl, rRg.aStart );
pTmpPos = pTmp->Start();
pTmpPos->nNode = rRg.aEnd;
pTmpPos->nContent.Assign(
pTmpPos->nNode.GetNode().GetContentNode(), 0 );
pDoc->getIDocumentRedlineAccess().AppendRedline( pTmp, true );
}
}
else
break;
} while( ++nRedlPos < pDoc->getIDocumentRedlineAccess().GetRedlineTable().size() );
pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
}
void lcl_RestoreRedlines(SwDoc *const pDoc, sal_uInt32 const nInsPos, SaveRedlines_t& rArr)
{
RedlineFlags eOld = pDoc->getIDocumentRedlineAccess().GetRedlineFlags();
pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld & ~RedlineFlags::Ignore) | RedlineFlags::On );
for(SaveRedline & rSvRedLine : rArr)
{
rSvRedLine.SetPos( nInsPos );
pDoc->getIDocumentRedlineAccess().AppendRedline( rSvRedLine.pRedl, true );
}
pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
}
bool lcl_SaveFootnote( const SwNodeIndex& rSttNd, const SwNodeIndex& rEndNd,
const SwNodeIndex& rInsPos,
SwFootnoteIdxs& rFootnoteArr, SwFootnoteIdxs& rSaveArr,
const SwIndex* pSttCnt = nullptr, const SwIndex* pEndCnt = nullptr )
{
bool bUpdateFootnote = false;
const SwNodes& rNds = rInsPos.GetNodes();
const bool bDelFootnote = rInsPos.GetIndex() < rNds.GetEndOfAutotext().GetIndex() &&
rSttNd.GetIndex() >= rNds.GetEndOfAutotext().GetIndex();
const bool bSaveFootnote = !bDelFootnote &&
rInsPos.GetIndex() >= rNds.GetEndOfExtras().GetIndex();
if( !rFootnoteArr.empty() )
{
size_t nPos = 0;
rFootnoteArr.SeekEntry( rSttNd, &nPos );
SwTextFootnote* pSrch;
const SwNode* pFootnoteNd;
// Delete/save all that come after it
while( nPos < rFootnoteArr.size() && ( pFootnoteNd =
&( pSrch = rFootnoteArr[ nPos ] )->GetTextNode())->GetIndex()
<= rEndNd.GetIndex() )
{
const sal_Int32 nFootnoteSttIdx = pSrch->GetStart();
if( ( pEndCnt && pSttCnt )
? (( &rSttNd.GetNode() == pFootnoteNd &&
pSttCnt->GetIndex() > nFootnoteSttIdx) ||
( &rEndNd.GetNode() == pFootnoteNd &&
nFootnoteSttIdx >= pEndCnt->GetIndex() ))
: ( &rEndNd.GetNode() == pFootnoteNd ))
{
++nPos; // continue searching
}
else
{
// delete it
if( bDelFootnote )
{
SwTextNode& rTextNd = const_cast<SwTextNode&>(pSrch->GetTextNode());
SwIndex aIdx( &rTextNd, nFootnoteSttIdx );
rTextNd.EraseText( aIdx, 1 );
}
else
{
pSrch->DelFrames(nullptr);
rFootnoteArr.erase( rFootnoteArr.begin() + nPos );
if( bSaveFootnote )
rSaveArr.insert( pSrch );
}
bUpdateFootnote = true;
}
}
while( nPos-- && ( pFootnoteNd = &( pSrch = rFootnoteArr[ nPos ] )->
GetTextNode())->GetIndex() >= rSttNd.GetIndex() )
{
const sal_Int32 nFootnoteSttIdx = pSrch->GetStart();
if( !pEndCnt || !pSttCnt ||
! (( &rSttNd.GetNode() == pFootnoteNd &&
pSttCnt->GetIndex() > nFootnoteSttIdx ) ||
( &rEndNd.GetNode() == pFootnoteNd &&
nFootnoteSttIdx >= pEndCnt->GetIndex() )) )
{
if( bDelFootnote )
{
// delete it
SwTextNode& rTextNd = const_cast<SwTextNode&>(pSrch->GetTextNode());
SwIndex aIdx( &rTextNd, nFootnoteSttIdx );
rTextNd.EraseText( aIdx, 1 );
}
else
{
pSrch->DelFrames(nullptr);
rFootnoteArr.erase( rFootnoteArr.begin() + nPos );
if( bSaveFootnote )
rSaveArr.insert( pSrch );
}
bUpdateFootnote = true;
}
}
}
// When moving from redline section into document content section, e.g.
// after loading a document with (delete-)redlines, the footnote array
// has to be adjusted... (#i70572)
if( bSaveFootnote )
{
SwNodeIndex aIdx( rSttNd );
while( aIdx < rEndNd ) // Check the moved section
{
SwNode* pNode = &aIdx.GetNode();
if( pNode->IsTextNode() ) // Looking for text nodes...
{
SwpHints *pHints = pNode->GetTextNode()->GetpSwpHints();
if( pHints && pHints->HasFootnote() ) //...with footnotes
{
bUpdateFootnote = true; // Heureka
const size_t nCount = pHints->Count();
for( size_t i = 0; i < nCount; ++i )
{
SwTextAttr *pAttr = pHints->Get( i );
if ( pAttr->Which() == RES_TXTATR_FTN )
{
rSaveArr.insert( static_cast<SwTextFootnote*>(pAttr) );
}
}
}
}
++aIdx;
}
}
return bUpdateFootnote;
}
bool lcl_MayOverwrite( const SwTextNode *pNode, const sal_Int32 nPos )
{
sal_Unicode const cChr = pNode->GetText()[nPos];
switch (cChr)
{
case CH_TXTATR_BREAKWORD:
case CH_TXTATR_INWORD:
return !pNode->GetTextAttrForCharAt(nPos);// how could there be none?
case CH_TXT_ATR_FIELDSTART:
case CH_TXT_ATR_FIELDEND:
case CH_TXT_ATR_FORMELEMENT:
return false;
default:
return true;
}
}
void lcl_SkipAttr( const SwTextNode *pNode, SwIndex &rIdx, sal_Int32 &rStart )
{
if( !lcl_MayOverwrite( pNode, rStart ) )
{
// skip all special attributes
do {
++rIdx;
rStart = rIdx.GetIndex();
} while (rStart < pNode->GetText().getLength()
&& !lcl_MayOverwrite(pNode, rStart) );
}
}
bool lcl_GetTokenToParaBreak( OUString& rStr, OUString& rRet, bool bRegExpRplc )
{
if( bRegExpRplc )
{
sal_Int32 nPos = 0;
const OUString sPara("\\n");
for (;;)
{
nPos = rStr.indexOf( sPara, nPos );
if (nPos<0)
{
break;
}
// Has this been escaped?
if( nPos && '\\' == rStr[nPos-1])
{
++nPos;
if( nPos >= rStr.getLength() )
{
break;
}
}
else
{
rRet = rStr.copy( 0, nPos );
rStr = rStr.copy( nPos + sPara.getLength() );
return true;
}
}
}
rRet = rStr;
rStr.clear();
return false;
}
}
namespace //local functions originally from docfmt.cxx
{
#define DELETECHARSETS if ( bDelete ) { delete pCharSet; delete pOtherSet; }
/// Insert Hints according to content types;
// Is used in SwDoc::Insert(..., SwFormatHint &rHt)
bool lcl_InsAttr(
SwDoc *const pDoc,
const SwPaM &rRg,
const SfxItemSet& rChgSet,
const SetAttrMode nFlags,
SwUndoAttr *const pUndo,
const bool bExpandCharToPara=false)
{
// Divide the Sets (for selections in Nodes)
const SfxItemSet* pCharSet = nullptr;
const SfxItemSet* pOtherSet = nullptr;
bool bDelete = false;
bool bCharAttr = false;
bool bOtherAttr = false;
// Check, if we can work with rChgSet or if we have to create additional SfxItemSets
if ( 1 == rChgSet.Count() )
{
SfxItemIter aIter( rChgSet );
const SfxPoolItem* pItem = aIter.FirstItem();
if (pItem && !IsInvalidItem(pItem))
{
const sal_uInt16 nWhich = pItem->Which();
if ( isCHRATR(nWhich) ||
(RES_TXTATR_CHARFMT == nWhich) ||
(RES_TXTATR_INETFMT == nWhich) ||
(RES_TXTATR_AUTOFMT == nWhich) ||
(RES_TXTATR_UNKNOWN_CONTAINER == nWhich) )
{
pCharSet = &rChgSet;
bCharAttr = true;
}
if ( isPARATR(nWhich)
|| isPARATR_LIST(nWhich)
|| isFRMATR(nWhich)
|| isGRFATR(nWhich)
|| isUNKNOWNATR(nWhich)
|| isDrawingLayerAttribute(nWhich) )
{
pOtherSet = &rChgSet;
bOtherAttr = true;
}
}
}
// Build new itemset if either
// - rChgSet.Count() > 1 or
// - The attribute in rChgSet does not belong to one of the above categories
if ( !bCharAttr && !bOtherAttr )
{
SfxItemSet* pTmpCharItemSet = new SfxItemSet(
pDoc->GetAttrPool(),
svl::Items<
RES_CHRATR_BEGIN, RES_CHRATR_END - 1,
RES_TXTATR_AUTOFMT, RES_TXTATR_CHARFMT,
RES_TXTATR_UNKNOWN_CONTAINER,
RES_TXTATR_UNKNOWN_CONTAINER>{});
SfxItemSet* pTmpOtherItemSet = new SfxItemSet(
pDoc->GetAttrPool(),
svl::Items<
RES_PARATR_BEGIN, RES_GRFATR_END - 1,
RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END - 1,
// FillAttribute support:
XATTR_FILL_FIRST, XATTR_FILL_LAST>{});
pTmpCharItemSet->Put( rChgSet );
pTmpOtherItemSet->Put( rChgSet );
pCharSet = pTmpCharItemSet;
pOtherSet = pTmpOtherItemSet;
bDelete = true;
}
SwHistory* pHistory = pUndo ? &pUndo->GetHistory() : nullptr;
bool bRet = false;
const SwPosition *pStt = rRg.Start(), *pEnd = rRg.End();
SwContentNode* pNode = pStt->nNode.GetNode().GetContentNode();
if( pNode && pNode->IsTextNode() )
{
// #i27615#
if (rRg.IsInFrontOfLabel())
{
SwTextNode * pTextNd = pNode->GetTextNode();
SwNumRule * pNumRule = pTextNd->GetNumRule();
if ( !pNumRule )
{
OSL_FAIL( "<InsAttr(..)> - PaM in front of label, but text node has no numbering rule set. This is a serious defect." );
DELETECHARSETS
return false;
}
int nLevel = pTextNd->GetActualListLevel();
if (nLevel < 0)
nLevel = 0;
if (nLevel >= MAXLEVEL)
nLevel = MAXLEVEL - 1;
SwNumFormat aNumFormat = pNumRule->Get(static_cast<sal_uInt16>(nLevel));
SwCharFormat * pCharFormat =
pDoc->FindCharFormatByName(aNumFormat.GetCharFormatName());
if (pCharFormat)
{
if (pHistory)
pHistory->Add(pCharFormat->GetAttrSet(), *pCharFormat);
if ( pCharSet )
pCharFormat->SetFormatAttr(*pCharSet);
}
DELETECHARSETS
return true;
}
const SwIndex& rSt = pStt->nContent;
// Attributes without an end do not have a range
if ( !bCharAttr && !bOtherAttr )
{
SfxItemSet aTextSet( pDoc->GetAttrPool(),
svl::Items<RES_TXTATR_NOEND_BEGIN, RES_TXTATR_NOEND_END-1>{} );
aTextSet.Put( rChgSet );
if( aTextSet.Count() )
{
SwRegHistory history( pNode, *pNode, pHistory );
bRet = history.InsertItems(
aTextSet, rSt.GetIndex(), rSt.GetIndex(), nFlags ) || bRet;
if (bRet && (pDoc->getIDocumentRedlineAccess().IsRedlineOn() || (!pDoc->getIDocumentRedlineAccess().IsIgnoreRedline()
&& !pDoc->getIDocumentRedlineAccess().GetRedlineTable().empty())))
{
SwPaM aPam( pStt->nNode, pStt->nContent.GetIndex()-1,
pStt->nNode, pStt->nContent.GetIndex() );
if( pUndo )
pUndo->SaveRedlineData( aPam, true );
if( pDoc->getIDocumentRedlineAccess().IsRedlineOn() )
pDoc->getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( nsRedlineType_t::REDLINE_INSERT, aPam ), true);
else
pDoc->getIDocumentRedlineAccess().SplitRedline( aPam );
}
}
}
// TextAttributes with an end never expand their range
if ( !bCharAttr && !bOtherAttr )
{
// CharFormat and URL attributes are treated separately!
// TEST_TEMP ToDo: AutoFormat!
SfxItemSet aTextSet(
pDoc->GetAttrPool(),
svl::Items<
RES_TXTATR_REFMARK, RES_TXTATR_METAFIELD,
RES_TXTATR_CJK_RUBY, RES_TXTATR_CJK_RUBY,
RES_TXTATR_INPUTFIELD, RES_TXTATR_INPUTFIELD>{});
aTextSet.Put( rChgSet );
if( aTextSet.Count() )
{
const sal_Int32 nInsCnt = rSt.GetIndex();
const sal_Int32 nEnd = pStt->nNode == pEnd->nNode
? pEnd->nContent.GetIndex()
: pNode->Len();
SwRegHistory history( pNode, *pNode, pHistory );
bRet = history.InsertItems( aTextSet, nInsCnt, nEnd, nFlags )
|| bRet;
if (bRet && (pDoc->getIDocumentRedlineAccess().IsRedlineOn() || (!pDoc->getIDocumentRedlineAccess().IsIgnoreRedline()
&& !pDoc->getIDocumentRedlineAccess().GetRedlineTable().empty())))
{
// Was text content inserted? (RefMark/TOXMarks without an end)
bool bTextIns = nInsCnt != rSt.GetIndex();
// Was content inserted or set over the selection?
SwPaM aPam( pStt->nNode, bTextIns ? nInsCnt + 1 : nEnd,
pStt->nNode, nInsCnt );
if( pUndo )
pUndo->SaveRedlineData( aPam, bTextIns );
if( pDoc->getIDocumentRedlineAccess().IsRedlineOn() )
pDoc->getIDocumentRedlineAccess().AppendRedline(
new SwRangeRedline(
bTextIns ? nsRedlineType_t::REDLINE_INSERT : nsRedlineType_t::REDLINE_FORMAT, aPam ),
true);
else if( bTextIns )
pDoc->getIDocumentRedlineAccess().SplitRedline( aPam );
}
}
}
}
// We always have to set the auto flag for PageDescs that are set at the Node!
if( pOtherSet && pOtherSet->Count() )
{
SwTableNode* pTableNd;
const SwFormatPageDesc* pDesc;
if( SfxItemState::SET == pOtherSet->GetItemState( RES_PAGEDESC,
false, reinterpret_cast<const SfxPoolItem**>(&pDesc) ))
{
if( pNode )
{
// Set auto flag. Only in the template it's without auto!
SwFormatPageDesc aNew( *pDesc );
// Tables now also know line breaks
if( !(nFlags & SetAttrMode::APICALL) &&
nullptr != ( pTableNd = pNode->FindTableNode() ) )
{
SwTableNode* pCurTableNd = pTableNd;
while ( nullptr != ( pCurTableNd = pCurTableNd->StartOfSectionNode()->FindTableNode() ) )
pTableNd = pCurTableNd;
// set the table format
SwFrameFormat* pFormat = pTableNd->GetTable().GetFrameFormat();
SwRegHistory aRegH( pFormat, *pTableNd, pHistory );
pFormat->SetFormatAttr( aNew );
bRet = true;
}
else
{
SwRegHistory aRegH( pNode, *pNode, pHistory );
bRet = pNode->SetAttr( aNew ) || bRet;
}
}
// bOtherAttr = true means that pOtherSet == rChgSet. In this case
// we know, that there is only one attribute in pOtherSet. We cannot
// perform the following operations, instead we return:
if ( bOtherAttr )
return bRet;
const_cast<SfxItemSet*>(pOtherSet)->ClearItem( RES_PAGEDESC );
if( !pOtherSet->Count() )
{
DELETECHARSETS
return bRet;
}
}
// Tables now also know line breaks
const SvxFormatBreakItem* pBreak;
if( pNode && !(nFlags & SetAttrMode::APICALL) &&
nullptr != (pTableNd = pNode->FindTableNode() ) &&
SfxItemState::SET == pOtherSet->GetItemState( RES_BREAK,
false, reinterpret_cast<const SfxPoolItem**>(&pBreak) ) )
{
SwTableNode* pCurTableNd = pTableNd;
while ( nullptr != ( pCurTableNd = pCurTableNd->StartOfSectionNode()->FindTableNode() ) )
pTableNd = pCurTableNd;
// set the table format
SwFrameFormat* pFormat = pTableNd->GetTable().GetFrameFormat();
SwRegHistory aRegH( pFormat, *pTableNd, pHistory );
pFormat->SetFormatAttr( *pBreak );
bRet = true;
// bOtherAttr = true means that pOtherSet == rChgSet. In this case
// we know, that there is only one attribute in pOtherSet. We cannot
// perform the following operations, instead we return:
if ( bOtherAttr )
return bRet;
const_cast<SfxItemSet*>(pOtherSet)->ClearItem( RES_BREAK );
if( !pOtherSet->Count() )
{
DELETECHARSETS
return bRet;
}
}
{
// If we have a PoolNumRule, create it if needed
const SwNumRuleItem* pRule;
sal_uInt16 nPoolId=0;
if( SfxItemState::SET == pOtherSet->GetItemState( RES_PARATR_NUMRULE,
false, reinterpret_cast<const SfxPoolItem**>(&pRule) ) &&
!pDoc->FindNumRulePtr( pRule->GetValue() ) &&
USHRT_MAX != (nPoolId = SwStyleNameMapper::GetPoolIdFromUIName ( pRule->GetValue(),
SwGetPoolIdFromName::NumRule )) )
pDoc->getIDocumentStylePoolAccess().GetNumRuleFromPool( nPoolId );
}
}
if( !rRg.HasMark() ) // no range
{
if( !pNode )
{
DELETECHARSETS
return bRet;
}
if( pNode->IsTextNode() && pCharSet && pCharSet->Count() )
{
SwTextNode* pTextNd = pNode->GetTextNode();
const SwIndex& rSt = pStt->nContent;
sal_Int32 nMkPos, nPtPos = rSt.GetIndex();
const OUString& rStr = pTextNd->GetText();
// Special case: if the Cursor is located within a URL attribute, we take over it's area
SwTextAttr const*const pURLAttr(
pTextNd->GetTextAttrAt(rSt.GetIndex(), RES_TXTATR_INETFMT));
if (pURLAttr && !pURLAttr->GetINetFormat().GetValue().isEmpty())
{
nMkPos = pURLAttr->GetStart();
nPtPos = *pURLAttr->End();
}
else
{
assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
Boundary aBndry = g_pBreakIt->GetBreakIter()->getWordBoundary(
pTextNd->GetText(), nPtPos,
g_pBreakIt->GetLocale( pTextNd->GetLang( nPtPos ) ),
WordType::ANY_WORD /*ANYWORD_IGNOREWHITESPACES*/,
true);
if( aBndry.startPos < nPtPos && nPtPos < aBndry.endPos )
{
nMkPos = aBndry.startPos;
nPtPos = aBndry.endPos;
}
else
nPtPos = nMkPos = rSt.GetIndex();
}
// Remove the overriding attributes from the SwpHintsArray,
// if the selection spans across the whole paragraph.
// These attributes are inserted as FormatAttributes and
// never override the TextAttributes!
if( !(nFlags & SetAttrMode::DONTREPLACE ) &&
pTextNd->HasHints() && !nMkPos && nPtPos == rStr.getLength())
{
SwIndex aSt( pTextNd );
if( pHistory )
{
// Save all attributes for the Undo.
SwRegHistory aRHst( *pTextNd, pHistory );
pTextNd->GetpSwpHints()->Register( &aRHst );
pTextNd->RstTextAttr( aSt, nPtPos, 0, pCharSet );
if( pTextNd->GetpSwpHints() )
pTextNd->GetpSwpHints()->DeRegister();
}
else
pTextNd->RstTextAttr( aSt, nPtPos, 0, pCharSet );
}
// the SwRegHistory inserts the attribute into the TextNode!
SwRegHistory history( pNode, *pNode, pHistory );
bRet = history.InsertItems( *pCharSet, nMkPos, nPtPos, nFlags )
|| bRet;
if( pDoc->getIDocumentRedlineAccess().IsRedlineOn() )
{
SwPaM aPam( *pNode, nMkPos, *pNode, nPtPos );
if( pUndo )
pUndo->SaveRedlineData( aPam, false );
pDoc->getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( nsRedlineType_t::REDLINE_FORMAT, aPam ), true);
}
}
if( pOtherSet && pOtherSet->Count() )
{
SwRegHistory aRegH( pNode, *pNode, pHistory );
// Need to check for unique item for DrawingLayer items of type NameOrIndex
// and evtl. correct that item to ensure unique names for that type. This call may
// modify/correct entries inside of the given SfxItemSet
SfxItemSet aTempLocalCopy(*pOtherSet);
pDoc->CheckForUniqueItemForLineFillNameOrIndex(aTempLocalCopy);
bRet = pNode->SetAttr(aTempLocalCopy) || bRet;
}
DELETECHARSETS
return bRet;
}
if( pDoc->getIDocumentRedlineAccess().IsRedlineOn() && pCharSet && pCharSet->Count() )
{
if( pUndo )
pUndo->SaveRedlineData( rRg, false );
pDoc->getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( nsRedlineType_t::REDLINE_FORMAT, rRg ), true);
}
/* now if range */
sal_uLong nNodes = 0;
SwNodeIndex aSt( pDoc->GetNodes() );
SwNodeIndex aEnd( pDoc->GetNodes() );
SwIndex aCntEnd( pEnd->nContent );
if( pNode )
{
const sal_Int32 nLen = pNode->Len();
if( pStt->nNode != pEnd->nNode )
aCntEnd.Assign( pNode, nLen );
if( pStt->nContent.GetIndex() != 0 || aCntEnd.GetIndex() != nLen )
{
// the SwRegHistory inserts the attribute into the TextNode!
if( pNode->IsTextNode() && pCharSet && pCharSet->Count() )
{
SwRegHistory history( pNode, *pNode, pHistory );
bRet = history.InsertItems(*pCharSet,
pStt->nContent.GetIndex(), aCntEnd.GetIndex(), nFlags)
|| bRet;
}
if( pOtherSet && pOtherSet->Count() )
{
SwRegHistory aRegH( pNode, *pNode, pHistory );
bRet = pNode->SetAttr( *pOtherSet ) || bRet;
}
// Only selection in a Node.
if( pStt->nNode == pEnd->nNode )
{
//The data parameter flag: bExpandCharToPara, comes from the data member of SwDoc,
//which is set in SW MS Word Binary filter WW8ImplRreader. With this flag on, means that
//current setting attribute set is a character range properties set and comes from a MS Word
//binary file, and the setting range include a paragraph end position (0X0D);
//more specifications, as such property inside the character range properties set recorded in
//MS Word binary file are dealed and inserted into data model (SwDoc) one by one, so we
//only dealing the scenario that the char properties set with 1 item inside;
if (bExpandCharToPara && pCharSet && pCharSet->Count() ==1 )
{
SwTextNode* pCurrentNd = pStt->nNode.GetNode().GetTextNode();
if (pCurrentNd)
{
pCurrentNd->TryCharSetExpandToNum(*pCharSet);
}
}
DELETECHARSETS
return bRet;
}
++nNodes;
aSt.Assign( pStt->nNode.GetNode(), +1 );
}
else
aSt = pStt->nNode;
aCntEnd = pEnd->nContent; // aEnd was changed!
}
else
aSt.Assign( pStt->nNode.GetNode(), +1 );
// aSt points to the first full Node now
/*
* The selection spans more than one Node.
*/
if( pStt->nNode < pEnd->nNode )
{
pNode = pEnd->nNode.GetNode().GetContentNode();
if(pNode)
{
if( aCntEnd.GetIndex() != pNode->Len() )
{
// the SwRegHistory inserts the attribute into the TextNode!
if( pNode->IsTextNode() && pCharSet && pCharSet->Count() )
{
SwRegHistory history( pNode, *pNode, pHistory );
(void)history.InsertItems(*pCharSet,
0, aCntEnd.GetIndex(), nFlags);
}
if( pOtherSet && pOtherSet->Count() )
{
SwRegHistory aRegH( pNode, *pNode, pHistory );
pNode->SetAttr( *pOtherSet );
}
++nNodes;
aEnd = pEnd->nNode;
}
else
aEnd.Assign( pEnd->nNode.GetNode(), +1 );
}
else
aEnd = pEnd->nNode;
}
else
aEnd.Assign( pEnd->nNode.GetNode(), +1 );
// aEnd points BEHIND the last full node now
/* Edit the fully selected Nodes. */
// Reset all attributes from the set!
if( pCharSet && pCharSet->Count() && !( SetAttrMode::DONTREPLACE & nFlags ) )
{
::sw::DocumentContentOperationsManager::ParaRstFormat aPara( pStt, pEnd, pHistory, pCharSet );
pDoc->GetNodes().ForEach( aSt, aEnd, ::sw::DocumentContentOperationsManager::lcl_RstTextAttr, &aPara );
}
bool bCreateSwpHints = pCharSet && (
SfxItemState::SET == pCharSet->GetItemState( RES_TXTATR_CHARFMT, false ) ||
SfxItemState::SET == pCharSet->GetItemState( RES_TXTATR_INETFMT, false ) );
for(; aSt < aEnd; ++aSt )
{
pNode = aSt.GetNode().GetContentNode();
if( !pNode )
continue;
SwTextNode* pTNd = pNode->GetTextNode();
if( pHistory )
{
SwRegHistory aRegH( pNode, *pNode, pHistory );
if( pTNd && pCharSet && pCharSet->Count() )
{
SwpHints *pSwpHints = bCreateSwpHints ? &pTNd->GetOrCreateSwpHints()
: pTNd->GetpSwpHints();
if( pSwpHints )
pSwpHints->Register( &aRegH );
pTNd->SetAttr(*pCharSet, 0, pTNd->GetText().getLength(), nFlags);
if( pSwpHints )
pSwpHints->DeRegister();
}
if( pOtherSet && pOtherSet->Count() )
pNode->SetAttr( *pOtherSet );
}
else
{
if( pTNd && pCharSet && pCharSet->Count() )
pTNd->SetAttr(*pCharSet, 0, pTNd->GetText().getLength(), nFlags);
if( pOtherSet && pOtherSet->Count() )
pNode->SetAttr( *pOtherSet );
}
++nNodes;
}
//The data parameter flag: bExpandCharToPara, comes from the data member of SwDoc,
//which is set in SW MS Word Binary filter WW8ImplRreader. With this flag on, means that
//current setting attribute set is a character range properties set and comes from a MS Word
//binary file, and the setting range include a paragraph end position (0X0D);
//more specifications, as such property inside the character range properties set recorded in
//MS Word binary file are dealed and inserted into data model (SwDoc) one by one, so we
//only dealing the scenario that the char properties set with 1 item inside;
if (bExpandCharToPara && pCharSet && pCharSet->Count() ==1)
{
SwPosition aStartPos (*rRg.Start());
SwPosition aEndPos (*rRg.End());
if (aEndPos.nNode.GetNode().GetTextNode() && aEndPos.nContent != aEndPos.nNode.GetNode().GetTextNode()->Len())
aEndPos.nNode--;
sal_uLong nStart = aStartPos.nNode.GetIndex();
sal_uLong nEnd = aEndPos.nNode.GetIndex();
for(; nStart <= nEnd; ++nStart)
{
SwNode* pNd = pDoc->GetNodes()[ nStart ];
if (!pNd || !pNd->IsTextNode())
continue;
SwTextNode *pCurrentNd = pNd->GetTextNode();
pCurrentNd->TryCharSetExpandToNum(*pCharSet);
}
}
DELETECHARSETS
return (nNodes != 0) || bRet;
}
}
namespace sw
{
DocumentContentOperationsManager::DocumentContentOperationsManager( SwDoc& i_rSwdoc ) : m_rDoc( i_rSwdoc )
{
}
// Copy an area into this document or into another document
bool
DocumentContentOperationsManager::CopyRange( SwPaM& rPam, SwPosition& rPos, const bool bCopyAll, bool bCheckPos ) const
{
const SwPosition *pStt = rPam.Start(), *pEnd = rPam.End();
SwDoc* pDoc = rPos.nNode.GetNode().GetDoc();
bool bColumnSel = pDoc->IsClipBoard() && pDoc->IsColumnSelection();
// Catch if there's no copy to do
if( !rPam.HasMark() || ( *pStt >= *pEnd && !bColumnSel ) )
return false;
// Prevent copying in Flys that are anchored in the area
if( pDoc == &m_rDoc && bCheckPos )
{
// Correct the Start-/EndNode
sal_uLong nStt = pStt->nNode.GetIndex(),
nEnd = pEnd->nNode.GetIndex(),
nDiff = nEnd - nStt +1;
SwNode* pNd = m_rDoc.GetNodes()[ nStt ];
if( pNd->IsContentNode() && pStt->nContent.GetIndex() )
{
++nStt;
--nDiff;
}
if( (pNd = m_rDoc.GetNodes()[ nEnd ])->IsContentNode() &&
static_cast<SwContentNode*>(pNd)->Len() != pEnd->nContent.GetIndex() )
{
--nEnd;
--nDiff;
}
if( nDiff &&
lcl_ChkFlyFly( pDoc, nStt, nEnd, rPos.nNode.GetIndex() ) )
{
return false;
}
}
SwPaM* pRedlineRange = nullptr;
if( pDoc->getIDocumentRedlineAccess().IsRedlineOn() ||
(!pDoc->getIDocumentRedlineAccess().IsIgnoreRedline() && !pDoc->getIDocumentRedlineAccess().GetRedlineTable().empty() ) )
pRedlineRange = new SwPaM( rPos );
RedlineFlags eOld = pDoc->getIDocumentRedlineAccess().GetRedlineFlags();
bool bRet = false;
if( pDoc != &m_rDoc )
{ // ordinary copy
bRet = CopyImpl( rPam, rPos, true, bCopyAll, pRedlineRange );
}
else if( ! ( *pStt <= rPos && rPos < *pEnd &&
( pStt->nNode != pEnd->nNode ||
!pStt->nNode.GetNode().IsTextNode() )) )
{
// Copy to a position outside of the area, or copy a single TextNode
// Do an ordinary copy
bRet = CopyImpl( rPam, rPos, true, bCopyAll, pRedlineRange );
}
else
{
// Copy the area in itself
// Special case for handling an area with several nodes,
// or a single node that is not a TextNode
OSL_ENSURE( &m_rDoc == pDoc, " invalid copy branch!" );
assert(!"mst: this is assumed to be dead code");
pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld | RedlineFlags::Ignore);
// Then copy the area to the underlying document area
// (with start/end nodes clamped) and move them to
// the desired position.
SwUndoCpyDoc* pUndo = nullptr;
// Save the Undo area
SwPaM aPam( rPos );
if (pDoc->GetIDocumentUndoRedo().DoesUndo())
{
pDoc->GetIDocumentUndoRedo().ClearRedo();
pUndo = new SwUndoCpyDoc( aPam );
}
{
::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
SwStartNode* pSttNd = SwNodes::MakeEmptySection(
SwNodeIndex( m_rDoc.GetNodes().GetEndOfAutotext() ));
aPam.GetPoint()->nNode = *pSttNd->EndOfSectionNode();
// copy without Frames
pDoc->GetDocumentContentOperationsManager().CopyImpl( rPam, *aPam.GetPoint(), false, bCopyAll, nullptr );
aPam.GetPoint()->nNode = pDoc->GetNodes().GetEndOfAutotext();
aPam.SetMark();
SwContentNode* pNode = SwNodes::GoPrevious( &aPam.GetMark()->nNode );
pNode->MakeEndIndex( &aPam.GetMark()->nContent );
aPam.GetPoint()->nNode = *aPam.GetNode().StartOfSectionNode();
pNode = pDoc->GetNodes().GoNext( &aPam.GetPoint()->nNode );
pNode->MakeStartIndex( &aPam.GetPoint()->nContent );
// move to desired position
pDoc->getIDocumentContentOperations().MoveRange( aPam, rPos, SwMoveFlags::DEFAULT );
pNode = aPam.GetContentNode();
*aPam.GetPoint() = rPos; // Move the cursor for Undo
aPam.SetMark(); // also move the Mark
aPam.DeleteMark(); // But don't mark any area
pDoc->getIDocumentContentOperations().DeleteSection( pNode ); // Delete the area again
}
// if Undo is enabled, store the insertion range
if (pDoc->GetIDocumentUndoRedo().DoesUndo())
{
pUndo->SetInsertRange( aPam );
pDoc->GetIDocumentUndoRedo().AppendUndo(pUndo);
}
if( pRedlineRange )
{
pRedlineRange->SetMark();
*pRedlineRange->GetPoint() = *aPam.GetPoint();
*pRedlineRange->GetMark() = *aPam.GetMark();
}
pDoc->getIDocumentState().SetModified();
bRet = true;
}
pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
if( pRedlineRange )
{
if( pDoc->getIDocumentRedlineAccess().IsRedlineOn() )
pDoc->getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( nsRedlineType_t::REDLINE_INSERT, *pRedlineRange ), true);
else
pDoc->getIDocumentRedlineAccess().SplitRedline( *pRedlineRange );
delete pRedlineRange;
}
return bRet;
}
/// Delete a full Section of the NodeArray.
/// The passed Node is located somewhere in the designated Section.
void DocumentContentOperationsManager::DeleteSection( SwNode *pNode )
{
assert(pNode && "Didn't pass a Node.");
SwStartNode* pSttNd = pNode->IsStartNode() ? static_cast<SwStartNode*>(pNode)
: pNode->StartOfSectionNode();
SwNodeIndex aSttIdx( *pSttNd ), aEndIdx( *pNode->EndOfSectionNode() );
// delete all Flys, Bookmarks, ...
DelFlyInRange( aSttIdx, aEndIdx );
m_rDoc.getIDocumentRedlineAccess().DeleteRedline( *pSttNd, true, USHRT_MAX );
DelBookmarks(aSttIdx, aEndIdx);
{
// move all Cursor/StackCursor/UnoCursor out of the to-be-deleted area
SwNodeIndex aMvStt( aSttIdx, 1 );
SwDoc::CorrAbs( aMvStt, aEndIdx, SwPosition( aSttIdx ), true );
}
m_rDoc.GetNodes().DelNodes( aSttIdx, aEndIdx.GetIndex() - aSttIdx.GetIndex() + 1 );
}
void DocumentContentOperationsManager::DeleteRange( SwPaM & rPam )
{
lcl_DoWithBreaks( *this, rPam, &DocumentContentOperationsManager::DeleteRangeImpl );
}
bool DocumentContentOperationsManager::DelFullPara( SwPaM& rPam )
{
const SwPosition &rStt = *rPam.Start(), &rEnd = *rPam.End();
const SwNode* pNd = &rStt.nNode.GetNode();
sal_uInt32 nSectDiff = pNd->StartOfSectionNode()->EndOfSectionIndex() -
pNd->StartOfSectionIndex();
sal_uInt32 nNodeDiff = rEnd.nNode.GetIndex() - rStt.nNode.GetIndex();
if ( nSectDiff-2 <= nNodeDiff || m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() ||
/* #i9185# Prevent getting the node after the end node (see below) */
rEnd.nNode.GetIndex() + 1 == m_rDoc.GetNodes().Count() )
{
return false;
}
// Move hard page brakes to the following Node.
bool bSavePageBreak = false, bSavePageDesc = false;
/* #i9185# This whould lead to a segmentation fault if not caught above. */
sal_uLong nNextNd = rEnd.nNode.GetIndex() + 1;
SwTableNode *const pTableNd = m_rDoc.GetNodes()[ nNextNd ]->GetTableNode();
if( pTableNd && pNd->IsContentNode() )
{
SwFrameFormat* pTableFormat = pTableNd->GetTable().GetFrameFormat();
{
const SfxPoolItem *pItem;
const SfxItemSet* pSet = static_cast<const SwContentNode*>(pNd)->GetpSwAttrSet();
if( pSet && SfxItemState::SET == pSet->GetItemState( RES_PAGEDESC,
false, &pItem ) )
{
pTableFormat->SetFormatAttr( *pItem );
bSavePageDesc = true;
}
if( pSet && SfxItemState::SET == pSet->GetItemState( RES_BREAK,
false, &pItem ) )
{
pTableFormat->SetFormatAttr( *pItem );
bSavePageBreak = true;
}
}
}
bool const bDoesUndo = m_rDoc.GetIDocumentUndoRedo().DoesUndo();
if( bDoesUndo )
{
if( !rPam.HasMark() )
rPam.SetMark();
else if( rPam.GetPoint() == &rStt )
rPam.Exchange();
rPam.GetPoint()->nNode++;
SwContentNode *pTmpNode = rPam.GetPoint()->nNode.GetNode().GetContentNode();
rPam.GetPoint()->nContent.Assign( pTmpNode, 0 );
bool bGoNext = (nullptr == pTmpNode);
pTmpNode = rPam.GetMark()->nNode.GetNode().GetContentNode();
rPam.GetMark()->nContent.Assign( pTmpNode, 0 );
m_rDoc.GetIDocumentUndoRedo().ClearRedo();
SwPaM aDelPam( *rPam.GetMark(), *rPam.GetPoint() );
{
SwPosition aTmpPos( *aDelPam.GetPoint() );
if( bGoNext )
{
pTmpNode = m_rDoc.GetNodes().GoNext( &aTmpPos.nNode );
aTmpPos.nContent.Assign( pTmpNode, 0 );
}
::PaMCorrAbs( aDelPam, aTmpPos );
}
SwUndoDelete* pUndo = new SwUndoDelete( aDelPam, true );
*rPam.GetPoint() = *aDelPam.GetPoint();
pUndo->SetPgBrkFlags( bSavePageBreak, bSavePageDesc );
m_rDoc.GetIDocumentUndoRedo().AppendUndo(pUndo);
rPam.DeleteMark();
}
else
{
SwNodeRange aRg( rStt.nNode, rEnd.nNode );
rPam.Normalize(false);
// Try to move past the End
if( !rPam.Move( fnMoveForward, GoInNode ) )
{
// Fair enough, at the Beginning then
rPam.Exchange();
if( !rPam.Move( fnMoveBackward, GoInNode ))
{
SAL_WARN("sw.core", "DelFullPara: no more Nodes");
return false;
}
}
// move bookmarks, redlines etc.
if (aRg.aStart == aRg.aEnd) // only first CorrAbs variant handles this
{
m_rDoc.CorrAbs( aRg.aStart, *rPam.GetPoint(), 0, true );
}
else
{
SwDoc::CorrAbs( aRg.aStart, aRg.aEnd, *rPam.GetPoint(), true );
}
// What's with Flys?
{
// If there are FlyFrames left, delete these too
for( size_t n = 0; n < m_rDoc.GetSpzFrameFormats()->size(); ++n )
{
SwFrameFormat* pFly = (*m_rDoc.GetSpzFrameFormats())[n];
const SwFormatAnchor* pAnchor = &pFly->GetAnchor();
SwPosition const*const pAPos = pAnchor->GetContentAnchor();
if (pAPos &&
((RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()) ||
(RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId())) &&
aRg.aStart <= pAPos->nNode && pAPos->nNode <= aRg.aEnd )
{
m_rDoc.getIDocumentLayoutAccess().DelLayoutFormat( pFly );
--n;
}
}
}
rPam.DeleteMark();
m_rDoc.GetNodes().Delete( aRg.aStart, nNodeDiff+1 );
}
m_rDoc.getIDocumentState().SetModified();
return true;
}
// #i100466# Add handling of new optional parameter <bForceJoinNext>
bool DocumentContentOperationsManager::DeleteAndJoin( SwPaM & rPam,
const bool bForceJoinNext )
{
if ( lcl_StrLenOverflow( rPam ) )
return false;
return lcl_DoWithBreaks( *this, rPam, (m_rDoc.getIDocumentRedlineAccess().IsRedlineOn())
? &DocumentContentOperationsManager::DeleteAndJoinWithRedlineImpl
: &DocumentContentOperationsManager::DeleteAndJoinImpl,
bForceJoinNext );
}
// It seems that this is mostly used by SwDoc internals; the only
// way to call this from the outside seems to be the special case in
// SwDoc::CopyRange (but I have not managed to actually hit that case).
bool DocumentContentOperationsManager::MoveRange( SwPaM& rPaM, SwPosition& rPos, SwMoveFlags eMvFlags )
{
// nothing moved: return
const SwPosition *pStt = rPaM.Start(), *pEnd = rPaM.End();
if( !rPaM.HasMark() || *pStt >= *pEnd || (*pStt <= rPos && rPos < *pEnd))
return false;
// Save the paragraph anchored Flys, so that they can be moved.
SaveFlyArr aSaveFlyArr;
SaveFlyInRange( rPaM, rPos.nNode, aSaveFlyArr, bool( SwMoveFlags::ALLFLYS & eMvFlags ) );
// save redlines (if DOC_MOVEREDLINES is used)
SaveRedlines_t aSaveRedl;
if( SwMoveFlags::REDLINES & eMvFlags && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )
{
lcl_SaveRedlines( rPaM, aSaveRedl );
// #i17764# unfortunately, code below relies on undos being
// in a particular order, and presence of bookmarks
// will change this order. Hence, we delete bookmarks
// here without undo.
::sw::UndoGuard const undoGuard(m_rDoc.GetIDocumentUndoRedo());
DelBookmarks(
pStt->nNode,
pEnd->nNode,
nullptr,
&pStt->nContent,
&pEnd->nContent);
}
bool bUpdateFootnote = false;
SwFootnoteIdxs aTmpFntIdx;
SwUndoMove * pUndoMove = nullptr;
if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
{
m_rDoc.GetIDocumentUndoRedo().ClearRedo();
pUndoMove = new SwUndoMove( rPaM, rPos );
pUndoMove->SetMoveRedlines( eMvFlags == SwMoveFlags::REDLINES );
}
else
{
bUpdateFootnote = lcl_SaveFootnote( pStt->nNode, pEnd->nNode, rPos.nNode,
m_rDoc.GetFootnoteIdxs(), aTmpFntIdx,
&pStt->nContent, &pEnd->nContent );
}
bool bSplit = false;
SwPaM aSavePam( rPos, rPos );
// Move the SPoint to the beginning of the range
if( rPaM.GetPoint() == pEnd )
rPaM.Exchange();
// If there is a TextNode before and after the Move, create a JoinNext in the EditShell.
SwTextNode* pSrcNd = rPaM.GetPoint()->nNode.GetNode().GetTextNode();
bool bCorrSavePam = pSrcNd && pStt->nNode != pEnd->nNode;
// If one ore more TextNodes are moved, SwNodes::Move will do a SplitNode.
// However, this does not update the cursor. So we create a TextNode to keep
// updating the indices. After the Move the Node is optionally deleted.
SwTextNode * pTNd = rPos.nNode.GetNode().GetTextNode();
if( pTNd && rPaM.GetPoint()->nNode != rPaM.GetMark()->nNode &&
( rPos.nContent.GetIndex() || ( pTNd->Len() && bCorrSavePam )) )
{
bSplit = true;
const sal_Int32 nMkContent = rPaM.GetMark()->nContent.GetIndex();
const std::shared_ptr<sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create());
pContentStore->Save( &m_rDoc, rPos.nNode.GetIndex(), rPos.nContent.GetIndex(), true );
SwTextNode * pOrigNode = pTNd;
assert(*aSavePam.GetPoint() == *aSavePam.GetMark() &&
*aSavePam.GetPoint() == rPos);
assert(aSavePam.GetPoint()->nContent.GetIdxReg() == pOrigNode);
assert(aSavePam.GetPoint()->nNode == rPos.nNode.GetIndex());
assert(rPos.nNode.GetIndex() == pOrigNode->GetIndex());
pTNd = pTNd->SplitContentNode( rPos )->GetTextNode();
//A new node was inserted before the orig pTNd and the content up to
//rPos moved into it. The old node is returned with the remainder
//of the content in it.
//
//aSavePam was created with rPos, it continues to point to the
//old node, but with the *original* content index into the node.
//Seeing as all the orignode content before that index has
//been removed, the new index into the original node should now be set
//to 0 and the content index of rPos should also be adapted to the
//truncated node
assert(*aSavePam.GetPoint() == *aSavePam.GetMark() &&
*aSavePam.GetPoint() == rPos);
assert(aSavePam.GetPoint()->nContent.GetIdxReg() == pOrigNode);
assert(aSavePam.GetPoint()->nNode == rPos.nNode.GetIndex());
assert(rPos.nNode.GetIndex() == pOrigNode->GetIndex());
aSavePam.GetPoint()->nContent.Assign(pOrigNode, 0);
rPos = *aSavePam.GetMark() = *aSavePam.GetPoint();
if( !pContentStore->Empty() )
pContentStore->Restore( &m_rDoc, rPos.nNode.GetIndex()-1, 0, true );
// correct the PaM!
if( rPos.nNode == rPaM.GetMark()->nNode )
{
rPaM.GetMark()->nNode = rPos.nNode.GetIndex()-1;
rPaM.GetMark()->nContent.Assign( pTNd, nMkContent );
}
}
// Put back the Pam by one "content"; so that it's always outside of
// the manipulated range.
// tdf#99692 don't Move() back if that would end up in another node
// because moving backward is not necessarily the inverse of forward then.
// (but do Move() back if we have split the node)
const bool bNullContent = !bSplit && aSavePam.GetPoint()->nContent == 0;
if( bNullContent )
{
aSavePam.GetPoint()->nNode--;
aSavePam.GetPoint()->nContent.Assign(aSavePam.GetContentNode(), 0);
}
else
{
bool const success(aSavePam.Move(fnMoveBackward, GoInContent));
assert(success);
(void) success;
}
// Copy all Bookmarks that are within the Move range into an array,
// that saves the position as an offset.
std::vector< ::sw::mark::SaveBookmark> aSaveBkmks;
DelBookmarks(
pStt->nNode,
pEnd->nNode,
&aSaveBkmks,
&pStt->nContent,
&pEnd->nContent);
// If there is no range anymore due to the above deletions (e.g. the
// footnotes got deleted), it's still a valid Move!
if( *rPaM.GetPoint() != *rPaM.GetMark() )
{
// now do the actual move
m_rDoc.GetNodes().MoveRange( rPaM, rPos, m_rDoc.GetNodes() );
// after a MoveRange() the Mark is deleted
if ( rPaM.HasMark() ) // => no Move occurred!
{
delete pUndoMove;
return false;
}
}
else
rPaM.DeleteMark();
OSL_ENSURE( *aSavePam.GetMark() == rPos ||
( aSavePam.GetMark()->nNode.GetNode().GetContentNode() == nullptr ),
"PaM was not moved. Aren't there ContentNodes at the beginning/end?" );
*aSavePam.GetMark() = rPos;
rPaM.SetMark(); // create a Sel. around the new range
pTNd = aSavePam.GetNode().GetTextNode();
if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
{
assert(!"mst: this is assumed to be dead code");
// correct the SavePam's Content first
if( bNullContent )
{
aSavePam.GetPoint()->nContent = 0;
}
// The method SwEditShell::Move() merges the TextNode after the Move,
// where the rPaM is located.
// If the Content was moved to the back and the SavePam's SPoint is
// in the next Node, we have to deal with this when saving the Undo object!
SwTextNode * pPamTextNd = nullptr;
// Is passed to SwUndoMove, which happens when subsequently calling Undo JoinNext.
// If it's not possible to call Undo JoinNext here.
bool bJoin = bSplit && pTNd;
if( bCorrSavePam )
{
pPamTextNd = rPaM.GetNode().GetTextNode();
bCorrSavePam = (pPamTextNd != nullptr)
&& pPamTextNd->CanJoinNext()
&& (*rPaM.GetPoint() <= *aSavePam.GetPoint());
}
// Do two Nodes have to be joined at the SavePam?
if( bJoin && pTNd->CanJoinNext() )
{
pTNd->JoinNext();
// No temporary Index when using &&.
// We probably only want to compare the indices.
if( bCorrSavePam && rPaM.GetPoint()->nNode.GetIndex()+1 ==
aSavePam.GetPoint()->nNode.GetIndex() )
{
aSavePam.GetPoint()->nContent += pPamTextNd->Len();
}
bJoin = false;
}
else if ( !aSavePam.Move( fnMoveForward, GoInContent ) )
{
aSavePam.GetPoint()->nNode++;
}
// The newly inserted range is now inbetween SPoint and GetMark.
pUndoMove->SetDestRange( aSavePam, *rPaM.GetPoint(),
bJoin, bCorrSavePam );
m_rDoc.GetIDocumentUndoRedo().AppendUndo( pUndoMove );
}
else
{
bool bRemove = true;
// Do two Nodes have to be joined at the SavePam?
if( bSplit && pTNd )
{
if( pTNd->CanJoinNext())
{
// Always join next, because <pTNd> has to stay as it is.
// A join previous from its next would more or less delete <pTNd>
pTNd->JoinNext();
bRemove = false;
}
}
if( bNullContent )
{
aSavePam.GetPoint()->nNode++;
aSavePam.GetPoint()->nContent.Assign( aSavePam.GetContentNode(), 0 );
}
else if( bRemove ) // No move forward after joining with next paragraph
{
aSavePam.Move( fnMoveForward, GoInContent );
}
}
// Insert the Bookmarks back into the Document.
*rPaM.GetMark() = *aSavePam.Start();
for(
std::vector< ::sw::mark::SaveBookmark>::iterator pBkmk = aSaveBkmks.begin();
pBkmk != aSaveBkmks.end();
++pBkmk)
pBkmk->SetInDoc(
&m_rDoc,
rPaM.GetMark()->nNode,
&rPaM.GetMark()->nContent);
*rPaM.GetPoint() = *aSavePam.End();
// Move the Flys to the new position.
RestFlyInRange( aSaveFlyArr, rPaM.Start()->nNode, &(rPos.nNode) );
// restore redlines (if DOC_MOVEREDLINES is used)
if( !aSaveRedl.empty() )
{
lcl_RestoreRedlines( &m_rDoc, *aSavePam.Start(), aSaveRedl );
}
if( bUpdateFootnote )
{
if( !aTmpFntIdx.empty() )
{
m_rDoc.GetFootnoteIdxs().insert( aTmpFntIdx );
aTmpFntIdx.clear();
}
m_rDoc.GetFootnoteIdxs().UpdateAllFootnote();
}
m_rDoc.getIDocumentState().SetModified();
return true;
}
bool DocumentContentOperationsManager::MoveNodeRange( SwNodeRange& rRange, SwNodeIndex& rPos,
SwMoveFlags eMvFlags )
{
// Moves all Nodes to the new position.
// Bookmarks are moved too (currently without Undo support).
// If footnotes are being moved to the special section, remove them now.
// Or else delete the Frames for all footnotes that are being moved
// and have it rebuild after the Move (footnotes can change pages).
// Additionally we have to correct the FootnoteIdx array's sorting.
bool bUpdateFootnote = false;
SwFootnoteIdxs aTmpFntIdx;
SwUndoMove* pUndo = nullptr;
if ((SwMoveFlags::CREATEUNDOOBJ & eMvFlags ) && m_rDoc.GetIDocumentUndoRedo().DoesUndo())
{
pUndo = new SwUndoMove( &m_rDoc, rRange, rPos );
}
else
{
bUpdateFootnote = lcl_SaveFootnote( rRange.aStart, rRange.aEnd, rPos,
m_rDoc.GetFootnoteIdxs(), aTmpFntIdx );
}
SaveRedlines_t aSaveRedl;
std::vector<SwRangeRedline*> aSavRedlInsPosArr;
if( SwMoveFlags::REDLINES & eMvFlags && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )
{
lcl_SaveRedlines( rRange, aSaveRedl );
// Find all RedLines that end at the InsPos.
// These have to be moved back to the "old" position after the Move.
SwRedlineTable::size_type nRedlPos = m_rDoc.getIDocumentRedlineAccess().GetRedlinePos( rPos.GetNode(), USHRT_MAX );
if( SwRedlineTable::npos != nRedlPos )
{
const SwPosition *pRStt, *pREnd;
do {
SwRangeRedline* pTmp = m_rDoc.getIDocumentRedlineAccess().GetRedlineTable()[ nRedlPos ];
pRStt = pTmp->Start();
pREnd = pTmp->End();
if( pREnd->nNode == rPos && pRStt->nNode < rPos )
{
aSavRedlInsPosArr.push_back( pTmp );
}
} while( pRStt->nNode < rPos && ++nRedlPos < m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().size());
}
}
// Copy all Bookmarks that are within the Move range into an array
// that stores all references to positions as an offset.
// The final mapping happens after the Move.
std::vector< ::sw::mark::SaveBookmark> aSaveBkmks;
DelBookmarks(rRange.aStart, rRange.aEnd, &aSaveBkmks);
// Save the paragraph-bound Flys, so that they can be moved.
SaveFlyArr aSaveFlyArr;
if( !m_rDoc.GetSpzFrameFormats()->empty() )
SaveFlyInRange( rRange, aSaveFlyArr );
// Set it to before the Position, so that it cannot be moved further.
SwNodeIndex aIdx( rPos, -1 );
SwNodeIndex* pSaveInsPos = nullptr;
if( pUndo )
pSaveInsPos = new SwNodeIndex( rRange.aStart, -1 );
// move the Nodes
bool bNoDelFrames = bool(SwMoveFlags::NO_DELFRMS & eMvFlags);
if( m_rDoc.GetNodes().MoveNodes( rRange, m_rDoc.GetNodes(), rPos, !bNoDelFrames ) )
{
++aIdx; // again back to old position
if( pSaveInsPos )
++(*pSaveInsPos);
}
else
{
aIdx = rRange.aStart;
delete pUndo;
pUndo = nullptr;
}
// move the Flys to the new position
if( !aSaveFlyArr.empty() )
RestFlyInRange( aSaveFlyArr, aIdx, nullptr );
// Add the Bookmarks back to the Document
for(
std::vector< ::sw::mark::SaveBookmark>::iterator pBkmk = aSaveBkmks.begin();
pBkmk != aSaveBkmks.end();
++pBkmk)
pBkmk->SetInDoc(&m_rDoc, aIdx);
if( !aSavRedlInsPosArr.empty() )
{
SwNode* pNewNd = &aIdx.GetNode();
for(SwRangeRedline* pTmp : aSavRedlInsPosArr)
{
if( m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().Contains( pTmp ) )
{
SwPosition* pEnd = pTmp->End();
pEnd->nNode = aIdx;
pEnd->nContent.Assign( pNewNd->GetContentNode(), 0 );
}
}
}
if( !aSaveRedl.empty() )
lcl_RestoreRedlines( &m_rDoc, aIdx.GetIndex(), aSaveRedl );
if( pUndo )
{
pUndo->SetDestRange( aIdx, rPos, *pSaveInsPos );
m_rDoc.GetIDocumentUndoRedo().AppendUndo(pUndo);
}
delete pSaveInsPos;
if( bUpdateFootnote )
{
if( !aTmpFntIdx.empty() )
{
m_rDoc.GetFootnoteIdxs().insert( aTmpFntIdx );
aTmpFntIdx.clear();
}
m_rDoc.GetFootnoteIdxs().UpdateAllFootnote();
}
m_rDoc.getIDocumentState().SetModified();
return true;
}
bool DocumentContentOperationsManager::MoveAndJoin( SwPaM& rPaM, SwPosition& rPos )
{
SwNodeIndex aIdx( rPaM.Start()->nNode );
bool bJoinText = aIdx.GetNode().IsTextNode();
bool bOneNode = rPaM.GetPoint()->nNode == rPaM.GetMark()->nNode;
aIdx--; // in front of the move area!
bool bRet = MoveRange( rPaM, rPos, SwMoveFlags::DEFAULT );
if( bRet && !bOneNode )
{
if( bJoinText )
++aIdx;
SwTextNode * pTextNd = aIdx.GetNode().GetTextNode();
SwNodeIndex aNxtIdx( aIdx );
if( pTextNd && pTextNd->CanJoinNext( &aNxtIdx ) )
{
{ // Block so SwIndex into node is deleted before Join
m_rDoc.CorrRel( aNxtIdx, SwPosition( aIdx, SwIndex(pTextNd,
pTextNd->GetText().getLength()) ), 0, true );
}
pTextNd->JoinNext();
}
}
return bRet;
}
bool DocumentContentOperationsManager::Overwrite( const SwPaM &rRg, const OUString &rStr )
{
SwPosition& rPt = *const_cast<SwPosition*>(rRg.GetPoint());
if( m_rDoc.GetAutoCorrExceptWord() ) // Add to AutoCorrect
{
if( 1 == rStr.getLength() )
m_rDoc.GetAutoCorrExceptWord()->CheckChar( rPt, rStr[ 0 ] );
m_rDoc.DeleteAutoCorrExceptWord();
}
SwTextNode *pNode = rPt.nNode.GetNode().GetTextNode();
if (!pNode || rStr.getLength() > pNode->GetSpaceLeft()) // worst case: no erase
{
return false;
}
if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
{
m_rDoc.GetIDocumentUndoRedo().ClearRedo(); // AppendUndo not always called
}
const size_t nOldAttrCnt = pNode->GetpSwpHints()
? pNode->GetpSwpHints()->Count() : 0;
SwDataChanged aTmp( rRg );
SwIndex& rIdx = rPt.nContent;
sal_Int32 nStart = 0;
bool bOldExpFlg = pNode->IsIgnoreDontExpand();
pNode->SetIgnoreDontExpand( true );
for( sal_Int32 nCnt = 0; nCnt < rStr.getLength(); ++nCnt )
{
// start behind the characters (to fix the attributes!)
nStart = rIdx.GetIndex();
if (nStart < pNode->GetText().getLength())
{
lcl_SkipAttr( pNode, rIdx, nStart );
}
sal_Unicode c = rStr[ nCnt ];
if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
{
bool bMerged(false);
if (m_rDoc.GetIDocumentUndoRedo().DoesGroupUndo())
{
SwUndo *const pUndo = m_rDoc.GetUndoManager().GetLastUndo();
SwUndoOverwrite *const pUndoOW(
dynamic_cast<SwUndoOverwrite *>(pUndo) );
if (pUndoOW)
{
// if CanGrouping() returns true it's already merged
bMerged = pUndoOW->CanGrouping( &m_rDoc, rPt, c );
}
}
if (!bMerged)
{
SwUndo *const pUndoOW( new SwUndoOverwrite(&m_rDoc, rPt, c) );
m_rDoc.GetIDocumentUndoRedo().AppendUndo(pUndoOW);
}
}
else
{
// start behind the characters (to fix the attributes!)
if (nStart < pNode->GetText().getLength())
++rIdx;
pNode->InsertText( OUString(c), rIdx, SwInsertFlags::EMPTYEXPAND );
if( nStart+1 < rIdx.GetIndex() )
{
rIdx = nStart;
pNode->EraseText( rIdx, 1 );
++rIdx;
}
}
}
pNode->SetIgnoreDontExpand( bOldExpFlg );
const size_t nNewAttrCnt = pNode->GetpSwpHints()
? pNode->GetpSwpHints()->Count() : 0;
if( nOldAttrCnt != nNewAttrCnt )
{
SwUpdateAttr aHint(0,0,0);
pNode->ModifyBroadcast(nullptr, &aHint);
}
if (!m_rDoc.GetIDocumentUndoRedo().DoesUndo() &&
!m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty())
{
SwPaM aPam( rPt.nNode, nStart, rPt.nNode, rPt.nContent.GetIndex() );
m_rDoc.getIDocumentRedlineAccess().DeleteRedline( aPam, true, USHRT_MAX );
}
else if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
{
// FIXME: this redline is WRONG: there is no DELETE, and the skipped
// characters are also included in aPam
SwPaM aPam( rPt.nNode, nStart, rPt.nNode, rPt.nContent.GetIndex() );
m_rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( nsRedlineType_t::REDLINE_INSERT, aPam ), true);
}
m_rDoc.getIDocumentState().SetModified();
return true;
}
bool DocumentContentOperationsManager::InsertString( const SwPaM &rRg, const OUString &rStr,
const SwInsertFlags nInsertMode )
{
// tdf#119019 accept tracked paragraph formatting to do not hide new insertions
if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
{
RedlineFlags eOld = m_rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
m_rDoc.getIDocumentRedlineAccess().AcceptRedlineParagraphFormatting( rRg );
if (eOld != m_rDoc.getIDocumentRedlineAccess().GetRedlineFlags())
m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags( eOld );
}
// fetching DoesUndo is surprisingly expensive
bool bDoesUndo = m_rDoc.GetIDocumentUndoRedo().DoesUndo();
if (bDoesUndo)
m_rDoc.GetIDocumentUndoRedo().ClearRedo(); // AppendUndo not always called!
const SwPosition& rPos = *rRg.GetPoint();
if( m_rDoc.GetAutoCorrExceptWord() ) // add to auto correction
{
if( 1 == rStr.getLength() && m_rDoc.GetAutoCorrExceptWord()->IsDeleted() )
{
m_rDoc.GetAutoCorrExceptWord()->CheckChar( rPos, rStr[ 0 ] );
}
m_rDoc.DeleteAutoCorrExceptWord();
}
SwTextNode *const pNode = rPos.nNode.GetNode().GetTextNode();
if(!pNode)
return false;
SwDataChanged aTmp( rRg );
if (!bDoesUndo || !m_rDoc.GetIDocumentUndoRedo().DoesGroupUndo())
{
OUString const ins(pNode->InsertText(rStr, rPos.nContent, nInsertMode));
if (bDoesUndo)
{
SwUndoInsert * const pUndo( new SwUndoInsert(rPos.nNode,
rPos.nContent.GetIndex(), ins.getLength(), nInsertMode));
m_rDoc.GetIDocumentUndoRedo().AppendUndo(pUndo);
}
}
else
{ // if Undo and grouping is enabled, everything changes!
SwUndoInsert * pUndo = nullptr;
// don't group the start if hints at the start should be expanded
if (!(nInsertMode & SwInsertFlags::FORCEHINTEXPAND))
{
SwUndo *const pLastUndo = m_rDoc.GetUndoManager().GetLastUndo();
SwUndoInsert *const pUndoInsert(
dynamic_cast<SwUndoInsert *>(pLastUndo) );
if (pUndoInsert && pUndoInsert->CanGrouping(rPos))
{
pUndo = pUndoInsert;
}
}
CharClass const& rCC = GetAppCharClass();
sal_Int32 nInsPos = rPos.nContent.GetIndex();
if (!pUndo)
{
pUndo = new SwUndoInsert( rPos.nNode, nInsPos, 0, nInsertMode,
!rCC.isLetterNumeric( rStr, 0 ) );
m_rDoc.GetIDocumentUndoRedo().AppendUndo( pUndo );
}
OUString const ins(pNode->InsertText(rStr, rPos.nContent, nInsertMode));
for (sal_Int32 i = 0; i < ins.getLength(); ++i)
{
nInsPos++;
// if CanGrouping() returns true, everything has already been done
if (!pUndo->CanGrouping(ins[i]))
{
pUndo = new SwUndoInsert(rPos.nNode, nInsPos, 1, nInsertMode,
!rCC.isLetterNumeric(ins, i));
m_rDoc.GetIDocumentUndoRedo().AppendUndo( pUndo );
}
}
}
// To-Do - add 'SwExtraRedlineTable' also ?
if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() || (!m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() ))
{
SwPaM aPam( rPos.nNode, aTmp.GetContent(),
rPos.nNode, rPos.nContent.GetIndex());
if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
{
m_rDoc.getIDocumentRedlineAccess().AppendRedline(
new SwRangeRedline( nsRedlineType_t::REDLINE_INSERT, aPam ), true);
}
else
{
m_rDoc.getIDocumentRedlineAccess().SplitRedline( aPam );
}
}
m_rDoc.getIDocumentState().SetModified();
return true;
}
void DocumentContentOperationsManager::TransliterateText(
const SwPaM& rPaM,
utl::TransliterationWrapper& rTrans )
{
SwUndoTransliterate *const pUndo = (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
? new SwUndoTransliterate( rPaM, rTrans )
: nullptr;
const SwPosition* pStt = rPaM.Start(),
* pEnd = rPaM.End();
sal_uLong nSttNd = pStt->nNode.GetIndex(),
nEndNd = pEnd->nNode.GetIndex();
sal_Int32 nSttCnt = pStt->nContent.GetIndex();
sal_Int32 nEndCnt = pEnd->nContent.GetIndex();
SwTextNode* pTNd = pStt->nNode.GetNode().GetTextNode();
if( pStt == pEnd && pTNd ) // no selection?
{
// set current word as 'area of effect'
assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
Boundary aBndry = g_pBreakIt->GetBreakIter()->getWordBoundary(
pTNd->GetText(), nSttCnt,
g_pBreakIt->GetLocale( pTNd->GetLang( nSttCnt ) ),
WordType::ANY_WORD /*ANYWORD_IGNOREWHITESPACES*/,
true);
if( aBndry.startPos < nSttCnt && nSttCnt < aBndry.endPos )
{
nSttCnt = aBndry.startPos;
nEndCnt = aBndry.endPos;
}
}
if( nSttNd != nEndNd ) // is more than one text node involved?
{
// iterate over all effected text nodes, the first and the last one
// may be incomplete because the selection starts and/or ends there
SwNodeIndex aIdx( pStt->nNode );
if( nSttCnt )
{
++aIdx;
if( pTNd )
pTNd->TransliterateText(
rTrans, nSttCnt, pTNd->GetText().getLength(), pUndo);
}
for( ; aIdx.GetIndex() < nEndNd; ++aIdx )
{
pTNd = aIdx.GetNode().GetTextNode();
if (pTNd)
{
pTNd->TransliterateText(
rTrans, 0, pTNd->GetText().getLength(), pUndo);
}
}
if( nEndCnt && nullptr != ( pTNd = pEnd->nNode.GetNode().GetTextNode() ))
pTNd->TransliterateText( rTrans, 0, nEndCnt, pUndo );
}
else if( pTNd && nSttCnt < nEndCnt )
pTNd->TransliterateText( rTrans, nSttCnt, nEndCnt, pUndo );
if( pUndo )
{
if( pUndo->HasData() )
{
m_rDoc.GetIDocumentUndoRedo().AppendUndo(pUndo);
}
else
delete pUndo;
}
m_rDoc.getIDocumentState().SetModified();
}
SwFlyFrameFormat* DocumentContentOperationsManager::InsertGraphic(
const SwPaM &rRg,
const OUString& rGrfName,
const OUString& rFltName,
const Graphic* pGraphic,
const SfxItemSet* pFlyAttrSet,
const SfxItemSet* pGrfAttrSet,
SwFrameFormat* pFrameFormat )
{
if( !pFrameFormat )
pFrameFormat = m_rDoc.getIDocumentStylePoolAccess().GetFrameFormatFromPool( RES_POOLFRM_GRAPHIC );
SwGrfNode* pSwGrfNode = SwNodes::MakeGrfNode(
SwNodeIndex( m_rDoc.GetNodes().GetEndOfAutotext() ),
rGrfName, rFltName, pGraphic,
m_rDoc.GetDfltGrfFormatColl() );
SwFlyFrameFormat* pSwFlyFrameFormat = InsNoTextNode( *rRg.GetPoint(), pSwGrfNode,
pFlyAttrSet, pGrfAttrSet, pFrameFormat );
return pSwFlyFrameFormat;
}
SwFlyFrameFormat* DocumentContentOperationsManager::InsertGraphicObject(
const SwPaM &rRg, const GraphicObject& rGrfObj,
const SfxItemSet* pFlyAttrSet,
const SfxItemSet* pGrfAttrSet )
{
SwFrameFormat* pFrameFormat = m_rDoc.getIDocumentStylePoolAccess().GetFrameFormatFromPool( RES_POOLFRM_GRAPHIC );
SwGrfNode* pSwGrfNode = SwNodes::MakeGrfNode(
SwNodeIndex( m_rDoc.GetNodes().GetEndOfAutotext() ),
rGrfObj, m_rDoc.GetDfltGrfFormatColl() );
SwFlyFrameFormat* pSwFlyFrameFormat = InsNoTextNode( *rRg.GetPoint(), pSwGrfNode,
pFlyAttrSet, pGrfAttrSet, pFrameFormat );
return pSwFlyFrameFormat;
}
SwFlyFrameFormat* DocumentContentOperationsManager::InsertEmbObject(
const SwPaM &rRg, const svt::EmbeddedObjectRef& xObj,
const SfxItemSet* pFlyAttrSet)
{
sal_uInt16 nId = RES_POOLFRM_OLE;
if (xObj.is())
{
SvGlobalName aClassName( xObj->getClassID() );
if (SotExchange::IsMath(aClassName))
nId = RES_POOLFRM_FORMEL;
}
SwFrameFormat* pFrameFormat = m_rDoc.getIDocumentStylePoolAccess().GetFrameFormatFromPool( nId );
return InsNoTextNode( *rRg.GetPoint(), m_rDoc.GetNodes().MakeOLENode(
SwNodeIndex( m_rDoc.GetNodes().GetEndOfAutotext() ),
xObj,
m_rDoc.GetDfltGrfFormatColl() ),
pFlyAttrSet, nullptr,
pFrameFormat );
}
SwFlyFrameFormat* DocumentContentOperationsManager::InsertOLE(const SwPaM &rRg, const OUString& rObjName,
sal_Int64 nAspect,
const SfxItemSet* pFlyAttrSet,
const SfxItemSet* pGrfAttrSet)
{
SwFrameFormat* pFrameFormat = m_rDoc.getIDocumentStylePoolAccess().GetFrameFormatFromPool( RES_POOLFRM_OLE );
return InsNoTextNode( *rRg.GetPoint(),
m_rDoc.GetNodes().MakeOLENode(
SwNodeIndex( m_rDoc.GetNodes().GetEndOfAutotext() ),
rObjName,
nAspect,
m_rDoc.GetDfltGrfFormatColl(),
nullptr ),
pFlyAttrSet, pGrfAttrSet,
pFrameFormat );
}
void DocumentContentOperationsManager::ReRead( SwPaM& rPam, const OUString& rGrfName,
const OUString& rFltName, const Graphic* pGraphic )
{
SwGrfNode *pGrfNd;
if( ( !rPam.HasMark()
|| rPam.GetPoint()->nNode.GetIndex() == rPam.GetMark()->nNode.GetIndex() )
&& nullptr != ( pGrfNd = rPam.GetPoint()->nNode.GetNode().GetGrfNode() ) )
{
if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
{
m_rDoc.GetIDocumentUndoRedo().AppendUndo(new SwUndoReRead(rPam, *pGrfNd));
}
// Because we don't know if we can mirror the graphic, the mirror attribute is always reset
if( MirrorGraph::Dont != pGrfNd->GetSwAttrSet().
GetMirrorGrf().GetValue() )
pGrfNd->SetAttr( SwMirrorGrf() );
pGrfNd->ReRead( rGrfName, rFltName, pGraphic );
m_rDoc.getIDocumentState().SetModified();
}
}
// Insert drawing object, which has to be already inserted in the DrawModel
SwDrawFrameFormat* DocumentContentOperationsManager::InsertDrawObj(
const SwPaM &rRg,
SdrObject& rDrawObj,
const SfxItemSet& rFlyAttrSet )
{
SwDrawFrameFormat* pFormat = m_rDoc.MakeDrawFrameFormat( OUString(), m_rDoc.GetDfltFrameFormat() );
const SwFormatAnchor* pAnchor = nullptr;
rFlyAttrSet.GetItemState( RES_ANCHOR, false, reinterpret_cast<const SfxPoolItem**>(&pAnchor) );
pFormat->SetFormatAttr( rFlyAttrSet );
// Didn't set the Anchor yet?
// DrawObjecte must never end up in the Header/Footer!
RndStdIds eAnchorId = pAnchor != nullptr ? pAnchor->GetAnchorId() : pFormat->GetAnchor().GetAnchorId();
const bool bIsAtContent = (RndStdIds::FLY_AT_PAGE != eAnchorId);
const SwNodeIndex* pChkIdx = nullptr;
if ( pAnchor == nullptr )
{
pChkIdx = &rRg.GetPoint()->nNode;
}
else if ( bIsAtContent )
{
pChkIdx =
pAnchor->GetContentAnchor() ? &pAnchor->GetContentAnchor()->nNode : &rRg.GetPoint()->nNode;
}
// allow drawing objects in header/footer, but control objects aren't allowed in header/footer.
if( pChkIdx != nullptr
&& ::CheckControlLayer( &rDrawObj )
&& m_rDoc.IsInHeaderFooter( *pChkIdx ) )
{
// apply at-page anchor format
eAnchorId = RndStdIds::FLY_AT_PAGE;
pFormat->SetFormatAttr( SwFormatAnchor( eAnchorId ) );
}
else if( pAnchor == nullptr
|| ( bIsAtContent
&& pAnchor->GetContentAnchor() == nullptr ) )
{
// apply anchor format
SwFormatAnchor aAnch( pAnchor != nullptr ? *pAnchor : pFormat->GetAnchor() );
eAnchorId = aAnch.GetAnchorId();
if ( eAnchorId == RndStdIds::FLY_AT_FLY )
{
SwPosition aPos( *rRg.GetNode().FindFlyStartNode() );
aAnch.SetAnchor( &aPos );
}
else
{
aAnch.SetAnchor( rRg.GetPoint() );
if ( eAnchorId == RndStdIds::FLY_AT_PAGE )
{
eAnchorId = dynamic_cast<const SdrUnoObj*>( &rDrawObj) != nullptr ? RndStdIds::FLY_AS_CHAR : RndStdIds::FLY_AT_PARA;
aAnch.SetType( eAnchorId );
}
}
pFormat->SetFormatAttr( aAnch );
}
// insert text attribute for as-character anchored drawing object
if ( eAnchorId == RndStdIds::FLY_AS_CHAR )
{
bool bAnchorAtPageAsFallback = true;
const SwFormatAnchor& rDrawObjAnchorFormat = pFormat->GetAnchor();
if ( rDrawObjAnchorFormat.GetContentAnchor() != nullptr )
{
SwTextNode* pAnchorTextNode =
rDrawObjAnchorFormat.GetContentAnchor()->nNode.GetNode().GetTextNode();
if ( pAnchorTextNode != nullptr )
{
const sal_Int32 nStt = rDrawObjAnchorFormat.GetContentAnchor()->nContent.GetIndex();
SwFormatFlyCnt aFormat( pFormat );
pAnchorTextNode->InsertItem( aFormat, nStt, nStt );
bAnchorAtPageAsFallback = false;
}
}
if ( bAnchorAtPageAsFallback )
{
OSL_ENSURE( false, "DocumentContentOperationsManager::InsertDrawObj(..) - missing content anchor for as-character anchored drawing object --> anchor at-page" );
pFormat->SetFormatAttr( SwFormatAnchor( RndStdIds::FLY_AT_PAGE ) );
}
}
SwDrawContact* pContact = new SwDrawContact( pFormat, &rDrawObj );
// Create Frames if necessary
if( m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell() )
{
// create layout representation
pFormat->MakeFrames();
// #i42319# - follow-up of #i35635#
// move object to visible layer
// #i79391#
if ( pContact->GetAnchorFrame() )
{
pContact->MoveObjToVisibleLayer( &rDrawObj );
}
}
if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
{
m_rDoc.GetIDocumentUndoRedo().AppendUndo( new SwUndoInsLayFormat(pFormat, 0, 0) );
}
m_rDoc.getIDocumentState().SetModified();
return pFormat;
}
bool DocumentContentOperationsManager::SplitNode( const SwPosition &rPos, bool bChkTableStart )
{
SwContentNode *pNode = rPos.nNode.GetNode().GetContentNode();
if(nullptr == pNode)
return false;
{
// BUG 26675: Send DataChanged before deleting, so that we notice which objects are in scope.
// After that they can be before/after the position.
SwDataChanged aTmp( &m_rDoc, rPos );
}
SwUndoSplitNode* pUndo = nullptr;
if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
{
m_rDoc.GetIDocumentUndoRedo().ClearRedo();
// insert the Undo object (currently only for TextNode)
if( pNode->IsTextNode() )
{
pUndo = new SwUndoSplitNode( &m_rDoc, rPos, bChkTableStart );
m_rDoc.GetIDocumentUndoRedo().AppendUndo(pUndo);
}
}
// Update the rsid of the old and the new node unless
// the old node is split at the beginning or at the end
SwTextNode *pTextNode = rPos.nNode.GetNode().GetTextNode();
const sal_Int32 nPos = rPos.nContent.GetIndex();
if( pTextNode && nPos && nPos != pTextNode->Len() )
{
m_rDoc.UpdateParRsid( pTextNode );
}
//JP 28.01.97: Special case for SplitNode at table start:
// If it is at the beginning of a Doc/Fly/Footer/... or right at after a table
// then insert a paragraph before it.
if( bChkTableStart && !rPos.nContent.GetIndex() && pNode->IsTextNode() )
{
sal_uLong nPrevPos = rPos.nNode.GetIndex() - 1;
const SwTableNode* pTableNd;
const SwNode* pNd = m_rDoc.GetNodes()[ nPrevPos ];
if( pNd->IsStartNode() &&
SwTableBoxStartNode == static_cast<const SwStartNode*>(pNd)->GetStartNodeType() &&
nullptr != ( pTableNd = m_rDoc.GetNodes()[ --nPrevPos ]->GetTableNode() ) &&
((( pNd = m_rDoc.GetNodes()[ --nPrevPos ])->IsStartNode() &&
SwTableBoxStartNode != static_cast<const SwStartNode*>(pNd)->GetStartNodeType() )
|| ( pNd->IsEndNode() && pNd->StartOfSectionNode()->IsTableNode() )
|| pNd->IsContentNode() ))
{
if( pNd->IsContentNode() )
{
//JP 30.04.99 Bug 65660:
// There are no page breaks outside of the normal body area,
// so this is not a valid condition to insert a paragraph.
if( nPrevPos < m_rDoc.GetNodes().GetEndOfExtras().GetIndex() )
pNd = nullptr;
else
{
// Only if the table has page breaks!
const SwFrameFormat* pFrameFormat = pTableNd->GetTable().GetFrameFormat();
if( SfxItemState::SET != pFrameFormat->GetItemState(RES_PAGEDESC, false) &&
SfxItemState::SET != pFrameFormat->GetItemState( RES_BREAK, false ) )
pNd = nullptr;
}
}
if( pNd )
{
SwTextNode* pTextNd = m_rDoc.GetNodes().MakeTextNode(
SwNodeIndex( *pTableNd ),
m_rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT ));
if( pTextNd )
{
const_cast<SwPosition&>(rPos).nNode = pTableNd->GetIndex()-1;
const_cast<SwPosition&>(rPos).nContent.Assign( pTextNd, 0 );
// only add page breaks/styles to the body area
if( nPrevPos > m_rDoc.GetNodes().GetEndOfExtras().GetIndex() )
{
SwFrameFormat* pFrameFormat = pTableNd->GetTable().GetFrameFormat();
const SfxPoolItem *pItem;
if( SfxItemState::SET == pFrameFormat->GetItemState( RES_PAGEDESC,
false, &pItem ) )
{
pTextNd->SetAttr( *pItem );
pFrameFormat->ResetFormatAttr( RES_PAGEDESC );
}
if( SfxItemState::SET == pFrameFormat->GetItemState( RES_BREAK,
false, &pItem ) )
{
pTextNd->SetAttr( *pItem );
pFrameFormat->ResetFormatAttr( RES_BREAK );
}
}
if( pUndo )
pUndo->SetTableFlag();
m_rDoc.getIDocumentState().SetModified();
return true;
}
}
}
}
const std::shared_ptr<sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create());
pContentStore->Save( &m_rDoc, rPos.nNode.GetIndex(), rPos.nContent.GetIndex(), true );
// FIXME: only SwTextNode has a valid implementation of SplitContentNode!
OSL_ENSURE(pNode->IsTextNode(), "splitting non-text node?");
pNode = pNode->SplitContentNode( rPos );
if (pNode)
{
// move all bookmarks, TOXMarks, FlyAtCnt
if( !pContentStore->Empty() )
pContentStore->Restore( &m_rDoc, rPos.nNode.GetIndex()-1, 0, true );
// To-Do - add 'SwExtraRedlineTable' also ?
if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() || (!m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() ))
{
SwPaM aPam( rPos );
aPam.SetMark();
aPam.Move( fnMoveBackward );
if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
m_rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( nsRedlineType_t::REDLINE_INSERT, aPam ), true);
else
m_rDoc.getIDocumentRedlineAccess().SplitRedline( aPam );
}
}
m_rDoc.getIDocumentState().SetModified();
return true;
}
bool DocumentContentOperationsManager::AppendTextNode( SwPosition& rPos )
{
// create new node before EndOfContent
SwTextNode * pCurNode = rPos.nNode.GetNode().GetTextNode();
if( !pCurNode )
{
// so then one can be created!
SwNodeIndex aIdx( rPos.nNode, 1 );
pCurNode = m_rDoc.GetNodes().MakeTextNode( aIdx,
m_rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD ));
}
else
pCurNode = pCurNode->AppendNode( rPos )->GetTextNode();
rPos.nNode++;
rPos.nContent.Assign( pCurNode, 0 );
if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
{
m_rDoc.GetIDocumentUndoRedo().AppendUndo( new SwUndoInsert( rPos.nNode ) );
}
// To-Do - add 'SwExtraRedlineTable' also ?
if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() || (!m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() ))
{
SwPaM aPam( rPos );
aPam.SetMark();
aPam.Move( fnMoveBackward );
if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
m_rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( nsRedlineType_t::REDLINE_INSERT, aPam ), true);
else
m_rDoc.getIDocumentRedlineAccess().SplitRedline( aPam );
}
m_rDoc.getIDocumentState().SetModified();
return true;
}
bool DocumentContentOperationsManager::ReplaceRange( SwPaM& rPam, const OUString& rStr,
const bool bRegExReplace )
{
// unfortunately replace works slightly differently from delete,
// so we cannot use lcl_DoWithBreaks here...
std::vector<sal_Int32> Breaks;
SwPaM aPam( *rPam.GetMark(), *rPam.GetPoint() );
aPam.Normalize(false);
if (aPam.GetPoint()->nNode != aPam.GetMark()->nNode)
{
aPam.Move(fnMoveBackward);
}
OSL_ENSURE((aPam.GetPoint()->nNode == aPam.GetMark()->nNode), "invalid pam?");
lcl_CalcBreaks(Breaks, aPam);
while (!Breaks.empty() // skip over prefix of dummy chars
&& (aPam.GetMark()->nContent.GetIndex() == *Breaks.begin()) )
{
// skip!
++aPam.GetMark()->nContent; // always in bounds if Breaks valid
Breaks.erase(Breaks.begin());
}
*rPam.Start() = *aPam.GetMark(); // update start of original pam w/ prefix
if (Breaks.empty())
{
// park aPam somewhere so it does not point to node that is deleted
aPam.DeleteMark();
*aPam.GetPoint() = SwPosition(m_rDoc.GetNodes().GetEndOfContent());
return ReplaceRangeImpl(rPam, rStr, bRegExReplace); // original pam!
}
// Deletion must be split into several parts if the text node
// contains a text attribute with end and with dummy character
// and the selection does not contain the text attribute completely,
// but overlaps its start (left), where the dummy character is.
bool bRet( true );
// iterate from end to start, to avoid invalidating the offsets!
std::vector<sal_Int32>::reverse_iterator iter( Breaks.rbegin() );
OSL_ENSURE(aPam.GetPoint() == aPam.End(), "wrong!");
SwPosition & rEnd( *aPam.End() );
SwPosition & rStart( *aPam.Start() );
// set end of temp pam to original end (undo Move backward above)
rEnd = *rPam.End();
// after first deletion, rEnd will point into the original text node again!
while (iter != Breaks.rend())
{
rStart.nContent = *iter + 1;
if (rEnd.nContent != rStart.nContent) // check if part is empty
{
bRet &= (m_rDoc.getIDocumentRedlineAccess().IsRedlineOn())
? DeleteAndJoinWithRedlineImpl(aPam)
: DeleteAndJoinImpl(aPam, false);
}
rEnd.nContent = *iter;
++iter;
}
rStart = *rPam.Start(); // set to original start
OSL_ENSURE(rEnd.nContent > rStart.nContent, "replace part empty!");
if (rEnd.nContent > rStart.nContent) // check if part is empty
{
bRet &= ReplaceRangeImpl(aPam, rStr, bRegExReplace);
}
rPam = aPam; // update original pam (is this required?)
return bRet;
}
///Add a para for the char attribute exp...
bool DocumentContentOperationsManager::InsertPoolItem(
const SwPaM &rRg,
const SfxPoolItem &rHt,
const SetAttrMode nFlags,
const bool bExpandCharToPara)
{
if (utl::ConfigManager::IsFuzzing())
return false;
SwDataChanged aTmp( rRg );
SwUndoAttr* pUndoAttr = nullptr;
if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
{
m_rDoc.GetIDocumentUndoRedo().ClearRedo();
pUndoAttr = new SwUndoAttr( rRg, rHt, nFlags );
}
SfxItemSet aSet( m_rDoc.GetAttrPool(), {{rHt.Which(), rHt.Which()}} );
aSet.Put( rHt );
const bool bRet = lcl_InsAttr( &m_rDoc, rRg, aSet, nFlags, pUndoAttr, bExpandCharToPara );
if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
{
m_rDoc.GetIDocumentUndoRedo().AppendUndo( pUndoAttr );
}
if( bRet )
{
m_rDoc.getIDocumentState().SetModified();
}
return bRet;
}
void DocumentContentOperationsManager::InsertItemSet ( const SwPaM &rRg, const SfxItemSet &rSet,
const SetAttrMode nFlags )
{
SwDataChanged aTmp( rRg );
SwUndoAttr* pUndoAttr = nullptr;
if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
{
m_rDoc.GetIDocumentUndoRedo().ClearRedo();
pUndoAttr = new SwUndoAttr( rRg, rSet, nFlags );
}
bool bRet = lcl_InsAttr( &m_rDoc, rRg, rSet, nFlags, pUndoAttr );
if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
{
m_rDoc.GetIDocumentUndoRedo().AppendUndo( pUndoAttr );
}
if( bRet )
m_rDoc.getIDocumentState().SetModified();
}
void DocumentContentOperationsManager::RemoveLeadingWhiteSpace(const SwPosition & rPos )
{
const SwTextNode* pTNd = rPos.nNode.GetNode().GetTextNode();
if ( pTNd )
{
const OUString& rText = pTNd->GetText();
sal_Int32 nIdx = 0;
while (nIdx < rText.getLength())
{
sal_Unicode const cCh = rText[nIdx];
if (('\t' != cCh) && (' ' != cCh))
{
break;
}
++nIdx;
}
if ( nIdx > 0 )
{
SwPaM aPam(rPos);
aPam.GetPoint()->nContent = 0;
aPam.SetMark();
aPam.GetMark()->nContent = nIdx;
DeleteRange( aPam );
}
}
}
// Copy method from SwDoc - "copy Flys in Flys"
void DocumentContentOperationsManager::CopyWithFlyInFly(
const SwNodeRange& rRg,
const sal_Int32 nEndContentIndex,
const SwNodeIndex& rInsPos,
const std::pair<const SwPaM&, const SwPosition&>* pCopiedPaM /*and real insert pos*/,
const bool bMakeNewFrames,
const bool bDelRedlines,
const bool bCopyFlyAtFly ) const
{
assert(!pCopiedPaM || pCopiedPaM->first.End()->nContent == nEndContentIndex);
assert(!pCopiedPaM || pCopiedPaM->first.End()->nNode == rRg.aEnd);
SwDoc* pDest = rInsPos.GetNode().GetDoc();
SaveRedlEndPosForRestore aRedlRest( rInsPos, 0 );
SwNodeIndex aSavePos( rInsPos, -1 );
bool bEndIsEqualEndPos = rInsPos == rRg.aEnd;
m_rDoc.GetNodes().CopyNodes( rRg, rInsPos, bMakeNewFrames, true );
++aSavePos;
if( bEndIsEqualEndPos )
const_cast<SwNodeIndex&>(rRg.aEnd) = aSavePos;
aRedlRest.Restore();
#if OSL_DEBUG_LEVEL > 0
{
//JP 17.06.99: Bug 66973 - check count only if the selection is in
// the same section or there's no section, because sections that are
// not fully selected are not copied.
const SwSectionNode* pSSectNd = rRg.aStart.GetNode().FindSectionNode();
SwNodeIndex aTmpI( rRg.aEnd, -1 );
const SwSectionNode* pESectNd = aTmpI.GetNode().FindSectionNode();
if( pSSectNd == pESectNd &&
!rRg.aStart.GetNode().IsSectionNode() &&
!aTmpI.GetNode().IsEndNode() )
{
// If the range starts with a SwStartNode, it isn't copied
sal_uInt16 offset = (rRg.aStart.GetNode().GetNodeType() != SwNodeType::Start) ? 1 : 0;
OSL_ENSURE( rInsPos.GetIndex() - aSavePos.GetIndex() ==
rRg.aEnd.GetIndex() - rRg.aStart.GetIndex() - 1 + offset,
"An insufficient number of nodes were copied!" );
}
}
#endif
{
::sw::UndoGuard const undoGuard(pDest->GetIDocumentUndoRedo());
CopyFlyInFlyImpl( rRg, nEndContentIndex, aSavePos, bCopyFlyAtFly );
}
SwNodeRange aCpyRange( aSavePos, rInsPos );
// Also copy all bookmarks
// guess this must be done before the DelDummyNodes below as that
// deletes nodes so would mess up the index arithmetic
if( m_rDoc.getIDocumentMarkAccess()->getAllMarksCount() )
{
SwPaM aRgTmp( rRg.aStart, rRg.aEnd );
SwPaM aCpyPaM(aCpyRange.aStart, aCpyRange.aEnd);
if (pCopiedPaM && rRg.aStart != pCopiedPaM->first.Start()->nNode)
{
// there is 1 (partially selected, maybe) paragraph before
assert(SwNodeIndex(rRg.aStart, -1) == pCopiedPaM->first.Start()->nNode);
// only use the passed in target SwPosition if the source PaM point
// is on a different node; if it was the same node then the target
// position was likely moved along by the copy operation and now
// points to the end of the range!
*aCpyPaM.GetPoint() = pCopiedPaM->second;
}
lcl_CopyBookmarks(pCopiedPaM ? pCopiedPaM->first : aRgTmp, aCpyPaM);
}
if( bDelRedlines && ( RedlineFlags::DeleteRedlines & pDest->getIDocumentRedlineAccess().GetRedlineFlags() ))
lcl_DeleteRedlines( rRg, aCpyRange );
pDest->GetNodes().DelDummyNodes( aCpyRange );
}
// TODO: there is a limitation here in that it's not possible to pass a start
// content index - which means that at-character anchored frames inside
// partial 1st paragraph of redline is not copied.
// But the DelFlyInRange() that is called from DelCopyOfSection() does not
// delete it either, and it also does not delete those on partial last para of
// redline, so copying those is suppressed here too ...
void DocumentContentOperationsManager::CopyFlyInFlyImpl(
const SwNodeRange& rRg,
const sal_Int32 nEndContentIndex,
const SwNodeIndex& rStartIdx,
const bool bCopyFlyAtFly ) const
{
// First collect all Flys, sort them according to their ordering number,
// and then only copy them. This maintains the ordering numbers (which are only
// managed in the DrawModel).
SwDoc *const pDest = rStartIdx.GetNode().GetDoc();
std::set< ZSortFly > aSet;
const size_t nArrLen = m_rDoc.GetSpzFrameFormats()->size();
SwTextBoxHelper::SavedLink aOldTextBoxes;
SwTextBoxHelper::saveLinks(*m_rDoc.GetSpzFrameFormats(), aOldTextBoxes);
SwTextBoxHelper::SavedContent aOldContent;
for ( size_t n = 0; n < nArrLen; ++n )
{
SwFrameFormat* pFormat = (*m_rDoc.GetSpzFrameFormats())[n];
SwFormatAnchor const*const pAnchor = &pFormat->GetAnchor();
SwPosition const*const pAPos = pAnchor->GetContentAnchor();
bool bAtContent = (pAnchor->GetAnchorId() == RndStdIds::FLY_AT_PARA);
if ( !pAPos )
continue;
sal_uLong nSkipAfter = pAPos->nNode.GetIndex();
sal_uLong nStart = rRg.aStart.GetIndex();
switch ( pAnchor->GetAnchorId() )
{
case RndStdIds::FLY_AT_FLY:
if(bCopyFlyAtFly)
++nSkipAfter;
else if(m_rDoc.getIDocumentRedlineAccess().IsRedlineMove())
++nStart;
break;
case RndStdIds::FLY_AT_CHAR:
case RndStdIds::FLY_AT_PARA:
if(m_rDoc.getIDocumentRedlineAccess().IsRedlineMove())
++nStart;
break;
default:
continue;
}
if ( nStart > nSkipAfter )
continue;
if ( pAPos->nNode > rRg.aEnd )
continue;
//frames at the last source node are not always copied:
//- if the node is empty and is the last node of the document or a table cell
// or a text frame then they have to be copied
//- if the content index in this node is > 0 then paragraph and frame bound objects are copied
//- to-character bound objects are copied if their index is <= nEndContentIndex
bool bAdd = false;
if( pAPos->nNode < rRg.aEnd )
bAdd = true;
if (!bAdd && !m_rDoc.getIDocumentRedlineAccess().IsRedlineMove()) // fdo#40599: not for redline move
{
bool bEmptyNode = false;
bool bLastNode = false;
// is the node empty?
const SwNodes& rNodes = pAPos->nNode.GetNodes();
SwTextNode* pTextNode;
if( nullptr != ( pTextNode = pAPos->nNode.GetNode().GetTextNode() ))
{
bEmptyNode = pTextNode->GetText().isEmpty();
if( bEmptyNode )
{
//last node information is only necessary to know for the last TextNode
SwNodeIndex aTmp( pAPos->nNode );
++aTmp;//goto next node
while (aTmp.GetNode().IsEndNode())
{
if( aTmp == rNodes.GetEndOfContent().GetIndex() )
{
bLastNode = true;
break;
}
++aTmp;
}
}
}
bAdd = bLastNode && bEmptyNode;
if( !bAdd )
{
if( bAtContent )
bAdd = nEndContentIndex > 0;
else
bAdd = pAPos->nContent <= nEndContentIndex;
}
}
if( bAdd )
{
// Make sure draw formats don't refer to content, so that such
// content can be removed without problems.
SwTextBoxHelper::resetLink(pFormat, aOldContent);
aSet.insert( ZSortFly( pFormat, pAnchor, nArrLen + aSet.size() ));
}
}
// Store all copied (and also the newly created) frames in another array.
// They are stored as matching the originals, so that we will be later
// able to build the chains accordingly.
std::vector< SwFrameFormat* > aVecSwFrameFormat;
std::set< ZSortFly >::const_iterator it=aSet.begin();
while (it != aSet.end())
{
// #i59964#
// correct determination of new anchor position
SwFormatAnchor aAnchor( *(*it).GetAnchor() );
assert( aAnchor.GetContentAnchor() != nullptr );
SwPosition newPos = *aAnchor.GetContentAnchor();
// for at-paragraph and at-character anchored objects the new anchor
// position can *not* be determined by the difference of the current
// anchor position to the start of the copied range, because not
// complete selected sections in the copied range aren't copied - see
// method <SwNodes::CopyNodes(..)>.
// Thus, the new anchor position in the destination document is found
// by counting the text nodes.
if ((aAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA) ||
(aAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR) )
{
// First, determine number of anchor text node in the copied range.
// Note: The anchor text node *have* to be inside the copied range.
sal_uLong nAnchorTextNdNumInRange( 0 );
bool bAnchorTextNdFound( false );
SwNodeIndex aIdx( rRg.aStart );
while ( !bAnchorTextNdFound && aIdx <= rRg.aEnd )
{
if ( aIdx.GetNode().IsTextNode() )
{
++nAnchorTextNdNumInRange;
bAnchorTextNdFound = aAnchor.GetContentAnchor()->nNode == aIdx;
}
++aIdx;
}
if ( !bAnchorTextNdFound )
{
// This case can *not* happen, but to be robust take the first
// text node in the destination document.
OSL_FAIL( "<SwDoc::_CopyFlyInFly(..)> - anchor text node in copied range not found" );
nAnchorTextNdNumInRange = 1;
}
// Second, search corresponding text node in destination document
// by counting forward from start insert position <rStartIdx> the
// determined number of text nodes.
aIdx = rStartIdx;
SwNodeIndex aAnchorNdIdx( rStartIdx );
const SwNode& aEndOfContentNd =
aIdx.GetNode().GetNodes().GetEndOfContent();
while ( nAnchorTextNdNumInRange > 0 &&
&(aIdx.GetNode()) != &aEndOfContentNd )
{
if ( aIdx.GetNode().IsTextNode() )
{
--nAnchorTextNdNumInRange;
aAnchorNdIdx = aIdx;
}
++aIdx;
}
if ( !aAnchorNdIdx.GetNode().IsTextNode() )
{
// This case can *not* happen, but to be robust take the first
// text node in the destination document.
OSL_FAIL( "<SwDoc::_CopyFlyInFly(..)> - found anchor node index isn't a text node" );
aAnchorNdIdx = rStartIdx;
while ( !aAnchorNdIdx.GetNode().IsTextNode() )
{
++aAnchorNdIdx;
}
}
// apply found anchor text node as new anchor position
newPos.nNode = aAnchorNdIdx;
}
else
{
long nOffset = newPos.nNode.GetIndex() - rRg.aStart.GetIndex();
SwNodeIndex aIdx( rStartIdx, nOffset );
newPos.nNode = aIdx;
}
// Set the character bound Flys back at the original character
if ((RndStdIds::FLY_AT_CHAR == aAnchor.GetAnchorId()) &&
newPos.nNode.GetNode().IsTextNode() )
{
newPos.nContent.Assign( newPos.nNode.GetNode().GetTextNode(), newPos.nContent.GetIndex() );
}
else
{
newPos.nContent.Assign( nullptr, 0 );
}
aAnchor.SetAnchor( &newPos );
// Check recursion: copy content in its own frame, then don't copy it.
if( pDest == &m_rDoc )
{
const SwFormatContent& rContent = (*it).GetFormat()->GetContent();
const SwStartNode* pSNd;
if( rContent.GetContentIdx() &&
nullptr != ( pSNd = rContent.GetContentIdx()->GetNode().GetStartNode() ) &&
pSNd->GetIndex() < rStartIdx.GetIndex() &&
rStartIdx.GetIndex() < pSNd->EndOfSectionIndex() )
{
it = aSet.erase(it);
continue;
}
}
// Copy the format and set the new anchor
aVecSwFrameFormat.push_back( pDest->getIDocumentLayoutAccess().CopyLayoutFormat( *(*it).GetFormat(),
aAnchor, false, true ) );
++it;
}
// Rebuild as much as possible of all chains that are available in the original,
OSL_ENSURE( aSet.size() == aVecSwFrameFormat.size(), "Missing new Flys" );
if ( aSet.size() == aVecSwFrameFormat.size() )
{
size_t n = 0;
for (std::set< ZSortFly >::const_iterator nIt=aSet.begin() ; nIt != aSet.end(); ++nIt, ++n )
{
const SwFrameFormat *pFormatN = (*nIt).GetFormat();
const SwFormatChain &rChain = pFormatN->GetChain();
int nCnt = int(nullptr != rChain.GetPrev());
nCnt += rChain.GetNext() ? 1: 0;
size_t k = 0;
for (std::set< ZSortFly >::const_iterator kIt=aSet.begin() ; kIt != aSet.end(); ++kIt, ++k )
{
const SwFrameFormat *pFormatK = (*kIt).GetFormat();
if ( rChain.GetPrev() == pFormatK )
{
::lcl_ChainFormats( static_cast< SwFlyFrameFormat* >(aVecSwFrameFormat[k]),
static_cast< SwFlyFrameFormat* >(aVecSwFrameFormat[n]) );
--nCnt;
}
else if ( rChain.GetNext() == pFormatK )
{
::lcl_ChainFormats( static_cast< SwFlyFrameFormat* >(aVecSwFrameFormat[n]),
static_cast< SwFlyFrameFormat* >(aVecSwFrameFormat[k]) );
--nCnt;
}
}
}
// Re-create content property of draw formats, knowing how old shapes
// were paired with old fly formats (aOldTextBoxes) and that aSet is
// parallel with aVecSwFrameFormat.
SwTextBoxHelper::restoreLinks(aSet, aVecSwFrameFormat, aOldTextBoxes, aOldContent);
}
}
/*
* Reset the text's hard formatting
*/
/** @params pArgs contains the document's ChrFormatTable
* Is need for selections at the beginning/end and with no SSelection.
*/
bool DocumentContentOperationsManager::lcl_RstTextAttr( const SwNodePtr& rpNd, void* pArgs )
{
ParaRstFormat* pPara = static_cast<ParaRstFormat*>(pArgs);
SwTextNode * pTextNode = rpNd->GetTextNode();
if( pTextNode && pTextNode->GetpSwpHints() )
{
SwIndex aSt( pTextNode, 0 );
sal_Int32 nEnd = pTextNode->Len();
if( &pPara->pSttNd->nNode.GetNode() == pTextNode &&
pPara->pSttNd->nContent.GetIndex() )
aSt = pPara->pSttNd->nContent.GetIndex();
if( &pPara->pEndNd->nNode.GetNode() == rpNd )
nEnd = pPara->pEndNd->nContent.GetIndex();
if( pPara->pHistory )
{
// Save all attributes for the Undo.
SwRegHistory aRHst( *pTextNode, pPara->pHistory );
pTextNode->GetpSwpHints()->Register( &aRHst );
pTextNode->RstTextAttr( aSt, nEnd - aSt.GetIndex(), pPara->nWhich,
pPara->pDelSet, pPara->bInclRefToxMark, pPara->bExactRange );
if( pTextNode->GetpSwpHints() )
pTextNode->GetpSwpHints()->DeRegister();
}
else
pTextNode->RstTextAttr( aSt, nEnd - aSt.GetIndex(), pPara->nWhich,
pPara->pDelSet, pPara->bInclRefToxMark, pPara->bExactRange );
}
return true;
}
DocumentContentOperationsManager::~DocumentContentOperationsManager()
{
}
//Private methods
bool DocumentContentOperationsManager::DeleteAndJoinWithRedlineImpl( SwPaM & rPam, const bool )
{
OSL_ENSURE( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn(), "DeleteAndJoinWithRedline: redline off" );
{
SwUndoRedlineDelete* pUndo = nullptr;
RedlineFlags eOld = m_rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
m_rDoc.GetDocumentRedlineManager().checkRedlining( eOld );
auto & rDMA(*m_rDoc.getIDocumentMarkAccess());
std::vector<std::unique_ptr<SwUndo>> MarkUndos;
for (auto iter = rDMA.getAnnotationMarksBegin();
iter != rDMA.getAnnotationMarksEnd(); )
{
// tdf#111524 remove annotation marks that have their field
// characters deleted
SwPosition const& rEndPos((**iter).GetMarkEnd());
if (*rPam.Start() < rEndPos && rEndPos <= *rPam.End())
{
if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
{
MarkUndos.emplace_back(o3tl::make_unique<SwUndoDeleteBookmark>(**iter));
}
// iter is into annotation mark vector so must be dereferenced!
rDMA.deleteMark(&**iter);
// this invalidates iter, have to start over...
iter = rDMA.getAnnotationMarksBegin();
}
else
{ // marks are sorted by start
if (*rPam.End() < (**iter).GetMarkStart())
{
break;
}
++iter;
}
}
// tdf#119019 accept tracked paragraph formatting to do not hide new deletions
if ( *rPam.GetPoint() != *rPam.GetMark() )
m_rDoc.getIDocumentRedlineAccess().AcceptRedlineParagraphFormatting( rPam );
if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
{
/* please don't translate -- for cultural reasons this comment is protected
until the redline implementation is finally fixed some day */
//JP 06.01.98: MUSS noch optimiert werden!!!
m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags(
RedlineFlags::On | RedlineFlags::ShowInsert | RedlineFlags::ShowDelete );
pUndo = new SwUndoRedlineDelete( rPam, SwUndoId::DELETE );
const SwRewriter aRewriter = pUndo->GetRewriter();
m_rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::DELETE, &aRewriter );
for (auto& it : MarkUndos)
{
m_rDoc.GetIDocumentUndoRedo().AppendUndo(it.release());
}
m_rDoc.GetIDocumentUndoRedo().AppendUndo( pUndo );
}
if ( *rPam.GetPoint() != *rPam.GetMark() )
m_rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( nsRedlineType_t::REDLINE_DELETE, rPam ), true );
m_rDoc.getIDocumentState().SetModified();
if ( pUndo )
{
m_rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::EMPTY, nullptr );
// ??? why the hell is the AppendUndo not below the
// CanGrouping, so this hideous cleanup wouldn't be necessary?
// bah, this is redlining, probably changing this would break it...
if ( m_rDoc.GetIDocumentUndoRedo().DoesGroupUndo() )
{
SwUndo * const pLastUndo( m_rDoc.GetUndoManager().GetLastUndo() );
SwUndoRedlineDelete * const pUndoRedlineDel( dynamic_cast< SwUndoRedlineDelete* >( pLastUndo ) );
if ( pUndoRedlineDel )
{
bool const bMerged = pUndoRedlineDel->CanGrouping( *pUndo );
if ( bMerged )
{
::sw::UndoGuard const undoGuard( m_rDoc.GetIDocumentUndoRedo() );
SwUndo const* const pDeleted = m_rDoc.GetUndoManager().RemoveLastUndo();
OSL_ENSURE( pDeleted == pUndo, "DeleteAndJoinWithRedlineImpl: "
"undo removed is not undo inserted?" );
delete pDeleted;
}
}
}
//JP 06.01.98: MUSS noch optimiert werden!!!
m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags( eOld );
}
return true;
}
}
bool DocumentContentOperationsManager::DeleteAndJoinImpl( SwPaM & rPam,
const bool bForceJoinNext )
{
bool bJoinText, bJoinPrev;
::sw_GetJoinFlags( rPam, bJoinText, bJoinPrev );
// #i100466#
if ( bForceJoinNext )
{
bJoinPrev = false;
}
{
bool const bSuccess( DeleteRangeImpl( rPam ) );
if (!bSuccess)
return false;
}
if( bJoinText )
{
::sw_JoinText( rPam, bJoinPrev );
}
return true;
}
bool DocumentContentOperationsManager::DeleteRangeImpl(SwPaM & rPam, const bool)
{
// Move all cursors out of the deleted range, but first copy the
// passed PaM, because it could be a cursor that would be moved!
SwPaM aDelPam( *rPam.GetMark(), *rPam.GetPoint() );
::PaMCorrAbs( aDelPam, *aDelPam.GetPoint() );
bool const bSuccess( DeleteRangeImplImpl( aDelPam ) );
if (bSuccess)
{ // now copy position from temp copy to given PaM
*rPam.GetPoint() = *aDelPam.GetPoint();
}
return bSuccess;
}
bool DocumentContentOperationsManager::DeleteRangeImplImpl(SwPaM & rPam)
{
SwPosition *pStt = rPam.Start(), *pEnd = rPam.End();
if( !rPam.HasMark() || *pStt >= *pEnd )
return false;
if( m_rDoc.GetAutoCorrExceptWord() )
{
// if necessary the saved Word for the exception
if( m_rDoc.GetAutoCorrExceptWord()->IsDeleted() || pStt->nNode != pEnd->nNode ||
pStt->nContent.GetIndex() + 1 != pEnd->nContent.GetIndex() ||
!m_rDoc.GetAutoCorrExceptWord()->CheckDelChar( *pStt ))
{ m_rDoc.DeleteAutoCorrExceptWord(); }
}
{
// Delete all empty TextHints at the Mark's position
SwTextNode* pTextNd = rPam.GetMark()->nNode.GetNode().GetTextNode();
SwpHints* pHts;
if( pTextNd && nullptr != ( pHts = pTextNd->GetpSwpHints()) && pHts->Count() )
{
const sal_Int32 nMkCntPos = rPam.GetMark()->nContent.GetIndex();
for( size_t n = pHts->Count(); n; )
{
const SwTextAttr* pAttr = pHts->Get( --n );
if( nMkCntPos > pAttr->GetStart() )
break;
const sal_Int32 *pEndIdx;
if( nMkCntPos == pAttr->GetStart() &&
nullptr != (pEndIdx = pAttr->End()) &&
*pEndIdx == pAttr->GetStart() )
pTextNd->DestroyAttr( pHts->Cut( n ) );
}
}
}
{
// Send DataChanged before deletion, so that we still know
// which objects are in the range.
// Afterwards they could be before/after the Position.
SwDataChanged aTmp( rPam );
}
if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
{
m_rDoc.GetIDocumentUndoRedo().ClearRedo();
bool bMerged(false);
if (m_rDoc.GetIDocumentUndoRedo().DoesGroupUndo())
{
SwUndo *const pLastUndo( m_rDoc.GetUndoManager().GetLastUndo() );
SwUndoDelete *const pUndoDelete(
dynamic_cast<SwUndoDelete *>(pLastUndo) );
if (pUndoDelete)
{
bMerged = pUndoDelete->CanGrouping( &m_rDoc, rPam );
// if CanGrouping() returns true it's already merged
}
}
if (!bMerged)
{
m_rDoc.GetIDocumentUndoRedo().AppendUndo( new SwUndoDelete( rPam ) );
}
m_rDoc.getIDocumentState().SetModified();
return true;
}
if( !m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )
m_rDoc.getIDocumentRedlineAccess().DeleteRedline( rPam, true, USHRT_MAX );
// Delete and move all "Flys at the paragraph", which are within the Selection
DelFlyInRange(rPam.GetMark()->nNode, rPam.GetPoint()->nNode);
DelBookmarks(
pStt->nNode,
pEnd->nNode,
nullptr,
&pStt->nContent,
&pEnd->nContent);
SwNodeIndex aSttIdx( pStt->nNode );
SwContentNode * pCNd = aSttIdx.GetNode().GetContentNode();
do { // middle checked loop!
if( pCNd )
{
SwTextNode * pStartTextNode( pCNd->GetTextNode() );
if ( pStartTextNode )
{
// now move the Content to the new Node
bool bOneNd = pStt->nNode == pEnd->nNode;
const sal_Int32 nLen = ( bOneNd ? pEnd->nContent.GetIndex()
: pCNd->Len() )
- pStt->nContent.GetIndex();
// Don't call again, if already empty
if( nLen )
{
pStartTextNode->EraseText( pStt->nContent, nLen );
if( !pStartTextNode->Len() )
{
// METADATA: remove reference if empty (consider node deleted)
pStartTextNode->RemoveMetadataReference();
}
}
if( bOneNd ) // that's it
break;
++aSttIdx;
}
else
{
// So that there are no indices left registered when deleted,
// we remove a SwPaM from the Content here.
pStt->nContent.Assign( nullptr, 0 );
}
}
pCNd = pEnd->nNode.GetNode().GetContentNode();
if( pCNd )
{
SwTextNode * pEndTextNode( pCNd->GetTextNode() );
if( pEndTextNode )
{
// if already empty, don't call again
if( pEnd->nContent.GetIndex() )
{
SwIndex aIdx( pCNd, 0 );
pEndTextNode->EraseText( aIdx, pEnd->nContent.GetIndex() );
if( !pEndTextNode->Len() )
{
// METADATA: remove reference if empty (consider node deleted)
pEndTextNode->RemoveMetadataReference();
}
}
}
else
{
// So that there are no indices left registered when deleted,
// we remove a SwPaM from the Content here.
pEnd->nContent.Assign( nullptr, 0 );
}
}
// if the end is not a content node, delete it as well
sal_uInt32 nEnde = pEnd->nNode.GetIndex();
if( pCNd == nullptr )
nEnde++;
if( aSttIdx != nEnde )
{
// delete the Nodes into the NodesArary
m_rDoc.GetNodes().Delete( aSttIdx, nEnde - aSttIdx.GetIndex() );
}
// If the Node that contained the Cursor has been deleted,
// the Content has to be assigned to the current Content.
pStt->nContent.Assign( pStt->nNode.GetNode().GetContentNode(),
pStt->nContent.GetIndex() );
// If we deleted across Node boundaries we have to correct the PaM,
// because they are in different Nodes now.
// Also, the Selection is revoked.
*pEnd = *pStt;
rPam.DeleteMark();
} while( false );
if( !m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )
m_rDoc.getIDocumentRedlineAccess().CompressRedlines();
m_rDoc.getIDocumentState().SetModified();
return true;
}
// It's possible to call Replace with a PaM that spans 2 paragraphs:
// search with regex for "$", then replace _all_
bool DocumentContentOperationsManager::ReplaceRangeImpl( SwPaM& rPam, const OUString& rStr,
const bool bRegExReplace )
{
if( !rPam.HasMark() || *rPam.GetPoint() == *rPam.GetMark() )
return false;
bool bJoinText, bJoinPrev;
::sw_GetJoinFlags( rPam, bJoinText, bJoinPrev );
{
// Create a copy of the Cursor in order to move all Pams from
// the other views out of the deletion range.
// Except for itself!
SwPaM aDelPam( *rPam.GetMark(), *rPam.GetPoint() );
::PaMCorrAbs( aDelPam, *aDelPam.GetPoint() );
SwPosition *pStt = aDelPam.Start(),
*pEnd = aDelPam.End();
OSL_ENSURE( pStt->nNode == pEnd->nNode ||
( pStt->nNode.GetIndex() + 1 == pEnd->nNode.GetIndex() &&
!pEnd->nContent.GetIndex() ),
"invalid range: Point and Mark on different nodes" );
bool bOneNode = pStt->nNode == pEnd->nNode;
// Own Undo?
OUString sRepl( rStr );
SwTextNode* pTextNd = pStt->nNode.GetNode().GetTextNode();
sal_Int32 nStt = pStt->nContent.GetIndex();
sal_Int32 nEnd;
SwDataChanged aTmp( aDelPam );
if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
{
RedlineFlags eOld = m_rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
m_rDoc.GetDocumentRedlineManager().checkRedlining(eOld);
if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
{
m_rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY, nullptr);
// If any Redline will change (split!) the node
const ::sw::mark::IMark* pBkmk =
m_rDoc.getIDocumentMarkAccess()->makeMark( aDelPam,
OUString(), IDocumentMarkAccess::MarkType::UNO_BOOKMARK,
::sw::mark::InsertMode::New);
//JP 06.01.98: MUSS noch optimiert werden!!!
m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags(
RedlineFlags::On | RedlineFlags::ShowInsert | RedlineFlags::ShowDelete );
*aDelPam.GetPoint() = pBkmk->GetMarkPos();
if(pBkmk->IsExpanded())
*aDelPam.GetMark() = pBkmk->GetOtherMarkPos();
m_rDoc.getIDocumentMarkAccess()->deleteMark(pBkmk);
pStt = aDelPam.Start();
pTextNd = pStt->nNode.GetNode().GetTextNode();
nStt = pStt->nContent.GetIndex();
}
if( !sRepl.isEmpty() )
{
// Apply the first character's attributes to the ReplaceText
SfxItemSet aSet( m_rDoc.GetAttrPool(),
svl::Items<RES_CHRATR_BEGIN, RES_TXTATR_WITHEND_END - 1,
RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1>{} );
pTextNd->GetAttr( aSet, nStt+1, nStt+1 );
aSet.ClearItem( RES_TXTATR_REFMARK );
aSet.ClearItem( RES_TXTATR_TOXMARK );
aSet.ClearItem( RES_TXTATR_CJK_RUBY );
aSet.ClearItem( RES_TXTATR_INETFMT );
aSet.ClearItem( RES_TXTATR_META );
aSet.ClearItem( RES_TXTATR_METAFIELD );
if( aDelPam.GetPoint() != aDelPam.End() )
aDelPam.Exchange();
// Remember the End
SwNodeIndex aPtNd( aDelPam.GetPoint()->nNode, -1 );
const sal_Int32 nPtCnt = aDelPam.GetPoint()->nContent.GetIndex();
bool bFirst = true;
OUString sIns;
while ( lcl_GetTokenToParaBreak( sRepl, sIns, bRegExReplace ) )
{
InsertString( aDelPam, sIns );
if( bFirst )
{
SwNodeIndex aMkNd( aDelPam.GetMark()->nNode, -1 );
const sal_Int32 nMkCnt = aDelPam.GetMark()->nContent.GetIndex();
SplitNode( *aDelPam.GetPoint(), false );
++aMkNd;
aDelPam.GetMark()->nNode = aMkNd;
aDelPam.GetMark()->nContent.Assign(
aMkNd.GetNode().GetContentNode(), nMkCnt );
bFirst = false;
}
else
SplitNode( *aDelPam.GetPoint(), false );
}
if( !sIns.isEmpty() )
{
InsertString( aDelPam, sIns );
}
SwPaM aTmpRange( *aDelPam.GetPoint() );
aTmpRange.SetMark();
++aPtNd;
aDelPam.GetPoint()->nNode = aPtNd;
aDelPam.GetPoint()->nContent.Assign( aPtNd.GetNode().GetContentNode(),
nPtCnt);
*aTmpRange.GetMark() = *aDelPam.GetPoint();
m_rDoc.RstTextAttrs( aTmpRange );
InsertItemSet( aTmpRange, aSet );
}
if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
{
SwUndo *const pUndoRD =
new SwUndoRedlineDelete( aDelPam, SwUndoId::REPLACE );
m_rDoc.GetIDocumentUndoRedo().AppendUndo(pUndoRD);
}
m_rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( nsRedlineType_t::REDLINE_DELETE, aDelPam ), true);
*rPam.GetMark() = *aDelPam.GetMark();
if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
{
*aDelPam.GetPoint() = *rPam.GetPoint();
m_rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY, nullptr);
// If any Redline will change (split!) the node
const ::sw::mark::IMark* pBkmk =
m_rDoc.getIDocumentMarkAccess()->makeMark( aDelPam,
OUString(), IDocumentMarkAccess::MarkType::UNO_BOOKMARK,
::sw::mark::InsertMode::New);
SwIndex& rIdx = aDelPam.GetPoint()->nContent;
rIdx.Assign( nullptr, 0 );
aDelPam.GetMark()->nContent = rIdx;
rPam.GetPoint()->nNode = 0;
rPam.GetPoint()->nContent = rIdx;
*rPam.GetMark() = *rPam.GetPoint();
//JP 06.01.98: MUSS noch optimiert werden!!!
m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags( eOld );
*rPam.GetPoint() = pBkmk->GetMarkPos();
if(pBkmk->IsExpanded())
*rPam.GetMark() = pBkmk->GetOtherMarkPos();
m_rDoc.getIDocumentMarkAccess()->deleteMark(pBkmk);
}
bJoinText = false;
}
else
{
if( !m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().size() )
m_rDoc.getIDocumentRedlineAccess().DeleteRedline( aDelPam, true, USHRT_MAX );
SwUndoReplace* pUndoRpl = nullptr;
bool const bDoesUndo = m_rDoc.GetIDocumentUndoRedo().DoesUndo();
if (bDoesUndo)
{
pUndoRpl = new SwUndoReplace(aDelPam, sRepl, bRegExReplace);
m_rDoc.GetIDocumentUndoRedo().AppendUndo(pUndoRpl);
}
::sw::UndoGuard const undoGuard(m_rDoc.GetIDocumentUndoRedo());
if( aDelPam.GetPoint() != pStt )
aDelPam.Exchange();
SwNodeIndex aPtNd( pStt->nNode, -1 );
const sal_Int32 nPtCnt = pStt->nContent.GetIndex();
// Set the values again, if Frames or footnotes on the Text have been removed.
nStt = nPtCnt;
nEnd = bOneNode ? pEnd->nContent.GetIndex()
: pTextNd->GetText().getLength();
bool bFirst = true;
OUString sIns;
while ( lcl_GetTokenToParaBreak( sRepl, sIns, bRegExReplace ) )
{
if (!bFirst || nStt == pTextNd->GetText().getLength())
{
InsertString( aDelPam, sIns );
}
else if( nStt < nEnd || !sIns.isEmpty() )
{
pTextNd->ReplaceText( pStt->nContent, nEnd - nStt, sIns );
}
SplitNode( *pStt, false);
bFirst = false;
}
if( bFirst || !sIns.isEmpty() )
{
if (!bFirst || nStt == pTextNd->GetText().getLength())
{
InsertString( aDelPam, sIns );
}
else if( nStt < nEnd || !sIns.isEmpty() )
{
pTextNd->ReplaceText( pStt->nContent, nEnd - nStt, sIns );
}
}
*rPam.GetPoint() = *aDelPam.GetMark();
++aPtNd;
rPam.GetMark()->nNode = aPtNd;
rPam.GetMark()->nContent.Assign( aPtNd.GetNode().GetContentNode(),
nPtCnt );
if (bJoinText)
{
assert(rPam.GetPoint() == rPam.End());
// move so that SetEnd remembers position after sw_JoinText
rPam.Move(fnMoveBackward);
}
else if (aDelPam.GetPoint() == pStt) // backward selection?
{
assert(*rPam.GetMark() <= *rPam.GetPoint());
rPam.Exchange(); // swap so that rPam is backwards
}
if( pUndoRpl )
{
pUndoRpl->SetEnd(rPam);
}
}
}
bool bRet(true);
if (bJoinText)
{
bRet = ::sw_JoinText(rPam, bJoinPrev);
}
m_rDoc.getIDocumentState().SetModified();
return bRet;
}
SwFlyFrameFormat* DocumentContentOperationsManager::InsNoTextNode( const SwPosition& rPos, SwNoTextNode* pNode,
const SfxItemSet* pFlyAttrSet,
const SfxItemSet* pGrfAttrSet,
SwFrameFormat* pFrameFormat)
{
SwFlyFrameFormat *pFormat = nullptr;
if( pNode )
{
pFormat = m_rDoc.MakeFlySection_( rPos, *pNode, RndStdIds::FLY_AT_PARA,
pFlyAttrSet, pFrameFormat );
if( pGrfAttrSet )
pNode->SetAttr( *pGrfAttrSet );
}
return pFormat;
}
#define NUMRULE_STATE \
SfxItemState aNumRuleState = SfxItemState::UNKNOWN; \
SwNumRuleItem aNumRuleItem; \
SfxItemState aListIdState = SfxItemState::UNKNOWN; \
SfxStringItem aListIdItem( RES_PARATR_LIST_ID, OUString() ); \
#define PUSH_NUMRULE_STATE \
lcl_PushNumruleState( aNumRuleState, aNumRuleItem, aListIdState, aListIdItem, pDestTextNd );
#define POP_NUMRULE_STATE \
lcl_PopNumruleState( aNumRuleState, aNumRuleItem, aListIdState, aListIdItem, pDestTextNd, rPam );
static void lcl_PushNumruleState( SfxItemState &aNumRuleState, SwNumRuleItem &aNumRuleItem,
SfxItemState &aListIdState, SfxStringItem &aListIdItem,
const SwTextNode *pDestTextNd )
{
// Safe numrule item at destination.
// #i86492# - Safe also <ListId> item of destination.
const SfxItemSet * pAttrSet = pDestTextNd->GetpSwAttrSet();
if (pAttrSet != nullptr)
{
const SfxPoolItem * pItem = nullptr;
aNumRuleState = pAttrSet->GetItemState(RES_PARATR_NUMRULE, false, &pItem);
if (SfxItemState::SET == aNumRuleState)
aNumRuleItem = *static_cast<const SwNumRuleItem *>( pItem);
aListIdState =
pAttrSet->GetItemState(RES_PARATR_LIST_ID, false, &pItem);
if (SfxItemState::SET == aListIdState)
{
aListIdItem.SetValue( static_cast<const SfxStringItem*>(pItem)->GetValue() );
}
}
}
static void lcl_PopNumruleState( SfxItemState aNumRuleState, const SwNumRuleItem &aNumRuleItem,
SfxItemState aListIdState, const SfxStringItem &aListIdItem,
SwTextNode *pDestTextNd, const SwPaM& rPam )
{
/* If only a part of one paragraph is copied
restore the numrule at the destination. */
// #i86492# - restore also <ListId> item
if ( !lcl_MarksWholeNode(rPam) )
{
if (SfxItemState::SET == aNumRuleState)
{
pDestTextNd->SetAttr(aNumRuleItem);
}
else
{
pDestTextNd->ResetAttr(RES_PARATR_NUMRULE);
}
if (SfxItemState::SET == aListIdState)
{
pDestTextNd->SetAttr(aListIdItem);
}
else
{
pDestTextNd->ResetAttr(RES_PARATR_LIST_ID);
}
}
}
bool DocumentContentOperationsManager::CopyImpl( SwPaM& rPam, SwPosition& rPos,
const bool bMakeNewFrames, const bool bCopyAll,
SwPaM *const pCpyRange ) const
{
SwDoc* pDoc = rPos.nNode.GetNode().GetDoc();
const bool bColumnSel = pDoc->IsClipBoard() && pDoc->IsColumnSelection();
SwPosition* pStt = rPam.Start();
SwPosition* pEnd = rPam.End();
// Catch when there's no copy to do.
if( !rPam.HasMark() || ( *pStt >= *pEnd && !bColumnSel ) ||
//JP 29.6.2001: 88963 - don't copy if inspos is in region of start to end
//JP 15.11.2001: don't test inclusive the end, ever exclusive
( pDoc == &m_rDoc && *pStt <= rPos && rPos < *pEnd ))
{
return false;
}
const bool bEndEqualIns = pDoc == &m_rDoc && rPos == *pEnd;
// If Undo is enabled, create the UndoCopy object
SwUndoCpyDoc* pUndo = nullptr;
// lcl_DeleteRedlines may delete the start or end node of the cursor when
// removing the redlines so use cursor that is corrected by PaMCorrAbs
std::shared_ptr<SwUnoCursor> const pCopyPam(pDoc->CreateUnoCursor(rPos));
SwTableNumFormatMerge aTNFM( m_rDoc, *pDoc );
if (pDoc->GetIDocumentUndoRedo().DoesUndo())
{
pUndo = new SwUndoCpyDoc(*pCopyPam);
pDoc->GetIDocumentUndoRedo().AppendUndo( pUndo );
}
RedlineFlags eOld = pDoc->getIDocumentRedlineAccess().GetRedlineFlags();
pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld | RedlineFlags::Ignore);
// Move the PaM one node back from the insert position, so that
// the position doesn't get moved
pCopyPam->SetMark();
bool bCanMoveBack = pCopyPam->Move(fnMoveBackward, GoInContent);
// If the position was shifted from more than one node, an end node has been skipped
bool bAfterTable = false;
if ((rPos.nNode.GetIndex() - pCopyPam->GetPoint()->nNode.GetIndex()) > 1)
{
// First go back to the original place
pCopyPam->GetPoint()->nNode = rPos.nNode;
pCopyPam->GetPoint()->nContent = rPos.nContent;
bCanMoveBack = false;
bAfterTable = true;
}
if( !bCanMoveBack )
pCopyPam->GetPoint()->nNode--;
SwNodeRange aRg( pStt->nNode, pEnd->nNode );
SwNodeIndex aInsPos( rPos.nNode );
const bool bOneNode = pStt->nNode == pEnd->nNode;
SwTextNode* pSttTextNd = pStt->nNode.GetNode().GetTextNode();
SwTextNode* pEndTextNd = pEnd->nNode.GetNode().GetTextNode();
SwTextNode* pDestTextNd = aInsPos.GetNode().GetTextNode();
bool bCopyCollFormat = !pDoc->IsInsOnlyTextGlossary() &&
( (pDestTextNd && !pDestTextNd->GetText().getLength()) ||
( !bOneNode && !rPos.nContent.GetIndex() ) );
bool bCopyBookmarks = true;
bool bCopyPageSource = false;
bool bStartIsTextNode = nullptr != pSttTextNd;
// #i104585# copy outline num rule to clipboard (for ASCII filter)
if (pDoc->IsClipBoard() && m_rDoc.GetOutlineNumRule())
{
pDoc->SetOutlineNumRule(*m_rDoc.GetOutlineNumRule());
}
// #i86492#
// Correct the search for a previous list:
// First search for non-outline numbering list. Then search for non-outline
// bullet list.
// Keep also the <ListId> value for possible propagation.
OUString aListIdToPropagate;
const SwNumRule* pNumRuleToPropagate =
pDoc->SearchNumRule( rPos, false, true, false, 0, aListIdToPropagate, true );
if ( !pNumRuleToPropagate )
{
pNumRuleToPropagate =
pDoc->SearchNumRule( rPos, false, false, false, 0, aListIdToPropagate, true );
}
// #i86492#
// Do not propagate previous found list, if
// - destination is an empty paragraph which is not in a list and
// - source contains at least one paragraph which is not in a list
if ( pNumRuleToPropagate &&
pDestTextNd && !pDestTextNd->GetText().getLength() &&
!pDestTextNd->IsInList() &&
!lcl_ContainsOnlyParagraphsInList( rPam ) )
{
pNumRuleToPropagate = nullptr;
}
// This do/while block is only there so that we can break out of it!
do {
if( pSttTextNd )
{
// Don't copy the beginning completely?
if( !bCopyCollFormat || bColumnSel || pStt->nContent.GetIndex() )
{
SwIndex aDestIdx( rPos.nContent );
bool bCopyOk = false;
if( !pDestTextNd )
{
if( pStt->nContent.GetIndex() || bOneNode )
pDestTextNd = pDoc->GetNodes().MakeTextNode( aInsPos,
pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD));
else
{
pDestTextNd = pSttTextNd->MakeCopy( pDoc, aInsPos )->GetTextNode();
bCopyOk = true;
}
aDestIdx.Assign( pDestTextNd, 0 );
bCopyCollFormat = true;
}
else if( !bOneNode || bColumnSel )
{
const sal_Int32 nContentEnd = pEnd->nContent.GetIndex();
{
::sw::UndoGuard const ug(pDoc->GetIDocumentUndoRedo());
pDoc->getIDocumentContentOperations().SplitNode( rPos, false );
}
if (bCanMoveBack && rPos == *pCopyPam->GetPoint())
{
// after the SplitNode, span the CpyPam correctly again
pCopyPam->Move( fnMoveBackward, GoInContent );
pCopyPam->Move( fnMoveBackward, GoInContent );
}
pDestTextNd = pDoc->GetNodes()[ aInsPos.GetIndex()-1 ]->GetTextNode();
aDestIdx.Assign(
pDestTextNd, pDestTextNd->GetText().getLength());
// Correct the area again
if( bEndEqualIns )
{
bool bChg = pEnd != rPam.GetPoint();
if( bChg )
rPam.Exchange();
rPam.Move( fnMoveBackward, GoInContent );
if( bChg )
rPam.Exchange();
}
else if( rPos == *pEnd )
{
// The end was also moved
pEnd->nNode--;
pEnd->nContent.Assign( pDestTextNd, nContentEnd );
}
// tdf#63022 always reset pEndTextNd after SplitNode
aRg.aEnd = pEnd->nNode;
pEndTextNd = pEnd->nNode.GetNode().GetTextNode();
}
NUMRULE_STATE
if( bCopyCollFormat && bOneNode )
{
PUSH_NUMRULE_STATE
}
if( !bCopyOk )
{
const sal_Int32 nCpyLen = ( bOneNode
? pEnd->nContent.GetIndex()
: pSttTextNd->GetText().getLength())
- pStt->nContent.GetIndex();
pSttTextNd->CopyText( pDestTextNd, aDestIdx,
pStt->nContent, nCpyLen );
if( bEndEqualIns )
pEnd->nContent -= nCpyLen;
}
if( bOneNode )
{
if (bCopyCollFormat)
{
pSttTextNd->CopyCollFormat( *pDestTextNd );
POP_NUMRULE_STATE
}
break;
}
aRg.aStart++;
}
}
else if( pDestTextNd )
{
// Problems with insertion of table selections into "normal" text solved.
// We have to set the correct PaM for Undo, if this PaM starts in a textnode,
// the undo operation will try to merge this node after removing the table.
// If we didn't split a textnode, the PaM should start at the inserted table node
if( rPos.nContent.GetIndex() == pDestTextNd->Len() )
{ // Insertion at the last position of a textnode (empty or not)
++aInsPos; // The table will be inserted behind the text node
}
else if( rPos.nContent.GetIndex() )
{ // Insertion in the middle of a text node, it has to be split
// (and joined from undo)
bStartIsTextNode = true;
const sal_Int32 nContentEnd = pEnd->nContent.GetIndex();
{
::sw::UndoGuard const ug(pDoc->GetIDocumentUndoRedo());
pDoc->getIDocumentContentOperations().SplitNode( rPos, false );
}
if (bCanMoveBack && rPos == *pCopyPam->GetPoint())
{
// after the SplitNode, span the CpyPam correctly again
pCopyPam->Move( fnMoveBackward, GoInContent );
pCopyPam->Move( fnMoveBackward, GoInContent );
}
// Correct the area again
if( bEndEqualIns )
aRg.aEnd--;
// The end would also be moved
else if( rPos == *pEnd )
{
rPos.nNode-=2;
rPos.nContent.Assign( rPos.nNode.GetNode().GetContentNode(),
nContentEnd );
rPos.nNode++;
aRg.aEnd--;
}
}
else if( bCanMoveBack )
{ //Insertion at the first position of a text node. It will not be splitted, the table
// will be inserted before the text node.
// See below, before the SetInsertRange function of the undo object will be called,
// the CpyPam would be moved to the next content position. This has to be avoided
// We want to be moved to the table node itself thus we have to set bCanMoveBack
// and to manipulate pCopyPam.
bCanMoveBack = false;
pCopyPam->GetPoint()->nNode--;
}
}
pDestTextNd = aInsPos.GetNode().GetTextNode();
if (pEndTextNd)
{
SwIndex aDestIdx( rPos.nContent );
if( !pDestTextNd )
{
pDestTextNd = pDoc->GetNodes().MakeTextNode( aInsPos,
pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD));
aDestIdx.Assign( pDestTextNd, 0 );
aInsPos--;
// if we have to insert an extra text node
// at the destination, this node will be our new destination
// (text) node, and thus we set bStartisTextNode to true. This
// will ensure that this node will be deleted during Undo
// using JoinNext.
OSL_ENSURE( !bStartIsTextNode, "Oops, undo may be instable now." );
bStartIsTextNode = true;
}
const bool bEmptyDestNd = pDestTextNd->GetText().isEmpty();
NUMRULE_STATE
if( bCopyCollFormat && ( bOneNode || bEmptyDestNd ))
{
PUSH_NUMRULE_STATE
}
pEndTextNd->CopyText( pDestTextNd, aDestIdx, SwIndex( pEndTextNd ),
pEnd->nContent.GetIndex() );
// Also copy all format templates
if( bCopyCollFormat && ( bOneNode || bEmptyDestNd ))
{
pEndTextNd->CopyCollFormat( *pDestTextNd );
if ( bOneNode )
{
POP_NUMRULE_STATE
}
}
}
if( bCopyAll || aRg.aStart != aRg.aEnd )
{
SfxItemSet aBrkSet( pDoc->GetAttrPool(), aBreakSetRange );
if (pSttTextNd && bCopyCollFormat && pDestTextNd->HasSwAttrSet())
{
aBrkSet.Put( *pDestTextNd->GetpSwAttrSet() );
if( SfxItemState::SET == aBrkSet.GetItemState( RES_BREAK, false ) )
pDestTextNd->ResetAttr( RES_BREAK );
if( SfxItemState::SET == aBrkSet.GetItemState( RES_PAGEDESC, false ) )
pDestTextNd->ResetAttr( RES_PAGEDESC );
}
SwPosition startPos(SwNodeIndex(pCopyPam->GetPoint()->nNode, +1),
SwIndex(SwNodeIndex(pCopyPam->GetPoint()->nNode, +1).GetNode().GetContentNode()));
if (bCanMoveBack)
{ // pCopyPam is actually 1 before the copy range so move it fwd
SwPaM temp(*pCopyPam->GetPoint());
temp.Move(fnMoveForward, GoInContent);
startPos = *temp.GetPoint();
}
assert(startPos.nNode.GetNode().IsContentNode());
std::pair<SwPaM const&, SwPosition const&> tmp(rPam, startPos);
if( aInsPos == pEnd->nNode )
{
SwNodeIndex aSaveIdx( aInsPos, -1 );
CopyWithFlyInFly( aRg, 0, aInsPos, &tmp, bMakeNewFrames, false );
++aSaveIdx;
pEnd->nNode = aSaveIdx;
pEnd->nContent.Assign( aSaveIdx.GetNode().GetTextNode(), 0 );
}
else
CopyWithFlyInFly( aRg, pEnd->nContent.GetIndex(), aInsPos, &tmp, bMakeNewFrames, false );
bCopyBookmarks = false;
// Put the breaks back into the first node
if( aBrkSet.Count() && nullptr != ( pDestTextNd = pDoc->GetNodes()[
pCopyPam->GetPoint()->nNode.GetIndex()+1 ]->GetTextNode()))
{
pDestTextNd->SetAttr( aBrkSet );
bCopyPageSource = true;
}
}
} while( false );
// it is not possible to make this test when copy from the clipBoard to document
// in this case the PageNum not exist anymore
// tdf#39400 and tdf#97526
// when copy from document to ClipBoard, and it is from the first page
// and not the source has the page break
if (pDoc->IsClipBoard() && (rPam.GetPageNum(pStt == rPam.GetPoint()) == 1) && !bCopyPageSource)
{
pDestTextNd->ResetAttr(RES_BREAK); // remove the page-break
pDestTextNd->ResetAttr(RES_PAGEDESC);
}
// Adjust position (in case it was moved / in another node)
rPos.nContent.Assign( rPos.nNode.GetNode().GetContentNode(),
rPos.nContent.GetIndex() );
if( rPos.nNode != aInsPos )
{
pCopyPam->GetMark()->nNode = aInsPos;
pCopyPam->GetMark()->nContent.Assign(pCopyPam->GetContentNode(false), 0);
rPos = *pCopyPam->GetMark();
}
else
*pCopyPam->GetMark() = rPos;
if ( !bAfterTable )
pCopyPam->Move( fnMoveForward, bCanMoveBack ? GoInContent : GoInNode );
else
{
// Reset the offset to 0 as it was before the insertion
pCopyPam->GetPoint()->nContent = 0;
pCopyPam->GetPoint()->nNode++;
// If the next node is a start node, then step back: the start node
// has been copied and needs to be in the selection for the undo
if (pCopyPam->GetPoint()->nNode.GetNode().IsStartNode())
pCopyPam->GetPoint()->nNode--;
}
pCopyPam->Exchange();
// Also copy all bookmarks
if( bCopyBookmarks && m_rDoc.getIDocumentMarkAccess()->getAllMarksCount() )
lcl_CopyBookmarks( rPam, *pCopyPam );
if( RedlineFlags::DeleteRedlines & eOld )
{
assert(*pCopyPam->GetPoint() == rPos);
// the Node rPos points to may be deleted so unregister ...
rPos.nContent.Assign(nullptr, 0);
lcl_DeleteRedlines(rPam, *pCopyPam);
rPos = *pCopyPam->GetPoint(); // ... and restore.
}
// If Undo is enabled, store the inserted area
if (pDoc->GetIDocumentUndoRedo().DoesUndo())
{
pUndo->SetInsertRange( *pCopyPam, true, bStartIsTextNode );
}
if( pCpyRange )
{
pCpyRange->SetMark();
*pCpyRange->GetPoint() = *pCopyPam->GetPoint();
*pCpyRange->GetMark() = *pCopyPam->GetMark();
}
if ( pNumRuleToPropagate != nullptr )
{
// #i86492# - use <SwDoc::SetNumRule(..)>, because it also handles the <ListId>
// Don't reset indent attributes, that would mean loss of direct
// formatting.
pDoc->SetNumRule( *pCopyPam, *pNumRuleToPropagate, false,
aListIdToPropagate, true, /*bResetIndentAttrs=*/false );
}
pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
pDoc->getIDocumentState().SetModified();
return true;
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V581 The conditional expressions of the 'if' statements situated alongside each other are identical. Check lines: 1144, 1173.
↑ V560 A part of conditional expression is always true: pTextNd.