Patches contributed by Oliver-Rainer Wittmann sw34bf06: #i117783# - Writer's implementation of XPagePrintable - apply print settings to new printing routines http://svn.apache.org/viewvc?view=revision&revision=1172115 sw34bf06: #o12311627# use <rtl_random> methods to create unique ids for list styles and list ids http://svn.apache.org/viewvc?view=revision&revision=1172112 sw34bf06 #i114725#,#i115828# - method <SwDoc::ClearDoc()> - clear list structures completely http://svn.apache.org/viewvc?view=revision&revision=1172122 i#118572 - remove ui string and help content regarding usage of Java Mail in Writer's Mail Merge as Java Mail is not used. http://svn.apache.org/viewvc?view=revision&revision=1197035 Patches contributed by Mathias Bauer cws mba34issues01: #i117718#: provide filter name in case storage of medium does not allow to detect one http://svn.apache.org/viewvc?view=revision&revision=1172350 cws mba34issues01: #i117721#: directly provide parameters retrieved from SfxMedium http://svn.apache.org/viewvc?view=revision&revision=1172353 gnumake4 work variously http://svn.apache.org/viewvc?view=revision&revision=1394707 http://svn.apache.org/viewvc?view=revision&revision=1394326 http://svn.apache.org/viewvc?view=revision&revision=1396797 http://svn.apache.org/viewvc?view=revision&revision=1397315 cws mba34issues01: #i117723#: convert assertion into trace http://svn.apache.org/viewvc?view=revision&revision=1172355 cws mba34issues01: #i117699#: keep layout alive until swdoc dies http://svn.apache.org/viewvc?view=revision&revision=1172362 cws mba34issues01: #i117943#: missing color attributes in RTF clipboard http://svn.apache.org/viewvc?view=revision&revision=1172363 Patch contributed by Henning Brinkmann imported patch i#103878 http://svn.apache.org/viewvc?view=revision&revision=1172109 Patches contributed by Michael Stahl sw34bf06: #i117955#: WW8 export: disable storing of section breaks in endnotes http://svn.apache.org/viewvc?view=revision&revision=1172119 Patch contributed by imacat Fixed the Asian language work count. http://svn.apache.org/viewvc?view=revision&revision=1241345 Patch contributed by Pedro Giffuni i#20878 - Add comment with BZ issue for reference. http://svn.apache.org/viewvc?view=revision&revision=1244517 Patch contributed by Andre Fischer Do not add targets for junit tests when junit is disabled. http://svn.apache.org/viewvc?view=revision&revision=1241508 add writerperfect dependency.
1274 lines
34 KiB
C++
1274 lines
34 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 <functional>
|
|
#include <SwNumberTree.hxx>
|
|
#include <osl/diagnose.h>
|
|
|
|
using std::vector;
|
|
using std::find;
|
|
|
|
|
|
SwNumberTreeNode::SwNumberTreeNode()
|
|
: mChildren(),
|
|
mpParent( 0 ),
|
|
mnNumber( 0 ),
|
|
mbContinueingPreviousSubTree( false ),
|
|
mbPhantom( false ),
|
|
mItLastValid()
|
|
{
|
|
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 == NULL, ": I'm not supposed to have a parent.");
|
|
|
|
mpParent = (SwNumberTreeNode *) 0xdeadbeef;
|
|
|
|
OSL_ENSURE(mChildren.empty(), "children left!");
|
|
}
|
|
|
|
SwNumberTreeNode * SwNumberTreeNode::CreatePhantom()
|
|
{
|
|
SwNumberTreeNode * pNew = NULL;
|
|
|
|
if (! mChildren.empty() &&
|
|
(*mChildren.begin())->IsPhantom())
|
|
{
|
|
OSL_FAIL("phantom already present");
|
|
}
|
|
else
|
|
{
|
|
pNew = Create();
|
|
pNew->SetPhantom(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 = NULL;
|
|
}
|
|
}
|
|
|
|
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())
|
|
{
|
|
(*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())
|
|
{
|
|
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;
|
|
|
|
SwNumberTree::tSwNumTreeNumber nTmpNumber = 0;
|
|
|
|
do
|
|
{
|
|
if (aIt == mChildren.end())
|
|
{
|
|
aIt = mChildren.begin();
|
|
|
|
nTmpNumber = GetStartValue();
|
|
}
|
|
else
|
|
++aIt;
|
|
|
|
if (aIt != mChildren.end())
|
|
{
|
|
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::ValidateTree()
|
|
{
|
|
if (! IsContinuous())
|
|
{
|
|
{
|
|
tSwNumberTreeChildren::reverse_iterator aIt = mChildren.rbegin();
|
|
|
|
if (aIt != mChildren.rend())
|
|
Validate(*aIt);
|
|
}
|
|
{
|
|
tSwNumberTreeChildren::iterator aIt;
|
|
|
|
for (aIt = mChildren.begin(); aIt != mChildren.end(); ++aIt)
|
|
(*aIt)->ValidateTree();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SwNumberTreeNode * pNode = GetLastDescendant();
|
|
|
|
if (pNode && pNode->mpParent)
|
|
pNode->mpParent->Validate(pNode);
|
|
}
|
|
}
|
|
|
|
void SwNumberTreeNode::_GetNumberVector(vector<SwNumberTree::tSwNumTreeNumber> & 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 __SW_NUMBER_TREE_SANITY_CHECK
|
|
if (! IsSane(false) || ! IsSane(&_rDestNode))
|
|
clog << __FUNCTION__ << "insanity!" << endl;
|
|
#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 = NULL;
|
|
|
|
if (pDest->mChildren.empty())
|
|
pDestLast = pDest->CreatePhantom();
|
|
else
|
|
pDestLast = *pDest->mChildren.rbegin();
|
|
|
|
pMyFirst->MoveChildren(pDestLast);
|
|
|
|
delete pMyFirst;
|
|
mChildren.erase(aItBegin);
|
|
|
|
aItBegin = mChildren.begin();
|
|
}
|
|
|
|
tSwNumberTreeChildren::iterator aIt;
|
|
for (aIt = mChildren.begin(); aIt != mChildren.end(); ++aIt)
|
|
(*aIt)->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 __SW_NUMBER_TREE_SANITY_CHECK
|
|
OSL_ENSURE(IsSane(false) && pDest->IsSane(false), "insanity!");
|
|
#endif
|
|
}
|
|
|
|
void SwNumberTreeNode::AddChild( SwNumberTreeNode * pChild,
|
|
const int nDepth )
|
|
{
|
|
/*
|
|
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 -> please inform OD." );
|
|
return;
|
|
}
|
|
|
|
if ( pChild->GetParent() != NULL || 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()), " unexspected phantom");
|
|
|
|
|
|
if (aInsertDeepIt == mChildren.begin())
|
|
{
|
|
SwNumberTreeNode * pNew = CreatePhantom();
|
|
|
|
SetLastValid(mChildren.end());
|
|
|
|
if (pNew)
|
|
pNew->AddChild(pChild, nDepth - 1);
|
|
}
|
|
else
|
|
{
|
|
--aInsertDeepIt;
|
|
(*aInsertDeepIt)->AddChild(pChild, nDepth - 1);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
pChild->PreAdd();
|
|
std::pair<tSwNumberTreeChildren::iterator, bool> aResult =
|
|
mChildren.insert(pChild);
|
|
|
|
if (aResult.second)
|
|
{
|
|
pChild->mpParent = this;
|
|
bool bNotification = pChild->IsNotificationEnabled();
|
|
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 unnessary 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();
|
|
}
|
|
NotifyInvalidChildren();
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef __SW_NUMBER_TREE_SANITY_CHECK
|
|
if (! IsSane(false))
|
|
clog << __FUNCTION__ << ": insanity!" << endl;
|
|
#endif
|
|
}
|
|
|
|
void SwNumberTreeNode::RemoveChild(SwNumberTreeNode * pChild)
|
|
{
|
|
/*
|
|
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 = NULL;
|
|
|
|
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();
|
|
}
|
|
|
|
// #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();
|
|
}
|
|
else
|
|
{
|
|
OSL_FAIL("RemoveChild: failed!");
|
|
}
|
|
|
|
pChild->PostRemove();
|
|
}
|
|
|
|
void SwNumberTreeNode::RemoveMe()
|
|
{
|
|
if (mpParent)
|
|
{
|
|
SwNumberTreeNode * pSavedParent = mpParent;
|
|
|
|
pSavedParent->RemoveChild(this);
|
|
|
|
while (pSavedParent && pSavedParent->IsPhantom() &&
|
|
pSavedParent->HasOnlyPhantoms())
|
|
pSavedParent = pSavedParent->GetParent();
|
|
|
|
if (pSavedParent)
|
|
pSavedParent->ClearObsoletePhantoms();
|
|
|
|
#ifdef __SW_NUMBER_TREE_SANITY_CHECK
|
|
if (! IsSane(false))
|
|
clog << __FUNCTION__ << ": insanity!" << endl;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
bool SwNumberTreeNode::IsValid() const
|
|
{
|
|
return mpParent ? mpParent->IsValid(this) : false;
|
|
}
|
|
|
|
SwNumberTree::tSwNumTreeNumber SwNumberTreeNode::GetNumber(bool bValidate)
|
|
const
|
|
{
|
|
if (bValidate && mpParent)
|
|
mpParent->Validate(this);
|
|
|
|
return mnNumber;
|
|
}
|
|
|
|
bool SwNumberTreeNode::IsContinueingPreviousSubTree() const
|
|
{
|
|
return mbContinueingPreviousSubTree;
|
|
}
|
|
|
|
|
|
vector<SwNumberTree::tSwNumTreeNumber> 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::IsPhantom() const
|
|
{
|
|
return mbPhantom;
|
|
}
|
|
|
|
void SwNumberTreeNode::SetPhantom(bool _bPhantom)
|
|
{
|
|
mbPhantom = _bPhantom;
|
|
}
|
|
|
|
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 phanton. In this case check, if the first phantom
|
|
// child have only phanton children
|
|
if ( bResult &&
|
|
this != *(GetParent()->mChildren.begin()) &&
|
|
!(*(GetParent()->mChildren.begin()))->HasOnlyPhantoms() )
|
|
{
|
|
bResult = false;
|
|
}
|
|
}
|
|
else
|
|
bResult = false;
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
void SwNumberTreeNode::SetLevelInListTree( const int nLevel )
|
|
{
|
|
if ( nLevel < 0 )
|
|
{
|
|
OSL_FAIL( "<SwNumberTreeNode::SetLevelInListTree(..)> - parameter <nLevel> out of valid range. Serious defect -> please inform OD." );
|
|
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 -> please inform OD." );
|
|
|
|
RemoveMe();
|
|
pRootTreeNode->AddChild( this, nLevel );
|
|
}
|
|
}
|
|
}
|
|
|
|
int SwNumberTreeNode::GetLevelInListTree() const
|
|
{
|
|
if (mpParent)
|
|
return mpParent->GetLevelInListTree() + 1;
|
|
|
|
return -1;
|
|
}
|
|
|
|
SwNumberTreeNode::tSwNumberTreeChildren::size_type
|
|
SwNumberTreeNode::GetChildCount() const
|
|
{
|
|
return mChildren.size();
|
|
}
|
|
|
|
#ifdef __SW_NUMBER_TREE_SANITY_CHECK
|
|
bool SwNumberTreeNode::IsSane(bool bRecursive) const
|
|
{
|
|
vector<const SwNumberTreeNode*> aParents;
|
|
|
|
return IsSane(bRecursive, aParents);
|
|
}
|
|
|
|
bool SwNumberTreeNode::IsSane(bool bRecursive,
|
|
vector<const SwNumberTreeNode *> rParents)
|
|
const
|
|
{
|
|
bool bResult = true;
|
|
|
|
tSwNumberTreeChildren::const_iterator aIt;
|
|
|
|
if (find(rParents.begin(), rParents.end(), this) != rParents.end())
|
|
{
|
|
OSL_FAIL(" I'm my own ancestor!");
|
|
|
|
bResult = false;
|
|
}
|
|
|
|
if (! rParents.empty() && rParents.back() != mpParent)
|
|
{
|
|
OSL_FAIL(" I'm a bastard!");
|
|
|
|
bResult = false;
|
|
}
|
|
|
|
rParents.push_back(this);
|
|
|
|
bool bFirst = true;
|
|
for (aIt = mChildren.begin(); aIt != mChildren.end(); ++aIt)
|
|
{
|
|
if (*aIt)
|
|
{
|
|
if ((*aIt)->IsPhantom())
|
|
{
|
|
if ((*aIt)->HasOnlyPhantoms())
|
|
{
|
|
bResult = false;
|
|
}
|
|
|
|
if (! bFirst)
|
|
{
|
|
OSL_FAIL(" found phantom not at first position.");
|
|
|
|
bResult = false;
|
|
}
|
|
}
|
|
|
|
if ((*aIt)->mpParent != (SwNumberTreeNode *) this)
|
|
{
|
|
OSL_FAIL("found a bastard");
|
|
|
|
bResult = false;
|
|
}
|
|
|
|
if (mpParent)
|
|
{
|
|
if (!(*aIt)->IsPhantom() && (*aIt)->LessThan(*this))
|
|
{
|
|
OSL_FAIL(" found child less than me");
|
|
|
|
bResult = false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
OSL_FAIL("found child that is NULL");
|
|
bResult = false;
|
|
}
|
|
|
|
if (bRecursive)
|
|
bResult = (*aIt)->IsSane(bRecursive, rParents) && bResult;
|
|
}
|
|
|
|
rParents.pop_back();
|
|
|
|
return bResult;
|
|
}
|
|
#endif // __SW_NUMBER_TREE_SANITY_CHECK
|
|
|
|
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 == NULL && pB != NULL)
|
|
bResult = true;
|
|
else if (pA != NULL && pB != NULL)
|
|
bResult = pA->LessThan(*pB);
|
|
|
|
return bResult;
|
|
}
|
|
|
|
SwNumberTreeNode * SwNumberTreeNode::GetLastDescendant() const
|
|
{
|
|
SwNumberTreeNode * pResult = NULL;
|
|
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 = NULL;
|
|
|
|
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 : NULL;
|
|
}
|
|
else
|
|
{
|
|
--aIt;
|
|
|
|
if ( !bSibling )
|
|
pResult = (*aIt)->GetLastDescendant();
|
|
else
|
|
pResult = (*aIt);
|
|
|
|
if (! pResult)
|
|
pResult = (*aIt);
|
|
}
|
|
}
|
|
|
|
return pResult;
|
|
}
|
|
|
|
void SwNumberTreeNode::SetLastValid
|
|
( 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;
|
|
}
|
|
|
|
SetLastValid(bValidating);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SwNumberTreeNode::SetLastValid(bool bValidating) const
|
|
{
|
|
if (mpParent)
|
|
{
|
|
tSwNumberTreeChildren::const_iterator aIt = mpParent->GetIterator(this);
|
|
|
|
mpParent->SetLastValid(aIt, bValidating);
|
|
}
|
|
}
|
|
|
|
void SwNumberTreeNode::InvalidateTree() const
|
|
{
|
|
// do not call SetInvalid, would cause loop !!!
|
|
mItLastValid = mChildren.end();
|
|
|
|
tSwNumberTreeChildren::const_iterator aIt;
|
|
|
|
for (aIt = mChildren.begin(); aIt != mChildren.end(); ++aIt)
|
|
(*aIt)->InvalidateTree();
|
|
}
|
|
|
|
void SwNumberTreeNode::Invalidate(SwNumberTreeNode * 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()
|
|
{
|
|
if (IsNotifiable())
|
|
{
|
|
if (! IsPhantom())
|
|
NotifyNode();
|
|
|
|
tSwNumberTreeChildren::iterator aIt;
|
|
|
|
for (aIt = mChildren.begin(); aIt != mChildren.end(); ++aIt)
|
|
(*aIt)->Notify();
|
|
}
|
|
}
|
|
|
|
void SwNumberTreeNode::NotifyInvalidChildren()
|
|
{
|
|
if (IsNotifiable())
|
|
{
|
|
tSwNumberTreeChildren::const_iterator aIt = mItLastValid;
|
|
|
|
if (aIt == mChildren.end())
|
|
aIt = mChildren.begin();
|
|
else
|
|
++aIt;
|
|
|
|
while (aIt != mChildren.end())
|
|
{
|
|
(*aIt)->Notify();
|
|
|
|
++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();
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (IsContinuous() && mpParent)
|
|
mpParent->NotifyInvalidChildren();
|
|
}
|
|
|
|
void SwNumberTreeNode::NotifyInvalidSiblings()
|
|
{
|
|
if (mpParent != NULL)
|
|
mpParent->NotifyInvalidChildren();
|
|
}
|
|
|
|
// #i81002#
|
|
const SwNumberTreeNode* SwNumberTreeNode::GetPrecedingNodeOf(
|
|
const SwNumberTreeNode& rNode ) const
|
|
{
|
|
const SwNumberTreeNode* pPrecedingNode( 0 );
|
|
|
|
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 == 0 && 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" );
|
|
|
|
SwNumberTreeNode::tSwNumberTreeChildren::iterator aChildIter =
|
|
mChildren.begin();
|
|
while ( aChildIter != mChildren.end() )
|
|
{
|
|
if ( nDepth == 0 )
|
|
{
|
|
(*aChildIter)->NotifyNode();
|
|
}
|
|
else
|
|
{
|
|
(*aChildIter)->NotifyChildrenOnDepth( nDepth - 1 );
|
|
}
|
|
|
|
++aChildIter;
|
|
}
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|