/**************************************************************
 * 
 * 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
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_svtools.hxx"

#define _TREELIST_CXX

#ifndef GCC
#endif

#include <svtools/treelist.hxx>

#ifdef DBG_UTIL
// Prueft Integritaet der Liste nach jeder Operation
//#define CHECK_INTEGRITY
#endif


DBG_NAME(SvListEntry);

SvListEntry::SvListEntry()
{
	DBG_CTOR(SvListEntry,0);
	pChilds     = 0;
	pParent     = 0;
	nListPos    = 0;
	nAbsPos     = 0;
}

SvListEntry::SvListEntry( const SvListEntry& rEntry )
{
	DBG_CTOR(SvListEntry,0);
	pChilds  = 0;
	pParent  = 0;
	nListPos &= 0x80000000;
	nListPos |= ( rEntry.nListPos & 0x7fffffff);
	nAbsPos  = rEntry.nAbsPos;
}

SvListEntry::~SvListEntry()
{
	DBG_DTOR(SvListEntry,0);
	if ( pChilds )
	{
		pChilds->DestroyAll();
		delete pChilds;
	}
#ifdef DBG_UTIL
	pChilds     = 0;
	pParent     = 0;
#endif
}

void SvListEntry::Clone( SvListEntry* pSource)
{
	DBG_CHKTHIS(SvListEntry,0);
	nListPos &= 0x80000000;
	nListPos |= ( pSource->nListPos & 0x7fffffff);
	nAbsPos		= pSource->nAbsPos;
}

void SvListEntry::SetListPositions()
{
	if( pChilds )
	{
		SvListEntry	*pEntry = (SvListEntry*)pChilds->First();
		sal_uLong		nCur = 0;
		while ( pEntry )
		{
			pEntry->nListPos &= 0x80000000;
			pEntry->nListPos |= nCur;
			nCur++;
			pEntry = (SvListEntry*)pChilds->Next();
		}
	}
	nListPos &= (~0x80000000);
}


DBG_NAME(SvViewData);

SvViewData::SvViewData()
{
	DBG_CTOR(SvViewData,0);
	nFlags = 0;
	nVisPos = 0;
}

SvViewData::SvViewData( const SvViewData& rData )
{
	DBG_CTOR(SvViewData,0);
	nFlags	= rData.nFlags;
	nFlags &= ~( SVLISTENTRYFLAG_SELECTED | SVLISTENTRYFLAG_FOCUSED );
	nVisPos	= rData.nVisPos;
}

SvViewData::~SvViewData()
{
	DBG_DTOR(SvViewData,0);
#ifdef DBG_UTIL
	nVisPos = 0x12345678;
	nFlags = 0x1234;
#endif
}

void SvTreeEntryList::DestroyAll()
{
	SvListEntry* pPtr = (SvListEntry*)First();
	while( pPtr )
	{
		delete pPtr;
		pPtr = (SvListEntry*)Next();
	}
}


/*************************************************************************
|*
|*    SvTreeList::
|*
|*    Beschreibung
|*    Ersterstellung    17.08.94
|*    Letzte Aenderung  17.08.94
|*
*************************************************************************/

SvTreeList::SvTreeList()
{
	nEntryCount = 0;
	bAbsPositionsValid = sal_False;
	nRefCount = 1;
	pRootItem = new SvListEntry;
	eSortMode = SortNone;
}


/*************************************************************************
|*
|*    SvTreeList::~SvTreeList
|*
|*    Beschreibung
|*    Ersterstellung    17.08.94
|*    Letzte Aenderung  17.08.94
|*
*************************************************************************/

SvTreeList::~SvTreeList()
{
	Clear();
	delete pRootItem;
#ifdef DBG_UTIL
	pRootItem = 0;
#endif
}

/*************************************************************************
|*
|*    SvTreeList::Broadcast
|*
|*    Beschreibung
|*    Ersterstellung    17.08.94
|*    Letzte Aenderung  17.08.94
|*
*************************************************************************/

void SvTreeList::Broadcast( sal_uInt16 nActionId, SvListEntry* pEntry1,
	SvListEntry* pEntry2, sal_uLong nPos )
{
	sal_uLong nViewCount = aViewList.Count();
	for( sal_uLong nCurView = 0; nCurView < nViewCount; nCurView++ )
	{
		SvListView* pView = (SvListView*)aViewList.GetObject( nCurView );
		if( pView )
			pView->ModelNotification( nActionId, pEntry1, pEntry2, nPos );
	}
}

void SvTreeList::InsertView( SvListView* pView)
{
	sal_uLong nPos = aViewList.GetPos( pView );
	if ( nPos == LIST_ENTRY_NOTFOUND )
	{
		aViewList.Insert( pView, LIST_APPEND );
		nRefCount++;
	}
}

void SvTreeList::RemoveView( SvListView* pView )
{
	sal_uLong nPos = aViewList.GetPos( pView );
	if ( nPos != LIST_ENTRY_NOTFOUND )
	{
		aViewList.Remove( pView );
		nRefCount--;
	}
}


// Ein Entry ist sichtbar, wenn alle Parents expandiert sind
sal_Bool SvTreeList::IsEntryVisible( const SvListView* pView, SvListEntry* pEntry ) const
{
	DBG_ASSERT(pView&&pEntry,"IsVisible:Invalid Params");
	sal_Bool bRetVal=sal_False;
	do
	{
		if ( pEntry == pRootItem )
		{
			bRetVal=sal_True;
			break;
		}
		pEntry = pEntry->pParent;
	}  while( pView->IsExpanded( pEntry ) );
	return bRetVal;
}

sal_uInt16 SvTreeList::GetDepth( SvListEntry* pEntry ) const
{
	DBG_ASSERT(pEntry&&pEntry!=pRootItem,"GetDepth:Bad Entry");
	sal_uInt16 nDepth = 0;
	while( pEntry->pParent != pRootItem )
	{
		nDepth++;
		pEntry = pEntry->pParent;
	}
	return nDepth;
}

/*************************************************************************
|*
|*    SvTreeList::
|*
|*    Beschreibung
|*    Ersterstellung    17.08.94
|*    Letzte Aenderung  17.08.94
|*
*************************************************************************/

void SvTreeList::Clear()
{
	Broadcast( LISTACTION_CLEARING );
	SvTreeEntryList* pRootList = pRootItem->pChilds;
	if ( pRootList )
	{
		SvListEntry* pEntry = (SvListEntry*)(pRootList->First());
		while( pEntry )
		{
			delete pEntry;
			pEntry = (SvListEntry*)(pRootList->Next());
		}
		delete pRootItem->pChilds;
		pRootItem->pChilds = 0;
	}
	nEntryCount = 0;
	Broadcast( LISTACTION_CLEARED );
}


/*************************************************************************
|*
|*    SvTreeList::
|*
|*    Beschreibung
|*    Ersterstellung    17.08.94
|*    Letzte Aenderung  17.08.94
|*
*************************************************************************/

sal_Bool SvTreeList::IsChild( SvListEntry* pParent, SvListEntry* pChild ) const
{
	if ( !pParent )
		pParent = pRootItem;

	sal_Bool bIsChild = sal_False;
	SvTreeEntryList* pList = pParent->pChilds;
	if ( !pList )
		return sal_False;
	SvListEntry* pActualChild = (SvListEntry*)(pList->First());
	while( !bIsChild && pActualChild )
	{
		if ( pActualChild == pChild )
			bIsChild = sal_True;
		else
		{
			if ( pActualChild->pChilds )
				bIsChild = IsChild( pActualChild, pChild );
			pActualChild = (SvListEntry*)(pList->Next());
		}
	}
	return bIsChild;
}

sal_uLong SvTreeList::Move(SvListEntry* pSrcEntry,SvListEntry* pTargetParent,sal_uLong nListPos)
{
	// pDest darf Null sein!
	DBG_ASSERT(pSrcEntry,"Entry?");
	if ( !pTargetParent )
		pTargetParent = pRootItem;
	DBG_ASSERT(pSrcEntry!=pTargetParent,"Move:Source=Target");

	Broadcast( LISTACTION_MOVING, pSrcEntry, pTargetParent, nListPos );

	if ( !pTargetParent->pChilds )
		pTargetParent->pChilds = new SvTreeEntryList;
	if ( pSrcEntry == pTargetParent )
		return pSrcEntry->GetChildListPos();

	bAbsPositionsValid = sal_False;

	SvTreeEntryList* pDstList = pTargetParent->pChilds;
	SvTreeEntryList* pSrcList = pSrcEntry->pParent->pChilds;

	// Dummy-Ptr einfuegen, weil nListPos durch das
	// folgende Remove ungueltig werden koennte
	SvListEntry* pDummy = 0; pDstList->Insert( pDummy, nListPos );

	// loeschen
	pSrcList->Remove( pSrcEntry );
	// Hat Parent noch Childs ?
	if ( pSrcList->Count() == 0 )
	{
		// Keine Childs, deshalb Child-List loeschen
		SvListEntry* pParent = pSrcEntry->pParent;
		pParent->pChilds = 0;
		delete pSrcList;
		pSrcList = 0;
	}

	// Parent umsetzen (erst hier, weil wir zum Loeschen
	// der ChildList den alten Parent noch benoetigen!)
	pSrcEntry->pParent = pTargetParent;

	pDstList->Replace( pSrcEntry, pDummy );

	// Listenpositionen in Zielliste korrigieren
	SetListPositions( pDstList );
	if ( pSrcList && (sal_uLong)pSrcList != (sal_uLong)pDstList )
		SetListPositions( pSrcList );

#ifdef CHECK_INTEGRITY
CheckIntegrity();
#endif

	sal_uLong nRetVal = pDstList->GetPos( pSrcEntry );
	DBG_ASSERT(nRetVal==pSrcEntry->GetChildListPos(),"ListPos not valid");
	Broadcast( LISTACTION_MOVED,pSrcEntry,pTargetParent,nRetVal);
	return nRetVal;
}

sal_uLong SvTreeList::Copy(SvListEntry* pSrcEntry,SvListEntry* pTargetParent,sal_uLong nListPos)
{
	// pDest darf Null sein!
	DBG_ASSERT(pSrcEntry,"Entry?");
	if ( !pTargetParent )
		pTargetParent = pRootItem;
	if ( !pTargetParent->pChilds )
		pTargetParent->pChilds = new SvTreeEntryList;

	bAbsPositionsValid = sal_False;

	sal_uLong nCloneCount = 0;
	SvListEntry* pClonedEntry = Clone( pSrcEntry, nCloneCount );
	nEntryCount += nCloneCount;

	SvTreeEntryList* pDstList = pTargetParent->pChilds;
	pClonedEntry->pParent = pTargetParent;		// Parent umsetzen
	pDstList->Insert( pClonedEntry, nListPos ); // Einfuegen
	SetListPositions( pDstList ); // Listenpositionen in Zielliste korrigieren

#ifdef CHECK_INTEGRITY
CheckIntegrity();
#endif
	Broadcast( LISTACTION_INSERTED_TREE, pClonedEntry );
	sal_uLong nRetVal = pDstList->GetPos( pClonedEntry );
	return nRetVal;
}



/*************************************************************************
|*
|*    SvTreeList::
|*
|*    Beschreibung
|*    Ersterstellung    17.08.94
|*    Letzte Aenderung  17.08.94
|*
*************************************************************************/

void SvTreeList::Move( SvListEntry* pSrcEntry, SvListEntry* pDstEntry )
{
	SvListEntry* pParent;
	sal_uLong nPos;

	if ( !pDstEntry )
	{
		pParent = pRootItem;
		nPos = 0UL;
	}
	else
	{
		pParent = pDstEntry->pParent;
		nPos = pDstEntry->GetChildListPos();
		nPos++;  // UNTER (Bildschirm) pDstEntry einfuegen
	}
	Move( pSrcEntry, pParent, nPos );
}

/*************************************************************************
|*
|*    SvTreeList::
|*
|*    Beschreibung
|*    Ersterstellung    17.08.94
|*    Letzte Aenderung  17.08.94
|*
*************************************************************************/

void SvTreeList::Copy( SvListEntry* pSrcEntry, SvListEntry* pDstEntry )
{
	SvListEntry* pParent;
	sal_uLong nPos;

	if ( !pDstEntry )
	{
		pParent = pRootItem;
		nPos = 0UL;
	}
	else
	{
		pParent = pDstEntry->pParent;
		nPos = pDstEntry->GetChildListPos()+1;
	}
	Copy( pSrcEntry, pParent, nPos );
}

/*************************************************************************
|*
|*    SvTreeList::
|*
|*    Beschreibung
|*    Ersterstellung    17.08.94
|*    Letzte Aenderung  17.08.94
|*
*************************************************************************/
void SvTreeList::InsertTree( SvListEntry* pSrcEntry, SvListEntry* pDstEntry)
{
	SvListEntry* pParent;
	sal_uLong nPos;

	if ( !pDstEntry )
	{
		pParent = pRootItem;
		nPos = 0UL;
	}
	else
	{
		pParent = pDstEntry->pParent;
		nPos = pDstEntry->GetChildListPos()+1;
	}
	InsertTree( pSrcEntry, pParent, nPos );
}


void SvTreeList::InsertTree(SvListEntry* pSrcEntry,
	SvListEntry* pTargetParent,sal_uLong nListPos)
{
	DBG_ASSERT(pSrcEntry,"InsertTree:Entry?");
	if ( !pSrcEntry )
		return;

	if ( !pTargetParent )
		pTargetParent = pRootItem;
	if ( !pTargetParent->pChilds )
		pTargetParent->pChilds = new SvTreeEntryList;

	// Sortierung beruecksichtigen
	GetInsertionPos( pSrcEntry, pTargetParent, nListPos );

	bAbsPositionsValid = sal_False;

	pSrcEntry->pParent = pTargetParent; // Parent umsetzen
	SvTreeEntryList* pDstList = pTargetParent->pChilds;
	pDstList->Insert( pSrcEntry, nListPos ); // einfuegen
	SetListPositions(pDstList); // Listenpositionen in Zielliste korrigieren
	nEntryCount += GetChildCount( pSrcEntry );
	nEntryCount++; // der Parent ist ja auch neu

#ifdef CHECK_INTEGRITY
CheckIntegrity();
#endif
	Broadcast(LISTACTION_INSERTED_TREE, pSrcEntry );
}

SvListEntry* SvTreeList::CloneEntry( SvListEntry* pSource ) const
{
	if( aCloneLink.IsSet() )
		return (SvListEntry*)aCloneLink.Call( pSource );
	SvListEntry* pEntry = CreateEntry();
	pSource->Clone( pEntry );
	return pSource;
}

SvListEntry* SvTreeList::CreateEntry() const
{
	return new SvListEntry;
}

/*************************************************************************
|*
|*    SvTreeList::
|*
|*    Beschreibung
|*    Ersterstellung    17.08.94
|*    Letzte Aenderung  17.08.94
|*
*************************************************************************/

SvListEntry* SvTreeList::Clone( SvListEntry* pEntry, sal_uLong& nCloneCount ) const
{
	SvListEntry* pClonedEntry = CloneEntry( pEntry );
	nCloneCount = 1;
	SvTreeEntryList* pChilds = pEntry->pChilds;
	if ( pChilds )
		pClonedEntry->pChilds=CloneChilds(pChilds,pClonedEntry,nCloneCount);
	return pClonedEntry;
}

/*************************************************************************
|*
|*    SvTreeList::
|*
|*    Beschreibung
|*    Ersterstellung    17.08.94
|*    Letzte Aenderung  17.08.94
|*
*************************************************************************/

SvTreeEntryList* SvTreeList::CloneChilds( SvTreeEntryList* pChilds,
									  SvListEntry* pNewParent,
									  sal_uLong& nCloneCount ) const
{
	DBG_ASSERT(pChilds->Count(),"Childs?");
	SvTreeEntryList* pClonedChilds = new SvTreeEntryList;
	SvListEntry* pChild = (SvListEntry*)pChilds->First();
	while ( pChild )
	{
		SvListEntry* pNewChild = CloneEntry( pChild );
		nCloneCount++;
		pNewChild->pParent = pNewParent;
		SvTreeEntryList* pSubChilds = pChild->pChilds;
		if ( pSubChilds )
		{
			pSubChilds = CloneChilds( pSubChilds, pNewChild, nCloneCount );
			pNewChild->pChilds = pSubChilds;
		}

		pClonedChilds->Insert( pNewChild, LIST_APPEND );
		pChild = (SvListEntry*)pChilds->Next();
	}
	return pClonedChilds;
}


/*************************************************************************
|*
|*    SvTreeList::GetChildCount
|*
|*    Beschreibung
|*    Ersterstellung    17.08.94
|*    Letzte Aenderung  17.08.94
|*
*************************************************************************/

sal_uLong SvTreeList::GetChildCount( SvListEntry* pParent ) const
{
	if ( !pParent )
		return GetEntryCount();

	if ( !pParent || !pParent->pChilds)
		return 0;
	sal_uLong nCount = 0;
	sal_uInt16 nRefDepth = GetDepth( pParent );
	sal_uInt16 nActDepth = nRefDepth;
	do
	{
		pParent = Next( pParent, &nActDepth );
		nCount++;
	} while( pParent && nRefDepth < nActDepth );
	nCount--;
	return nCount;
}

/*************************************************************************
|*
|*    SvTreeList::
|*
|*    Beschreibung
|*    Ersterstellung    17.08.94
|*    Letzte Aenderung  17.08.94
|*
*************************************************************************/

sal_uLong SvTreeList::GetVisibleChildCount(const SvListView* pView, SvListEntry* pParent) const
{
	DBG_ASSERT(pView,"GetVisChildCount:No View");
	if ( !pParent )
		pParent = pRootItem;
	if ( !pParent || !pView->IsExpanded(pParent) || !pParent->pChilds )
		return 0;
	sal_uLong nCount = 0;
	sal_uInt16 nRefDepth = GetDepth( pParent );
	sal_uInt16 nActDepth = nRefDepth;
	do
	{
		pParent = NextVisible( pView, pParent, &nActDepth );
		nCount++;
	} while( pParent && nRefDepth < nActDepth );
	nCount--;
	return nCount;
}

sal_uLong SvTreeList::GetChildSelectionCount(const SvListView* pView,SvListEntry* pParent) const
{
	DBG_ASSERT(pView,"GetChildSelCount:No View");
	if ( !pParent )
		pParent = pRootItem;
	if ( !pParent || !pParent->pChilds)
		return 0;
	sal_uLong nCount = 0;
	sal_uInt16 nRefDepth = GetDepth( pParent );
	sal_uInt16 nActDepth = nRefDepth;
	do
	{
		pParent = Next( pParent, &nActDepth );
		if( pParent && pView->IsSelected( pParent ) && nRefDepth < nActDepth)
			nCount++;
	} while( pParent && nRefDepth < nActDepth );
//	nCount--;
	return nCount;
}


/*************************************************************************
|*
|*    SvTreeList::
|*
|*    Beschreibung
|*    Ersterstellung    17.08.94
|*    Letzte Aenderung  17.08.94
|*
*************************************************************************/

SvListEntry* SvTreeList::First() const
{
	if ( nEntryCount )
		return (SvListEntry*)(pRootItem->pChilds->GetObject(0));
	else
		return 0;
}

/*************************************************************************
|*
|*    SvTreeList::Next
|*
|*    Beschreibung
|*    Ersterstellung    17.08.94
|*    Letzte Aenderung  17.08.94
|*
*************************************************************************/
SvListEntry* SvTreeList::Next( SvListEntry* pActEntry, sal_uInt16* pDepth ) const
{
    DBG_ASSERT( pActEntry && pActEntry->pParent, "SvTreeList::Next: invalid entry/parent!" );
    if ( !pActEntry || !pActEntry->pParent )
        return NULL;

	sal_uInt16 nDepth = 0;
	int bWithDepth = sal_False;
	if ( pDepth )
	{
		nDepth = *pDepth;
		bWithDepth = sal_True;
	}

	SvTreeEntryList* pActualList = pActEntry->pParent->pChilds;
	sal_uLong nActualPos = pActEntry->GetChildListPos();

	if ( pActEntry->pChilds /* && pActEntry->pChilds->Count() */ )
	{
		nDepth++;
		pActEntry = (SvListEntry*)(pActEntry->pChilds->GetObject(0));
		if ( bWithDepth )
			*pDepth = nDepth;
		return pActEntry;
	}

	if ( pActualList->Count() > ( nActualPos + 1 ) )
	{
		pActEntry = (SvListEntry*)(pActualList->GetObject( nActualPos + 1 ));
		if ( bWithDepth )
			*pDepth = nDepth;
		return pActEntry;
	}

	SvListEntry* pParent = pActEntry->pParent;
	nDepth--;
	while( pParent != pRootItem && pParent != 0 )
	{
		DBG_ASSERT(pParent!=0,"TreeData corrupt!");
		pActualList = pParent->pParent->pChilds;
		DBG_ASSERT(pActualList,"TreeData corrupt!");
		nActualPos = pParent->GetChildListPos();
		if ( pActualList->Count() > ( nActualPos + 1 ) )
		{
			pActEntry = (SvListEntry*)(pActualList->GetObject( nActualPos + 1 ));
			if ( bWithDepth )
				*pDepth = nDepth;
			return pActEntry;
		}
		pParent = pParent->pParent;
		nDepth--;
	}
	return 0;
}

/*************************************************************************
|*
|*    SvTreeList::Prev
|*
|*    Beschreibung
|*    Ersterstellung    17.08.94
|*    Letzte Aenderung  17.08.94
|*
*************************************************************************/
SvListEntry* SvTreeList::Prev( SvListEntry* pActEntry, sal_uInt16* pDepth ) const
{
	DBG_ASSERT(pActEntry!=0,"Entry?");

	sal_uInt16 nDepth = 0;
	int bWithDepth = sal_False;
	if ( pDepth )
	{
		nDepth = *pDepth;
		bWithDepth = sal_True;
	}

	SvTreeEntryList* pActualList = pActEntry->pParent->pChilds;
	sal_uLong nActualPos = pActEntry->GetChildListPos();

	if ( nActualPos > 0 )
	{
		pActEntry = (SvListEntry*)(pActualList->GetObject( nActualPos - 1 ));
		while( pActEntry->pChilds /* && pActEntry->pChilds->Count() */ )
		{
			pActualList = pActEntry->pChilds;
			nDepth++;
			pActEntry = (SvListEntry*)(pActualList->Last());
		}
		if ( bWithDepth )
			*pDepth = nDepth;
		return pActEntry;
	}
	if ( pActEntry->pParent == pRootItem )
		return 0;

	pActEntry = pActEntry->pParent;

	if ( pActEntry )
	{
		nDepth--;
		if ( bWithDepth )
			*pDepth = nDepth;
		return pActEntry;
	}
	return 0;
}

/*************************************************************************
|*
|*    SvTreeList::
|*
|*    Beschreibung
|*    Ersterstellung    17.08.94
|*    Letzte Aenderung  17.08.94
|*
*************************************************************************/

SvListEntry* SvTreeList::Last( sal_uInt16* /* nDepth */ ) const
{
	SvTreeEntryList* pActList = pRootItem->pChilds;
//	if ( pActList->Count() == 0 )
//		return 0;
	SvListEntry* pEntry = 0;
	while( pActList )
	{
		pEntry = (SvListEntry*)(pActList->Last());
		pActList = pEntry->pChilds;
//		if ( pActList->Count() == 0 )
//			pActList = 0;
	}
	return pEntry;
}

/*************************************************************************
|*
|*    SvTreeList::
|*
|*    Beschreibung
|*    Ersterstellung    17.08.94
|*    Letzte Aenderung  17.08.94
|*
*************************************************************************/

sal_uLong SvTreeList::GetVisiblePos( const SvListView* pView, SvListEntry* pEntry ) const
{
	DBG_ASSERT(pView&&pEntry,"View/Entry?");

	if ( !pView->bVisPositionsValid )
	{
		// damit GetVisibleCount die Positionen aktualisiert
		((SvListView*)pView)->nVisibleCount = 0;
		GetVisibleCount( pView );
	}
	SvViewData* pViewData = pView->GetViewData( pEntry );
	return pViewData->nVisPos;
}

/*************************************************************************
|*
|*    SvTreeList::
|*
|*    Beschreibung
|*    Ersterstellung    17.08.94
|*    Letzte Aenderung  17.08.94
|*
*************************************************************************/

sal_uLong SvTreeList::GetVisibleCount( const SvListView* pView ) const
{
	DBG_ASSERT(pView,"GetVisCount:No View");
	if( !pView->HasViewData() )
		return 0;
	if ( pView->nVisibleCount )
		return pView->nVisibleCount;

	sal_uLong nPos = 0;
	SvListEntry* pEntry = First();  // erster Eintrag immer sichtbar
	while ( pEntry )
	{
		SvViewData* pViewData = pView->GetViewData( pEntry );
		pViewData->nVisPos = nPos;
		nPos++;
		pEntry = NextVisible( pView, pEntry );
	}
#ifdef DBG_UTIL
	if( nPos > 10000000 )
	{
		DBG_ERROR("nVisibleCount bad");
	}
#endif
	((SvListView*)pView)->nVisibleCount = nPos;
	((SvListView*)pView)->bVisPositionsValid = sal_True;
	return nPos;
}


/*************************************************************************
|*
|*    SvTreeList::
|*
|*    Beschreibung
|*    Ersterstellung    17.08.94
|*    Letzte Aenderung  17.08.94
|*
*************************************************************************/

// Funktion geht aus Geschwindigkeitsgruenden davon aus,
// das der uebergebene Eintrag bereits sichtbar ist

SvListEntry* SvTreeList::NextVisible(const SvListView* pView,SvListEntry* pActEntry,sal_uInt16* pActDepth) const
{
	DBG_ASSERT(pView,"NextVisible:No View");
	if ( !pActEntry )
		return 0;

	sal_uInt16 nDepth = 0;
	int bWithDepth = sal_False;
	if ( pActDepth )
	{
		nDepth = *pActDepth;
		bWithDepth = sal_True;
	}

	SvTreeEntryList* pActualList = pActEntry->pParent->pChilds;
	sal_uLong nActualPos = pActEntry->GetChildListPos();

	if ( pView->IsExpanded(pActEntry) )
	{
		DBG_ASSERT(pActEntry->pChilds,"Childs?");
		nDepth++;
		pActEntry = (SvListEntry*)(pActEntry->pChilds->GetObject(0));
		if ( bWithDepth )
			*pActDepth = nDepth;
		return pActEntry;
	}

	nActualPos++;
	if ( pActualList->Count() > nActualPos  )
	{
		pActEntry = (SvListEntry*)(pActualList->GetObject( nActualPos ));
		if ( bWithDepth )
			*pActDepth = nDepth;
		return pActEntry;
	}

	SvListEntry* pParent = pActEntry->pParent;
	nDepth--;
	while( pParent != pRootItem )
	{
		pActualList = pParent->pParent->pChilds;
		nActualPos = pParent->GetChildListPos();
		nActualPos++;
		if ( pActualList->Count() > nActualPos )
		{
			pActEntry = (SvListEntry*)(pActualList->GetObject( nActualPos ));
			if ( bWithDepth )
				*pActDepth = nDepth;
			return pActEntry;
		}
		pParent = pParent->pParent;
		nDepth--;
	}
	return 0;
}


/*************************************************************************
|*
|*    SvTreeList::
|*
|*    Beschreibung
|*    Ersterstellung    17.08.94
|*    Letzte Aenderung  17.08.94
|*
*************************************************************************/

// Funktion geht aus Geschwindigkeitsgruenden davon aus,
// das der uebergebene Eintrag bereits sichtbar ist

SvListEntry* SvTreeList::PrevVisible(const SvListView* pView, SvListEntry* pActEntry, sal_uInt16* pActDepth) const
{
	DBG_ASSERT(pView&&pActEntry,"PrevVis:View/Entry?");

	sal_uInt16 nDepth = 0;
	int bWithDepth = sal_False;
	if ( pActDepth )
	{
		nDepth = *pActDepth;
		bWithDepth = sal_True;
	}

	SvTreeEntryList* pActualList = pActEntry->pParent->pChilds;
	sal_uLong nActualPos = pActEntry->GetChildListPos();

	if ( nActualPos > 0 )
	{
		pActEntry = (SvListEntry*)(pActualList->GetObject( nActualPos - 1 ));
		while( pView->IsExpanded(pActEntry) )
		{
			pActualList = pActEntry->pChilds;
			nDepth++;
			pActEntry = (SvListEntry*)(pActualList->Last());
		}
		if ( bWithDepth )
			*pActDepth = nDepth;
		return pActEntry;
	}

	if ( pActEntry->pParent == pRootItem )
		return 0;

	pActEntry = pActEntry->pParent;
	if ( pActEntry )
	{
		nDepth--;
		if ( bWithDepth )
			*pActDepth = nDepth;
		return pActEntry;
	}
	return 0;
}

/*************************************************************************
|*
|*    SvTreeList::
|*
|*    Beschreibung
|*    Ersterstellung    17.08.94
|*    Letzte Aenderung  17.08.94
|*
*************************************************************************/

SvListEntry* SvTreeList::LastVisible( const SvListView* pView, sal_uInt16* pDepth) const
{
	DBG_ASSERT(pView,"LastVis:No View");
	SvListEntry* pEntry = Last();
	while( pEntry && !IsEntryVisible( pView, pEntry ) )
		pEntry = PrevVisible( pView, pEntry );
	if ( pEntry && pDepth )
		*pDepth = GetDepth( pEntry );
	return pEntry;
}

/*************************************************************************
|*
|*    SvTreeList::
|*
|*    Beschreibung
|*    Ersterstellung    17.08.94
|*    Letzte Aenderung  17.08.94
|*
*************************************************************************/

SvListEntry* SvTreeList::NextVisible(const SvListView* pView,SvListEntry* pEntry,sal_uInt16& nDelta) const
{
	DBG_ASSERT(pView&&pEntry&&IsEntryVisible(pView,pEntry),"NextVis:Wrong Prms/!Vis");

	sal_uLong nVisPos = GetVisiblePos( pView, pEntry );
	// nDelta Eintraege vorhanden ?
	// Beispiel: 0,1,2,3,4,5,6,7,8,9 nVisPos=5 nDelta=7
	//           nNewDelta = 10-nVisPos-1 == 4
	if (  nVisPos+nDelta >= pView->nVisibleCount )
	{
		nDelta = (sal_uInt16)(pView->nVisibleCount-nVisPos);
		nDelta--;
	}
	sal_uInt16 nDeltaTmp = nDelta;
	while( nDeltaTmp )
	{
		pEntry = NextVisible( pView, pEntry );
		nDeltaTmp--;
		DBG_ASSERT(pEntry,"Entry?");
	}
	return pEntry;
}

/*************************************************************************
|*
|*    SvTreeList::
|*
|*    Beschreibung
|*    Ersterstellung    17.08.94
|*    Letzte Aenderung  17.08.94
|*
*************************************************************************/

SvListEntry* SvTreeList::PrevVisible( const SvListView* pView, SvListEntry* pEntry, sal_uInt16& nDelta ) const
{
	DBG_ASSERT(pView&&pEntry&&IsEntryVisible(pView,pEntry),"PrevVis:Parms/!Vis");

	sal_uLong nVisPos = GetVisiblePos( pView, pEntry );
	// nDelta Eintraege vorhanden ?
	// Beispiel: 0,1,2,3,4,5,6,7,8,9 nVisPos=8 nDelta=20
	//           nNewDelta = nNewVisPos
	if (  nDelta > nVisPos )
		nDelta = (sal_uInt16)nVisPos;
	sal_uInt16 nDeltaTmp = nDelta;
	while( nDeltaTmp )
	{
		pEntry = PrevVisible( pView, pEntry );
		nDeltaTmp--;
		DBG_ASSERT(pEntry,"Entry?");
	}
	return pEntry;
}

/*************************************************************************
|*
|*    SvTreeList::
|*
|*    Beschreibung
|*    Ersterstellung    17.08.94
|*    Letzte Aenderung  17.08.94
|*
*************************************************************************/

SvListEntry* SvTreeList::FirstSelected( const SvListView* pView) const
{
	DBG_ASSERT(pView,"FirstSel:No View");
	if( !pView )
		return 0;
	SvListEntry* pActSelEntry = First();
	while( pActSelEntry && !pView->IsSelected(pActSelEntry) )
		pActSelEntry = NextVisible( pView, pActSelEntry );
	return pActSelEntry;
}


SvListEntry* SvTreeList::FirstChild( SvListEntry* pParent ) const
{
	if ( !pParent )
		pParent = pRootItem;
	SvListEntry* pResult;
	if ( pParent->pChilds )
		pResult = (SvListEntry*)(pParent->pChilds->GetObject( 0 ));
	else
		pResult = 0;
	return pResult;
}

SvListEntry* SvTreeList::NextSibling( SvListEntry* pEntry ) const
{
	DBG_ASSERT(pEntry,"Entry?");
	if( !pEntry )
		return 0;
	SvTreeEntryList* pList = pEntry->pParent->pChilds;
//	sal_uLong nPos = pList->GetPos( pEntry );
	sal_uLong nPos = pEntry->GetChildListPos();
	nPos++;
	pEntry = (SvListEntry*)(pList->GetObject( nPos ));
	return pEntry;
}

SvListEntry* SvTreeList::PrevSibling( SvListEntry* pEntry ) const
{
	DBG_ASSERT(pEntry,"Entry?");
	if( !pEntry )
		return 0;

	SvTreeEntryList* pList = pEntry->pParent->pChilds;
	// sal_uLong nPos = pList->GetPos( pEntry );
	sal_uLong nPos = pEntry->GetChildListPos();
	if ( nPos == 0 )
		return 0;
	nPos--;
	pEntry = (SvListEntry*)(pList->GetObject( nPos ));
	return pEntry;
}


SvListEntry* SvTreeList::LastSibling( SvListEntry* pEntry ) const
{
	DBG_ASSERT(pEntry,"LastSibling:Entry?");
	if( !pEntry )
		return 0;
	SvListEntry* pSib = 0;
	SvTreeEntryList* pSibs = pEntry->pParent->pChilds;
	if ( pSibs )
		pSib = (SvListEntry*)(pSibs->Last());
	return pSib;
}



/*************************************************************************
|*
|*    SvTreeList::
|*
|*    Beschreibung
|*    Ersterstellung    17.08.94
|*    Letzte Aenderung  17.08.94
|*
*************************************************************************/

SvListEntry* SvTreeList::NextSelected( const SvListView* pView, SvListEntry* pEntry ) const
{
	DBG_ASSERT(pView&&pEntry,"NextSel:View/Entry?");
	pEntry = Next( pEntry );
	while( pEntry && !pView->IsSelected(pEntry) )
		pEntry = Next( pEntry );
	return pEntry;
}

/*************************************************************************
|*
|*    SvTreeList::
|*
|*    Beschreibung
|*    Ersterstellung    17.08.94
|*    Letzte Aenderung  17.08.94
|*
*************************************************************************/

SvListEntry* SvTreeList::PrevSelected( const SvListView* pView, SvListEntry* pEntry) const
{
	DBG_ASSERT(pView&&pEntry,"PrevSel:View/Entry?");
	pEntry = Prev( pEntry );
	while( pEntry && !pView->IsSelected(pEntry) )
		pEntry = Prev( pEntry );

	return pEntry;
}

/*************************************************************************
|*
|*    SvTreeList::
|*
|*    Beschreibung
|*    Ersterstellung    17.08.94
|*    Letzte Aenderung  17.08.94
|*
*************************************************************************/

SvListEntry* SvTreeList::LastSelected( const SvListView* pView ) const
{
	DBG_ASSERT(pView,"LastSel:No View");
	SvListEntry* pEntry = Last();
	while( pEntry && !pView->IsSelected(pEntry) )
		pEntry = Prev( pEntry );
	return pEntry;
}

/*************************************************************************
|*
|*    SvTreeList::Insert
|*
|*    Beschreibung
|*    Ersterstellung    17.08.94
|*    Letzte Aenderung  17.08.94
|*
*************************************************************************/
sal_uLong SvTreeList::Insert( SvListEntry* pEntry,SvListEntry* pParent,sal_uLong nPos )
{
	DBG_ASSERT( pEntry,"Entry?");

	if ( !pParent )
		pParent = pRootItem;


	SvTreeEntryList* pList = pParent->pChilds;
	if ( !pList )
	{
		// Parent bekommt zum erstenmal ein Kind
		pList = new SvTreeEntryList;
		pParent->pChilds = pList;
	}

	// Sortierung beruecksichtigen
	GetInsertionPos( pEntry, pParent, nPos );

	bAbsPositionsValid = sal_False;
	pEntry->pParent = pParent;

	pList->Insert( pEntry, nPos );
	nEntryCount++;
	if( nPos != LIST_APPEND && (nPos != (pList->Count()-1)) )
		SetListPositions( pList );
	else
		pEntry->nListPos = pList->Count()-1;

#ifdef CHECK_INTEGRITY
CheckIntegrity();
#endif
	Broadcast( LISTACTION_INSERTED, pEntry );
	return nPos; // pEntry->nListPos;
}

/*************************************************************************
|*
|*    SvTreeList::
|*
|*    Beschreibung
|*    Ersterstellung    17.08.94
|*    Letzte Aenderung  17.08.94
|*
*************************************************************************/

sal_uLong SvTreeList::GetAbsPos( SvListEntry* pEntry) const
{
	if ( !bAbsPositionsValid )
		((SvTreeList*)this)->SetAbsolutePositions();
	return pEntry->nAbsPos;
}

/*************************************************************************
|*
|*    SvTreeList::
|*
|*    Beschreibung
|*    Ersterstellung    17.08.94
|*    Letzte Aenderung  17.08.94
|*
*************************************************************************/

void SvTreeList::SetAbsolutePositions()
{
	sal_uLong nPos = 0;
	SvListEntry* pEntry = First();
	while ( pEntry )
	{
		pEntry->nAbsPos = nPos;
		nPos++;
		pEntry = Next( pEntry );
	}
	bAbsPositionsValid = sal_True;
#ifdef CHECK_INTEGRITY
CheckIntegrity();
#endif
}


/*************************************************************************
|*
|*    SvTreeList::Expand
|*
|*    Beschreibung
|*    Ersterstellung    17.08.94
|*    Letzte Aenderung  17.08.94
|*
*************************************************************************/

void SvTreeList::Expand( SvListView* pView, SvListEntry* pEntry )
{
	DBG_ASSERT(pEntry&&pView,"Expand:View/Entry?");
	if ( pView->IsExpanded(pEntry) )
		return;

	DBG_ASSERT(pEntry->pChilds,"Expand:No Childs!");

	SvViewData* pViewData = pView->GetViewData(pEntry);
	pViewData->nFlags |= SVLISTENTRYFLAG_EXPANDED;
	SvListEntry* pParent = pEntry->pParent;
	// wenn Parent sichtbar dann Statusdaten invalidieren
	if ( pView->IsExpanded( pParent ) )
	{
		pView->bVisPositionsValid = sal_False;
		pView->nVisibleCount = 0;
	}
#ifdef CHECK_INTEGRITY
CheckIntegrity();
#endif
}

/*************************************************************************
|*
|*    SvTreeList::Collapse
|*
|*    Beschreibung
|*    Ersterstellung    17.08.94
|*    Letzte Aenderung  17.08.94
|*
*************************************************************************/

void SvTreeList::Collapse( SvListView* pView, SvListEntry* pEntry )
{
	DBG_ASSERT(pView&&pEntry,"Collapse:View/Entry?");
	if ( !pView->IsExpanded(pEntry) )
		return;

	DBG_ASSERT(pEntry->pChilds,"Collapse:No Childs!");

	SvViewData* pViewData = pView->GetViewData( pEntry );
	pViewData->nFlags &=(~SVLISTENTRYFLAG_EXPANDED);

	SvListEntry* pParent = pEntry->pParent;
	if ( pView->IsExpanded(pParent) )
	{
		pView->nVisibleCount = 0;
		pView->bVisPositionsValid = sal_False;
	}
#ifdef CHECK_INTEGRITY
CheckIntegrity();
#endif
}


/*************************************************************************
|*
|*    SvTreeList::
|*
|*    Beschreibung
|*    Ersterstellung    17.08.94
|*    Letzte Aenderung  17.08.94
|*
*************************************************************************/

sal_Bool SvTreeList::Select( SvListView* pView, SvListEntry* pEntry, sal_Bool bSelect )
{
	DBG_ASSERT(pView&&pEntry,"Select:View/Entry?");
	SvViewData* pViewData = pView->GetViewData( pEntry );
	if ( bSelect )
	{
		if ( pViewData->IsSelected() || !pViewData->IsSelectable() )
			return sal_False;
		else
		{
			pViewData->nFlags |= SVLISTENTRYFLAG_SELECTED;
			pView->nSelectionCount++;
		}
	}
	else
	{
		if ( !pViewData->IsSelected() )
			return sal_False;
		else
		{
			pViewData->nFlags &= ~( SVLISTENTRYFLAG_SELECTED );
			pView->nSelectionCount--;
		}
	}
#ifdef CHECK_INTEGRITY
CheckIntegrity();
#endif
	return sal_True;
}

/*************************************************************************
|*
|*    SvTreeList::Remove
|*
|*    Beschreibung
|*    Ersterstellung    17.08.94
|*    Letzte Aenderung  05.04.01
|*
*************************************************************************/
sal_Bool SvTreeList::Remove( SvListEntry* pEntry )
{
	DBG_ASSERT(pEntry,"Cannot remove root, use clear");

	if( !pEntry->pParent )
	{
		DBG_ERROR("Removing entry not in model!");
		// unter gewissen Umstaenden (welche?) loescht der
		// Explorer aus der View Eintraege, die er nicht in die View
		// eingefuegt hat. Da sich der Kunde fuer ein platzendes
		// Office nichts kaufen kann, fange ich diesen Fall ab.
		return sal_False;
	}

	Broadcast( LISTACTION_REMOVING, pEntry );
	sal_uLong nRemoved = 1 + GetChildCount(pEntry);
	bAbsPositionsValid = sal_False;

	SvListEntry* pParent = pEntry->pParent;
	SvTreeEntryList* pList = pParent->pChilds;
	DBG_ASSERT(pList,"Remove:No Childlist");
	sal_Bool bLastEntry = sal_False;

	if ( pEntry->HasChildListPos() )
	{
		sal_uLong nListPos = pEntry->GetChildListPos();
		bLastEntry = (nListPos == (pList->Count()-1) ) ? sal_True : sal_False;
		pList->Remove( nListPos );
	}
	else
	{
		pList->Remove( (void*) pEntry );
	}

	
	// moved to end of method because it is used later with Broadcast
	// delete pEntry; // loescht auch alle Childs 

	if ( pList->Count() == 0 )
	{
		pParent->pChilds = 0;
		delete pList;
	}
	else
	{
		if( !bLastEntry )
			SetListPositions( pList );
	}
	nEntryCount -= nRemoved;

#ifdef CHECK_INTEGRITY
CheckIntegrity();
#endif
	Broadcast( LISTACTION_REMOVED, pEntry );
	
	delete pEntry; // loescht auch alle Childs
	return sal_True;
}

/*************************************************************************
|*
|*    SvTreeList::
|*
|*    Beschreibung
|*    Ersterstellung    17.08.94
|*    Letzte Aenderung  17.08.94
|*
*************************************************************************/

sal_uLong SvTreeList::SelectChilds(SvListView* pView, SvListEntry* pParent,sal_Bool bSelect )
{
	DBG_ASSERT(pView&&pParent,"SelChilds:View/Parent?");
	if ( !pParent->pChilds )
		return 0;
	if ( pParent->pChilds->Count() == 0 )
		return 0;

	sal_uInt16 nRefDepth = GetDepth( pParent );
	sal_uInt16 nDepth = nRefDepth;
	sal_uLong nCount = 0;
	pParent = Next( pParent );
	do
	{
		if ( Select( pView, pParent, bSelect ) )
			nCount++; // nur die tatsaechlichen Selektierungen zaehlen
		pParent = Next( pParent, &nDepth );
	}
	while( pParent && nDepth > nRefDepth );
#ifdef CHECK_INTEGRITY
CheckIntegrity();
#endif
	return nCount;
}

void SvTreeList::SelectAll( SvListView* pView, sal_Bool bSelect )
{
	DBG_ASSERT(pView,"SelectAll:NoView");
	SvListEntry* pEntry = First();
	while ( pEntry )
	{
		SvViewData* pViewData = pView->GetViewData( pEntry );
		if ( bSelect )
			pViewData->nFlags |= SVLISTENTRYFLAG_SELECTED;
		else
			pViewData->nFlags &= (~SVLISTENTRYFLAG_SELECTED);

		pEntry = Next( pEntry );
	}
	if ( bSelect )
		pView->nSelectionCount = nEntryCount;
	else
		pView->nSelectionCount = 0;
#ifdef CHECK_INTEGRITY
CheckIntegrity();
#endif
}


SvListEntry* SvTreeList::GetEntryAtAbsPos( sal_uLong nAbsPos ) const
{
	SvListEntry* pEntry = First();
	while ( nAbsPos && pEntry )
	{
		pEntry = Next( pEntry );
		nAbsPos--;
	}
	return pEntry;
}

SvListEntry* SvTreeList::GetEntryAtVisPos( const SvListView* pView, sal_uLong nVisPos ) const
{
	DBG_ASSERT(pView,"GetEntryAtVisPos:No View");
	SvListEntry* pEntry = First();
	while ( nVisPos && pEntry )
	{
		pEntry = NextVisible( pView, pEntry );
		nVisPos--;
	}
	return pEntry;
}

void SvTreeList::SetListPositions( SvTreeEntryList* pList )
{
	if( pList->Count() )
	{
		SvListEntry* pEntry = (SvListEntry*)(pList->GetObject(0));
		if( pEntry->pParent )
			pEntry->pParent->InvalidateChildrensListPositions();
	}
	/*
	sal_uLong nListPos = 0;
	SvListEntry* pEntry = (SvListEntry*)(pList->First());
	while( pEntry )
	{
		pEntry->nListPos = nListPos;
		nListPos++;
		pEntry = (SvListEntry*)(pList->Next());
	}
	*/
}


void SvTreeList::InvalidateEntry( SvListEntry* pEntry )
{
	Broadcast( LISTACTION_INVALIDATE_ENTRY, pEntry );
}

sal_Bool SvTreeList::IsInChildList( SvListEntry* pParent, SvListEntry* pChild) const
{
	if ( !pParent )
		pParent = pRootItem;
	sal_Bool bIsChild = sal_False;
	if ( pParent->pChilds )
		bIsChild = (sal_Bool)(pParent->pChilds->GetPos(pChild) != LIST_ENTRY_NOTFOUND);
	return bIsChild;
}


void lcl_CheckList( SvTreeEntryList* pList )
{
	SvListEntry* pEntry = (SvListEntry*)(pList->First());
	sal_uLong nPos = 0;
	while ( pEntry )
	{
		DBG_ASSERT(pEntry->GetChildListPos()==nPos,"Wrong ListPos");
		pEntry = (SvListEntry*)(pList->Next());
		nPos++;
	}
}

void SvTreeList::CheckIntegrity() const
{
	sal_uLong nMyEntryCount = 0;
	if ( pRootItem->pChilds )
	{
		lcl_CheckList( pRootItem->pChilds );
		SvListEntry* pEntry = First();
		while( pEntry )
		{
			nMyEntryCount++;
			if ( pEntry->pChilds )
				lcl_CheckList( pEntry->pChilds );
			pEntry = Next( pEntry );
		}
	}
	DBG_ASSERT(nMyEntryCount==GetEntryCount(),"Entry count invalid");
}

SvListEntry* SvTreeList::GetRootLevelParent( SvListEntry* pEntry ) const
{
	DBG_ASSERT(pEntry,"GetRootLevelParent:No Entry");
	SvListEntry* pCurParent = 0;
	if ( pEntry )
	{
		pCurParent = pEntry->pParent;
		if ( pCurParent == pRootItem )
			return pEntry; // ist sein eigener Parent
		while( pCurParent && pCurParent->pParent != pRootItem )
			pCurParent = pCurParent->pParent;
	}
	return pCurParent;
}




//*************************************************************************
//*************************************************************************
//*************************************************************************
//*************************************************************************
//*************************************************************************
//*************************************************************************
//*************************************************************************
//*************************************************************************

DBG_NAME(SvListView);

SvListView::SvListView( SvTreeList* pModell )
{
	DBG_CTOR(SvListView,0);
	pModel = 0;
	nSelectionCount = 0;
	nVisibleCount = 0;
	bVisPositionsValid = sal_False;
	SetModel( pModell );
}

SvListView::SvListView()
{
	DBG_CTOR(SvListView,0);
	pModel = 0;
	nSelectionCount = 0;
	nVisibleCount = 0;
	bVisPositionsValid = sal_False;
}


SvListView::~SvListView()
{
	DBG_DTOR(SvListView,0);
	ClearTable();
}

void SvListView::InitTable()
{
	DBG_CHKTHIS(SvListView,0);
	DBG_ASSERT(pModel,"InitTable:No Model");
	DBG_ASSERT(!nSelectionCount&&!nVisibleCount&&!bVisPositionsValid,"InitTable: Not cleared!");

	if( aDataTable.Count() )
	{
		DBG_ASSERT(aDataTable.Count()==1,"InitTable: TableCount != 1");
		// die im Clear fuer die Root allozierten View-Daten loeschen
		// Achtung: Das zu dem RootEntry (und damit auch der Entry)
		// gehoerende Model kann bereits geloescht sein!
		SvViewData* pViewData = (SvViewData*)aDataTable.GetObject( 0 );
		delete pViewData;
		aDataTable.Clear();
	}

	SvListEntry* pEntry;
	SvViewData* pViewData;

	// RootEntry einfuegen
	pEntry = pModel->pRootItem;
	pViewData = new SvViewData;
	pViewData->nFlags = SVLISTENTRYFLAG_EXPANDED;
	aDataTable.Insert( (sal_uLong)pEntry, pViewData );
	// Jetzt alle anderen Entries
	pEntry = pModel->First();
	while( pEntry )
	{
		pViewData = CreateViewData( pEntry );
		DBG_ASSERT(pViewData,"InitTable:No ViewData");
		InitViewData( pViewData, pEntry );
		aDataTable.Insert( (sal_uLong)pEntry, pViewData );
		pEntry = pModel->Next( pEntry );
	}
}

SvViewData* SvListView::CreateViewData( SvListEntry* )
{
	DBG_CHKTHIS(SvListView,0);
	return new SvViewData;
}

void SvListView::ClearTable()
{
	DBG_CHKTHIS(SvListView,0);
	SvViewData* pViewData = (SvViewData*)aDataTable.First();
	while( pViewData )
	{
		delete pViewData;
		pViewData = (SvViewData*)aDataTable.Next();
	}
	aDataTable.Clear();
}

void SvListView::Clear()
{
	ClearTable();
	nSelectionCount = 0;
	nVisibleCount = 0;
	bVisPositionsValid = sal_False;
	if( pModel )
	{
		// RootEntry einfuegen
		SvListEntry* pEntry = pModel->pRootItem;
		SvViewData* pViewData = new SvViewData;
		pViewData->nFlags = SVLISTENTRYFLAG_EXPANDED;
		aDataTable.Insert( (sal_uLong)pEntry, pViewData );
	}
}

void SvListView::SetModel( SvTreeList* pNewModel )
{
	DBG_CHKTHIS(SvListView,0);
	sal_Bool bBroadcastCleared = sal_False;
	if ( pModel )
	{
		pModel->RemoveView( this );
		bBroadcastCleared = sal_True;
		ModelNotification( LISTACTION_CLEARING,0,0,0 );
		if ( pModel->GetRefCount() == 0 )
			delete pModel;
	}
	pModel = pNewModel;
	InitTable();
	pNewModel->InsertView( this );
	if( bBroadcastCleared )
		ModelNotification( LISTACTION_CLEARED,0,0,0 );
}


void SvListView::ModelHasCleared()
{
	DBG_CHKTHIS(SvListView,0);
}

void SvListView::ModelHasInserted( SvListEntry* )
{
	DBG_CHKTHIS(SvListView,0);
}

void SvListView::ModelHasInsertedTree( SvListEntry* )
{
	DBG_CHKTHIS(SvListView,0);
}

void SvListView::ModelIsMoving( SvListEntry* /*  pSource */ ,
	SvListEntry* /* pTargetParent */ ,	sal_uLong /* nPos */	)
{
	DBG_CHKTHIS(SvListView,0);
}


void SvListView::ModelHasMoved( SvListEntry* )
{
	DBG_CHKTHIS(SvListView,0);
}

void SvListView::ModelIsRemoving( SvListEntry* )
{
	DBG_CHKTHIS(SvListView,0);
}

void SvListView::ModelHasRemoved( SvListEntry* )
{
	DBG_CHKTHIS(SvListView,0);
}

void SvListView::ModelHasEntryInvalidated( SvListEntry*)
{
	DBG_CHKTHIS(SvListView,0);
}

void SvListView::ActionMoving( SvListEntry* pEntry,SvListEntry*,sal_uLong)
{
	DBG_CHKTHIS(SvListView,0);
	SvListEntry* pParent = pEntry->pParent;
	DBG_ASSERT(pParent,"Model not consistent");
	if( pParent != pModel->pRootItem && pParent->pChilds->Count() == 1 )
	{
		SvViewData* pViewData = (SvViewData*)aDataTable.Get( (sal_uLong)pParent );
		pViewData->nFlags &= (~SVLISTENTRYFLAG_EXPANDED);
	}
	// vorlaeufig
	nVisibleCount = 0;
	bVisPositionsValid = sal_False;
}

void SvListView::ActionMoved( SvListEntry* /* pEntry */ ,
							SvListEntry* /* pTargetPrnt */ ,
							sal_uLong /* nChildPos */ )
{
	DBG_CHKTHIS(SvListView,0);
	nVisibleCount = 0;
	bVisPositionsValid = sal_False;
}

void SvListView::ActionInserted( SvListEntry* pEntry )
{
	DBG_CHKTHIS(SvListView,0);
	DBG_ASSERT(pEntry,"Insert:No Entry");
	SvViewData* pData = CreateViewData( pEntry );
	InitViewData( pData, pEntry );
    #ifdef DBG_UTIL
	sal_Bool bSuccess =
    #endif
        aDataTable.Insert( (sal_uLong)pEntry, pData );
	DBG_ASSERT(bSuccess,"Entry already in View");
	if ( nVisibleCount && pModel->IsEntryVisible( this, pEntry ))
	{
		nVisibleCount = 0;
		bVisPositionsValid = sal_False;
	}
}

void SvListView::ActionInsertedTree( SvListEntry* pEntry )
{
	DBG_CHKTHIS(SvListView,0);
	if ( pModel->IsEntryVisible( this, pEntry ))
	{
		nVisibleCount = 0;
		bVisPositionsValid = sal_False;
	}
	// ueber Entry und seine Childs iterieren
	SvListEntry* pCurEntry = pEntry;
	sal_uInt16 nRefDepth = pModel->GetDepth( pCurEntry );
	while( pCurEntry )
	{
		DBG_ASSERT(aDataTable.Get((sal_uLong)pCurEntry)==0,"Entry already in Table");
		SvViewData* pViewData = CreateViewData( pCurEntry );
		DBG_ASSERT(pViewData,"No ViewData");
		InitViewData( pViewData, pEntry );
		aDataTable.Insert( (sal_uLong)pCurEntry, pViewData );
		pCurEntry = pModel->Next( pCurEntry );
		if ( pCurEntry && pModel->GetDepth(pCurEntry) <= nRefDepth)
			pCurEntry = 0;
	}
}

void SvListView::RemoveViewData( SvListEntry* pParent )
{
	SvTreeEntryList* pChilds = pParent->pChilds;
	if( pChilds )
	{
		SvListEntry* pCur = (SvListEntry*)pChilds->First();
		while( pCur )
		{
			SvViewData* pViewData = (SvViewData*)aDataTable.Get((sal_uLong)pCur);
			delete pViewData;
			aDataTable.Remove( (sal_uLong)pCur );
			if( pCur->HasChilds())
				RemoveViewData( pCur );
			pCur = (SvListEntry*)pChilds->Next();
		}
	}
}



void SvListView::ActionRemoving( SvListEntry* pEntry )
{
	DBG_CHKTHIS(SvListView,0);
	DBG_ASSERT(pEntry,"Remove:No Entry");

	SvViewData* pViewData = (SvViewData*)aDataTable.Get( (sal_uLong)pEntry );
	sal_uLong nSelRemoved = 0;
	if ( pViewData->IsSelected() )
		nSelRemoved = 1 + pModel->GetChildSelectionCount( this, pEntry );
	nSelectionCount -= nSelRemoved;
	sal_uLong nVisibleRemoved = 0;
	if ( pModel->IsEntryVisible( this, pEntry ) )
		nVisibleRemoved = 1 + pModel->GetVisibleChildCount( this, pEntry );
	if( nVisibleCount )
	{
#ifdef DBG_UTIL
		if( nVisibleCount < nVisibleRemoved )
		{
			DBG_ERROR("nVisibleRemoved bad");
		}
#endif
		nVisibleCount -= nVisibleRemoved;
	}
	bVisPositionsValid = sal_False;

	pViewData = (SvViewData*)aDataTable.Get((sal_uLong)pEntry);
	delete pViewData;
	aDataTable.Remove( (sal_uLong)pEntry );
	RemoveViewData( pEntry );

	SvListEntry* pCurEntry = pEntry->pParent;
	if ( pCurEntry && pCurEntry != pModel->pRootItem &&
		 pCurEntry->pChilds->Count() == 1 )
	{
		pViewData = (SvViewData*)aDataTable.Get((sal_uLong)pCurEntry);
		pViewData->nFlags &= (~SVLISTENTRYFLAG_EXPANDED);
	}
}

void SvListView::ActionRemoved( SvListEntry* /* pEntry  */ )
{
	DBG_CHKTHIS(SvListView,0);
}

void SvListView::ActionClear()
{
	DBG_CHKTHIS(SvListView,0);
	Clear();
}

void SvListView::ModelNotification( sal_uInt16 nActionId, SvListEntry* pEntry1,
						SvListEntry* pEntry2, sal_uLong nPos )
{
	DBG_CHKTHIS(SvListView,0);
	switch( nActionId )
	{
		case LISTACTION_INSERTED:
			ActionInserted( pEntry1 );
			ModelHasInserted( pEntry1 );
			break;
		case LISTACTION_INSERTED_TREE:
			ActionInsertedTree( pEntry1 );
			ModelHasInsertedTree( pEntry1 );
			break;
		case LISTACTION_REMOVING:
			ModelIsRemoving( pEntry1 );
			ActionRemoving( pEntry1 );
			break;
		case LISTACTION_REMOVED:
			ActionRemoved( pEntry1 );
			ModelHasRemoved( pEntry1 );
			break;
		case LISTACTION_MOVING:
			ModelIsMoving( pEntry1, pEntry2, nPos );
			ActionMoving( pEntry1, pEntry2, nPos );
			break;
		case LISTACTION_MOVED:
			ActionMoved( pEntry1, pEntry2, nPos );
			ModelHasMoved( pEntry1 );
			break;
		case LISTACTION_CLEARING:
			ActionClear();
			ModelHasCleared(); //sic! wg. Kompatibilitaet!
			break;
		case LISTACTION_CLEARED:
			break;
		case LISTACTION_INVALIDATE_ENTRY:
			// keine Action fuer die Basisklasse
			ModelHasEntryInvalidated( pEntry1 );
			break;
		case LISTACTION_RESORTED:
			bVisPositionsValid = sal_False;
			break;
		case LISTACTION_RESORTING:
			break;
		default:
			DBG_ERROR("unknown ActionId");
	}
}

void SvListView::InitViewData( SvViewData*, SvListEntry* )
{
}

StringCompare SvTreeList::Compare( SvListEntry* pLeft, SvListEntry* pRight) const
{
	if( aCompareLink.IsSet())
	{
		SvSortData aSortData;
		aSortData.pLeft = pLeft;
		aSortData.pRight = pRight;
		return (StringCompare)aCompareLink.Call( &aSortData );
	}
	return COMPARE_EQUAL;
}

void SvTreeList::Resort()
{
	Broadcast( LISTACTION_RESORTING );
	bAbsPositionsValid = sal_False;
	ResortChilds( pRootItem );
	Broadcast( LISTACTION_RESORTED );
}

void SvTreeList::ResortChilds( SvListEntry* pParent )
{
	DBG_ASSERT(pParent,"Parent not set");
	List* pChildList = pParent->pChilds;
	if( !pChildList )
		return;
	List aList( *pChildList );
	pChildList->Clear();

	sal_uLong nCount = aList.Count();
	for( sal_uLong nCur = 0; nCur < nCount; nCur++ )
	{
		SvListEntry* pCurEntry = (SvListEntry*)aList.GetObject( nCur );
		sal_uLong nListPos = LIST_APPEND;
		GetInsertionPos( pCurEntry, pParent, nListPos );
		pChildList->Insert( pCurEntry, nListPos );
		if( pCurEntry->pChilds )
			ResortChilds( pCurEntry );
	}
	SetListPositions( (SvTreeEntryList*)pChildList );
}

void SvTreeList::GetInsertionPos( SvListEntry* pEntry, SvListEntry* pParent,
	sal_uLong& rPos )
{
	DBG_ASSERT(pEntry,"No Entry");

	if( eSortMode == SortNone )
		return;

	rPos = LIST_APPEND;
	SvTreeEntryList* pChildList = GetChildList( pParent );

	if( pChildList && pChildList->Count() )
	{
		long i = 0;
		long j = pChildList->Count()-1;
		long k;
		StringCompare eCompare = COMPARE_GREATER;

		do
		{
			k = (i+j)/2;
			SvListEntry* pTempEntry = (SvListEntry*)(pChildList->GetObject(k));
			eCompare = Compare( pEntry, pTempEntry );
			if( eSortMode == SortDescending && eCompare != COMPARE_EQUAL )
			{
				if( eCompare == COMPARE_LESS )
					eCompare = COMPARE_GREATER;
				else
					eCompare = COMPARE_LESS;
			}
			if( eCompare == COMPARE_GREATER )
				i = k + 1;
			else
				j = k - 1;
		} while( (eCompare != COMPARE_EQUAL) && (i <= j) );

		if( eCompare != COMPARE_EQUAL )
		{
			if(i > ((long)pChildList->Count() - 1)) // nicht gefunden, Ende der Liste
				rPos = LIST_APPEND;
			else
				rPos = i;              // nicht gefunden, Mitte
		}
		else
			rPos = k;
	}
}


