/* -*- 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 .
*/
namespace { void lcl_ResetIndentAttrs(SwDoc *pDoc, const SwPaM &rPam, const o3tl::sorted_vector<sal_uInt16> aResetAttrsArray,
SwRootFrame const*const pLayout)
{ // #i114929# // On a selection setup a corresponding Point-and-Mark in order to get // the indentation attribute reset on all paragraphs touched by the selection if ( rPam.HasMark() &&
rPam.End()->GetNode().GetTextNode() )
{
SwPaM aPam( rPam.Start()->GetNode(), 0,
rPam.End()->GetNode(), rPam.End()->GetNode().GetTextNode()->Len() );
pDoc->ResetAttrs( aPam, false, aResetAttrsArray, true, pLayout );
} else
{
pDoc->ResetAttrs( rPam, false, aResetAttrsArray, true, pLayout );
}
}
void ExpandPamForParaPropsNodes(SwPaM& rPam, SwRootFrame const*const pLayout)
{ if (!pLayout) return;
// ensure that selection from the Shell includes the para-props node // to which the attributes should be applied if (rPam.GetPoint()->GetNode().IsTextNode())
{
rPam.GetPoint()->Assign( *sw::GetParaPropsNode(*pLayout, rPam.GetPoint()->GetNode()) );
} if (rPam.GetMark()->GetNode().IsTextNode())
{
rPam.GetMark()->Assign( *sw::GetParaPropsNode(*pLayout, rPam.GetMark()->GetNode()) );
}
}
}
// assure that the outline numbering rule is an automatic rule
mpOutlineRule->SetAutoRule( true );
// test whether the optional CharFormats are defined in this Document
mpOutlineRule->CheckCharFormats( *this );
// notify text nodes, which are registered at the outline style, about the // changed outline style
SwNumRule::tTextNodeList aTextNodeList;
mpOutlineRule->GetTextNodeList( aTextNodeList ); for ( SwTextNode* pTextNd : aTextNodeList )
{
pTextNd->NumRuleChgd();
// assure that list level corresponds to outline level if ( pTextNd->GetTextColl()->IsAssignedToListLevelOfOutlineStyle() &&
pTextNd->GetAttrListLevel() != pTextNd->GetTextColl()->GetAssignedOutlineStyleLevel() )
{
pTextNd->SetAttrListLevel( pTextNd->GetTextColl()->GetAssignedOutlineStyleLevel() );
}
}
// update if we have foot notes && numbering by chapter if( !GetFootnoteIdxs().empty() && FTNNUM_CHAPTER == GetFootnoteInfo().m_eNum )
GetFootnoteIdxs().UpdateAllFootnote();
if (GetIDocumentUndoRedo().DoesUndo())
{
GetIDocumentUndoRedo().EndUndo(SwUndoId::OUTLINE_EDIT, nullptr);
}
getIDocumentState().SetModified();
}
void SwDoc::PropagateOutlineRule()
{
SwNumRule* pMyOutlineRule = GetOutlineNumRule(); if (!pMyOutlineRule) return;
for (auto pColl : *mpTextFormatCollTable)
{ if(pColl->IsAssignedToListLevelOfOutlineStyle())
{ // Check only the list style, which is set at the paragraph style const SwNumRuleItem & rCollRuleItem = pColl->GetNumRule( false );
/* Find the last occupied level (backward). */ for (n = MAXLEVEL - 1; n > 0; n--)
{ if (aCollArr[n] != nullptr) break;
}
/* If an occupied level is found, choose next level (which IS unoccupied) until a valid level is found. If no occupied level was found n is 0 and aCollArr[0] is 0. In this case no demoting
is possible. */ if (aCollArr[n] != nullptr)
{ while (n < MAXLEVEL - 1)
{
n++;
/* Find the first occupied level (forward). */ for (n = 0; n < MAXLEVEL - 1; n++)
{ if (aCollArr[n] != nullptr) break;
}
/* If an occupied level is found, choose previous level (which IS unoccupied) until a valid level is found. If no occupied level was found n is MAXLEVEL - 1 and aCollArr[MAXLEVEL - 1] is 0. In
this case no demoting is possible. */ if (aCollArr[n] != nullptr)
{ while (n > 0)
{
n--;
Build a move table that states from which level to which other level an outline will be moved.
the move table: aMoveArr[n] = m: replace aCollArr[n] with aCollArr[m]
*/ int aMoveArr[MAXLEVEL]; int nStep; // step size for searching in aCollArr: -1 or 1 int nNum; // amount of steps for stepping in aCollArr
/* traverse aCollArr */ for (n = 0; n < MAXLEVEL; n++)
{ /* If outline level n has an assigned paragraph style step nNum steps forwards (nStep == 1) or backwards (nStep == -1). One step is to go to the next non-null entry in aCollArr in the selected direction. If nNum steps were possible write the index of the entry found to aCollArr[n], i.e. outline level n will be replaced by outline level aCollArr[n].
If outline level n has no assigned paragraph style aMoveArr[n] is set to -1.
*/ if (aCollArr[n] != nullptr)
{ int m = n; int nCount = nNum;
while (nCount > 0 && m + nStep >= 0 && m + nStep < MAXLEVEL)
{
m += nStep;
if (aCollArr[m] != nullptr)
nCount--;
}
if (nCount == 0)
aMoveArr[n] = m; else
aMoveArr[n] = -1;
} else
aMoveArr[n] = -1;
}
/* If moving of the outline levels is applicable, i.e. for all outline levels occurring in the document there has to be a valid
target outline level implied by aMoveArr. */ bool bMoveApplicable = true; for (auto i = nSttPos; i < nEndPos; ++i)
{
SwTextNode* pTextNd = rOutlNds[ i ]->GetTextNode(); if (pLayout && !sw::IsParaPropsNode(*pLayout, *pTextNd))
{ continue;
}
SwTextFormatColl* pColl = pTextNd->GetTextColl();
// Check on outline level attribute of text node, if text node is // not an outline via a to outline style assigned paragraph style. else
{ constint nNewOutlineLevel = pTextNd->GetAttrOutlineLevel() + nOffset; if ( nNewOutlineLevel < 1 || nNewOutlineLevel > MAXLEVEL )
{
bMoveApplicable = false;
}
}
}
if (! bMoveApplicable ) returnfalse;
if (GetIDocumentUndoRedo().DoesUndo())
{
GetIDocumentUndoRedo().StartUndo(SwUndoId::OUTLINE_LR, nullptr);
GetIDocumentUndoRedo().AppendUndo(
std::make_unique<SwUndoOutlineLeftRight>(aPam, nOffset) );
}
// 2. Apply the new style to all Nodes for (auto i = nSttPos; i < nEndPos; ++i)
{
SwTextNode* pTextNd = rOutlNds[ i ]->GetTextNode(); if (pLayout && !sw::IsParaPropsNode(*pLayout, *pTextNd))
{ continue;
}
SwTextFormatColl* pColl = pTextNd->GetTextColl();
if( !pOutlineNodesInline && !GetNodes().GetOutLineNds().Seek_Entry( pSrch, &nCurrentPos ) )
{ if( !nCurrentPos ) returnfalse; // Promoting or demoting before the first outline => no.
assert(nCurrentPos > 0 && "coverity#1645558"); if( --nCurrentPos )
aSttRg = *GetNodes().GetOutLineNds()[ nCurrentPos ]; elseif( 0 > nOffset ) returnfalse; // Promoting at the top of document?! else
aSttRg = *GetNodes().GetEndOfContent().StartOfSectionNode();
} elseif ( pOutlineNodesInline )
{ if ( !pOutlineNodesInline->Seek_Entry_By_Anchor(pSrch, &nCurrentPosInline) )
{ if( !nCurrentPosInline ) returnfalse; // Promoting or demoting before the first outline => no.
assert(nCurrentPosInline > 0 && "coverity#1645558"); if( --nCurrentPosInline )
{
aSttRg = *SwOutlineNodes::GetRootNode((*pOutlineNodesInline)[ nCurrentPosInline ]);
} elseif( 0 > nOffset ) returnfalse; // Promoting at the top of document?! else
aSttRg = *GetNodes().GetEndOfContent().StartOfSectionNode();
}
}
SwOutlineNodes::size_type nTmpPos = 0;
SwOutlineNodesInline::size_type nTmpPosInline = 0; // If the given range ends at an outlined text node we have to decide if it has to be a part of // the moving range or not. Normally it will be a sub outline of our chapter // and has to be moved, too. But if the chapter ends with a table(or a section end), // the next text node will be chosen and this could be the next outline of the same level. // The criteria has to be the outline level: sub level => incorporate, same/higher level => no. if( !pOutlineNodesInline && GetNodes().GetOutLineNds().Seek_Entry( pEndSrch, &nTmpPos ) )
{ if( !pEndSrch->IsTextNode() || pEndSrch == pSrch ||
nOutLineLevel < pEndSrch->GetTextNode()->GetAttrOutlineLevel()-1 )
++nTmpPos; // For sub outlines only!
} elseif ( pOutlineNodesInline )
{ if ( pOutlineNodesInline->Seek_Entry_By_Anchor(pEndSrch, &nTmpPosInline) && (
!pEndSrch->IsTextNode() || pEndSrch == pSrch || nOutLineLevel <
pEndSrch->GetTextNode()->GetAttrOutlineLevel(/*bInlineHeading=*/true)-1 ) )
{
++nTmpPosInline;
}
}
const SwNode* pNd; // The following code corrects the range to handle sections (start/end nodes) // The range will be extended if the least node before the range is a start node // which ends inside the range => The complete section will be moved. // The range will be shrunk if the last position is a start node. // The range will be shrunk if the last node is an end node which starts before the range.
--aSttRg; while( aSttRg.GetNode().IsStartNode() )
{
pNd = aSttRg.GetNode().EndOfSectionNode(); if( pNd->GetIndex() >= aEndRg.GetIndex() ) break;
--aSttRg;
}
++aSttRg;
// And now a correction of the insert position if necessary...
SwNodeIndex aInsertPos( *pNd, -1 ); while( aInsertPos.GetNode().IsStartNode() )
{ // Just before the insert position starts a section: // when I'm moving forward I do not want to enter the section, // when I'm moving backward I want to stay in the section if I'm already a part of, // I want to stay outside if I was outside before. if( nOffset < 0 )
{
pNd = aInsertPos.GetNode().EndOfSectionNode(); if( pNd->GetIndex() >= aEndRg.GetIndex() ) break;
}
--aInsertPos;
--nNewPos;
}
if( nOffset >= 0 )
{ // When just before the insert position a section ends, it is okay when I'm moving backward // because I want to stay outside the section. // When moving forward I've to check if I started inside or outside the section // because I don't want to enter of leave such a section while( aInsertPos.GetNode().IsEndNode() )
{
pNd = aInsertPos.GetNode().StartOfSectionNode(); if( pNd->GetIndex() >= aSttRg.GetIndex() ) break;
--aInsertPos;
--nNewPos;
}
} // We do not want to move into tables (at the moment)
++aInsertPos;
pNd = &aInsertPos.GetNode(); if( pNd->IsTableNode() )
pNd = pNd->StartOfSectionNode(); if( pNd->FindTableNode() ) returnfalse;
// If a Position inside the special nodes array sections was calculated, // set it to document start instead. // Sections or Tables at the document start will be pushed backwards.
nNewPos = std::max( nNewPos, GetNodes().GetEndOfExtras().GetIndex() + SwNodeOffset(2) );
sName = sName.substr( nPos );
nPos = 0;
sNum = o3tl::getToken(sName, 0, '.', nPos ); // #i4533# without this check all parts delimited by a dot are treated as outline numbers if(!comphelper::string::isdigitAsciiString(sNum)) break;
}
rName = sName; // that's the follow-up text
// read all levels, so search the document for this outline
// Without OutlineNodes searching doesn't pay off // and we save a crash if( rOutlNds.empty() ) return nullptr;
// search in the existing outline nodes for the required outline num array for( auto pOutlNd : rOutlNds )
{
SwTextNode* pNd = pOutlNd->GetTextNode(); if ( pNd->GetAttrOutlineLevel() == nLevel )
{ // #i51089#, #i68289# // Assure, that text node has the correct numbering level. Otherwise, // its number vector will not fit to the searched level. if (pNd->GetNum(pLayout) && pNd->GetActualListLevel() == nLevel - 1)
{ const SwNodeNum & rNdNum = *(pNd->GetNum(pLayout));
SwNumberTree::tNumberVector aLevelVal = rNdNum.GetNumberVector(); // now compare with the one searched for bool bEqual = true;
nLevel = std::min<int>(nLevel, MAXLEVEL); for( int n = 0; n < nLevel; ++n )
{ if ( aLevelVal[n] != nLevelVal[n] )
{
bEqual = false; break;
}
} if (bEqual) return pNd;
} else
{ // A text node, which has an outline paragraph style applied and // has as hard attribute 'no numbering' set, has an outline level, // but no numbering tree node. Thus, consider this situation in // the assertion condition.
OSL_ENSURE( !pNd->GetNumRule(), "<lcl_FindOutlineNum(..)> - text node with outline level and numbering rule, but without numbering tree node. This is a serious defect" );
}
}
}
return nullptr;
}
// rName can contain a Number and/or the Text. // First, we try to find the correct Entry via the Number. // If it exists, we compare the Text to see if it's the right one. // If that's not the case, we search again via the Text. If it is // found, we got the right entry. Or else we use the one found by // searching for the Number. // If we don't have a Number, we search via the Text only. bool SwDoc::GotoOutline(SwPosition& rPos, const OUString& rName, SwRootFrame const*const pLayout) const
{ if( !rName.isEmpty() )
{ const SwOutlineNodes& rOutlNds = GetNodes().GetOutLineNds();
// 1. step: via the Number:
OUString sName( rName );
SwTextNode* pNd = ::lcl_FindOutlineNum(rOutlNds, sName, pLayout); if ( pNd )
{
OUString sExpandedText = pNd->GetExpandText(pLayout); //#i4533# leading numbers followed by a dot have been remove while //searching for the outline position //to compensate this they must be removed from the paragraphs text content, too while(!sExpandedText.isEmpty())
{
sal_Int32 nPos = 0;
std::u16string_view sTempNum = o3tl::getToken(sExpandedText, 0, '.', nPos); if( sTempNum.empty() || -1 == nPos ||
!comphelper::string::isdigitAsciiString(sTempNum)) break;
sExpandedText = sExpandedText.copy(nPos);
}
if( sExpandedText != sName )
{
SwTextNode *pTmpNd = ::lcl_FindOutlineName(rOutlNds, pLayout, sName, true); if ( pTmpNd ) // found via the Name
{ if (pLayout && !sw::IsParaPropsNode(*pLayout, *pTmpNd))
{ // found the correct node but it's deleted! returnfalse; // avoid fallback to inexact search
}
pNd = pTmpNd;
}
}
rPos.Assign(*pNd); returntrue;
}
SwUndoInsNum * pUndo = nullptr; if (GetIDocumentUndoRedo().DoesUndo())
{ // Start/End for attributes!
GetIDocumentUndoRedo().StartUndo( SwUndoId::INSNUM, nullptr );
pUndo = new SwUndoInsNum( aPam, rRule );
GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndo));
}
SwNumRule* pNewOrChangedNumRule = FindNumRulePtr( rRule.GetName() ); bool bNewNumRuleCreated = false; if ( pNewOrChangedNumRule == nullptr )
{ // create new numbering rule based on given one
pNewOrChangedNumRule = ( *mpNumRuleTable )[MakeNumRule( rRule.GetName(), &rRule )];
bNewNumRuleCreated = true;
} elseif ( rRule != *pNewOrChangedNumRule )
{ // change existing numbering rule if (pUndo)
{
pUndo->SaveOldNumRule( *pNewOrChangedNumRule );
}
::lcl_ChgNumRule( *this, rRule ); if (pUndo)
{
pUndo->SetLRSpaceEndPos();
}
}
if (!(eMode & SetNumRuleMode::DontSetItem))
{ if (eMode & SetNumRuleMode::CreateNewList)
{ if ( bNewNumRuleCreated )
{ // apply list id of list, which has been created for the new list style
sListId = pNewOrChangedNumRule->GetDefaultListId();
} else
{ // create new list and apply its list id const SwList* pNewList = getIDocumentListsAccess().createList( OUString(), pNewOrChangedNumRule->GetName() );
OSL_ENSURE( pNewList, "<SwDoc::SetNumRule(..)> - could not create new list. Serious defect." );
sListId = pNewList->GetListId();
}
} elseif ( !sContinuedListId.isEmpty() )
{ // apply given list id
sListId = sContinuedListId;
} if (!sListId.isEmpty())
{
getIDocumentContentOperations().InsertPoolItem(aPam,
SfxStringItem(RES_PARATR_LIST_ID, sListId),
SetAttrMode::DEFAULT, pLayout);
}
}
if (!aPam.HasMark())
{
SwTextNode * pTextNd = aPam.GetPoint()->GetNode().GetTextNode(); // robust code: consider case that the PaM doesn't denote a text node - e.g. it denotes a graphic node if ( pTextNd != nullptr )
{
assert(!pLayout || sw::IsParaPropsNode(*pLayout, *pTextNd));
SwNumRule * pRule = pTextNd->GetNumRule();
if (pRule && pRule->GetName() == pNewOrChangedNumRule->GetName())
{
eMode |= SetNumRuleMode::DontSetItem; if ( !pTextNd->IsInList() )
{
pTextNd->AddToList();
}
} // Only clear numbering attribute at text node, if at paragraph // style the new numbering rule is found. elseif ( !pRule )
{
SwTextFormatColl* pColl = pTextNd->GetTextColl(); if ( pColl )
{
SwNumRule* pCollRule = FindNumRulePtr(pColl->GetNumRule().GetValue()); if ( pCollRule && pCollRule->GetName() == pNewOrChangedNumRule->GetName() )
{
pTextNd->ResetAttr( RES_PARATR_NUMRULE );
eMode |= SetNumRuleMode::DontSetItem;
}
}
}
}
}
if (!(eMode & SetNumRuleMode::DontSetItem))
{ if (eMode & SetNumRuleMode::DontSetIfAlreadyApplied)
{ for (SwNodeIndex i = aPam.Start()->nNode; i <= aPam.End()->nNode; ++i)
{ if (SwTextNode const*const pNode = i.GetNode().GetTextNode())
{ if (pNode->GetNumRule(true) != pNewOrChangedNumRule)
{ // only apply if it doesn't already have it - to // avoid overriding indents from style
SwPaM const temp(*pNode, 0, *pNode, pNode->Len());
getIDocumentContentOperations().InsertPoolItem(temp,
SwNumRuleItem(pNewOrChangedNumRule->GetName()),
SetAttrMode::DEFAULT, pLayout); // apply provided margins to get visually same result if (pTextLeftMarginToPropagate)
{
getIDocumentContentOperations().InsertPoolItem(temp,
*pTextLeftMarginToPropagate,
SetAttrMode::DEFAULT, pLayout);
} if (pFirstLineIndentToPropagate)
{
getIDocumentContentOperations().InsertPoolItem(temp,
*pFirstLineIndentToPropagate,
SetAttrMode::DEFAULT, pLayout);
}
}
}
}
} else
{
getIDocumentContentOperations().InsertPoolItem(aPam,
SwNumRuleItem(pNewOrChangedNumRule->GetName()),
SetAttrMode::DEFAULT, pLayout); if (pTextLeftMarginToPropagate)
{
getIDocumentContentOperations().InsertPoolItem(aPam,
*pTextLeftMarginToPropagate,
SetAttrMode::DEFAULT, pLayout);
} if (pFirstLineIndentToPropagate)
{
getIDocumentContentOperations().InsertPoolItem(aPam,
*pFirstLineIndentToPropagate,
SetAttrMode::DEFAULT, pLayout);
}
}
}
if ( !pTextNd->HasAttrListRestartValue() ||
pTextNd->GetAttrListRestartValue() != nStt )
{ if (GetIDocumentUndoRedo().DoesUndo())
{
GetIDocumentUndoRedo().AppendUndo(
std::make_unique<SwUndoNumRuleStart>(rPos, nStt) );
}
pTextNd->SetAttrListRestartValue( nStt );
getIDocumentState().SetModified();
}
}
// We can only delete if the Rule is unused! bool SwDoc::DelNumRule( const UIName& rName, bool bBroadcast )
{
sal_uInt16 nPos = FindNumRule( rName );
if (nPos == USHRT_MAX) returnfalse;
if ( (*mpNumRuleTable)[ nPos ] == GetOutlineNumRule() )
{
OSL_FAIL( "<SwDoc::DelNumRule(..)> - No deletion of outline list style. This is serious defect" ); returnfalse;
}
if( !bRet && !bOverUpper && pLast ) // do not iterate over higher numbers, but still to the end
{ if( bNext )
rPos.Assign(aIdx); else
rPos.Assign( *pLast );
bRet = true;
}
// - the start node has also been investigated, if requested. const SwNode * pNode = nullptr; do
{ if ( !bInvestigateStartNode )
{ if (bForward)
sw::GotoNextLayoutTextFrame(aIdx, pLayout); else
sw::GotoPrevLayoutTextFrame(aIdx, pLayout);
}
if (aIdx.GetNode().IsTextNode())
{
pTextNd = aIdx.GetNode().GetTextNode();
const SwNumRule * pNumRule = pTextNd->GetNumRule(); if (pNumRule)
{ if ( ( pNumRule->IsOutlineRule() == bOutline ) &&
( ( bNum && pNumRule->Get(0).IsEnumeration()) ||
( !bNum && pNumRule->Get(0).IsItemize() ) ) ) // #i22362#, #i29560#
{
pResult = pNumRule; // provide also the list id, to which the text node belongs.
sListId = pTextNd->GetListId(); // also get the margins that override the numrule intconst nListLevel{pTextNd->GetActualListLevel()}; if ((o_ppTextLeftMargin || o_ppFirstLineIndent)
&& 0 <= nListLevel
&& pNumRule->Get(o3tl::narrowing<sal_uInt16>(nListLevel))
.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT)
{
::sw::ListLevelIndents const indents{pTextNd->AreListLevelIndentsApplicable()}; if (!(indents & ::sw::ListLevelIndents::LeftMargin)
&& o_ppTextLeftMargin)
{
*o_ppTextLeftMargin = &pTextNd->SwContentNode::GetAttr(RES_MARGIN_TEXTLEFT);
} if (!(indents & ::sw::ListLevelIndents::FirstLine)
&& o_ppFirstLineIndent)
{
*o_ppFirstLineIndent = &pTextNd->SwContentNode::GetAttr(RES_MARGIN_FIRSTLINE);
}
}
}
if (bOnlyOutline)
bRet = OutlineUpDown(rPam, nDiff, pLayout); elseif (bOnlyNonOutline)
{ /* #i24560# Only promote or demote if all selected paragraphs are promotable resp. demotable.
*/ for (SwNodeOffset nTmp = nStt; nTmp <= nEnd; ++nTmp)
{
SwTextNode* pTNd = GetNodes()[ nTmp ]->GetTextNode();
// Make code robust: consider case that the node doesn't denote a // text node. if ( pTNd )
{ if (pLayout)
{
pTNd = sw::GetParaPropsNode(*pLayout, *pTNd);
}
// this function doesn't contain any numbering-related code, but it is // primarily called to move numbering-relevant paragraphs around, hence // it will expand its selection to include full SwTextFrames. bool SwDoc::MoveParagraph(SwPaM& rPam, SwNodeOffset nOffset, boolconst bIsOutlMv)
{
MakeAllOutlineContentTemporarilyVisible a(this);
// sw_redlinehide: as long as a layout with Hide mode exists, only // move nodes that have merged frames *completely*
SwRootFrame const* pLayout(nullptr); for (SwRootFrame const*const pLay : GetAllLayouts())
{ if (pLay->HasMergedParas())
{
pLayout = pLay;
}
} if (pLayout)
{
std::pair<SwTextNode *, SwTextNode *> nodes(
sw::GetFirstAndLastNode(*pLayout, rPam.Start()->GetNode())); if (nodes.first && nodes.first != &rPam.Start()->GetNode())
{
assert(nodes.second); if (nOffset < SwNodeOffset(0))
{
nOffset += rPam.Start()->GetNodeIndex() - nodes.first->GetIndex(); if (SwNodeOffset(0) <= nOffset) // hack: there are callers that know what
{ // node they want; those should never need
nOffset = SwNodeOffset(-1); // this; other callers just pass in -1
} // and those should still move
} if (!rPam.HasMark())
{
rPam.SetMark();
}
assert(nodes.first->GetIndex() < rPam.Start()->GetNodeIndex());
rPam.Start()->Assign(*nodes.first);
}
nodes = sw::GetFirstAndLastNode(*pLayout, rPam.End()->GetNode()); if (nodes.second && nodes.second != &rPam.End()->GetNode())
{
assert(nodes.first); if (SwNodeOffset(0) < nOffset)
{
nOffset -= nodes.second->GetIndex() - rPam.End()->GetNodeIndex(); if (nOffset <= SwNodeOffset(0)) // hack: there are callers that know what
{ // node they want; those should never need
nOffset = SwNodeOffset(+1); // this; other callers just pass in +1
} // and those should still move
} if (!rPam.HasMark())
{
rPam.SetMark();
}
assert(rPam.End()->GetNodeIndex() < nodes.second->GetIndex()); // until end, otherwise Impl will detect overlapping redline
rPam.End()->Assign(*nodes.second, nodes.second->GetTextNode()->Len());
}
if (nOffset > SwNodeOffset(0))
{ // sw_redlinehide: avoid moving into delete redline, skip forward if (GetNodes().GetEndOfContent().GetIndex() <= rPam.End()->GetNodeIndex() + nOffset)
{ returnfalse; // can't move
}
SwNode const* pNode(GetNodes()[rPam.End()->GetNodeIndex() + nOffset + 1]); if ( pNode->GetRedlineMergeFlag() != SwNode::Merge::None
&& pNode->GetRedlineMergeFlag() != SwNode::Merge::First)
{ for ( ; ; ++nOffset)
{
pNode = GetNodes()[rPam.End()->GetNodeIndex() + nOffset]; if (pNode->IsTextNode())
{
nodes = GetFirstAndLastNode(*pLayout, *pNode->GetTextNode());
assert(nodes.first && nodes.second);
nOffset += nodes.second->GetIndex() - pNode->GetIndex(); // on last; will be incremented below to behind-last break;
}
}
}
} else
{ // sw_redlinehide: avoid moving into delete redline, skip backward if (rPam.Start()->GetNodeIndex() + nOffset < SwNodeOffset(1))
{ returnfalse; // can't move
}
SwNode const* pNode(GetNodes()[rPam.Start()->GetNodeIndex() + nOffset]); if ( pNode->GetRedlineMergeFlag() != SwNode::Merge::None
&& pNode->GetRedlineMergeFlag() != SwNode::Merge::First)
{ for ( ; ; --nOffset)
{
pNode = GetNodes()[rPam.Start()->GetNodeIndex() + nOffset]; if (pNode->IsTextNode())
{
nodes = GetFirstAndLastNode(*pLayout, *pNode->GetTextNode());
assert(nodes.first && nodes.second);
nOffset -= pNode->GetIndex() - nodes.first->GetIndex(); // on first break;
}
}
}
}
} return MoveParagraphImpl(rPam, nOffset, bIsOutlMv, pLayout);
}
// Here are some sophisticated checks whether the wished PaM will be moved or not. // For moving outlines (bIsOutlMv) I've already done some checks, so here are two different // checks...
SwNode *pTmp1;
SwNode *pTmp2; if( bIsOutlMv )
{ // For moving chapters (outline) the following reason will deny the move: // if a start node is inside the moved range and its end node outside or vice versa. // If a start node is the first moved paragraph, its end node has to be within the moved // range, too (e.g. as last node). // If an end node is the last node of the moved range, its start node has to be a part of // the moved section, too.
pTmp1 = GetNodes()[ nStIdx ]; if( pTmp1->IsStartNode() )
{ // coverity[copy_paste_error : FALSE] - First is a start node
pTmp2 = pTmp1->EndOfSectionNode(); if( pTmp2->GetIndex() > nEndIdx ) returnfalse; // Its end node is behind the moved range
}
pTmp1 = pTmp1->StartOfSectionNode()->EndOfSectionNode(); if( pTmp1->GetIndex() <= nEndIdx ) returnfalse; // End node inside but start node before moved range => no.
pTmp1 = GetNodes()[ nEndIdx ]; if( pTmp1->IsEndNode() )
{ // The last one is an end node
pTmp1 = pTmp1->StartOfSectionNode(); if( pTmp1->GetIndex() < nStIdx ) returnfalse; // Its start node is before the moved range.
}
pTmp1 = pTmp1->StartOfSectionNode(); if( pTmp1->GetIndex() >= nStIdx ) returnfalse; // A start node which ends behind the moved range => no.
}
if( !bIsOutlMv )
{ // And here the restrictions for moving paragraphs other than chapters (outlines) // The plan is to exchange [nStIdx,nInEndIdx] and [nStartIdx,nEndIdx] // It will checked if the both "start" nodes as well as the both "end" notes belongs to // the same start-end-section. This is more restrictive than the conditions checked above. // E.g. a paragraph will not escape from a section or be inserted to another section.
pTmp1 = GetNodes()[ nStIdx ]->StartOfSectionNode();
pTmp2 = GetNodes()[ nInStIdx ]->StartOfSectionNode(); if( pTmp1 != pTmp2 ) returnfalse; // "start" nodes in different sections
pTmp1 = GetNodes()[ nEndIdx ]; bool bIsEndNode = pTmp1->IsEndNode(); if( !pTmp1->IsStartNode() )
{
pTmp1 = pTmp1->StartOfSectionNode(); if( bIsEndNode ) // For end nodes the first start node is of course inside the range,
pTmp1 = pTmp1->StartOfSectionNode(); // I've to check the start node of the start node.
}
pTmp1 = pTmp1->EndOfSectionNode();
pTmp2 = GetNodes()[ nInEndIdx ]; if( !pTmp2->IsStartNode() )
{
bIsEndNode = pTmp2->IsEndNode();
pTmp2 = pTmp2->StartOfSectionNode(); if( bIsEndNode )
pTmp2 = pTmp2->StartOfSectionNode();
}
pTmp2 = pTmp2->EndOfSectionNode(); if( pTmp1 != pTmp2 ) returnfalse; // The "end" notes are in different sections
}
// Test for Redlining - Can the Selection be moved at all, actually? if( !getIDocumentRedlineAccess().IsIgnoreRedline() )
{
SwRedlineTable::size_type nRedlPos = getIDocumentRedlineAccess().GetRedlinePos( pStart->GetNode(), RedlineType::Delete ); if( SwRedlineTable::npos != nRedlPos )
{
SwContentNode* pCNd = pEnd->GetNode().GetContentNode();
SwPosition aStPos( pStart->GetNode() );
SwPosition aEndPos( pEnd->GetNode(), pCNd, pCNd ? pCNd->Len() : 1 ); bool bCheckDel = true;
// There is a some Redline Delete Object for the range for( ; nRedlPos < getIDocumentRedlineAccess().GetRedlineTable().size(); ++nRedlPos )
{ const SwRangeRedline* pTmp = getIDocumentRedlineAccess().GetRedlineTable()[ nRedlPos ]; if( !bCheckDel || RedlineType::Delete == pTmp->GetType() )
{ auto [pRStt, pREnd] = pTmp->StartEnd(); // SwPosition* switch( ComparePosition( *pRStt, *pREnd, aStPos, aEndPos ))
{ case SwComparePosition::CollideStart: case SwComparePosition::Behind: // Pos1 comes after Pos2
nRedlPos = getIDocumentRedlineAccess().GetRedlineTable().size(); break;
case SwComparePosition::CollideEnd: case SwComparePosition::Before: // Pos1 comes before Pos2 break; case SwComparePosition::Inside: // Pos1 is completely inside Pos2 // that's valid, but check all following for overlapping
bCheckDel = false; break;
case SwComparePosition::Outside: // Pos2 is completely inside Pos1 case SwComparePosition::Equal: // Pos1 is equal to Pos2 case SwComparePosition::OverlapBefore: // Pos1 overlaps Pos2 in the beginning case SwComparePosition::OverlapBehind: // Pos1 overlaps Pos2 at the end returnfalse;
}
}
}
}
}
{ // Send DataChanged before moving. We then can detect // which objects are still in the range. // After the move they could come before/after the // Position.
SwDataChanged aTmp( rPam );
}
SwRangeRedline* pOwnRedl = nullptr; if( getIDocumentRedlineAccess().IsRedlineOn() )
{ // If the range is completely in the own Redline, we can move it!
SwRedlineTable::size_type nRedlPos = getIDocumentRedlineAccess().GetRedlinePos( pStart->GetNode(), RedlineType::Insert ); if( SwRedlineTable::npos != nRedlPos )
{
SwRangeRedline* pTmp = getIDocumentRedlineAccess().GetRedlineTable()[ nRedlPos ]; auto [pRStt, pREnd] = pTmp->StartEnd(); // SwPosition*
SwRangeRedline aTmpRedl( RedlineType::Insert, rPam ); const SwContentNode* pCEndNd = pEnd->GetNode().GetContentNode(); // Is completely in the range and is the own Redline too? if( aTmpRedl.IsOwnRedline( *pTmp ) &&
(pRStt->GetNode() < pStart->GetNode() ||
(pRStt->GetNode() == pStart->GetNode() && !pRStt->GetContentIndex()) ) &&
(pEnd->GetNode() < pREnd->GetNode() ||
(pEnd->GetNode() == pREnd->GetNode() &&
pCEndNd ? pREnd->GetContentIndex() == pCEndNd->Len()
: !pREnd->GetContentIndex() )) )
{
pOwnRedl = pTmp; if( nRedlPos + 1 < getIDocumentRedlineAccess().GetRedlineTable().size() )
{
pTmp = getIDocumentRedlineAccess().GetRedlineTable()[ nRedlPos+1 ]; if( *pTmp->Start() == *pREnd ) // then don't!
pOwnRedl = nullptr;
}
if( pOwnRedl &&
( pRStt->GetNode() > aIdx.GetNode() || aIdx > pREnd->GetNode() || // pOwnRedl doesn't start at the beginning of a node, so it's not // possible to resize it to contain the line moved before it
( pRStt->GetNode() == aIdx.GetNode() && pRStt->GetContentIndex() > 0 ) ) )
{ // it's not in itself, so don't move it
pOwnRedl = nullptr;
}
}
}
/* When copying to a non-content node Copy will insert a paragraph before that node and insert before that inserted node. Copy creates an SwUndoInserts that does not cover the extra paragraph. Thus we insert the extra paragraph ourselves, _with_ correct undo
information. */ if (bDelLastPara)
{ /* aInsPos points to the non-content node. Move it to
the previous content node. */
SwPaM aInsPam(aInsPos); constbool bMoved = aInsPam.Move(fnMoveBackward);
OSL_ENSURE(bMoved, "No content node found!");
if (bMoved)
{ /* Append the new node after the content node found. The new position to insert the moved paragraph at is before the inserted
paragraph. */
getIDocumentContentOperations().AppendTextNode(*aInsPam.GetPoint());
aInsPos = *aInsPam.GetPoint();
}
}
// now delete all the delete redlines that were copied #ifndef NDEBUG
size_t nRedlines(getIDocumentRedlineAccess().GetRedlineTable().size()); #endif if (nOffset > SwNodeOffset(0))
assert(oPam->End()->GetNodeIndex() - oPam->Start()->GetNodeIndex() + nOffset == aInsPos.GetNodeIndex() - oPam->End()->GetNodeIndex()); else
assert(oPam->Start()->GetNodeIndex() - oPam->End()->GetNodeIndex() + nOffset == aInsPos.GetNodeIndex() - oPam->End()->GetNodeIndex());
SwRedlineTable::size_type i;
getIDocumentRedlineAccess().GetRedline(*oPam->End(), &i); for ( ; 0 < i; --i)
{ // iterate backwards and offset via the start nodes difference
SwRangeRedline const*const pRedline = getIDocumentRedlineAccess().GetRedlineTable()[i - 1]; if (*pRedline->End() < *oPam->Start())
{ break;
} if (pRedline->GetType() == RedlineType::Delete && // tdf#145066 skip full-paragraph deletion which was jumped over // in Show Changes mode to avoid of deleting an extra row
*oPam->Start() <= *pRedline->Start())
{
SwRangeRedline* pNewRedline;
{
SwPaM pam(*pRedline, nullptr);
SwNodeOffset const nCurrentOffset(
nOrigIdx - oPam->Start()->GetNodeIndex());
pam.GetPoint()->Assign(pam.GetPoint()->GetNodeIndex() + nCurrentOffset,
pam.GetPoint()->GetContentIndex());
pam.GetMark()->Assign(pam.GetMark()->GetNodeIndex() + nCurrentOffset,
pam.GetMark()->GetContentIndex());
pNewRedline = new SwRangeRedline( RedlineType::Delete, pam, nMovedID );
} // note: effectively this will DeleteAndJoin the pam!
getIDocumentRedlineAccess().AppendRedline(pNewRedline, true);
assert(getIDocumentRedlineAccess().GetRedlineTable().size() <= nRedlines);
}
}
if( bDelLastPara )
{ // We need to remove the last empty Node again
aIdx = aInsPos.GetNode();
SwContentNode* pCNd = SwNodes::GoPrevious( &aInsPos ); if (pCNd)
aInsPos.AssignEndIndex( *pCNd );
// All, that are in the to-be-deleted Node, need to be // moved to the next Node for(SwRangeRedline* pTmp : getIDocumentRedlineAccess().GetRedlineTable())
{
SwPosition* pPos = &pTmp->GetBound(); if( pPos->GetNode() == aIdx.GetNode() )
{
pPos->Adjust(SwNodeOffset(1));
}
pPos = &pTmp->GetBound(false); if( pPos->GetNode() == aIdx.GetNode() )
{
pPos->Adjust(SwNodeOffset(1));
}
}
CorrRel( aIdx.GetNode(), aInsPos );
RedlineFlags eOld = getIDocumentRedlineAccess().GetRedlineFlags(); if (GetIDocumentUndoRedo().DoesUndo())
{ // this should no longer happen in calls from the UI but maybe via API
SAL_WARN_IF((eOld & RedlineFlags::ShowMask) != RedlineFlags::ShowMask, "sw.core", "redlines will be moved in DeleteAndJoin");
(void) pLayout; // note: move will insert between aIdx-1 and aIdx
assert(!pLayout // check not moving *into* delete redline (caller's fault)
|| aIdx.GetNode().GetRedlineMergeFlag() == SwNode::Merge::None
|| aIdx.GetNode().GetRedlineMergeFlag() == SwNode::Merge::First);
getIDocumentContentOperations().MoveNodeRange( aMvRg, aIdx.GetNode(), SwMoveFlags::REDLINES );
if( pUndo )
{ // i57907: Under circumstances (sections at the end of a chapter) // the rPam.Start() is not moved to the new position. // But aIdx should be at the new end position and as long as the // number of moved paragraphs is nMoved, I know, where the new // position is.
pUndo->SetStartNode( aIdx.GetIndex() - nMoved );
GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.