/* -*- 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(), " - 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, " - 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( !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);
}
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.