1181 lines
31 KiB
C++
1181 lines
31 KiB
C++
/* -*- 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 *>(sal_uIntPtr(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: */
|