diff options
Diffstat (limited to 'sw/source/core/SwNumberTree')
-rw-r--r-- | sw/source/core/SwNumberTree/SwNodeNum.cxx | 369 | ||||
-rw-r--r-- | sw/source/core/SwNumberTree/SwNumberTree.cxx | 1181 |
2 files changed, 1550 insertions, 0 deletions
diff --git a/sw/source/core/SwNumberTree/SwNodeNum.cxx b/sw/source/core/SwNumberTree/SwNodeNum.cxx new file mode 100644 index 0000000000..9c160d3c0f --- /dev/null +++ b/sw/source/core/SwNumberTree/SwNodeNum.cxx @@ -0,0 +1,369 @@ +/* -*- 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 <osl/diagnose.h> +#include <numrule.hxx> +#include <SwNodeNum.hxx> +#include <ndtxt.hxx> +#include <pam.hxx> +#include <IDocumentListItems.hxx> +#include <doc.hxx> + +SwNodeNum::SwNodeNum(SwTextNode* pTextNode, bool const isHiddenRedlines) + : mpTextNode( pTextNode ) + , mpNumRule( nullptr ) + , m_isHiddenRedlines(isHiddenRedlines) +{ +} + +SwNodeNum::SwNodeNum( SwNumRule* pNumRule ) + : mpTextNode( nullptr ) + , mpNumRule( pNumRule ) + , m_isHiddenRedlines(false) +{ +} + +SwNodeNum::~SwNodeNum() +{ +} + + +void SwNodeNum::ChangeNumRule( SwNumRule& rNumRule ) +{ + OSL_ENSURE( GetNumRule() && GetTextNode(), + "<SwNodeNum::ChangeNumRule(..)> - missing list style and/or text node. Serious defect -> please inform OD." ); + if ( GetNumRule() && GetTextNode() ) + { + GetNumRule()->RemoveTextNode( *(GetTextNode()) ); + } + + mpNumRule = &rNumRule; + + if ( GetNumRule() && GetTextNode() ) + { + GetNumRule()->AddTextNode( *(GetTextNode()) ); + } +} + +SwPosition SwNodeNum::GetPosition() const +{ + OSL_ENSURE( GetTextNode(), + "<SwNodeNum::GetPosition()> - no text node set at <SwNodeNum> instance" ); + return SwPosition(*mpTextNode); +} + +SwNumberTreeNode * SwNodeNum::Create() const +{ + SwNodeNum * pResult = new SwNodeNum( GetNumRule() ); + + return pResult; +} + +void SwNodeNum::PreAdd() +{ + OSL_ENSURE( GetTextNode(), + "<SwNodeNum::PreAdd()> - no text node set at <SwNodeNum> instance" ); + if ( !GetNumRule() && GetTextNode() ) + { + mpNumRule = GetTextNode()->GetNumRule(); + } + OSL_ENSURE( GetNumRule(), + "<SwNodeNum::PreAdd()> - no list style set at <SwNodeNum> instance" ); + if (!m_isHiddenRedlines && GetNumRule() && GetTextNode()) + { + GetNumRule()->AddTextNode( *(GetTextNode()) ); + } + + if (!m_isHiddenRedlines) + { + if ( GetTextNode() && + GetTextNode()->GetNodes().IsDocNodes() ) + { + GetTextNode()->getIDocumentListItems().addListItem( *this ); + } + } +} + +void SwNodeNum::PostRemove() +{ + OSL_ENSURE( GetTextNode(), + "<SwNodeNum::PostRemove()> - no text node set at <SwNodeNum> instance" ); + OSL_ENSURE( GetNumRule(), + "<SwNodeNum::PostRemove()> - no list style set at <SwNodeNum> instance" ); + + if (!m_isHiddenRedlines && GetTextNode()) + { + GetTextNode()->getIDocumentListItems().removeListItem( *this ); + } + + if ( GetNumRule() ) + { + if (!m_isHiddenRedlines && GetTextNode()) + { + GetNumRule()->RemoveTextNode( *(GetTextNode()) ); + } + mpNumRule = nullptr; + } +} + +bool SwNodeNum::IsNotifiable(const SwDoc& rDoc) const +{ + bool aResult; + + if (const SwTextNode* pTextNode = GetTextNode()) + aResult = pTextNode->IsNotifiable(); + else + aResult = IsNotificationEnabled(rDoc); + + return aResult; +} + +bool SwNodeNum::IsNotificationEnabled(const SwDoc& rDoc) const +{ + bool aResult; + + if (const SwTextNode* pTextNode = GetTextNode()) + aResult = pTextNode->IsNotificationEnabled(); + else + aResult = !rDoc.IsInReading() && !rDoc.IsInDtor(); + + return aResult; +} + +bool SwNodeNum::IsContinuous() const +{ + bool aResult = false; + + // #i64311# + if ( GetNumRule() ) + { + aResult = mpNumRule->IsContinusNum(); + } + else if ( GetParent() ) + { + aResult = GetParent()->IsContinuous(); + } + else + { + OSL_FAIL( "<SwNodeNum::IsContinuous()> - OD debug" ); + } + + return aResult; +} + +bool SwNodeNum::IsCounted() const +{ + bool aResult = false; + + if ( GetTextNode() ) + { + // #i59559# + // <SwTextNode::IsCounted()> determines, if a text node is counted for numbering + aResult = GetTextNode()->IsCountedInList(); + } + else + aResult = SwNumberTreeNode::IsCounted(); + + return aResult; +} + +// #i64010# +bool SwNodeNum::HasCountedChildren() const +{ + return std::any_of(mChildren.begin(), mChildren.end(), + [](SwNumberTreeNode* pNode) { + SwNodeNum* pChild( dynamic_cast<SwNodeNum*>(pNode) ); + OSL_ENSURE( pChild, "<SwNodeNum::HasCountedChildren()> - unexpected type of child" ); + return pChild && (pChild->IsCountedForNumbering() || pChild->HasCountedChildren()); + }); +} +// #i64010# +bool SwNodeNum::IsCountedForNumbering() const +{ + return IsCounted() && + ( IsPhantom() || // phantoms + !GetTextNode() || // root node + GetTextNode()->HasNumber() || // text node + GetTextNode()->HasBullet() ); // text node +} + +void SwNodeNum::NotifyNode() +{ + ValidateMe(); + + if (mpTextNode) + { + mpTextNode->NumRuleChgd(); + } +} + +bool SwNodeNum::LessThan(const SwNumberTreeNode & rNode) const +{ + bool bResult = false; + const SwNodeNum & rTmpNode = static_cast<const SwNodeNum &>(rNode); + + if (mpTextNode == nullptr && rTmpNode.mpTextNode != nullptr) + bResult = true; + else if (mpTextNode != nullptr && rTmpNode.mpTextNode != nullptr) + { + // #i83479# - refactoring + // simplify comparison by comparing the indexes of the text nodes + bResult = ( mpTextNode->GetIndex() < rTmpNode.mpTextNode->GetIndex() ); + } + + return bResult; +} + +bool SwNodeNum::IsRestart() const +{ + bool bIsRestart = false; + + if ( GetTextNode() ) + { + bIsRestart = GetTextNode()->IsListRestart(); + } + + return bIsRestart; +} + +bool SwNodeNum::IsCountPhantoms() const +{ + bool bResult = true; + + // #i64311# + // phantoms aren't counted in consecutive numbering rules + if ( mpNumRule ) + bResult = !mpNumRule->IsContinusNum() && + mpNumRule->IsCountPhantoms(); + else + { + OSL_FAIL( "<SwNodeNum::IsCountPhantoms(): missing numbering rule" ); + } + + return bResult; +} + +SwNumberTree::tSwNumTreeNumber SwNodeNum::GetStartValue() const +{ + SwNumberTree::tSwNumTreeNumber aResult = 1; + + if ( IsRestart() && GetTextNode() ) + { + aResult = GetTextNode()->GetActualListStartValue(); + } + else + { + SwNumRule * pRule = GetNumRule(); + + if (pRule) + { + int nLevel = GetParent() ? GetLevelInListTree() : 0; + + if (nLevel >= 0 && nLevel < MAXLEVEL) + { + const SwNumFormat * pFormat = pRule->GetNumFormat( o3tl::narrowing<sal_uInt16>(nLevel)); + + if (pFormat) + aResult = pFormat->GetStart(); + } + } + } + + return aResult; +} + +void SwNodeNum::HandleNumberTreeRootNodeDelete( SwNodeNum& rNodeNum ) +{ + SwNodeNum* pRootNode = rNodeNum.GetParent() + ? dynamic_cast<SwNodeNum*>(rNodeNum.GetRoot()) + : &rNodeNum; + if ( !pRootNode ) + { + // no root node -> nothing do. + return; + } + + // unregister all number tree node entries, which correspond to a text node, + // about the deletion of the number tree root node. + UnregisterMeAndChildrenDueToRootDelete( *pRootNode ); +} + +void SwNodeNum::UnregisterMeAndChildrenDueToRootDelete( SwNodeNum& rNodeNum ) +{ + const bool bIsPhantom( rNodeNum.IsPhantom() ); + tSwNumberTreeChildren::size_type nAllowedChildCount( 0 ); + bool bDone( false ); + while ( !bDone && + rNodeNum.GetChildCount() > nAllowedChildCount ) + { + SwNodeNum* pChildNode( dynamic_cast<SwNodeNum*>((*rNodeNum.mChildren.begin())) ); + if ( !pChildNode ) + { + OSL_FAIL( "<SwNodeNum::UnregisterMeAndChildrenDueToRootDelete(..)> - unknown number tree node child" ); + ++nAllowedChildCount; + continue; + } + + // Unregistering the last child of a phantom will destroy the phantom. + // Thus <rNodeNum> will be destroyed and access on <rNodeNum> has to + // be suppressed. + if ( bIsPhantom && rNodeNum.GetChildCount() == 1 ) + { + bDone = true; + } + + UnregisterMeAndChildrenDueToRootDelete( *pChildNode ); + } + + if ( bIsPhantom ) + return; + + SwTextNode* pTextNode( rNodeNum.GetTextNode() ); + if ( !pTextNode ) + return; + + pTextNode->RemoveFromList(); + // --> clear all list attributes and the list style + const o3tl::sorted_vector<sal_uInt16> aResetAttrsArray{ + RES_PARATR_LIST_ID, RES_PARATR_LIST_LEVEL, RES_PARATR_LIST_ISRESTART, + RES_PARATR_LIST_RESTARTVALUE, RES_PARATR_LIST_ISCOUNTED, RES_PARATR_NUMRULE + }; + SwPaM aPam( *pTextNode ); + pTextNode->GetDoc().ResetAttrs( aPam, false, + aResetAttrsArray, + false ); +} + +// #i81002# +const SwNodeNum* SwNodeNum::GetPrecedingNodeNumOf( const SwTextNode& rTextNode ) const +{ + const SwNodeNum* pPrecedingNodeNum( nullptr ); + + // #i83479# + SwNodeNum aNodeNumForTextNode( const_cast<SwTextNode*>(&rTextNode), false/*doesn't matter*/ ); + + pPrecedingNodeNum = dynamic_cast<const SwNodeNum*>( + GetRoot() + ? GetRoot()->GetPrecedingNodeOf( aNodeNumForTextNode ) + : GetPrecedingNodeOf( aNodeNumForTextNode ) ); + + return pPrecedingNodeNum; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/SwNumberTree/SwNumberTree.cxx b/sw/source/core/SwNumberTree/SwNumberTree.cxx new file mode 100644 index 0000000000..30cdff34b7 --- /dev/null +++ b/sw/source/core/SwNumberTree/SwNumberTree.cxx @@ -0,0 +1,1181 @@ +/* -*- 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 <algorithm> +#include <SwNumberTree.hxx> +#include <osl/diagnose.h> +#include <sal/log.hxx> + +#include <cassert> + +using std::vector; +using std::find; + +SwNumberTreeNode::SwNumberTreeNode() + : mpParent( nullptr ), + mnNumber( 0 ), + mbContinueingPreviousSubTree( false ), + mbPhantom( false ) +{ + mItLastValid = mChildren.end(); +} + +SwNumberTreeNode::~SwNumberTreeNode() +{ + if (GetChildCount() > 0) + { + if (HasOnlyPhantoms()) + { + delete *mChildren.begin(); + + mChildren.clear(); + mItLastValid = mChildren.end(); + } + else + { + OSL_FAIL("lost children!"); + } + } + + OSL_ENSURE( IsPhantom() || mpParent == nullptr, ": I'm not supposed to have a parent."); + + mpParent = reinterpret_cast<SwNumberTreeNode *>(0xdeadbeef); + + OSL_ENSURE(mChildren.empty(), "children left!"); +} + +SwNumberTreeNode * SwNumberTreeNode::CreatePhantom() +{ + SwNumberTreeNode * pNew = nullptr; + + if (! mChildren.empty() && + (*mChildren.begin())->IsPhantom()) + { + OSL_FAIL("phantom already present"); + } + else + { + pNew = Create(); + pNew->mbPhantom = true; + pNew->mpParent = this; + + std::pair<tSwNumberTreeChildren::iterator, bool> aInsert = + mChildren.insert(pNew); + + if (! aInsert.second) + { + OSL_FAIL("insert of phantom failed!"); + + delete pNew; + pNew = nullptr; + } + } + + return pNew; +} + +SwNumberTreeNode * SwNumberTreeNode::GetRoot() const +{ + SwNumberTreeNode * pResult = mpParent; + + if (pResult) + while (pResult->mpParent) + pResult = pResult->mpParent; + + return pResult; +} + +void SwNumberTreeNode::ClearObsoletePhantoms() +{ + tSwNumberTreeChildren::iterator aIt = mChildren.begin(); + + if (!(aIt != mChildren.end() && (*aIt)->IsPhantom())) + return; + + (*aIt)->ClearObsoletePhantoms(); + + if ((*aIt)->mChildren.empty()) + { + // #i60652# + // Because <mChildren.erase(aIt)> could destroy the element, which + // is referenced by <mItLastValid>, it's needed to adjust + // <mItLastValid> before erasing <aIt>. + SetLastValid(mChildren.end()); + + delete *aIt; + mChildren.erase(aIt); + } +} + +void SwNumberTreeNode::ValidateHierarchical(const SwNumberTreeNode * pNode) const +{ + tSwNumberTreeChildren::const_iterator aValidateIt = + GetIterator(pNode); + + if (aValidateIt == mChildren.end()) + return; + + OSL_ENSURE((*aValidateIt)->mpParent == this, "wrong parent"); + + tSwNumberTreeChildren::const_iterator aIt = mItLastValid; + + // --> + // improvement: + // - Only one time checked for <mChildren.end()>. + // - Less checks for each loop run. + // correction: + // - consider case that current node isn't counted and isn't the first + // child of its parent. In this case the number of last counted child + // of the previous node determines the start value for the following + // children loop, if all children have to be validated and the first + // one doesn't restart the counting. + SwNumberTree::tSwNumTreeNumber nTmpNumber( 0 ); + if (aIt != mChildren.end()) + nTmpNumber = (*aIt)->mnNumber; + else + { + aIt = mChildren.begin(); + (*aIt)->mbContinueingPreviousSubTree = false; + + // determine default start value + // consider the case that the first child isn't counted. + nTmpNumber = (*aIt)->GetStartValue(); + if ( !(*aIt)->IsCounted() && + ( !(*aIt)->HasCountedChildren() || (*aIt)->IsPhantom() ) ) + { + --nTmpNumber; + } + + // determine special start value for the case that first child + // doesn't restart the numbering and the parent node isn't counted + // and isn't the first child. + const bool bParentCounted( IsCounted() && + ( !IsPhantom() || + HasPhantomCountedParent() ) ); + if ( !(*aIt)->IsRestart() && + GetParent() && !bParentCounted ) + { + tSwNumberTreeChildren::const_iterator aParentChildIt = + GetParent()->GetIterator( this ); + while ( aParentChildIt != GetParent()->mChildren.begin() ) + { + --aParentChildIt; + SwNumberTreeNode* pPrevNode( *aParentChildIt ); + if ( pPrevNode->GetChildCount() > 0 ) + { + (*aIt)->mbContinueingPreviousSubTree = true; + nTmpNumber = (*(pPrevNode->mChildren.rbegin()))->GetNumber(); + if ( (*aIt)->IsCounted() && + ( !(*aIt)->IsPhantom() || + (*aIt)->HasPhantomCountedParent() ) ) + { + ++nTmpNumber; + } + break; + } + else if ( pPrevNode->IsCounted() ) + { + break; + } + else + { + // Previous node has no children and is not counted. + // Thus, next turn and check for the previous node. + } + } + } + + (*aIt)->mnNumber = nTmpNumber; + } + + while (aIt != aValidateIt) + { + ++aIt; + (*aIt)->mbContinueingPreviousSubTree = false; + + // --> only for counted nodes the number + // has to be adjusted, compared to the previous node. + // this condition is hold also for nodes, which restart the numbering. + if ( (*aIt)->IsCounted() ) + { + if ((*aIt)->IsRestart()) + nTmpNumber = (*aIt)->GetStartValue(); + else + ++nTmpNumber; + } + + (*aIt)->mnNumber = nTmpNumber; + } + + SetLastValid(aIt, true); +} + +void SwNumberTreeNode::ValidateContinuous(const SwNumberTreeNode * pNode) const +{ + tSwNumberTreeChildren::const_iterator aIt = mItLastValid; + + do + { + if (aIt == mChildren.end()) + { + aIt = mChildren.begin(); + } + else + ++aIt; + + if (aIt != mChildren.end()) + { + SwNumberTree::tSwNumTreeNumber nTmpNumber = 0; + SwNumberTreeNode * pPred = (*aIt)->GetPred(); + + // #i64311# + // correct consideration of phantoms + // correct consideration of restart at a number tree node + if ( pPred ) + { + if ( !(*aIt)->IsCounted() ) + // #i65284# + nTmpNumber = pPred->GetNumber( pPred->GetParent() != (*aIt)->GetParent() ); + else + { + if ( (*aIt)->IsRestart() ) + nTmpNumber = (*aIt)->GetStartValue(); + else + nTmpNumber = pPred->GetNumber( pPred->GetParent() != (*aIt)->GetParent() ) + 1; + } + } + else + { + if ( !(*aIt)->IsCounted() ) + nTmpNumber = GetStartValue() - 1; + else + { + if ( (*aIt)->IsRestart() ) + nTmpNumber = (*aIt)->GetStartValue(); + else + nTmpNumber = GetStartValue(); + } + } + + (*aIt)->mnNumber = nTmpNumber; + } + } + while (aIt != mChildren.end() && *aIt != pNode); + + // #i74748# - applied patch from garnier_romain + // number tree node has to be validated. + SetLastValid( aIt, true ); +} + +void SwNumberTreeNode::Validate(const SwNumberTreeNode * pNode) const +{ + if (! IsValid(pNode)) + { + if (IsContinuous()) + ValidateContinuous(pNode); + else + ValidateHierarchical(pNode); + } +} + +void SwNumberTreeNode::GetNumberVector_(SwNumberTree::tNumberVector & rVector, + bool bValidate) const +{ + if (mpParent) + { + mpParent->GetNumberVector_(rVector, bValidate); + rVector.push_back(GetNumber(bValidate)); + } +} + +SwNumberTreeNode * SwNumberTreeNode::GetFirstNonPhantomChild() +{ + if (IsPhantom()) + return (*mChildren.begin())->GetFirstNonPhantomChild(); + + return this; +} + +/** Moves all children of this node that are greater than a given node + to the destination node. +*/ +void SwNumberTreeNode::MoveGreaterChildren( SwNumberTreeNode& _rCompareNode, + SwNumberTreeNode& _rDestNode ) +{ + if ( mChildren.empty() ) + return; + + // determine first child, which has to move to <_rDestNode> + tSwNumberTreeChildren::iterator aItUpper( mChildren.end() ); + if ((*mChildren.begin())->IsPhantom() && + _rCompareNode.LessThan(*(*mChildren.begin())->GetFirstNonPhantomChild())) + { + aItUpper = mChildren.begin(); + } + else + { + aItUpper = mChildren.upper_bound(&_rCompareNode); + } + + // move children + if (aItUpper != mChildren.end()) + { + tSwNumberTreeChildren::iterator aIt; + for (aIt = aItUpper; aIt != mChildren.end(); ++aIt) + (*aIt)->mpParent = &_rDestNode; + + _rDestNode.mChildren.insert(aItUpper, mChildren.end()); + + // #i60652# + // Because <mChildren.erase(aItUpper, mChildren.end())> could destroy + // the element, which is referenced by <mItLastValid>, it's needed to + // adjust <mItLastValid> before erasing <aIt>. + SetLastValid( mChildren.end() ); + + mChildren.erase(aItUpper, mChildren.end()); + + // #i60652# + if ( !mChildren.empty() ) + { + SetLastValid( --(mChildren.end()) ); + } + } + +#ifdef DBG_UTIL + IsSane(false); + _rDestNode.IsSane(true); +#endif +} + +void SwNumberTreeNode::MoveChildren(SwNumberTreeNode * pDest) +{ + if (! mChildren.empty()) + { + tSwNumberTreeChildren::iterator aItBegin = mChildren.begin(); + SwNumberTreeNode * pMyFirst = *mChildren.begin(); + + // #i60652# + // Because <mChildren.erase(aItBegin)> could destroy the element, + // which is referenced by <mItLastValid>, it's needed to adjust + // <mItLastValid> before erasing <aItBegin>. + SetLastValid(mChildren.end()); + + if (pMyFirst->IsPhantom()) + { + SwNumberTreeNode * pDestLast = nullptr; + + if (pDest->mChildren.empty()) + pDestLast = pDest->CreatePhantom(); + else + pDestLast = *pDest->mChildren.rbegin(); + + pMyFirst->MoveChildren(pDestLast); + + delete pMyFirst; + mChildren.erase(aItBegin); + + aItBegin = mChildren.begin(); + } + + for (auto& rpChild : mChildren) + rpChild->mpParent = pDest; + + pDest->mChildren.insert(mChildren.begin(), mChildren.end()); + mChildren.clear(); + // <stl::set.clear()> destroys all existing iterators. + // Thus, <mItLastValid> is also destroyed and reset becomes necessary + mItLastValid = mChildren.end(); + } + + OSL_ENSURE(mChildren.empty(), "MoveChildren failed!"); + +#ifdef DBG_UTIL + IsSane(false); + pDest->IsSane(false); +#endif +} + +void SwNumberTreeNode::AddChild(SwNumberTreeNode* pChild, + const int nDepth, + const SwDoc& rDoc) +{ + /* + Algorithm: + + Search first child A that is greater than pChild, + A may be the end of children. + If nDepth > 0 then + { + if A is first child then + create new phantom child B at beginning of child list + else + B is A + + Add child to B with depth nDepth - 1. + } + else + { + Insert pNode before A. + + if A has predecessor B then + remove children of B that are greater as A and insert them as + children of A. + } + +*/ + + if ( nDepth < 0 ) + { + OSL_FAIL( "<SwNumberTreeNode::AddChild(..)> - parameter <nDepth> out of valid range. Serious defect." ); + return; + } + + if ( pChild->GetParent() != nullptr || pChild->GetChildCount() > 0 ) + { + OSL_FAIL("only orphans allowed."); + return; + } + + if (nDepth > 0) + { + tSwNumberTreeChildren::iterator aInsertDeepIt = + mChildren.upper_bound(pChild); + + OSL_ENSURE(! (aInsertDeepIt != mChildren.end() && + (*aInsertDeepIt)->IsPhantom()), " unexpected phantom"); + + if (aInsertDeepIt == mChildren.begin()) + { + SwNumberTreeNode * pNew = CreatePhantom(); + + SetLastValid(mChildren.end()); + + if (pNew) + pNew->AddChild(pChild, nDepth - 1, rDoc); + } + else + { + --aInsertDeepIt; + (*aInsertDeepIt)->AddChild(pChild, nDepth - 1, rDoc); + } + + } + else + { + pChild->PreAdd(); + std::pair<tSwNumberTreeChildren::iterator, bool> aResult = + mChildren.insert(pChild); + + if (aResult.second) + { + pChild->mpParent = this; + bool bNotification = pChild->IsNotificationEnabled(rDoc); + tSwNumberTreeChildren::iterator aInsertedIt = aResult.first; + + if (aInsertedIt != mChildren.begin()) + { + tSwNumberTreeChildren::iterator aPredIt = aInsertedIt; + --aPredIt; + + // --> + // Move greater children of previous node to new child. + // This has to be done recursively on the children levels. + // Initialize loop variables <pPrevChildNode> and <pDestNode> + // for loop on children levels. + SwNumberTreeNode* pPrevChildNode( *aPredIt ); + SwNumberTreeNode* pDestNode( pChild ); + while ( pDestNode && pPrevChildNode && + pPrevChildNode->GetChildCount() > 0 ) + { + // move children + pPrevChildNode->MoveGreaterChildren( *pChild, *pDestNode ); + + // prepare next loop: + // - search of last child of <pPrevChildNode + // - If found, determine destination node + if ( pPrevChildNode->GetChildCount() > 0 ) + { + tSwNumberTreeChildren::reverse_iterator aIt = + pPrevChildNode->mChildren.rbegin(); + pPrevChildNode = *aIt; + // determine new destination node + if ( pDestNode->GetChildCount() > 0 ) + { + pDestNode = *(pDestNode->mChildren.begin()); + if ( !pDestNode->IsPhantom() ) + { + pDestNode = pDestNode->mpParent->CreatePhantom(); + } + } + else + { + pDestNode = pDestNode->CreatePhantom(); + } + } + else + { + // ready -> break loop. + break; + } + } + // assure that unnecessary created phantoms at <pChild> are deleted. + pChild->ClearObsoletePhantoms(); + + if ((*aPredIt)->IsValid()) + SetLastValid(aPredIt); + } + else + SetLastValid(mChildren.end()); + + ClearObsoletePhantoms(); + + if( bNotification ) + { + // invalidation of not counted parent + // and notification of its siblings. + if ( !IsCounted() ) + { + InvalidateMe(); + NotifyInvalidSiblings(rDoc); + } + NotifyInvalidChildren(rDoc); + } + } + } + +#ifdef DBG_UTIL + IsSane(false); +#endif +} + +void SwNumberTreeNode::RemoveChild(SwNumberTreeNode* pChild, const SwDoc& rDoc) +{ + /* + Algorithm: + + if pChild has predecessor A then + B is A + else + create phantom child B at beginning of child list + + Move children of pChild to B. + */ + + if (pChild->IsPhantom()) + { + OSL_FAIL("not applicable to phantoms!"); + + return; + } + + tSwNumberTreeChildren::const_iterator aRemoveIt = GetIterator(pChild); + + if (aRemoveIt != mChildren.end()) + { + SwNumberTreeNode * pRemove = *aRemoveIt; + + pRemove->mpParent = nullptr; + + tSwNumberTreeChildren::const_iterator aItPred = mChildren.end(); + + if (aRemoveIt == mChildren.begin()) + { + if (! pRemove->mChildren.empty()) + { + CreatePhantom(); + + aItPred = mChildren.begin(); + } + } + else + { + aItPred = aRemoveIt; + --aItPred; + } + + if (! pRemove->mChildren.empty()) + { + pRemove->MoveChildren(*aItPred); + (*aItPred)->InvalidateTree(); + (*aItPred)->NotifyInvalidChildren(rDoc); + } + + // #i60652# + // Because <mChildren.erase(aRemoveIt)> could destroy the element, + // which is referenced by <mItLastValid>, it's needed to adjust + // <mItLastValid> before erasing <aRemoveIt>. + if (aItPred != mChildren.end() && (*aItPred)->IsPhantom()) + SetLastValid(mChildren.end()); + else + SetLastValid(aItPred); + + mChildren.erase(aRemoveIt); + + NotifyInvalidChildren(rDoc); + } + else + { + OSL_FAIL("RemoveChild: failed!"); + } + + pChild->PostRemove(); +} + +void SwNumberTreeNode::RemoveMe(const SwDoc& rDoc) +{ + if (!mpParent) + return; + + SwNumberTreeNode * pSavedParent = mpParent; + + pSavedParent->RemoveChild(this, rDoc); + + while (pSavedParent && pSavedParent->IsPhantom() && + pSavedParent->HasOnlyPhantoms()) + pSavedParent = pSavedParent->GetParent(); + + if (pSavedParent) + pSavedParent->ClearObsoletePhantoms(); + +#ifdef DBG_UTIL + IsSane(false); +#endif +} + +bool SwNumberTreeNode::IsValid() const +{ + return mpParent && mpParent->IsValid(this); +} + +SwNumberTree::tSwNumTreeNumber SwNumberTreeNode::GetNumber(bool bValidate) + const +{ + if (bValidate && mpParent) + mpParent->Validate(this); + + return mnNumber; +} + + +SwNumberTree::tNumberVector SwNumberTreeNode::GetNumberVector() const +{ + vector<SwNumberTree::tSwNumTreeNumber> aResult; + + GetNumberVector_(aResult); + + return aResult; +} + +bool SwNumberTreeNode::IsValid(const SwNumberTreeNode * pChild) const +{ + bool bResult = false; + + if (mItLastValid != mChildren.end()) + { + if (pChild && pChild->mpParent == this) + { + bResult = ! (*mItLastValid)->LessThan(*pChild); + } + } + + return bResult; +} + + +bool SwNumberTreeNode::HasOnlyPhantoms() const +{ + bool bResult = false; + + if (GetChildCount() == 1) + { + tSwNumberTreeChildren::const_iterator aIt = mChildren.begin(); + + bResult = (*aIt)->IsPhantom() && (*aIt)->HasOnlyPhantoms(); + } + else if (GetChildCount() == 0) + bResult = true; + + return bResult; +} + +bool SwNumberTreeNode::IsCounted() const +{ + return !IsPhantom() || + ( IsCountPhantoms() && HasCountedChildren() ); +} + +bool SwNumberTreeNode::HasPhantomCountedParent() const +{ + bool bRet( false ); + + OSL_ENSURE( IsPhantom(), + "<SwNumberTreeNode::HasPhantomCountedParent()> - wrong usage of method - it's only for phantoms" ); + if ( IsPhantom() && mpParent ) + { + if ( mpParent == GetRoot() ) + { + bRet = true; + } + else if ( !mpParent->IsPhantom() ) + { + bRet = mpParent->IsCounted(); + } + else + { + bRet = mpParent->IsCounted() && mpParent->HasPhantomCountedParent(); + } + } + + return bRet; +} + +bool SwNumberTreeNode::IsFirst(const SwNumberTreeNode * pNode) const +{ + tSwNumberTreeChildren::const_iterator aIt = mChildren.begin(); + + if ((*aIt)->IsPhantom()) + ++aIt; + + return *aIt == pNode; +} + +bool SwNumberTreeNode::IsFirst() const +{ + bool bResult = true; + + if (GetParent()) + { + if (GetParent()->IsFirst(this)) + { + SwNumberTreeNode * pNode = GetParent(); + + while (pNode) + { + if (!pNode->IsPhantom() && pNode->GetParent()) + { + bResult = false; + break; + } + + pNode = pNode->GetParent(); + } + + // If node isn't the first child, it is the second child and the + // first child is a phantom. In this case check, if the first phantom + // child have only phantom children + if ( bResult && + this != *(GetParent()->mChildren.begin()) && + !(*(GetParent()->mChildren.begin()))->HasOnlyPhantoms() ) + { + bResult = false; + } + } + else + bResult = false; + } + + return bResult; +} + +void SwNumberTreeNode::SetLevelInListTree(const int nLevel, const SwDoc& rDoc) +{ + if ( nLevel < 0 ) + { + OSL_FAIL( "<SwNumberTreeNode::SetLevelInListTree(..)> - parameter <nLevel> out of valid range. Serious defect." ); + return; + } + + OSL_ENSURE( GetParent(), + "<SwNumberTreeNode::SetLevelInListTree(..)> - can only be called for number tree nodes in a list tree" ); + if ( GetParent() ) + { + if ( nLevel != GetLevelInListTree() ) + { + SwNumberTreeNode* pRootTreeNode = GetRoot(); + OSL_ENSURE( pRootTreeNode, + "<SwNumberTreeNode::SetLevelInListTree(..)> - no root tree node found. Serious defect." ); + + RemoveMe(rDoc); + pRootTreeNode->AddChild(this, nLevel, rDoc); + } + } +} + +int SwNumberTreeNode::GetLevelInListTree() const +{ + if (mpParent) + return mpParent->GetLevelInListTree() + 1; + + return -1; +} + +SwNumberTreeNode::tSwNumberTreeChildren::size_type +SwNumberTreeNode::GetChildCount() const +{ + return mChildren.size(); +} + +#ifdef DBG_UTIL +void SwNumberTreeNode::IsSane(bool bRecursive) const +{ + vector<const SwNumberTreeNode*> aParents; + + return IsSane(bRecursive, std::move(aParents)); +} + +void SwNumberTreeNode::IsSane(bool bRecursive, + vector<const SwNumberTreeNode *> rParents) + const +{ + assert(find(rParents.begin(), rParents.end(), this) == rParents.end()); + + assert(rParents.empty() || rParents.back() == mpParent); + + rParents.push_back(this); + + bool bFirst = true; + for (const auto& rpChild : mChildren) + { + if (rpChild) + { + if (rpChild->IsPhantom()) + { + SAL_WARN_IF(rpChild->HasOnlyPhantoms(), "sw.core", + "HasOnlyPhantoms: is this an error?"); + + assert(bFirst && "found phantom not at first position."); + } + + assert(rpChild->mpParent == this); + + if (mpParent) + { + assert(rpChild->IsPhantom() || !rpChild->LessThan(*this)); + } + } + else + { + assert(!"found child that is NULL"); + } + + if (bRecursive) + { + rpChild->IsSane(bRecursive, rParents); + } + + bFirst = false; + } + + rParents.pop_back(); +} +#endif // DBG_UTIL + +SwNumberTreeNode::tSwNumberTreeChildren::const_iterator +SwNumberTreeNode::GetIterator(const SwNumberTreeNode * pChild) const +{ + tSwNumberTreeChildren::const_iterator aItResult = + mChildren.find(const_cast<SwNumberTreeNode *>(pChild)); + + OSL_ENSURE( aItResult != mChildren.end(), + "something went wrong getting the iterator for a child"); + + return aItResult; +} + +bool SwNumberTreeNodeLessThan(const SwNumberTreeNode * pA, + const SwNumberTreeNode * pB) +{ + bool bResult = false; + + if (pA == nullptr && pB != nullptr) + bResult = true; + else if (pA != nullptr && pB != nullptr) + bResult = pA->LessThan(*pB); + + return bResult; +} + +SwNumberTreeNode * SwNumberTreeNode::GetLastDescendant() const +{ + SwNumberTreeNode * pResult = nullptr; + tSwNumberTreeChildren::const_reverse_iterator aIt = mChildren.rbegin(); + + if (aIt != mChildren.rend()) + { + pResult = (*aIt)->GetLastDescendant(); + + if (! pResult) + pResult = *aIt; + } + + return pResult; +} + +bool SwNumberTreeNode::LessThan(const SwNumberTreeNode & rTreeNode) const +{ + return this < &rTreeNode; +} + +SwNumberTreeNode * SwNumberTreeNode::GetPred(bool bSibling) const +{ + SwNumberTreeNode * pResult = nullptr; + + if (mpParent) + { + tSwNumberTreeChildren::const_iterator aIt = + mpParent->GetIterator(this); + + if ( aIt == mpParent->mChildren.begin() ) + { + // #i64311# + // root node is no valid predecessor + pResult = mpParent->GetParent() ? mpParent : nullptr; + } + else + { + --aIt; + + if ( !bSibling ) + pResult = (*aIt)->GetLastDescendant(); + else + pResult = (*aIt); + + if (! pResult) + pResult = (*aIt); + } + } + + return pResult; +} + +void SwNumberTreeNode::SetLastValid + ( const SwNumberTreeNode::tSwNumberTreeChildren::const_iterator& aItValid, + bool bValidating ) const +{ + OSL_ENSURE( (aItValid == mChildren.end() || GetIterator(*aItValid) != mChildren.end()), + "last-valid iterator"); + + if ( + bValidating || + aItValid == mChildren.end() || + (mItLastValid != mChildren.end() && + (*aItValid)->LessThan(**mItLastValid)) + ) + { + mItLastValid = aItValid; + // invalidation of children of next not counted is needed + if ( GetParent() ) + { + tSwNumberTreeChildren::const_iterator aParentChildIt = + GetParent()->GetIterator( this ); + ++aParentChildIt; + if ( aParentChildIt != GetParent()->mChildren.end() ) + { + SwNumberTreeNode* pNextNode( *aParentChildIt ); + if ( !pNextNode->IsCounted() ) + { + pNextNode->InvalidateChildren(); + } + } + } + } + + { + if (IsContinuous()) + { + tSwNumberTreeChildren::const_iterator aIt = mItLastValid; + + if (aIt != mChildren.end()) + ++aIt; + else + aIt = mChildren.begin(); + + while (aIt != mChildren.end()) + { + (*aIt)->InvalidateTree(); + + ++aIt; + } + + if (mpParent) + { + mpParent->SetLastValid(mpParent->GetIterator(this), bValidating); + } + } + } +} + +void SwNumberTreeNode::InvalidateTree() const +{ + // do not call SetInvalid, would cause loop !!! + mItLastValid = mChildren.end(); + + for (const auto& rpChild : mChildren) + rpChild->InvalidateTree(); +} + +void SwNumberTreeNode::Invalidate(SwNumberTreeNode const * pChild) +{ + if (pChild->IsValid()) + { + tSwNumberTreeChildren::const_iterator aIt = GetIterator(pChild); + + if (aIt != mChildren.begin()) + --aIt; + else + aIt = mChildren.end(); + + SetLastValid(aIt); + + } +} + +void SwNumberTreeNode::InvalidateMe() +{ + if (mpParent) + mpParent->Invalidate(this); +} + +void SwNumberTreeNode::ValidateMe() +{ + if (mpParent) + mpParent->Validate(this); +} + +void SwNumberTreeNode::Notify(const SwDoc& rDoc) +{ + if (IsNotifiable(rDoc)) + { + if (! IsPhantom()) + NotifyNode(); + + for (auto& rpChild : mChildren) + rpChild->Notify(rDoc); + } +} + +void SwNumberTreeNode::NotifyInvalidChildren(const SwDoc& rDoc) +{ + if (IsNotifiable(rDoc)) + { + tSwNumberTreeChildren::const_iterator aIt = mItLastValid; + + if (aIt == mChildren.end()) + aIt = mChildren.begin(); + else + ++aIt; + + while (aIt != mChildren.end()) + { + (*aIt)->Notify(rDoc); + + ++aIt; + } + // notification of next not counted node is also needed. + if ( GetParent() ) + { + tSwNumberTreeChildren::const_iterator aParentChildIt = + GetParent()->GetIterator( this ); + ++aParentChildIt; + if ( aParentChildIt != GetParent()->mChildren.end() ) + { + SwNumberTreeNode* pNextNode( *aParentChildIt ); + if ( !pNextNode->IsCounted() ) + { + pNextNode->NotifyInvalidChildren(rDoc); + } + } + } + + } + + if (IsContinuous() && mpParent) + mpParent->NotifyInvalidChildren(rDoc); +} + +void SwNumberTreeNode::NotifyInvalidSiblings(const SwDoc& rDoc) +{ + if (mpParent != nullptr) + mpParent->NotifyInvalidChildren(rDoc); +} + +// #i81002# +const SwNumberTreeNode* SwNumberTreeNode::GetPrecedingNodeOf( + const SwNumberTreeNode& rNode ) const +{ + const SwNumberTreeNode* pPrecedingNode( nullptr ); + + if ( GetChildCount() > 0 ) + { + tSwNumberTreeChildren::const_iterator aUpperBoundIt = + mChildren.upper_bound( const_cast<SwNumberTreeNode*>(&rNode) ); + if ( aUpperBoundIt != mChildren.begin() ) + { + --aUpperBoundIt; + pPrecedingNode = (*aUpperBoundIt)->GetPrecedingNodeOf( rNode ); + } + } + + if ( pPrecedingNode == nullptr && GetRoot() ) + { + // <this> node has no children or the given node precedes all its children + // and the <this> node isn't the root node. + // Thus, compare the given node with the <this> node in order to check, + // if the <this> node precedes the given node. + if ( !(rNode.LessThan( *this )) ) + { + pPrecedingNode = this; + } + } + + return pPrecedingNode; +} + +void SwNumberTreeNode::NotifyNodesOnListLevel( const int nListLevel ) +{ + if ( nListLevel < 0 ) + { + OSL_FAIL( "<SwNumberTreeNode::NotifyNodesOnListLevel(..)> - invalid list level provided" ); + return; + } + + SwNumberTreeNode* pRootNode = GetParent() ? GetRoot() : this; + + pRootNode->NotifyChildrenOnDepth( nListLevel ); +} + +void SwNumberTreeNode::NotifyChildrenOnDepth( const int nDepth ) +{ + OSL_ENSURE( nDepth >= 0, + "<SwNumberTreeNode::NotifyChildrenOnDepth(..)> - misusage" ); + + for ( const auto& rpChild : mChildren ) + { + if ( nDepth == 0 ) + { + rpChild->NotifyNode(); + } + else + { + rpChild->NotifyChildrenOnDepth( nDepth - 1 ); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |