1525 lines
42 KiB
C++
1525 lines
42 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 <vcl/toolkit/treelist.hxx>
|
|
#include <vcl/toolkit/treelistentry.hxx>
|
|
#include <vcl/toolkit/viewdataentry.hxx>
|
|
#include <tools/debug.hxx>
|
|
#include <osl/diagnose.h>
|
|
|
|
#include <cassert>
|
|
#include <memory>
|
|
#include <unordered_map>
|
|
|
|
|
|
typedef std::unordered_map<SvTreeListEntry*, std::unique_ptr<SvViewDataEntry>> SvDataTable;
|
|
|
|
struct SvListView::Impl
|
|
{
|
|
SvListView & m_rThis;
|
|
|
|
SvDataTable m_DataTable; // Mapping SvTreeListEntry -> ViewData
|
|
|
|
sal_uInt32 m_nVisibleCount;
|
|
sal_uInt32 m_nSelectionCount;
|
|
bool m_bVisPositionsValid;
|
|
|
|
explicit Impl(SvListView & rThis)
|
|
: m_rThis(rThis)
|
|
, m_nVisibleCount(0)
|
|
, m_nSelectionCount(0)
|
|
, m_bVisPositionsValid(false)
|
|
{}
|
|
|
|
void InitTable();
|
|
void RemoveViewData( SvTreeListEntry* pParent );
|
|
|
|
void ActionMoving(SvTreeListEntry* pEntry);
|
|
void ActionMoved();
|
|
void ActionInserted(SvTreeListEntry* pEntry);
|
|
void ActionInsertedTree(SvTreeListEntry* pEntry);
|
|
void ActionRemoving(SvTreeListEntry* pEntry);
|
|
void ActionClear();
|
|
};
|
|
|
|
|
|
SvTreeList::SvTreeList(SvListView& listView) :
|
|
mrOwnerListView(listView),
|
|
mbEnableInvalidate(true)
|
|
{
|
|
nEntryCount = 0;
|
|
bAbsPositionsValid = false;
|
|
pRootItem.reset(new SvTreeListEntry);
|
|
eSortMode = SvSortMode::None;
|
|
}
|
|
|
|
SvTreeList::~SvTreeList()
|
|
{
|
|
}
|
|
|
|
void SvTreeList::Broadcast(
|
|
SvListAction nActionId,
|
|
SvTreeListEntry* pEntry1,
|
|
SvTreeListEntry* pEntry2,
|
|
sal_uInt32 nPos
|
|
)
|
|
{
|
|
mrOwnerListView.ModelNotification(nActionId, pEntry1, pEntry2, nPos);
|
|
}
|
|
|
|
// an entry is visible if all parents are expanded
|
|
bool SvTreeList::IsEntryVisible( const SvListView* pView, SvTreeListEntry* pEntry ) const
|
|
{
|
|
assert(pView && pEntry && "IsVisible:Invalid Params");
|
|
bool bRetVal = false;
|
|
do
|
|
{
|
|
if ( pEntry == pRootItem.get() )
|
|
{
|
|
bRetVal = true;
|
|
break;
|
|
}
|
|
pEntry = pEntry->pParent;
|
|
} while( pView->IsExpanded( pEntry ) );
|
|
return bRetVal;
|
|
}
|
|
|
|
sal_uInt16 SvTreeList::GetDepth( const SvTreeListEntry* pEntry ) const
|
|
{
|
|
DBG_ASSERT(pEntry && pEntry!=pRootItem.get(),"GetDepth:Bad Entry");
|
|
sal_uInt16 nDepth = 0;
|
|
while( pEntry && pEntry->pParent != pRootItem.get() )
|
|
{
|
|
nDepth++;
|
|
pEntry = pEntry->pParent;
|
|
}
|
|
return nDepth;
|
|
}
|
|
|
|
bool SvTreeList::IsAtRootDepth( const SvTreeListEntry* pEntry ) const
|
|
{
|
|
return pEntry->pParent == pRootItem.get();
|
|
}
|
|
|
|
void SvTreeList::Clear()
|
|
{
|
|
Broadcast( SvListAction::CLEARING );
|
|
pRootItem->ClearChildren();
|
|
nEntryCount = 0;
|
|
Broadcast( SvListAction::CLEARED );
|
|
}
|
|
|
|
bool SvTreeList::IsChild(const SvTreeListEntry* pParent, const SvTreeListEntry* pChild) const
|
|
{
|
|
if ( !pParent )
|
|
pParent = pRootItem.get();
|
|
|
|
if (pParent->m_Children.empty())
|
|
return false;
|
|
|
|
for (auto const& it : pParent->m_Children)
|
|
{
|
|
const SvTreeListEntry* pThis = it.get();
|
|
if (pThis == pChild)
|
|
return true;
|
|
else
|
|
{
|
|
bool bIsChild = IsChild(pThis, pChild);
|
|
if (bIsChild)
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
namespace {
|
|
|
|
class FindByPointer
|
|
{
|
|
const SvTreeListEntry* mpEntry;
|
|
public:
|
|
explicit FindByPointer(const SvTreeListEntry* p) : mpEntry(p) {}
|
|
|
|
bool operator() (std::unique_ptr<SvTreeListEntry> const& rpEntry) const
|
|
{
|
|
return mpEntry == rpEntry.get();
|
|
}
|
|
};
|
|
|
|
sal_uInt32 findEntryPosition(const SvTreeListEntries& rDst, const SvTreeListEntry* pEntry)
|
|
{
|
|
SvTreeListEntries::const_iterator itPos = std::find_if(rDst.begin(), rDst.end(), FindByPointer(pEntry));
|
|
if (itPos == rDst.end())
|
|
return static_cast<sal_uInt32>(~0);
|
|
|
|
return static_cast<sal_uInt32>(std::distance(rDst.begin(), itPos));
|
|
}
|
|
|
|
}
|
|
|
|
sal_uInt32 SvTreeList::Move(SvTreeListEntry* pSrcEntry,SvTreeListEntry* pTargetParent,sal_uInt32 nListPos)
|
|
{
|
|
// pDest may be 0!
|
|
assert(pSrcEntry && "Entry?");
|
|
if ( !pTargetParent )
|
|
pTargetParent = pRootItem.get();
|
|
DBG_ASSERT(pSrcEntry!=pTargetParent,"Move:Source=Target");
|
|
|
|
Broadcast( SvListAction::MOVING, pSrcEntry, pTargetParent, nListPos );
|
|
|
|
if ( pSrcEntry == pTargetParent )
|
|
// You can't move an entry onto itself as the parent. Just return its
|
|
// position and bail out.
|
|
return pSrcEntry->GetChildListPos();
|
|
|
|
bAbsPositionsValid = false;
|
|
|
|
SvTreeListEntries& rDst = pTargetParent->m_Children;
|
|
SvTreeListEntries& rSrc = pSrcEntry->pParent->m_Children;
|
|
|
|
bool bSameParent = pTargetParent == pSrcEntry->pParent;
|
|
|
|
// Find the position of the entry being moved in the source container.
|
|
SvTreeListEntries::iterator itSrcPos = rSrc.begin(), itEnd = rSrc.end();
|
|
for (; itSrcPos != itEnd; ++itSrcPos)
|
|
{
|
|
const SvTreeListEntry* p = (*itSrcPos).get();
|
|
if (p == pSrcEntry)
|
|
// Found
|
|
break;
|
|
}
|
|
|
|
if (itSrcPos == itEnd)
|
|
{
|
|
OSL_FAIL("Source entry not found! This should never happen.");
|
|
return pSrcEntry->GetChildListPos();
|
|
}
|
|
|
|
if (bSameParent)
|
|
{
|
|
// Moving within the same parent.
|
|
|
|
size_t nSrcPos = std::distance(rSrc.begin(), itSrcPos);
|
|
if (nSrcPos == nListPos)
|
|
// Nothing to move here.
|
|
return pSrcEntry->GetChildListPos();
|
|
|
|
if (nSrcPos < nListPos)
|
|
// Destination position shifts left after removing the original.
|
|
--nListPos;
|
|
|
|
// Release the original.
|
|
std::unique_ptr<SvTreeListEntry> pOriginal(std::move(*itSrcPos));
|
|
assert(pOriginal);
|
|
rSrc.erase(itSrcPos);
|
|
|
|
// Determine the insertion position.
|
|
SvTreeListEntries::iterator itDstPos = rSrc.end();
|
|
if (nListPos < rSrc.size())
|
|
{
|
|
itDstPos = rSrc.begin();
|
|
std::advance(itDstPos, nListPos);
|
|
}
|
|
rSrc.insert(itDstPos, std::move(pOriginal));
|
|
}
|
|
else
|
|
{
|
|
// Moving from one parent to another.
|
|
SvTreeListEntries::iterator itDstPos = rDst.end();
|
|
if (nListPos < rDst.size())
|
|
{
|
|
itDstPos = rDst.begin();
|
|
std::advance(itDstPos, nListPos);
|
|
}
|
|
std::unique_ptr<SvTreeListEntry> pOriginal(std::move(*itSrcPos));
|
|
assert(pOriginal);
|
|
rSrc.erase(itSrcPos);
|
|
rDst.insert(itDstPos, std::move(pOriginal));
|
|
}
|
|
|
|
// move parent (do this only now, because we need the parent for
|
|
// deleting the old child list!)
|
|
pSrcEntry->pParent = pTargetParent;
|
|
|
|
// correct list position in target list
|
|
SetListPositions(rDst);
|
|
if (!bSameParent)
|
|
SetListPositions(rSrc);
|
|
|
|
sal_uInt32 nRetVal = findEntryPosition(rDst, pSrcEntry);
|
|
OSL_ENSURE(nRetVal == pSrcEntry->GetChildListPos(), "ListPos not valid");
|
|
Broadcast( SvListAction::MOVED,pSrcEntry,pTargetParent,nRetVal);
|
|
return nRetVal;
|
|
}
|
|
|
|
sal_uInt32 SvTreeList::Copy(SvTreeListEntry* pSrcEntry,SvTreeListEntry* pTargetParent,sal_uInt32 nListPos)
|
|
{
|
|
// pDest may be 0!
|
|
DBG_ASSERT(pSrcEntry,"Entry?");
|
|
if ( !pTargetParent )
|
|
pTargetParent = pRootItem.get();
|
|
|
|
bAbsPositionsValid = false;
|
|
|
|
sal_uInt32 nCloneCount = 0;
|
|
SvTreeListEntry* pClonedEntry = Clone( pSrcEntry, nCloneCount );
|
|
nEntryCount += nCloneCount;
|
|
|
|
SvTreeListEntries& rDst = pTargetParent->m_Children;
|
|
|
|
pClonedEntry->pParent = pTargetParent; // move parent
|
|
|
|
if (nListPos < rDst.size())
|
|
{
|
|
SvTreeListEntries::iterator itPos = rDst.begin(); // insertion position.
|
|
std::advance(itPos, nListPos);
|
|
rDst.insert(itPos, std::unique_ptr<SvTreeListEntry>(pClonedEntry));
|
|
}
|
|
else
|
|
rDst.push_back(std::unique_ptr<SvTreeListEntry>(pClonedEntry));
|
|
|
|
SetListPositions(rDst); // correct list position in target list
|
|
|
|
Broadcast( SvListAction::INSERTED_TREE, pClonedEntry );
|
|
sal_uInt32 nRetVal = findEntryPosition(rDst, pClonedEntry);
|
|
return nRetVal;
|
|
}
|
|
|
|
void SvTreeList::Move( SvTreeListEntry* pSrcEntry, SvTreeListEntry* pDstEntry )
|
|
{
|
|
SvTreeListEntry* pParent;
|
|
sal_uInt32 nPos;
|
|
|
|
if ( !pDstEntry )
|
|
{
|
|
pParent = pRootItem.get();
|
|
nPos = 0;
|
|
}
|
|
else
|
|
{
|
|
pParent = pDstEntry->pParent;
|
|
nPos = pDstEntry->GetChildListPos();
|
|
nPos++; // (On screen:) insert _below_ pDstEntry
|
|
}
|
|
Move( pSrcEntry, pParent, nPos );
|
|
}
|
|
|
|
void SvTreeList::InsertTree(SvTreeListEntry* pSrcEntry,
|
|
SvTreeListEntry* pTargetParent,sal_uInt32 nListPos)
|
|
{
|
|
DBG_ASSERT(pSrcEntry,"InsertTree:Entry?");
|
|
if ( !pSrcEntry )
|
|
return;
|
|
|
|
if ( !pTargetParent )
|
|
pTargetParent = pRootItem.get();
|
|
|
|
// take sorting into account
|
|
GetInsertionPos( pSrcEntry, pTargetParent, nListPos );
|
|
|
|
bAbsPositionsValid = false;
|
|
|
|
pSrcEntry->pParent = pTargetParent; // move parent
|
|
SvTreeListEntries& rDst = pTargetParent->m_Children;
|
|
|
|
if (nListPos < rDst.size())
|
|
{
|
|
SvTreeListEntries::iterator itPos = rDst.begin();
|
|
std::advance(itPos, nListPos);
|
|
rDst.insert(itPos, std::unique_ptr<SvTreeListEntry>(pSrcEntry));
|
|
}
|
|
else
|
|
rDst.push_back(std::unique_ptr<SvTreeListEntry>(pSrcEntry));
|
|
|
|
SetListPositions(rDst); // correct list position in target list
|
|
nEntryCount += GetChildCount( pSrcEntry );
|
|
nEntryCount++; // the parent is new, too
|
|
|
|
Broadcast(SvListAction::INSERTED_TREE, pSrcEntry );
|
|
}
|
|
|
|
SvTreeListEntry* SvTreeList::CloneEntry( SvTreeListEntry* pSource ) const
|
|
{
|
|
if( aCloneLink.IsSet() )
|
|
return aCloneLink.Call( pSource );
|
|
SvTreeListEntry* pEntry = new SvTreeListEntry;
|
|
pEntry->Clone(pSource);
|
|
return pEntry;
|
|
}
|
|
|
|
SvTreeListEntry* SvTreeList::Clone( SvTreeListEntry* pEntry, sal_uInt32& nCloneCount ) const
|
|
{
|
|
SvTreeListEntry* pClonedEntry = CloneEntry( pEntry );
|
|
nCloneCount = 1;
|
|
if (!pEntry->m_Children.empty())
|
|
// Clone the child entries.
|
|
CloneChildren(pClonedEntry->m_Children, nCloneCount, pEntry->m_Children, *pClonedEntry);
|
|
|
|
return pClonedEntry;
|
|
}
|
|
|
|
void SvTreeList::CloneChildren(
|
|
SvTreeListEntries& rDst, sal_uInt32& rCloneCount, SvTreeListEntries& rSrc, SvTreeListEntry& rNewParent) const
|
|
{
|
|
SvTreeListEntries aClone;
|
|
for (auto const& elem : rSrc)
|
|
{
|
|
SvTreeListEntry& rEntry = *elem;
|
|
std::unique_ptr<SvTreeListEntry> pNewEntry(CloneEntry(&rEntry));
|
|
++rCloneCount;
|
|
pNewEntry->pParent = &rNewParent;
|
|
if (!rEntry.m_Children.empty())
|
|
// Clone entries recursively.
|
|
CloneChildren(pNewEntry->m_Children, rCloneCount, rEntry.m_Children, *pNewEntry);
|
|
|
|
aClone.push_back(std::move(pNewEntry));
|
|
}
|
|
|
|
rDst.swap(aClone);
|
|
}
|
|
|
|
sal_uInt32 SvTreeList::GetChildCount( const SvTreeListEntry* pParent ) const
|
|
{
|
|
if ( !pParent )
|
|
return GetEntryCount();
|
|
|
|
if (pParent->m_Children.empty())
|
|
return 0;
|
|
|
|
sal_uInt32 nCount = 0;
|
|
sal_uInt16 nRefDepth = GetDepth( pParent );
|
|
sal_uInt16 nActDepth = nRefDepth;
|
|
do
|
|
{
|
|
pParent = Next(const_cast<SvTreeListEntry*>(pParent), &nActDepth);
|
|
nCount++;
|
|
} while( pParent && nRefDepth < nActDepth );
|
|
|
|
assert(nCount > 0 && "given do...while");
|
|
return nCount - 1;
|
|
}
|
|
|
|
sal_uInt32 SvTreeList::GetVisibleChildCount(const SvListView* pView, SvTreeListEntry* pParent) const
|
|
{
|
|
assert(pView && "GetVisChildCount:No View");
|
|
if ( !pParent )
|
|
pParent = pRootItem.get();
|
|
|
|
if (!pParent || !pView->IsExpanded(pParent) || pParent->m_Children.empty())
|
|
return 0;
|
|
|
|
sal_uInt32 nCount = 0;
|
|
sal_uInt16 nRefDepth = GetDepth( pParent );
|
|
sal_uInt16 nActDepth = nRefDepth;
|
|
do
|
|
{
|
|
pParent = NextVisible( pView, pParent, &nActDepth );
|
|
nCount++;
|
|
} while( pParent && nRefDepth < nActDepth );
|
|
|
|
assert(nCount > 0 && "given do...while");
|
|
return nCount - 1;
|
|
}
|
|
|
|
sal_uInt32 SvTreeList::GetChildSelectionCount(const SvListView* pView,SvTreeListEntry* pParent) const
|
|
{
|
|
assert(pView && "GetChildSelCount:No View");
|
|
if ( !pParent )
|
|
pParent = pRootItem.get();
|
|
|
|
if (!pParent || pParent->m_Children.empty())
|
|
return 0;
|
|
|
|
sal_uInt32 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 );
|
|
|
|
return nCount;
|
|
}
|
|
|
|
SvTreeListEntry* SvTreeList::First() const
|
|
{
|
|
if ( nEntryCount )
|
|
return pRootItem->m_Children[0].get();
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
SvTreeListEntry* SvTreeList::Next( SvTreeListEntry* pActEntry, sal_uInt16* pDepth ) const
|
|
{
|
|
DBG_ASSERT( pActEntry && pActEntry->pParent, "SvTreeList::Next: invalid entry/parent!" );
|
|
if ( !pActEntry || !pActEntry->pParent )
|
|
return nullptr;
|
|
|
|
sal_uInt16 nDepth = 0;
|
|
bool bWithDepth = false;
|
|
if ( pDepth )
|
|
{
|
|
nDepth = *pDepth;
|
|
bWithDepth = true;
|
|
}
|
|
|
|
// Get the list where the current entry belongs to (from its parent).
|
|
SvTreeListEntries* pActualList = &pActEntry->pParent->m_Children;
|
|
sal_uInt32 nActualPos = pActEntry->GetChildListPos();
|
|
|
|
if (!pActEntry->m_Children.empty())
|
|
{
|
|
// The current entry has children. Get its first child entry.
|
|
nDepth++;
|
|
pActEntry = pActEntry->m_Children[0].get();
|
|
if ( bWithDepth )
|
|
*pDepth = nDepth;
|
|
return pActEntry;
|
|
}
|
|
|
|
if (pActualList->size() > (nActualPos+1))
|
|
{
|
|
// Get the next sibling of the current entry.
|
|
pActEntry = (*pActualList)[nActualPos+1].get();
|
|
if ( bWithDepth )
|
|
*pDepth = nDepth;
|
|
return pActEntry;
|
|
}
|
|
|
|
// Move up level(s) until we find the level where the next sibling exists.
|
|
SvTreeListEntry* pParent = pActEntry->pParent;
|
|
nDepth--;
|
|
while( pParent != pRootItem.get() && pParent != nullptr )
|
|
{
|
|
DBG_ASSERT(pParent!=nullptr,"TreeData corrupt!");
|
|
pActualList = &pParent->pParent->m_Children;
|
|
nActualPos = pParent->GetChildListPos();
|
|
if (pActualList->size() > (nActualPos+1))
|
|
{
|
|
pActEntry = (*pActualList)[nActualPos+1].get();
|
|
if ( bWithDepth )
|
|
*pDepth = nDepth;
|
|
return pActEntry;
|
|
}
|
|
pParent = pParent->pParent;
|
|
nDepth--;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
SvTreeListEntry* SvTreeList::Prev( SvTreeListEntry* pActEntry ) const
|
|
{
|
|
assert(pActEntry && "Entry?");
|
|
|
|
SvTreeListEntries* pActualList = &pActEntry->pParent->m_Children;
|
|
sal_uInt32 nActualPos = pActEntry->GetChildListPos();
|
|
|
|
if ( nActualPos > 0 )
|
|
{
|
|
pActEntry = (*pActualList)[nActualPos-1].get();
|
|
while (!pActEntry->m_Children.empty())
|
|
{
|
|
pActualList = &pActEntry->m_Children;
|
|
pActEntry = pActualList->back().get();
|
|
}
|
|
return pActEntry;
|
|
}
|
|
if ( pActEntry->pParent == pRootItem.get() )
|
|
return nullptr;
|
|
|
|
pActEntry = pActEntry->pParent;
|
|
|
|
if ( pActEntry )
|
|
{
|
|
return pActEntry;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
SvTreeListEntry* SvTreeList::Last() const
|
|
{
|
|
SvTreeListEntries* pActList = &pRootItem->m_Children;
|
|
SvTreeListEntry* pEntry = nullptr;
|
|
while (!pActList->empty())
|
|
{
|
|
pEntry = pActList->back().get();
|
|
pActList = &pEntry->m_Children;
|
|
}
|
|
return pEntry;
|
|
}
|
|
|
|
sal_uInt32 SvTreeList::GetVisiblePos( const SvListView* pView, SvTreeListEntry const * pEntry ) const
|
|
{
|
|
assert(pView && "View?");
|
|
DBG_ASSERT(pEntry,"Entry?");
|
|
|
|
if (!pView->m_pImpl->m_bVisPositionsValid)
|
|
{
|
|
// to make GetVisibleCount refresh the positions
|
|
const_cast<SvListView*>(pView)->m_pImpl->m_nVisibleCount = 0;
|
|
GetVisibleCount( const_cast<SvListView*>(pView) );
|
|
}
|
|
const SvViewDataEntry* pViewData = pView->GetViewData( pEntry );
|
|
if (!pViewData)
|
|
return 0;
|
|
return pViewData->nVisPos;
|
|
}
|
|
|
|
sal_uInt32 SvTreeList::GetVisibleCount( SvListView* pView ) const
|
|
{
|
|
assert(pView && "GetVisCount:No View");
|
|
if( !pView->HasViewData() )
|
|
return 0;
|
|
if (pView->m_pImpl->m_nVisibleCount)
|
|
return pView->m_pImpl->m_nVisibleCount;
|
|
|
|
sal_uInt32 nPos = 0;
|
|
SvTreeListEntry* pEntry = First(); // first entry is always visible
|
|
while ( pEntry )
|
|
{
|
|
if (SvViewDataEntry* pViewData = pView->GetViewData( pEntry ))
|
|
pViewData->nVisPos = nPos;
|
|
nPos++;
|
|
pEntry = NextVisible( pView, pEntry );
|
|
}
|
|
#ifdef DBG_UTIL
|
|
if( nPos > 10000000 )
|
|
{
|
|
OSL_FAIL("nVisibleCount bad");
|
|
}
|
|
#endif
|
|
pView->m_pImpl->m_nVisibleCount = nPos;
|
|
pView->m_pImpl->m_bVisPositionsValid = true;
|
|
return nPos;
|
|
}
|
|
|
|
|
|
// For performance reasons, this function assumes that the passed entry is
|
|
// already visible.
|
|
SvTreeListEntry* SvTreeList::NextVisible(const SvListView* pView,SvTreeListEntry* pActEntry,sal_uInt16* pActDepth) const
|
|
{
|
|
if ( !pActEntry )
|
|
return nullptr;
|
|
|
|
assert(pView && "NextVisible:No View");
|
|
|
|
sal_uInt16 nDepth = 0;
|
|
bool bWithDepth = false;
|
|
if ( pActDepth )
|
|
{
|
|
nDepth = *pActDepth;
|
|
bWithDepth = true;
|
|
}
|
|
|
|
SvTreeListEntries* pActualList = &pActEntry->pParent->m_Children;
|
|
sal_uInt32 nActualPos = pActEntry->GetChildListPos();
|
|
|
|
if ( pView->IsExpanded(pActEntry) )
|
|
{
|
|
OSL_ENSURE(!pActEntry->m_Children.empty(), "Pass entry is supposed to have child entries.");
|
|
|
|
nDepth++;
|
|
pActEntry = pActEntry->m_Children[0].get();
|
|
if ( bWithDepth )
|
|
*pActDepth = nDepth;
|
|
return pActEntry;
|
|
}
|
|
|
|
nActualPos++;
|
|
if ( pActualList->size() > nActualPos )
|
|
{
|
|
pActEntry = (*pActualList)[nActualPos].get();
|
|
if ( bWithDepth )
|
|
*pActDepth = nDepth;
|
|
return pActEntry;
|
|
}
|
|
|
|
SvTreeListEntry* pParent = pActEntry->pParent;
|
|
nDepth--;
|
|
while( pParent != pRootItem.get() )
|
|
{
|
|
pActualList = &pParent->pParent->m_Children;
|
|
nActualPos = pParent->GetChildListPos();
|
|
nActualPos++;
|
|
if ( pActualList->size() > nActualPos )
|
|
{
|
|
pActEntry = (*pActualList)[nActualPos].get();
|
|
if ( bWithDepth )
|
|
*pActDepth = nDepth;
|
|
return pActEntry;
|
|
}
|
|
pParent = pParent->pParent;
|
|
nDepth--;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
// For performance reasons, this function assumes that the passed entry is
|
|
// already visible.
|
|
|
|
SvTreeListEntry* SvTreeList::PrevVisible(const SvListView* pView, SvTreeListEntry* pActEntry) const
|
|
{
|
|
assert(pView && pActEntry && "PrevVis:View/Entry?");
|
|
|
|
SvTreeListEntries* pActualList = &pActEntry->pParent->m_Children;
|
|
sal_uInt32 nActualPos = pActEntry->GetChildListPos();
|
|
|
|
if ( nActualPos > 0 )
|
|
{
|
|
pActEntry = (*pActualList)[nActualPos-1].get();
|
|
while( pView->IsExpanded(pActEntry) )
|
|
{
|
|
pActualList = &pActEntry->m_Children;
|
|
pActEntry = pActualList->back().get();
|
|
}
|
|
return pActEntry;
|
|
}
|
|
|
|
if ( pActEntry->pParent == pRootItem.get() )
|
|
return nullptr;
|
|
|
|
pActEntry = pActEntry->pParent;
|
|
if ( pActEntry )
|
|
{
|
|
return pActEntry;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
SvTreeListEntry* SvTreeList::LastVisible( const SvListView* pView) const
|
|
{
|
|
DBG_ASSERT(pView,"LastVis:No View");
|
|
SvTreeListEntry* pEntry = Last();
|
|
while( pEntry && !IsEntryVisible( pView, pEntry ) )
|
|
pEntry = PrevVisible( pView, pEntry );
|
|
return pEntry;
|
|
}
|
|
|
|
SvTreeListEntry* SvTreeList::NextVisible(const SvListView* pView,SvTreeListEntry* pEntry,sal_uInt16& nDelta) const
|
|
{
|
|
assert(pView && pEntry && "NextVis:Wrong Prms!");
|
|
DBG_ASSERT(IsEntryVisible(pView,pEntry), "NextVis:Wrong Vis");
|
|
|
|
sal_uInt32 nVisPos = GetVisiblePos( pView, pEntry );
|
|
// nDelta entries existent?
|
|
// example: 0,1,2,3,4,5,6,7,8,9 nVisPos=5 nDelta=7
|
|
// nNewDelta = 10-nVisPos-1 == 4
|
|
if (nVisPos+nDelta >= pView->m_pImpl->m_nVisibleCount)
|
|
{
|
|
nDelta = static_cast<sal_uInt16>(pView->m_pImpl->m_nVisibleCount-nVisPos);
|
|
nDelta--;
|
|
}
|
|
sal_uInt16 nDeltaTmp = nDelta;
|
|
while( nDeltaTmp )
|
|
{
|
|
pEntry = NextVisible( pView, pEntry );
|
|
nDeltaTmp--;
|
|
DBG_ASSERT(pEntry,"Entry?");
|
|
}
|
|
return pEntry;
|
|
}
|
|
|
|
SvTreeListEntry* SvTreeList::PrevVisible( const SvListView* pView, SvTreeListEntry* pEntry, sal_uInt16& nDelta ) const
|
|
{
|
|
DBG_ASSERT(pView&&pEntry&&IsEntryVisible(pView,pEntry),"PrevVis:Parms/!Vis");
|
|
|
|
sal_uInt32 nVisPos = GetVisiblePos( pView, pEntry );
|
|
// nDelta entries existent?
|
|
// example: 0,1,2,3,4,5,6,7,8,9 nVisPos=8 nDelta=20
|
|
// nNewDelta = nNewVisPos
|
|
if ( nDelta > nVisPos )
|
|
nDelta = static_cast<sal_uInt16>(nVisPos);
|
|
sal_uInt16 nDeltaTmp = nDelta;
|
|
while( nDeltaTmp )
|
|
{
|
|
pEntry = PrevVisible( pView, pEntry );
|
|
nDeltaTmp--;
|
|
DBG_ASSERT(pEntry,"Entry?");
|
|
}
|
|
return pEntry;
|
|
}
|
|
|
|
SvTreeListEntry* SvTreeList::FirstSelected( const SvListView* pView) const
|
|
{
|
|
DBG_ASSERT(pView,"FirstSel:No View");
|
|
if( !pView )
|
|
return nullptr;
|
|
SvTreeListEntry* pActSelEntry = First();
|
|
while( pActSelEntry && !pView->IsSelected(pActSelEntry) )
|
|
pActSelEntry = NextVisible( pView, pActSelEntry );
|
|
return pActSelEntry;
|
|
}
|
|
|
|
|
|
SvTreeListEntry* SvTreeList::FirstChild( SvTreeListEntry* pParent ) const
|
|
{
|
|
if ( !pParent )
|
|
pParent = pRootItem.get();
|
|
SvTreeListEntry* pResult;
|
|
if (!pParent->m_Children.empty())
|
|
pResult = pParent->m_Children[0].get();
|
|
else
|
|
pResult = nullptr;
|
|
return pResult;
|
|
}
|
|
|
|
SvTreeListEntry* SvTreeList::NextSelected( const SvListView* pView, SvTreeListEntry* pEntry ) const
|
|
{
|
|
assert(pView && pEntry && "NextSel:View/Entry?");
|
|
pEntry = Next( pEntry );
|
|
while( pEntry && !pView->IsSelected(pEntry) )
|
|
pEntry = Next( pEntry );
|
|
return pEntry;
|
|
}
|
|
|
|
sal_uInt32 SvTreeList::Insert( SvTreeListEntry* pEntry,SvTreeListEntry* pParent,sal_uInt32 nPos )
|
|
{
|
|
assert(pEntry && "Entry?");
|
|
|
|
if ( !pParent )
|
|
pParent = pRootItem.get();
|
|
|
|
SvTreeListEntries& rList = pParent->m_Children;
|
|
|
|
// take sorting into account
|
|
GetInsertionPos( pEntry, pParent, nPos );
|
|
|
|
bAbsPositionsValid = false;
|
|
pEntry->pParent = pParent;
|
|
|
|
if (nPos < rList.size())
|
|
{
|
|
SvTreeListEntries::iterator itPos = rList.begin();
|
|
std::advance(itPos, nPos);
|
|
rList.insert(itPos, std::unique_ptr<SvTreeListEntry>(pEntry));
|
|
}
|
|
else
|
|
rList.push_back(std::unique_ptr<SvTreeListEntry>(pEntry));
|
|
|
|
nEntryCount++;
|
|
if (nPos != TREELIST_APPEND && (nPos != (rList.size()-1)))
|
|
SetListPositions(rList);
|
|
else
|
|
pEntry->nListPos = rList.size()-1;
|
|
|
|
Broadcast( SvListAction::INSERTED, pEntry );
|
|
return nPos; // pEntry->nListPos;
|
|
}
|
|
|
|
sal_uInt32 SvTreeList::GetAbsPos( const SvTreeListEntry* pEntry) const
|
|
{
|
|
if ( !bAbsPositionsValid )
|
|
const_cast<SvTreeList*>(this)->SetAbsolutePositions();
|
|
return pEntry->nAbsPos;
|
|
}
|
|
|
|
sal_uInt32 SvTreeList::GetRelPos( const SvTreeListEntry* pChild )
|
|
{
|
|
return pChild->GetChildListPos();
|
|
}
|
|
|
|
void SvTreeList::SetAbsolutePositions()
|
|
{
|
|
sal_uInt32 nPos = 0;
|
|
SvTreeListEntry* pEntry = First();
|
|
while ( pEntry )
|
|
{
|
|
pEntry->nAbsPos = nPos;
|
|
nPos++;
|
|
pEntry = Next( pEntry );
|
|
}
|
|
bAbsPositionsValid = true;
|
|
}
|
|
|
|
void SvListView::ExpandListEntry( SvTreeListEntry* pEntry )
|
|
{
|
|
assert(pEntry && "Expand:View/Entry?");
|
|
SvViewDataEntry* pViewData = GetViewData(pEntry);
|
|
if (!pViewData)
|
|
return;
|
|
|
|
if (pViewData->IsExpanded())
|
|
return;
|
|
|
|
DBG_ASSERT(!pEntry->m_Children.empty(), "SvTreeList::Expand: We expected to have child entries.");
|
|
|
|
pViewData->SetExpanded(true);
|
|
SvTreeListEntry* pParent = pEntry->pParent;
|
|
// if parent is visible, invalidate status data
|
|
if ( IsExpanded( pParent ) )
|
|
{
|
|
m_pImpl->m_bVisPositionsValid = false;
|
|
m_pImpl->m_nVisibleCount = 0;
|
|
}
|
|
}
|
|
|
|
void SvListView::CollapseListEntry( SvTreeListEntry* pEntry )
|
|
{
|
|
assert(pEntry && "Collapse:View/Entry?");
|
|
SvViewDataEntry* pViewData = GetViewData( pEntry );
|
|
if (!pViewData)
|
|
return;
|
|
|
|
if (!pViewData->IsExpanded())
|
|
return;
|
|
|
|
DBG_ASSERT(!pEntry->m_Children.empty(), "SvTreeList::Collapse: We expected to have child entries.");
|
|
|
|
pViewData->SetExpanded(false);
|
|
|
|
SvTreeListEntry* pParent = pEntry->pParent;
|
|
if ( IsExpanded(pParent) )
|
|
{
|
|
m_pImpl->m_nVisibleCount = 0;
|
|
m_pImpl->m_bVisPositionsValid = false;
|
|
}
|
|
}
|
|
|
|
bool SvListView::SelectListEntry( SvTreeListEntry* pEntry, bool bSelect )
|
|
{
|
|
DBG_ASSERT(pEntry,"Select:View/Entry?");
|
|
|
|
SvViewDataEntry* pViewData = GetViewData( pEntry );
|
|
if (!pViewData)
|
|
return false;
|
|
|
|
if ( bSelect )
|
|
{
|
|
if ( pViewData->IsSelected() || !pViewData->IsSelectable() )
|
|
return false;
|
|
else
|
|
{
|
|
pViewData->SetSelected(true);
|
|
m_pImpl->m_nSelectionCount++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( !pViewData->IsSelected() )
|
|
return false;
|
|
else
|
|
{
|
|
pViewData->SetSelected(false);
|
|
m_pImpl->m_nSelectionCount--;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool SvTreeList::Remove( const SvTreeListEntry* pEntry )
|
|
{
|
|
assert(pEntry && "Cannot remove root, use clear");
|
|
|
|
if( !pEntry->pParent )
|
|
{
|
|
OSL_FAIL("Removing entry not in model!");
|
|
// Under certain circumstances (which?), the explorer deletes entries
|
|
// from the view that it hasn't inserted into the view. We don't want
|
|
// to crash, so we catch this case here.
|
|
return false;
|
|
}
|
|
|
|
Broadcast(SvListAction::REMOVING, const_cast<SvTreeListEntry*>(pEntry));
|
|
sal_uInt32 nRemoved = 1 + GetChildCount(pEntry);
|
|
bAbsPositionsValid = false;
|
|
|
|
SvTreeListEntry* pParent = pEntry->pParent;
|
|
SvTreeListEntries& rList = pParent->m_Children;
|
|
bool bLastEntry = false;
|
|
|
|
// Since we need the live instance of SvTreeListEntry for broadcasting,
|
|
// we first need to pop it from the container, broadcast it, then delete
|
|
// the instance manually at the end.
|
|
|
|
std::unique_ptr<SvTreeListEntry> pEntryDeleter;
|
|
if ( pEntry->HasChildListPos() )
|
|
{
|
|
size_t nListPos = pEntry->GetChildListPos();
|
|
bLastEntry = (nListPos == (rList.size()-1));
|
|
SvTreeListEntries::iterator it = rList.begin();
|
|
std::advance(it, nListPos);
|
|
pEntryDeleter = std::move(*it);
|
|
rList.erase(it);
|
|
}
|
|
else
|
|
{
|
|
SvTreeListEntries::iterator it =
|
|
std::find_if(rList.begin(), rList.end(), FindByPointer(pEntry));
|
|
if (it != rList.end())
|
|
{
|
|
pEntryDeleter = std::move(*it);
|
|
rList.erase(it);
|
|
}
|
|
}
|
|
|
|
if (!rList.empty() && !bLastEntry)
|
|
SetListPositions(rList);
|
|
|
|
nEntryCount -= nRemoved;
|
|
Broadcast(SvListAction::REMOVED, const_cast<SvTreeListEntry*>(pEntry));
|
|
|
|
return true;
|
|
}
|
|
|
|
SvTreeListEntry* SvTreeList::GetEntryAtAbsPos( sal_uInt32 nAbsPos ) const
|
|
{
|
|
SvTreeListEntry* pEntry = First();
|
|
while ( nAbsPos && pEntry )
|
|
{
|
|
pEntry = Next( pEntry );
|
|
nAbsPos--;
|
|
}
|
|
return pEntry;
|
|
}
|
|
|
|
SvTreeListEntry* SvTreeList::GetEntryAtVisPos( const SvListView* pView, sal_uInt32 nVisPos ) const
|
|
{
|
|
DBG_ASSERT(pView,"GetEntryAtVisPos:No View");
|
|
SvTreeListEntry* pEntry = First();
|
|
while ( nVisPos && pEntry )
|
|
{
|
|
pEntry = NextVisible( pView, pEntry );
|
|
nVisPos--;
|
|
}
|
|
return pEntry;
|
|
}
|
|
|
|
void SvTreeList::SetListPositions( SvTreeListEntries& rEntries )
|
|
{
|
|
if (rEntries.empty())
|
|
return;
|
|
|
|
SvTreeListEntry& rFirst = *rEntries.front();
|
|
if (rFirst.pParent)
|
|
rFirst.pParent->InvalidateChildrensListPositions();
|
|
}
|
|
|
|
void SvTreeList::EnableInvalidate( bool bEnable )
|
|
{
|
|
mbEnableInvalidate = bEnable;
|
|
}
|
|
|
|
void SvTreeList::InvalidateEntry( SvTreeListEntry* pEntry )
|
|
{
|
|
if (!mbEnableInvalidate)
|
|
return;
|
|
|
|
Broadcast( SvListAction::INVALIDATE_ENTRY, pEntry );
|
|
}
|
|
|
|
SvListView::SvListView()
|
|
: m_pImpl(new Impl(*this))
|
|
{
|
|
pModel.reset(new SvTreeList(*this));
|
|
m_pImpl->InitTable();
|
|
}
|
|
|
|
void SvListView::dispose()
|
|
{
|
|
pModel.reset();
|
|
}
|
|
|
|
SvListView::~SvListView()
|
|
{
|
|
m_pImpl->m_DataTable.clear();
|
|
}
|
|
|
|
sal_uInt32 SvListView::GetSelectionCount() const
|
|
{ return m_pImpl->m_nSelectionCount; }
|
|
|
|
bool SvListView::HasViewData() const
|
|
{ return m_pImpl->m_DataTable.size() > 1; } // There's always a ROOT
|
|
|
|
|
|
void SvListView::Impl::InitTable()
|
|
{
|
|
DBG_ASSERT(m_rThis.pModel,"InitTable:No Model");
|
|
DBG_ASSERT(!m_nSelectionCount && !m_nVisibleCount && !m_bVisPositionsValid,
|
|
"InitTable: Not cleared!");
|
|
|
|
if (!m_DataTable.empty())
|
|
{
|
|
DBG_ASSERT(m_DataTable.size() == 1, "InitTable: TableCount != 1");
|
|
// Delete the view data allocated to the Clear in the root.
|
|
// Attention: The model belonging to the root entry (and thus the entry
|
|
// itself) might already be deleted.
|
|
m_DataTable.clear();
|
|
}
|
|
|
|
SvTreeListEntry* pEntry;
|
|
|
|
// insert root entry
|
|
pEntry = m_rThis.pModel->pRootItem.get();
|
|
std::unique_ptr<SvViewDataEntry> pViewData(new SvViewDataEntry);
|
|
pViewData->SetExpanded(true);
|
|
m_DataTable.insert(std::make_pair(pEntry, std::move(pViewData)));
|
|
// now all the other entries
|
|
pEntry = m_rThis.pModel->First();
|
|
while( pEntry )
|
|
{
|
|
pViewData = std::make_unique<SvViewDataEntry>();
|
|
m_rThis.InitViewData( pViewData.get(), pEntry );
|
|
m_DataTable.insert(std::make_pair(pEntry, std::move(pViewData)));
|
|
pEntry = m_rThis.pModel->Next( pEntry );
|
|
}
|
|
}
|
|
|
|
void SvListView::Clear()
|
|
{
|
|
m_pImpl->m_DataTable.clear();
|
|
m_pImpl->m_nSelectionCount = 0;
|
|
m_pImpl->m_nVisibleCount = 0;
|
|
m_pImpl->m_bVisPositionsValid = false;
|
|
if( pModel )
|
|
{
|
|
// insert root entry
|
|
SvTreeListEntry* pEntry = pModel->pRootItem.get();
|
|
std::unique_ptr<SvViewDataEntry> pViewData(new SvViewDataEntry);
|
|
pViewData->SetExpanded(true);
|
|
m_pImpl->m_DataTable.insert(std::make_pair(pEntry, std::move(pViewData)));
|
|
}
|
|
}
|
|
|
|
void SvListView::ModelHasCleared()
|
|
{
|
|
}
|
|
|
|
void SvListView::ModelHasInserted( SvTreeListEntry* )
|
|
{
|
|
}
|
|
|
|
void SvListView::ModelHasInsertedTree( SvTreeListEntry* )
|
|
{
|
|
}
|
|
|
|
void SvListView::ModelIsMoving( SvTreeListEntry* /* pSource */ )
|
|
{
|
|
}
|
|
|
|
|
|
void SvListView::ModelHasMoved( SvTreeListEntry* )
|
|
{
|
|
}
|
|
|
|
void SvListView::ModelIsRemoving( SvTreeListEntry* )
|
|
{
|
|
}
|
|
|
|
void SvListView::ModelHasRemoved( SvTreeListEntry* )
|
|
{
|
|
//WARNING WARNING WARNING
|
|
//The supplied pointer should have been deleted
|
|
//before this call. Be careful not to use it!!!
|
|
}
|
|
|
|
void SvListView::ModelHasEntryInvalidated( SvTreeListEntry*)
|
|
{
|
|
}
|
|
|
|
void SvListView::Impl::ActionMoving( SvTreeListEntry* pEntry )
|
|
{
|
|
SvTreeListEntry* pParent = pEntry->pParent;
|
|
assert(pParent && "Model not consistent");
|
|
if (pParent != m_rThis.pModel->pRootItem.get() && pParent->m_Children.size() == 1)
|
|
{
|
|
const auto iter = m_DataTable.find(pParent);
|
|
assert(iter != m_DataTable.end());
|
|
SvViewDataEntry* pViewData = iter->second.get();
|
|
pViewData->SetExpanded(false);
|
|
}
|
|
// preliminary
|
|
m_nVisibleCount = 0;
|
|
m_bVisPositionsValid = false;
|
|
}
|
|
|
|
void SvListView::Impl::ActionMoved()
|
|
{
|
|
m_nVisibleCount = 0;
|
|
m_bVisPositionsValid = false;
|
|
}
|
|
|
|
void SvListView::Impl::ActionInserted( SvTreeListEntry* pEntry )
|
|
{
|
|
DBG_ASSERT(pEntry,"Insert:No Entry");
|
|
std::unique_ptr<SvViewDataEntry> pData(new SvViewDataEntry());
|
|
m_rThis.InitViewData( pData.get(), pEntry );
|
|
std::pair<SvDataTable::iterator, bool> aSuccess =
|
|
m_DataTable.insert(std::make_pair(pEntry, std::move(pData)));
|
|
DBG_ASSERT(aSuccess.second,"Entry already in View");
|
|
if (m_nVisibleCount && m_rThis.pModel->IsEntryVisible(&m_rThis, pEntry))
|
|
{
|
|
m_nVisibleCount = 0;
|
|
m_bVisPositionsValid = false;
|
|
}
|
|
}
|
|
|
|
void SvListView::Impl::ActionInsertedTree( SvTreeListEntry* pEntry )
|
|
{
|
|
if (m_rThis.pModel->IsEntryVisible(&m_rThis, pEntry))
|
|
{
|
|
m_nVisibleCount = 0;
|
|
m_bVisPositionsValid = false;
|
|
}
|
|
// iterate over entry and its children
|
|
SvTreeListEntry* pCurEntry = pEntry;
|
|
sal_uInt16 nRefDepth = m_rThis.pModel->GetDepth( pCurEntry );
|
|
while( pCurEntry )
|
|
{
|
|
DBG_ASSERT(m_DataTable.find(pCurEntry) != m_DataTable.end(),"Entry already in Table");
|
|
std::unique_ptr<SvViewDataEntry> pViewData(new SvViewDataEntry());
|
|
m_rThis.InitViewData( pViewData.get(), pEntry );
|
|
m_DataTable.insert(std::make_pair(pCurEntry, std::move(pViewData)));
|
|
pCurEntry = m_rThis.pModel->Next( pCurEntry );
|
|
if ( pCurEntry && m_rThis.pModel->GetDepth(pCurEntry) <= nRefDepth)
|
|
pCurEntry = nullptr;
|
|
}
|
|
}
|
|
|
|
void SvListView::Impl::RemoveViewData( SvTreeListEntry* pParent )
|
|
{
|
|
for (auto const& it : pParent->m_Children)
|
|
{
|
|
SvTreeListEntry& rEntry = *it;
|
|
m_DataTable.erase(&rEntry);
|
|
if (rEntry.HasChildren())
|
|
RemoveViewData(&rEntry);
|
|
}
|
|
}
|
|
|
|
|
|
void SvListView::Impl::ActionRemoving( SvTreeListEntry* pEntry )
|
|
{
|
|
assert(pEntry && "Remove:No Entry");
|
|
const auto iter = m_DataTable.find(pEntry);
|
|
assert(iter != m_DataTable.end());
|
|
SvViewDataEntry* pViewData = iter->second.get();
|
|
sal_uInt32 nSelRemoved = 0;
|
|
if ( pViewData->IsSelected() )
|
|
nSelRemoved = 1 + m_rThis.pModel->GetChildSelectionCount(&m_rThis, pEntry);
|
|
m_nSelectionCount -= nSelRemoved;
|
|
sal_uInt32 nVisibleRemoved = 0;
|
|
if (m_rThis.pModel->IsEntryVisible(&m_rThis, pEntry))
|
|
nVisibleRemoved = 1 + m_rThis.pModel->GetVisibleChildCount(&m_rThis, pEntry);
|
|
if( m_nVisibleCount )
|
|
{
|
|
#ifdef DBG_UTIL
|
|
if (m_nVisibleCount < nVisibleRemoved)
|
|
{
|
|
OSL_FAIL("nVisibleRemoved bad");
|
|
}
|
|
#endif
|
|
m_nVisibleCount -= nVisibleRemoved;
|
|
}
|
|
m_bVisPositionsValid = false;
|
|
|
|
m_DataTable.erase(pEntry);
|
|
RemoveViewData( pEntry );
|
|
|
|
SvTreeListEntry* pCurEntry = pEntry->pParent;
|
|
if (pCurEntry && pCurEntry != m_rThis.pModel->pRootItem.get() && pCurEntry->m_Children.size() == 1)
|
|
{
|
|
SvDataTable::iterator itr = m_DataTable.find(pCurEntry);
|
|
assert(itr != m_DataTable.end() && "Entry not in Table");
|
|
pViewData = itr->second.get();
|
|
pViewData->SetExpanded(false);
|
|
}
|
|
}
|
|
|
|
void SvListView::Impl::ActionClear()
|
|
{
|
|
m_rThis.Clear();
|
|
}
|
|
|
|
void SvListView::ModelNotification( SvListAction nActionId, SvTreeListEntry* pEntry1,
|
|
SvTreeListEntry* /*pEntry2*/, sal_uInt32 /*nPos*/ )
|
|
{
|
|
|
|
switch( nActionId )
|
|
{
|
|
case SvListAction::INSERTED:
|
|
m_pImpl->ActionInserted( pEntry1 );
|
|
ModelHasInserted( pEntry1 );
|
|
break;
|
|
case SvListAction::INSERTED_TREE:
|
|
m_pImpl->ActionInsertedTree( pEntry1 );
|
|
ModelHasInsertedTree( pEntry1 );
|
|
break;
|
|
case SvListAction::REMOVING:
|
|
ModelIsRemoving( pEntry1 );
|
|
m_pImpl->ActionRemoving( pEntry1 );
|
|
break;
|
|
case SvListAction::REMOVED:
|
|
ModelHasRemoved( pEntry1 );
|
|
break;
|
|
case SvListAction::MOVING:
|
|
ModelIsMoving( pEntry1 );
|
|
m_pImpl->ActionMoving( pEntry1 );
|
|
break;
|
|
case SvListAction::MOVED:
|
|
m_pImpl->ActionMoved();
|
|
ModelHasMoved( pEntry1 );
|
|
break;
|
|
case SvListAction::CLEARING:
|
|
m_pImpl->ActionClear();
|
|
ModelHasCleared(); // sic! for compatibility reasons!
|
|
break;
|
|
case SvListAction::CLEARED:
|
|
break;
|
|
case SvListAction::INVALIDATE_ENTRY:
|
|
// no action for the base class
|
|
ModelHasEntryInvalidated( pEntry1 );
|
|
break;
|
|
case SvListAction::RESORTED:
|
|
m_pImpl->m_bVisPositionsValid = false;
|
|
break;
|
|
case SvListAction::RESORTING:
|
|
break;
|
|
default:
|
|
OSL_FAIL("unknown ActionId");
|
|
}
|
|
}
|
|
|
|
void SvListView::InitViewData( SvViewDataEntry*, SvTreeListEntry* )
|
|
{
|
|
}
|
|
|
|
bool SvListView::IsExpanded( SvTreeListEntry* pEntry ) const
|
|
{
|
|
DBG_ASSERT(pEntry,"IsExpanded:No Entry");
|
|
SvDataTable::const_iterator itr = m_pImpl->m_DataTable.find(pEntry);
|
|
DBG_ASSERT(itr != m_pImpl->m_DataTable.end(),"Entry not in Table");
|
|
if (itr == m_pImpl->m_DataTable.end())
|
|
return false;
|
|
return itr->second->IsExpanded();
|
|
}
|
|
|
|
bool SvListView::IsAllExpanded( SvTreeListEntry* pEntry ) const
|
|
{
|
|
DBG_ASSERT(pEntry,"IsAllExpanded:No Entry");
|
|
if (!IsExpanded(pEntry))
|
|
return false;
|
|
const SvTreeListEntries& rChildren = pEntry->GetChildEntries();
|
|
for (auto& rChild : rChildren)
|
|
{
|
|
if (rChild->HasChildren() || rChild->HasChildrenOnDemand())
|
|
{
|
|
if (!IsAllExpanded(rChild.get()))
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool SvListView::IsSelected(const SvTreeListEntry* pEntry) const
|
|
{
|
|
DBG_ASSERT(pEntry,"IsExpanded:No Entry");
|
|
SvDataTable::const_iterator itr = m_pImpl->m_DataTable.find(const_cast<SvTreeListEntry*>(pEntry));
|
|
if (itr == m_pImpl->m_DataTable.end())
|
|
return false;
|
|
return itr->second->IsSelected();
|
|
}
|
|
|
|
void SvListView::SetEntryFocus( SvTreeListEntry* pEntry, bool bFocus )
|
|
{
|
|
DBG_ASSERT(pEntry,"SetEntryFocus:No Entry");
|
|
SvDataTable::iterator itr = m_pImpl->m_DataTable.find(pEntry);
|
|
assert(itr != m_pImpl->m_DataTable.end() && "Entry not in Table");
|
|
itr->second->SetFocus(bFocus);
|
|
}
|
|
|
|
const SvViewDataEntry* SvListView::GetViewData( const SvTreeListEntry* pEntry ) const
|
|
{
|
|
SvDataTable::const_iterator itr =
|
|
m_pImpl->m_DataTable.find(const_cast<SvTreeListEntry*>(pEntry));
|
|
assert(itr != m_pImpl->m_DataTable.end() && "Entry not in model or wrong view");
|
|
if (itr == m_pImpl->m_DataTable.end())
|
|
return nullptr;
|
|
return itr->second.get();
|
|
}
|
|
|
|
SvViewDataEntry* SvListView::GetViewData( SvTreeListEntry* pEntry )
|
|
{
|
|
return const_cast<SvViewDataEntry*>(const_cast<const SvListView*>(this)->GetViewData(pEntry));
|
|
}
|
|
|
|
sal_Int32 SvTreeList::Compare(const SvTreeListEntry* pLeft, const SvTreeListEntry* pRight) const
|
|
{
|
|
if( aCompareLink.IsSet())
|
|
{
|
|
SvSortData aSortData;
|
|
aSortData.pLeft = pLeft;
|
|
aSortData.pRight = pRight;
|
|
return aCompareLink.Call( aSortData );
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void SvTreeList::Resort()
|
|
{
|
|
Broadcast( SvListAction::RESORTING );
|
|
bAbsPositionsValid = false;
|
|
ResortChildren( pRootItem.get() );
|
|
Broadcast( SvListAction::RESORTED );
|
|
}
|
|
|
|
namespace {
|
|
|
|
class SortComparator
|
|
{
|
|
SvTreeList& mrList;
|
|
public:
|
|
|
|
explicit SortComparator( SvTreeList& rList ) : mrList(rList) {}
|
|
|
|
bool operator() (std::unique_ptr<SvTreeListEntry> const& rpLeft,
|
|
std::unique_ptr<SvTreeListEntry> const& rpRight) const
|
|
{
|
|
int nCompare = mrList.Compare(rpLeft.get(), rpRight.get());
|
|
if (nCompare != 0 && mrList.GetSortMode() == SvSortMode::Descending)
|
|
{
|
|
if( nCompare < 0 )
|
|
nCompare = 1;
|
|
else
|
|
nCompare = -1;
|
|
}
|
|
return nCompare < 0;
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
void SvTreeList::ResortChildren( SvTreeListEntry* pParent )
|
|
{
|
|
assert(pParent && "Parent not set");
|
|
|
|
if (pParent->m_Children.empty())
|
|
return;
|
|
|
|
SortComparator aComp(*this);
|
|
std::sort(pParent->m_Children.begin(), pParent->m_Children.end(), aComp);
|
|
|
|
// Recursively sort child entries.
|
|
for (auto const& it : pParent->m_Children)
|
|
{
|
|
SvTreeListEntry& r = *it;
|
|
ResortChildren(&r);
|
|
}
|
|
|
|
SetListPositions(pParent->m_Children); // correct list position in target list
|
|
}
|
|
|
|
void SvTreeList::GetInsertionPos( SvTreeListEntry const * pEntry, SvTreeListEntry* pParent,
|
|
sal_uInt32& rPos )
|
|
{
|
|
DBG_ASSERT(pEntry,"No Entry");
|
|
|
|
if( eSortMode == SvSortMode::None )
|
|
return;
|
|
|
|
rPos = TREELIST_ENTRY_NOTFOUND;
|
|
const SvTreeListEntries& rChildList = GetChildList(pParent);
|
|
|
|
if (rChildList.empty())
|
|
return;
|
|
|
|
tools::Long i = 0;
|
|
tools::Long j = rChildList.size()-1;
|
|
tools::Long k;
|
|
sal_Int32 nCompare = 1;
|
|
|
|
do
|
|
{
|
|
k = (i+j)/2;
|
|
const SvTreeListEntry* pTempEntry = rChildList[k].get();
|
|
nCompare = Compare( pEntry, pTempEntry );
|
|
if (nCompare != 0 && eSortMode == SvSortMode::Descending)
|
|
{
|
|
if( nCompare < 0 )
|
|
nCompare = 1;
|
|
else
|
|
nCompare = -1;
|
|
}
|
|
if( nCompare > 0 )
|
|
i = k + 1;
|
|
else
|
|
j = k - 1;
|
|
} while( (nCompare != 0) && (i <= j) );
|
|
|
|
if( nCompare != 0 )
|
|
{
|
|
if (i > static_cast<tools::Long>(rChildList.size()-1)) // not found, end of list
|
|
rPos = TREELIST_ENTRY_NOTFOUND;
|
|
else
|
|
rPos = i; // not found, middle of list
|
|
}
|
|
else
|
|
rPos = k;
|
|
}
|
|
|
|
SvTreeListEntry* SvTreeList::GetEntry( SvTreeListEntry* pParent, sal_uInt32 nPos ) const
|
|
{ if ( !pParent )
|
|
pParent = pRootItem.get();
|
|
SvTreeListEntry* pRet = nullptr;
|
|
if (nPos < pParent->m_Children.size())
|
|
pRet = pParent->m_Children[nPos].get();
|
|
return pRet;
|
|
}
|
|
|
|
SvTreeListEntry* SvTreeList::GetEntry( sal_uInt32 nRootPos ) const
|
|
{
|
|
SvTreeListEntry* pRet = nullptr;
|
|
if (nEntryCount && nRootPos < pRootItem->m_Children.size())
|
|
pRet = pRootItem->m_Children[nRootPos].get();
|
|
return pRet;
|
|
}
|
|
|
|
const SvTreeListEntries& SvTreeList::GetChildList( SvTreeListEntry* pParent ) const
|
|
{
|
|
if ( !pParent )
|
|
pParent = pRootItem.get();
|
|
return pParent->m_Children;
|
|
}
|
|
|
|
SvTreeListEntries& SvTreeList::GetChildList( SvTreeListEntry* pParent )
|
|
{
|
|
if ( !pParent )
|
|
pParent = pRootItem.get();
|
|
return pParent->m_Children;
|
|
}
|
|
|
|
const SvTreeListEntry* SvTreeList::GetParent( const SvTreeListEntry* pEntry ) const
|
|
{
|
|
const SvTreeListEntry* pParent = pEntry->pParent;
|
|
if (pParent == pRootItem.get())
|
|
pParent = nullptr;
|
|
return pParent;
|
|
}
|
|
|
|
SvTreeListEntry* SvTreeList::GetParent( SvTreeListEntry* pEntry )
|
|
{
|
|
SvTreeListEntry* pParent = pEntry->pParent;
|
|
if (pParent == pRootItem.get())
|
|
pParent = nullptr;
|
|
return pParent;
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|