/* -*- 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/svapp.hxx>
#include <vcl/salnativewidgets.hxx>
#include <vcl/help.hxx>
#include <vcl/settings.hxx>
#include <vcl/commandevent.hxx>

#include <cstdlib>
#include <memory>
#include <stack>

#include <vcl/treelistbox.hxx>
#include <vcl/svlbitm.hxx>
#include <vcl/svimpbox.hxx>
#include <rtl/instance.hxx>
#include <tools/wintypes.hxx>
#include <bitmaps.hlst>
#include <comphelper/processfactory.hxx>
#include <comphelper/string.hxx>

#include <vcl/treelistentry.hxx>
#include <vcl/viewdataentry.hxx>

// #i27063# (pl), #i32300# (pb) never access VCL after DeInitVCL - also no destructors
Image*  SvImpLBox::s_pDefCollapsed      = nullptr;
Image*  SvImpLBox::s_pDefExpanded       = nullptr;
oslInterlockedCount SvImpLBox::s_nImageRefCount   = 0;

SvImpLBox::SvImpLBox( SvTreeListBox* pLBView, SvTreeList* pLBTree, WinBits nWinStyle)
    : aHorSBar(VclPtr<ScrollBar>::Create(pLBView, WB_DRAG | WB_HSCROLL))
    , aScrBarBox(VclPtr<ScrollBarBox>::Create(pLBView))
    , aFctSet(this, pLBView)
    , bAreChildrenTransient(true)
    , mbForceMakeVisible (false)
    , aVerSBar(VclPtr<ScrollBar>::Create(pLBView, WB_DRAG | WB_VSCROLL))
    , aOutputSize(0, 0)
    , mbNoAutoCurEntry(false)
    , aSelEng(pLBView, nullptr)
    , nNextVerVisSize(0)
{
    osl_atomic_increment(&s_nImageRefCount);
    pView = pLBView;
    pTree = pLBTree;
    aSelEng.SetFunctionSet( static_cast<FunctionSet*>(&aFctSet) );
    aSelEng.ExpandSelectionOnMouseMove( false );
    SetStyle( nWinStyle );
    SetSelectionMode( SelectionMode::Single );
    SetDragDropMode( DragDropMode::NONE );

    aVerSBar->SetScrollHdl( LINK( this, SvImpLBox, ScrollUpDownHdl ) );
    aHorSBar->SetScrollHdl( LINK( this, SvImpLBox, ScrollLeftRightHdl ) );
    aHorSBar->SetEndScrollHdl( LINK( this, SvImpLBox, EndScrollHdl ) );
    aVerSBar->SetEndScrollHdl( LINK( this, SvImpLBox, EndScrollHdl ) );
    aVerSBar->SetRange( Range(0,0) );
    aVerSBar->Hide();
    aHorSBar->SetRange( Range(0,0) );
    aHorSBar->SetPageSize( 24 ); // pixels
    aHorSBar->SetLineSize( 8 ); // pixels

    nHorSBarHeight = static_cast<short>(aHorSBar->GetSizePixel().Height());
    nVerSBarWidth = static_cast<short>(aVerSBar->GetSizePixel().Width());

    pStartEntry = nullptr;
    pCursor             = nullptr;
    pAnchor             = nullptr;
    nVisibleCount       = 0;    // number of rows of data in control
    nNodeBmpTabDistance = NODE_BMP_TABDIST_NOTVALID;
    nNodeBmpWidth       = 0;

    bAsyncBeginDrag     = false;
    aAsyncBeginDragIdle.SetPriority( TaskPriority::HIGHEST );
    aAsyncBeginDragIdle.SetInvokeHandler( LINK(this,SvImpLBox,BeginDragHdl));
    // button animation in listbox
    pActiveButton = nullptr;
    pActiveEntry = nullptr;
    pActiveTab = nullptr;

    nFlags = LBoxFlags::NONE;
    nCurTabPos = FIRST_ENTRY_TAB;

    aEditIdle.SetPriority( TaskPriority::LOWEST );
    aEditIdle.SetInvokeHandler( LINK(this,SvImpLBox,EditTimerCall) );

    nMostRight = -1;
    pMostRightEntry = nullptr;
    nCurUserEvent = nullptr;

    bUpdateMode = true;
    bInVScrollHdl = false;
    nFlags |= LBoxFlags::Filling;

    bSubLstOpRet = bSubLstOpLR = bContextMenuHandling = bIsCellFocusEnabled = false;
    bSubLstOpDblClick = true;
}

SvImpLBox::~SvImpLBox()
{
    aEditIdle.Stop();
    StopUserEvent();

    if ( osl_atomic_decrement(&s_nImageRefCount) == 0 )
    {
        DELETEZ(s_pDefCollapsed);
        DELETEZ(s_pDefExpanded);
    }
    aVerSBar.disposeAndClear();
    aHorSBar.disposeAndClear();
    aScrBarBox.disposeAndClear();
}

void SvImpLBox::UpdateStringSorter()
{
    const css::lang::Locale& rNewLocale = Application::GetSettings().GetLanguageTag().getLocale();

    if( m_pStringSorter )
    {
        // different Locale from the older one, drop it and force recreate
        const css::lang::Locale &aLocale = m_pStringSorter->getLocale();
        if( aLocale.Language != rNewLocale.Language ||
            aLocale.Country != rNewLocale.Country ||
            aLocale.Variant != rNewLocale.Variant )
            m_pStringSorter.reset();
    }

    if( !m_pStringSorter )
    {
        m_pStringSorter.reset(new comphelper::string::NaturalStringSorter(
                              ::comphelper::getProcessComponentContext(),
                              rNewLocale));
    }
}

short SvImpLBox::UpdateContextBmpWidthVector( SvTreeListEntry const * pEntry, short nWidth )
{
    DBG_ASSERT( pView->pModel, "View and Model aren't valid!" );

    sal_uInt16 nDepth = pView->pModel->GetDepth( pEntry );
    // initialize vector if necessary
    std::vector< short >::size_type nSize = aContextBmpWidthVector.size();
    while ( nDepth > nSize )
    {
        aContextBmpWidthVector.resize( nSize + 1 );
        aContextBmpWidthVector.at( nSize ) = nWidth;
        ++nSize;
    }
    if( aContextBmpWidthVector.size() == nDepth )
    {
        aContextBmpWidthVector.resize( nDepth + 1 );
        aContextBmpWidthVector.at( nDepth ) = 0;
    }
    short nContextBmpWidth = aContextBmpWidthVector[ nDepth ];
    if( nContextBmpWidth < nWidth )
    {
        aContextBmpWidthVector.at( nDepth ) = nWidth;
        return nWidth;
    }
    else
        return nContextBmpWidth;
}

void SvImpLBox::UpdateContextBmpWidthVectorFromMovedEntry( SvTreeListEntry* pEntry )
{
    DBG_ASSERT( pEntry, "Moved Entry is invalid!" );

    SvLBoxContextBmp* pBmpItem = static_cast< SvLBoxContextBmp* >( pEntry->GetFirstItem(SvLBoxItemType::ContextBmp) );
    short nExpWidth = static_cast<short>(pBmpItem->GetBitmap1().GetSizePixel().Width());
    short nColWidth = static_cast<short>(pBmpItem->GetBitmap2().GetSizePixel().Width());
    short nMax = std::max(nExpWidth, nColWidth);
    UpdateContextBmpWidthVector( pEntry, nMax );

    if( pEntry->HasChildren() ) // recursive call, whether expanded or not
    {
        SvTreeListEntry* pChild = pView->FirstChild( pEntry );
        DBG_ASSERT( pChild, "The first child is invalid!" );
        do
        {
            UpdateContextBmpWidthVectorFromMovedEntry( pChild );
            pChild = pView->Next( pChild );
        } while ( pChild );
    }
}

void SvImpLBox::UpdateContextBmpWidthMax( SvTreeListEntry const * pEntry )
{
    sal_uInt16 nDepth = pView->pModel->GetDepth( pEntry );
    if( aContextBmpWidthVector.empty() )
        return;
    short nWidth = aContextBmpWidthVector[ nDepth ];
    if( nWidth != pView->nContextBmpWidthMax ) {
        pView->nContextBmpWidthMax = nWidth;
        nFlags |= LBoxFlags::IgnoreChangedTabs;
        pView->SetTabs();
        nFlags &= ~LBoxFlags::IgnoreChangedTabs;
    }
}

void SvImpLBox::CalcCellFocusRect( SvTreeListEntry const * pEntry, tools::Rectangle& rRect )
{
    if ( !(pEntry && bIsCellFocusEnabled) )
        return;

    if ( nCurTabPos > FIRST_ENTRY_TAB )
    {
        SvLBoxItem& rItem = pCursor->GetItem( nCurTabPos );
        rRect.SetLeft( pView->GetTab( pCursor, &rItem )->GetPos() );
    }
    if (pCursor->ItemCount() > static_cast<size_t>(nCurTabPos+1))
    {
        SvLBoxItem& rNextItem = pCursor->GetItem( nCurTabPos + 1 );
        long nRight = pView->GetTab( pCursor, &rNextItem )->GetPos() - 1;
        if ( nRight < rRect.Right() )
            rRect.SetRight( nRight );
    }
}

void SvImpLBox::SetStyle( WinBits i_nWinStyle )
{
    m_nStyle = i_nWinStyle;
    if ( ( m_nStyle & WB_SIMPLEMODE) && ( aSelEng.GetSelectionMode() == SelectionMode::Multiple ) )
        aSelEng.AddAlways( true );
}

void SvImpLBox::SetNoAutoCurEntry( bool b )
{
    mbNoAutoCurEntry = b;
}

// don't touch the model any more
void SvImpLBox::Clear()
{
    StopUserEvent();
    pStartEntry = nullptr;
    pAnchor = nullptr;

    pActiveButton = nullptr;
    pActiveEntry = nullptr;
    pActiveTab = nullptr;

    nMostRight = -1;
    pMostRightEntry = nullptr;

    // don't touch the cursor any more
    if( pCursor )
    {
        if( pView->HasFocus() )
            pView->HideFocus();
        pCursor = nullptr;
    }
    aVerSBar->Hide();
    aVerSBar->SetThumbPos( 0 );
    Range aRange( 0, 0 );
    aVerSBar->SetRange( aRange );
    aOutputSize = pView->Control::GetOutputSizePixel();
    aHorSBar->Hide();
    aHorSBar->SetThumbPos( 0 );
    MapMode aMapMode( pView->GetMapMode());
    aMapMode.SetOrigin( Point(0,0) );
    pView->Control::SetMapMode( aMapMode );
    aHorSBar->SetRange( aRange );
    aHorSBar->SetSizePixel(Size(aOutputSize.Width(),nHorSBarHeight));
    pView->SetClipRegion();
    if( GetUpdateMode() )
        pView->Invalidate( GetVisibleArea() );
    nFlags |= LBoxFlags::Filling;
    if( !aHorSBar->IsVisible() && !aVerSBar->IsVisible() )
        aScrBarBox->Hide();

    aContextBmpWidthVector.clear();

    CallEventListeners( VclEventId::ListboxItemRemoved );
}

// *********************************************************************
// Paint, navigate, scroll
// *********************************************************************

IMPL_LINK_NOARG(SvImpLBox, EndScrollHdl, ScrollBar*, void)
{
    if( nFlags & LBoxFlags::EndScrollSetVisSize )
    {
        aVerSBar->SetVisibleSize( nNextVerVisSize );
        nFlags &= ~LBoxFlags::EndScrollSetVisSize;
    }
    EndScroll();
}

// handler for vertical scrollbar

IMPL_LINK( SvImpLBox, ScrollUpDownHdl, ScrollBar *, pScrollBar, void )
{
    DBG_ASSERT(!bInVScrollHdl,"Scroll handler out-paces itself!");
    long nDelta = pScrollBar->GetDelta();
    if( !nDelta )
        return;

    nFlags &= (~LBoxFlags::Filling);

    bInVScrollHdl = true;

    if( pView->IsEditingActive() )
    {
        pView->EndEditing( true ); // Cancel
        pView->Update();
    }
    BeginScroll();

    if( nDelta > 0 )
    {
        if( nDelta == 1 )
            CursorDown();
        else
            PageDown( static_cast<sal_uInt16>(nDelta) );
    }
    else
    {
        nDelta *= -1;
        if( nDelta == 1 )
            CursorUp();
        else
            PageUp( static_cast<sal_uInt16>(nDelta) );
    }
    bInVScrollHdl = false;
}


void SvImpLBox::CursorDown()
{
    if (!pStartEntry)
        return;

    SvTreeListEntry* pNextFirstToDraw = pView->NextVisible(pStartEntry);
    if( pNextFirstToDraw )
    {
        nFlags &= (~LBoxFlags::Filling);
        ShowCursor( false );
        pView->Update();
        pStartEntry = pNextFirstToDraw;
        tools::Rectangle aArea( GetVisibleArea() );
        pView->Scroll( 0, -(pView->GetEntryHeight()), aArea, ScrollFlags::NoChildren );
        pView->Update();
        ShowCursor( true );
        pView->NotifyScrolled();
    }
}

void SvImpLBox::CursorUp()
{
    if (!pStartEntry)
        return;

    SvTreeListEntry* pPrevFirstToDraw = pView->PrevVisible(pStartEntry);
    if( !pPrevFirstToDraw )
        return;

    nFlags &= (~LBoxFlags::Filling);
    long nEntryHeight = pView->GetEntryHeight();
    ShowCursor( false );
    pView->Update();
    pStartEntry = pPrevFirstToDraw;
    tools::Rectangle aArea( GetVisibleArea() );
    aArea.AdjustBottom( -nEntryHeight );
    pView->Scroll( 0, nEntryHeight, aArea, ScrollFlags::NoChildren );
    pView->Update();
    ShowCursor( true );
    pView->NotifyScrolled();
}

void SvImpLBox::PageDown( sal_uInt16 nDelta )
{
    sal_uInt16 nRealDelta = nDelta;

    if( !nDelta )
        return;

    if (!pStartEntry)
        return;

    SvTreeListEntry* pNext = pView->NextVisible(pStartEntry, nRealDelta);
    if( pNext == pStartEntry )
        return;

    ShowCursor( false );

    nFlags &= (~LBoxFlags::Filling);
    pView->Update();
    pStartEntry = pNext;

    if( nRealDelta >= nVisibleCount )
    {
        pView->Invalidate( GetVisibleArea() );
        pView->Update();
    }
    else
    {
        tools::Rectangle aArea( GetVisibleArea() );
        long nScroll = pView->GetEntryHeight() * static_cast<long>(nRealDelta);
        nScroll = -nScroll;
        pView->Update();
        pView->Scroll( 0, nScroll, aArea, ScrollFlags::NoChildren );
        pView->Update();
        pView->NotifyScrolled();
    }

    ShowCursor( true );
}

void SvImpLBox::PageUp( sal_uInt16 nDelta )
{
    sal_uInt16 nRealDelta = nDelta;
    if( !nDelta )
        return;

    if (!pStartEntry)
        return;

    SvTreeListEntry* pPrev = pView->PrevVisible(pStartEntry, nRealDelta);
    if( pPrev == pStartEntry )
        return;

    nFlags &= (~LBoxFlags::Filling);
    ShowCursor( false );

    pView->Update();
    pStartEntry = pPrev;
    if( nRealDelta >= nVisibleCount )
    {
        pView->Invalidate( GetVisibleArea() );
        pView->Update();
    }
    else
    {
        long nEntryHeight = pView->GetEntryHeight();
        tools::Rectangle aArea( GetVisibleArea() );
        pView->Update();
        pView->Scroll( 0, nEntryHeight*nRealDelta, aArea, ScrollFlags::NoChildren );
        pView->Update();
        pView->NotifyScrolled();
    }

    ShowCursor( true );
}

void SvImpLBox::KeyUp( bool bPageUp )
{
    if( !aVerSBar->IsVisible() )
        return;

    long nDelta;
    if( bPageUp )
        nDelta = aVerSBar->GetPageSize();
    else
        nDelta = 1;

    long nThumbPos = aVerSBar->GetThumbPos();

    if( nThumbPos < nDelta )
        nDelta = nThumbPos;

    if( nDelta <= 0 )
        return;

    nFlags &= (~LBoxFlags::Filling);
    BeginScroll();

    aVerSBar->SetThumbPos( nThumbPos - nDelta );
    if( bPageUp )
        PageUp( static_cast<short>(nDelta) );
    else
        CursorUp();

    EndScroll();
}


void SvImpLBox::KeyDown( bool bPageDown )
{
    if( !aVerSBar->IsVisible() )
        return;

    long nDelta;
    if( bPageDown )
        nDelta = aVerSBar->GetPageSize();
    else
        nDelta = 1;

    long nThumbPos = aVerSBar->GetThumbPos();
    long nVisibleSize = aVerSBar->GetVisibleSize();
    long nRange = aVerSBar->GetRange().Len();

    long nTmp = nThumbPos+nVisibleSize;
    while( (nDelta > 0) && (nTmp+nDelta) >= nRange )
        nDelta--;

    if( nDelta <= 0 )
        return;

    nFlags &= (~LBoxFlags::Filling);
    BeginScroll();

    aVerSBar->SetThumbPos( nThumbPos+nDelta );
    if( bPageDown )
        PageDown( static_cast<short>(nDelta) );
    else
        CursorDown();

    EndScroll();
}


void SvImpLBox::InvalidateEntriesFrom( long nY ) const
{
    if( !(nFlags & LBoxFlags::InPaint ))
    {
        tools::Rectangle aRect( GetVisibleArea() );
        aRect.SetTop( nY );
        pView->Invalidate( aRect );
    }
}

void SvImpLBox::InvalidateEntry( long nY ) const
{
    if( !(nFlags & LBoxFlags::InPaint ))
    {
        tools::Rectangle aRect( GetVisibleArea() );
        long nMaxBottom = aRect.Bottom();
        aRect.SetTop( nY );
        aRect.SetBottom( nY ); aRect.AdjustBottom(pView->GetEntryHeight() );
        if( aRect.Top() > nMaxBottom )
            return;
        if( aRect.Bottom() > nMaxBottom )
            aRect.SetBottom( nMaxBottom );
        pView->Invalidate( aRect );
    }
}

void SvImpLBox::InvalidateEntry( SvTreeListEntry* pEntry )
{
    if( GetUpdateMode() )
    {
        long nPrev = nMostRight;
        SetMostRight( pEntry );
        if( nPrev < nMostRight )
            ShowVerSBar();
    }
    if( !(nFlags & LBoxFlags::InPaint ))
    {
        bool bHasFocusRect = false;
        if( pEntry==pCursor && pView->HasFocus() )
        {
            bHasFocusRect = true;
            ShowCursor( false );
        }
        InvalidateEntry( GetEntryLine( pEntry ) );
        if( bHasFocusRect )
            ShowCursor( true );
    }
}


void SvImpLBox::RecalcFocusRect()
{
    if( pView->HasFocus() && pCursor )
    {
        pView->HideFocus();
        long nY = GetEntryLine( pCursor );
        tools::Rectangle aRect = pView->GetFocusRect( pCursor, nY );
        CalcCellFocusRect( pCursor, aRect );
        vcl::Region aOldClip( pView->GetClipRegion());
        vcl::Region aClipRegion( GetClipRegionRect() );
        pView->SetClipRegion( aClipRegion );
        pView->ShowFocus( aRect );
        pView->SetClipRegion( aOldClip );
    }
}


//  Sets cursor. When using SingleSelection, the selection is adjusted.


void SvImpLBox::SetCursor( SvTreeListEntry* pEntry, bool bForceNoSelect )
{
    SvViewDataEntry* pViewDataNewCur = nullptr;
    if( pEntry )
        pViewDataNewCur= pView->GetViewDataEntry(pEntry);
    if( pEntry &&
        pEntry == pCursor &&
        pViewDataNewCur &&
        pViewDataNewCur->HasFocus() &&
        pViewDataNewCur->IsSelected())
    {
        return;
    }

    // if this cursor is not selectable, find first visible that is and use it
    while( pEntry && pViewDataNewCur && !pViewDataNewCur->IsSelectable() )
    {
        pEntry = pView->NextVisible(pEntry);
        pViewDataNewCur = pEntry ? pView->GetViewDataEntry(pEntry) : nullptr;
    }

    SvTreeListEntry* pOldCursor = pCursor;
    if( pCursor && pEntry != pCursor )
    {
        pView->SetEntryFocus( pCursor, false );
        if( bSimpleTravel )
            pView->Select( pCursor, false );
        pView->HideFocus();
    }
    pCursor = pEntry;
    if( pCursor )
    {
        if (pViewDataNewCur)
            pViewDataNewCur->SetFocus( true );
        if(!bForceNoSelect && bSimpleTravel && !(nFlags & LBoxFlags::DeselectAll) && GetUpdateMode())
        {
            pView->Select( pCursor );
            CallEventListeners( VclEventId::ListboxTreeFocus, pCursor );
        }
        // multiple selection: select in cursor move if we're not in
        // Add mode (Ctrl-F8)
        else if( GetUpdateMode() &&
                 pView->GetSelectionMode() == SelectionMode::Multiple &&
                 !(nFlags & LBoxFlags::DeselectAll) && !aSelEng.IsAddMode() &&
                 !bForceNoSelect )
        {
            pView->Select( pCursor );
            CallEventListeners( VclEventId::ListboxTreeFocus, pCursor );
        }
        else
        {
            ShowCursor( true );
            if (bForceNoSelect && GetUpdateMode())
            {
                CallEventListeners( VclEventId::ListboxTreeFocus, pCursor);
            }
        }

        if( pAnchor )
        {
            DBG_ASSERT(aSelEng.GetSelectionMode() != SelectionMode::Single,"Mode?");
            SetAnchorSelection( pOldCursor, pCursor );
        }
    }
    nFlags &= (~LBoxFlags::DeselectAll);

    pView->OnCurrentEntryChanged();
}

void SvImpLBox::ShowCursor( bool bShow )
{
    if( !bShow || !pCursor || !pView->HasFocus() )
    {
        vcl::Region aOldClip( pView->GetClipRegion());
        vcl::Region aClipRegion( GetClipRegionRect() );
        pView->SetClipRegion( aClipRegion );
        pView->HideFocus();
        pView->SetClipRegion( aOldClip );
    }
    else
    {
        long nY = GetEntryLine( pCursor );
        tools::Rectangle aRect = pView->GetFocusRect( pCursor, nY );
        CalcCellFocusRect( pCursor, aRect );
        vcl::Region aOldClip( pView->GetClipRegion());
        vcl::Region aClipRegion( GetClipRegionRect() );
        pView->SetClipRegion( aClipRegion );
        pView->ShowFocus( aRect );
        pView->SetClipRegion( aOldClip );
    }
}


void SvImpLBox::UpdateAll( bool bInvalidateCompleteView )
{
    FindMostRight(nullptr);
    aVerSBar->SetRange( Range(0, pView->GetVisibleCount()-1 ) );
    SyncVerThumb();
    FillView();
    ShowVerSBar();
    if( bSimpleTravel && pCursor && pView->HasFocus() )
        pView->Select( pCursor );
    ShowCursor( true );
    if( bInvalidateCompleteView )
        pView->Invalidate();
    else
        pView->Invalidate( GetVisibleArea() );
}

IMPL_LINK( SvImpLBox, ScrollLeftRightHdl, ScrollBar *, pScrollBar, void )
{
    long nDelta = pScrollBar->GetDelta();
    if( nDelta )
    {
        if( pView->IsEditingActive() )
        {
            pView->EndEditing( true ); // Cancel
            pView->Update();
        }
        pView->nFocusWidth = -1;
        KeyLeftRight( nDelta );
    }
}

void SvImpLBox::KeyLeftRight( long nDelta )
{
    if( !(nFlags & LBoxFlags::InResize) )
        pView->Update();
    BeginScroll();
    nFlags &= (~LBoxFlags::Filling);
    ShowCursor( false );

    // calculate new origin
    long nPos = aHorSBar->GetThumbPos();
    Point aOrigin( -nPos, 0 );

    MapMode aMapMode( pView->GetMapMode() );
    aMapMode.SetOrigin( aOrigin );
    pView->SetMapMode( aMapMode );

    if( !(nFlags & LBoxFlags::InResize) )
    {
        tools::Rectangle aRect( GetVisibleArea() );
        pView->Scroll( -nDelta, 0, aRect, ScrollFlags::NoChildren );
    }
    else
        pView->Invalidate();
    RecalcFocusRect();
    ShowCursor( true );
    pView->NotifyScrolled();
}


// returns the last entry if position is just past the last entry
SvTreeListEntry* SvImpLBox::GetClickedEntry( const Point& rPoint ) const
{
    DBG_ASSERT( pView->GetModel(), "SvImpLBox::GetClickedEntry: how can this ever happen? Please tell me (frank.schoenheit@sun.com) how to reproduce!" );
    if ( !pView->GetModel() )
        // this is quite impossible. Nevertheless, stack traces from the crash reporter
        // suggest it isn't. Okay, make it safe, and wait for somebody to reproduce it
        // reliably :-\ ....
        // #122359# / 2005-05-23 / frank.schoenheit@sun.com
        return nullptr;
    if( pView->GetEntryCount() == 0 || !pStartEntry || !pView->GetEntryHeight())
        return nullptr;

    sal_uInt16 nClickedEntry = static_cast<sal_uInt16>(rPoint.Y() / pView->GetEntryHeight() );
    sal_uInt16 nTemp = nClickedEntry;
    SvTreeListEntry* pEntry = pView->NextVisible(pStartEntry, nTemp);
    return pEntry;
}


//  checks if the entry was hit "the right way"
//  (Focusrect+ ContextBitmap at TreeListBox)

bool SvImpLBox::EntryReallyHit(SvTreeListEntry* pEntry, const Point& rPosPixel, long nLine)
{
    bool bRet;
    // we are not too exact when it comes to "special" entries
    // (with CheckButtons etc.)
    if( pEntry->ItemCount() >= 3 )
        return true;

    tools::Rectangle aRect( pView->GetFocusRect( pEntry, nLine ));
    aRect.SetRight( GetOutputSize().Width() - pView->GetMapMode().GetOrigin().X() );

    SvLBoxContextBmp* pBmp = static_cast<SvLBoxContextBmp*>(pEntry->GetFirstItem(SvLBoxItemType::ContextBmp));
    aRect.AdjustLeft( -(pBmp->GetSize(pView,pEntry).Width()) );
    aRect.AdjustLeft( -4 ); // a little tolerance

    Point aPos( rPosPixel );
    aPos -= pView->GetMapMode().GetOrigin();
    bRet = aRect.IsInside( aPos );
    return bRet;
}


// returns 0 if position is just past the last entry
SvTreeListEntry* SvImpLBox::GetEntry( const Point& rPoint ) const
{
    if( (pView->GetEntryCount() == 0) || !pStartEntry ||
        (rPoint.Y() > aOutputSize.Height())
        || !pView->GetEntryHeight())
        return nullptr;

    sal_uInt16 nClickedEntry = static_cast<sal_uInt16>(rPoint.Y() / pView->GetEntryHeight() );
    sal_uInt16 nTemp = nClickedEntry;
    SvTreeListEntry* pEntry = pView->NextVisible(pStartEntry, nTemp);
    if( nTemp != nClickedEntry )
        pEntry = nullptr;
    return pEntry;
}


SvTreeListEntry* SvImpLBox::MakePointVisible(const Point& rPoint)
{
    if( !pCursor )
        return nullptr;
    long nY = rPoint.Y();
    SvTreeListEntry* pEntry = nullptr;
    long nMax = aOutputSize.Height();
    if( nY < 0 || nY >= nMax ) // aOutputSize.Height() )
    {
        if( nY < 0 )
            pEntry = pView->PrevVisible(pCursor);
        else
            pEntry = pView->NextVisible(pCursor);

        if( pEntry && pEntry != pCursor )
            pView->SetEntryFocus( pCursor, false );

        if( nY < 0 )
            KeyUp( false );
        else
            KeyDown( false );
    }
    else
    {
        pEntry = GetClickedEntry( rPoint );
        if( !pEntry )
        {
            sal_uInt16 nSteps = 0xFFFF;
            // TODO: LastVisible is not yet implemented!
            pEntry = pView->NextVisible(pStartEntry, nSteps);
        }
        if( pEntry )
        {
            if( pEntry != pCursor &&
                 aSelEng.GetSelectionMode() == SelectionMode::Single
            )
                pView->Select( pCursor, false );
        }
    }
    return pEntry;
}

tools::Rectangle SvImpLBox::GetClipRegionRect() const
{
    Point aOrigin( pView->GetMapMode().GetOrigin() );
    aOrigin.setX( aOrigin.X() * -1 ); // conversion document coordinates
    tools::Rectangle aClipRect( aOrigin, aOutputSize );
    aClipRect.AdjustBottom( 1 );
    return aClipRect;
}


void SvImpLBox::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
{
    if (!pView->GetVisibleCount())
        return;

    nFlags |= LBoxFlags::InPaint;

    if (nFlags & LBoxFlags::Filling)
    {
        SvTreeListEntry* pFirst = pView->First();
        if (pFirst != pStartEntry)
        {
            ShowCursor(false);
            pStartEntry = pView->First();
            aVerSBar->SetThumbPos( 0 );
            StopUserEvent();
            ShowCursor(true);
            nCurUserEvent = Application::PostUserEvent(LINK(this, SvImpLBox, MyUserEvent),
                                                       reinterpret_cast<void*>(1));
            return;
        }
    }

    if (!pStartEntry)
    {
        pStartEntry = pView->First();
    }

    if (nNodeBmpTabDistance == NODE_BMP_TABDIST_NOTVALID)
        SetNodeBmpTabDistance();

    long nRectHeight = rRect.GetHeight();
    long nEntryHeight = pView->GetEntryHeight();

    // calculate area for the entries we want to draw
    sal_uInt16 nStartLine = static_cast<sal_uInt16>(rRect.Top() / nEntryHeight);
    sal_uInt16 nCount = static_cast<sal_uInt16>(nRectHeight / nEntryHeight);
    nCount += 2; // don't miss a row

    long nY = nStartLine * nEntryHeight;
    SvTreeListEntry* pEntry = pStartEntry;
    while (nStartLine && pEntry)
    {
        pEntry = pView->NextVisible(pEntry);
        nStartLine--;
    }

    vcl::Region aClipRegion(GetClipRegionRect());

    // first draw the lines, then clip them!
    rRenderContext.SetClipRegion();
    if (m_nStyle & (WB_HASLINES | WB_HASLINESATROOT))
        DrawNet(rRenderContext);

    rRenderContext.SetClipRegion(aClipRegion);

    if (!pCursor && !mbNoAutoCurEntry)
    {
        // do not select if multiselection or explicit set
        bool bNotSelect = (aSelEng.GetSelectionMode() == SelectionMode::Multiple ) || ((m_nStyle & WB_NOINITIALSELECTION) == WB_NOINITIALSELECTION);
        SetCursor(pStartEntry, bNotSelect);
    }

    for(sal_uInt16 n=0; n< nCount && pEntry; n++)
    {
        /*long nMaxRight=*/
        pView->PaintEntry1(*pEntry, nY, rRenderContext );
        nY += nEntryHeight;
        pEntry = pView->NextVisible(pEntry);
    }

    nFlags &= (~LBoxFlags::DeselectAll);
    rRenderContext.SetClipRegion();
    nFlags &= (~LBoxFlags::InPaint);
}

void SvImpLBox::MakeVisible( SvTreeListEntry* pEntry, bool bMoveToTop )
{
    if( !pEntry )
        return;

    bool bInView = IsEntryInView( pEntry );

    if( bInView && (!bMoveToTop || pStartEntry == pEntry) )
        return;  // is already visible

    if( pStartEntry || mbForceMakeVisible )
        nFlags &= (~LBoxFlags::Filling);
    if( !bInView )
    {
        if( !pView->IsEntryVisible(pEntry) )  // Parent(s) collapsed?
        {
            SvTreeListEntry* pParent = pView->GetParent( pEntry );
            while( pParent )
            {
                if( !pView->IsExpanded( pParent ) )
                {
                    bool bRet = pView->Expand( pParent );
                    DBG_ASSERT(bRet,"Not expanded!");
                }
                pParent = pView->GetParent( pParent );
            }
            // do the parent's children fit into the view or do we have to scroll?
            if( IsEntryInView( pEntry ) && !bMoveToTop )
                return;  // no need to scroll
        }
    }

    pStartEntry = pEntry;
    ShowCursor( false );
    FillView();
    aVerSBar->SetThumbPos( static_cast<long>(pView->GetVisiblePos( pStartEntry )) );
    ShowCursor( true );
    pView->Invalidate();
}

void SvImpLBox::ScrollToAbsPos( long nPos )
{
    if( pView->GetVisibleCount() == 0 )
        return;
    long nLastEntryPos = pView->GetAbsPos( pView->Last() );

    if( nPos < 0 )
        nPos = 0;
    else if( nPos > nLastEntryPos )
        nPos = nLastEntryPos;

    SvTreeListEntry* pEntry = pView->GetEntryAtAbsPos( nPos );
    if( !pEntry || pEntry == pStartEntry )
        return;

    if( pStartEntry || mbForceMakeVisible )
        nFlags &= (~LBoxFlags::Filling);

    if( pView->IsEntryVisible(pEntry) )
    {
        pStartEntry = pEntry;
        ShowCursor( false );
        aVerSBar->SetThumbPos( nPos );
        ShowCursor( true );
        if (GetUpdateMode())
            pView->Invalidate();
    }
}

void SvImpLBox::DrawNet(vcl::RenderContext& rRenderContext)
{
    if (pView->GetVisibleCount() < 2 && !pStartEntry->HasChildrenOnDemand() &&
        !pStartEntry->HasChildren())
    {
        return;
    }

    // for platforms that don't have nets, DrawNativeControl does nothing and returns true
    // so that SvImpLBox::DrawNet() doesn't draw anything either
     if (rRenderContext.IsNativeControlSupported(ControlType::ListNet, ControlPart::Entire))
     {
        ImplControlValue aControlValue;
        if (rRenderContext.DrawNativeControl(ControlType::ListNet, ControlPart::Entire,
                                             tools::Rectangle(), ControlState::ENABLED, aControlValue, OUString()))
        {
            return;
        }
    }

    long nEntryHeight = pView->GetEntryHeight();
    long nEntryHeightDIV2 = nEntryHeight / 2;
    if( nEntryHeightDIV2 && !(nEntryHeight & 0x0001))
        nEntryHeightDIV2--;

    SvTreeListEntry* pChild;
    SvTreeListEntry* pEntry = pStartEntry;

    SvLBoxTab* pFirstDynamicTab = pView->GetFirstDynamicTab();
    while (pTree->GetDepth( pEntry ) > 0)
    {
        pEntry = pView->GetParent(pEntry);
    }
    sal_uInt16 nOffs = static_cast<sal_uInt16>(pView->GetVisiblePos(pStartEntry) - pView->GetVisiblePos(pEntry));
    long nY = 0;
    nY -= (nOffs * nEntryHeight);

    DBG_ASSERT(pFirstDynamicTab,"No Tree!");

    rRenderContext.Push(PushFlags::LINECOLOR);

    const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
    Color aCol = rStyleSettings.GetFaceColor();

    if (aCol.IsRGBEqual(rRenderContext.GetBackground().GetColor()))
        aCol = rStyleSettings.GetShadowColor();
    rRenderContext.SetLineColor(aCol);
    Point aPos1, aPos2;
    sal_uInt16 nDistance;
    sal_uLong nMax = nVisibleCount + nOffs + 1;

    const Image& rExpandedNodeBitmap = GetExpandedNodeBmp();

    for (sal_uLong n=0; n< nMax && pEntry; n++)
    {
        if (pView->IsExpanded(pEntry))
        {
            aPos1.setX( pView->GetTabPos(pEntry, pFirstDynamicTab) );
            // if it is not a context bitmap, go a little to the right below the
            // first text (node bitmap, too)
            if (!pView->nContextBmpWidthMax)
                aPos1.AdjustX(rExpandedNodeBitmap.GetSizePixel().Width() / 2 );

            aPos1.setY( nY );
            aPos1.AdjustY(nEntryHeightDIV2 );

            pChild = pView->FirstChild( pEntry );
            assert(pChild && "Child?");
            pChild = pChild->LastSibling();
            nDistance = static_cast<sal_uInt16>(pView->GetVisiblePos(pChild) - pView->GetVisiblePos(pEntry));
            aPos2 = aPos1;
            aPos2.AdjustY(nDistance * nEntryHeight );
            rRenderContext.DrawLine(aPos1, aPos2);
        }
        // visible in control?
        if (n >= nOffs && ((m_nStyle & WB_HASLINESATROOT) || !pTree->IsAtRootDepth(pEntry)))
        {
            // can we recycle aPos1?
            if (!pView->IsExpanded(pEntry))
            {
                // nope
                aPos1.setX( pView->GetTabPos(pEntry, pFirstDynamicTab) );
                // if it is not a context bitmap, go a little to the right below
                // the first text (node bitmap, too)
                if (!pView->nContextBmpWidthMax)
                    aPos1.AdjustX(rExpandedNodeBitmap.GetSizePixel().Width() / 2 );
                aPos1.setY( nY );
                aPos1.AdjustY(nEntryHeightDIV2 );
                aPos2.setX( aPos1.X() );
            }
            aPos2.setY( aPos1.Y() );
            aPos2.AdjustX( -(pView->GetIndent()) );
            rRenderContext.DrawLine(aPos1, aPos2);
        }
        nY += nEntryHeight;
        pEntry = pView->NextVisible(pEntry);
    }
    if (m_nStyle & WB_HASLINESATROOT)
    {
        pEntry = pView->First();
        aPos1.setX( pView->GetTabPos(pEntry, pFirstDynamicTab) );
        // if it is not a context bitmap, go a little to the right below the
        // first text (node bitmap, too)
        if (!pView->nContextBmpWidthMax)
            aPos1.AdjustX(rExpandedNodeBitmap.GetSizePixel().Width() / 2 );
        aPos1.AdjustX( -(pView->GetIndent()) );
        aPos1.setY( GetEntryLine( pEntry ) );
        aPos1.AdjustY(nEntryHeightDIV2 );
        pChild = pEntry->LastSibling();
        aPos2.setX( aPos1.X() );
        aPos2.setY( GetEntryLine( pChild ) );
        aPos2.AdjustY(nEntryHeightDIV2 );
        rRenderContext.DrawLine(aPos1, aPos2);
    }
    rRenderContext.Pop();
}

void SvImpLBox::PositionScrollBars( Size& rSize, sal_uInt16 nMask )
{
    long nOverlap = 0;

    Size aVerSize( nVerSBarWidth, rSize.Height() );
    Size aHorSize( rSize.Width(), nHorSBarHeight );

    if( nMask & 0x0001 )
        aHorSize.AdjustWidth( -(nVerSBarWidth) );
    if( nMask & 0x0002 )
        aVerSize.AdjustHeight( -(nHorSBarHeight) );

    aVerSize.AdjustHeight(2 * nOverlap );
    Point aVerPos( rSize.Width() - aVerSize.Width() + nOverlap, -nOverlap );
    aVerSBar->SetPosSizePixel( aVerPos, aVerSize );

    aHorSize.AdjustWidth(2 * nOverlap );
    Point aHorPos( -nOverlap, rSize.Height() - aHorSize.Height() + nOverlap );

    aHorSBar->SetPosSizePixel( aHorPos, aHorSize );

    if( nMask & 0x0001 )
        rSize.setWidth( aVerPos.X() );
    if( nMask & 0x0002 )
        rSize.setHeight( aHorPos.Y() );

    if( (nMask & (0x0001|0x0002)) == (0x0001|0x0002) )
        aScrBarBox->Show();
    else
        aScrBarBox->Hide();
}

void SvImpLBox::AdjustScrollBars( Size& rSize )
{
    long nEntryHeight = pView->GetEntryHeight();
    if( !nEntryHeight )
        return;

    sal_uInt16 nResult = 0;

    Size aOSize( pView->Control::GetOutputSizePixel() );

    const WinBits nWindowStyle = pView->GetStyle();
    bool bVerSBar = ( nWindowStyle & WB_VSCROLL ) != 0;
    bool bHorBar = false;
    long nMaxRight = aOSize.Width(); //GetOutputSize().Width();
    Point aOrigin( pView->GetMapMode().GetOrigin() );
    aOrigin.setX( aOrigin.X() * -1 );
    nMaxRight += aOrigin.X() - 1;
    long nVis = nMostRight - aOrigin.X();
    if( (nWindowStyle & (WB_AUTOHSCROLL|WB_HSCROLL)) &&
        (nVis < nMostRight || nMaxRight < nMostRight) )
    {
        bHorBar = true;
    }

    // number of entries that are not collapsed
    sal_uLong nTotalCount = pView->GetVisibleCount();

    // number of entries visible within the view
    nVisibleCount = aOSize.Height() / nEntryHeight;

    // do we need a vertical scrollbar?
    if( bVerSBar || nTotalCount > nVisibleCount )
    {
        nResult = 1;
        nMaxRight -= nVerSBarWidth;
        if( !bHorBar )
        {
            if( (nWindowStyle & (WB_AUTOHSCROLL|WB_HSCROLL)) &&
                (nVis < nMostRight || nMaxRight < nMostRight) )
                bHorBar = true;
        }
    }

    // do we need a horizontal scrollbar?
    if( bHorBar )
    {
        nResult |= 0x0002;
        // the number of entries visible within the view has to be recalculated
        // because the horizontal scrollbar is now visible.
        nVisibleCount =  (aOSize.Height() - nHorSBarHeight) / nEntryHeight;
        // we might actually need a vertical scrollbar now
        if( !(nResult & 0x0001) &&
            ((nTotalCount > nVisibleCount) || bVerSBar) )
        {
            nResult = 3;
        }
    }

    PositionScrollBars( aOSize, nResult );

    // adapt Range, VisibleRange etc.

    // refresh output size, in case we have to scroll
    tools::Rectangle aRect;
    aRect.SetSize( aOSize );
    aSelEng.SetVisibleArea( aRect );

    // vertical scrollbar
    long nTemp = static_cast<long>(nVisibleCount);
    nTemp--;
    if( nTemp != aVerSBar->GetVisibleSize() )
    {
        if( !bInVScrollHdl )
        {
            aVerSBar->SetPageSize( nTemp - 1 );
            aVerSBar->SetVisibleSize( nTemp );
        }
        else
        {
            nFlags |= LBoxFlags::EndScrollSetVisSize;
            nNextVerVisSize = nTemp;
        }
    }

    // horizontal scrollbar
    nTemp = aHorSBar->GetThumbPos();
    aHorSBar->SetVisibleSize( aOSize.Width() );
    long nNewThumbPos = aHorSBar->GetThumbPos();
    Range aRange( aHorSBar->GetRange() );
    if( aRange.Max() < nMostRight+25 )
    {
        aRange.Max() = nMostRight+25;
        aHorSBar->SetRange( aRange );
    }

    if( nTemp != nNewThumbPos )
    {
        nTemp = nNewThumbPos - nTemp;
        if( pView->IsEditingActive() )
        {
            pView->EndEditing( true ); // Cancel
            pView->Update();
        }
        pView->nFocusWidth = -1;
        KeyLeftRight( nTemp );
    }

    if( nResult & 0x0001 )
        aVerSBar->Show();
    else
        aVerSBar->Hide();

    if( nResult & 0x0002 )
        aHorSBar->Show();
    else
    {
        aHorSBar->Hide();
    }
    rSize = aOSize;
}

void SvImpLBox::InitScrollBarBox()
{
    aScrBarBox->SetSizePixel( Size(nVerSBarWidth, nHorSBarHeight) );
    Size aSize( pView->Control::GetOutputSizePixel() );
    aScrBarBox->SetPosPixel( Point(aSize.Width()-nVerSBarWidth, aSize.Height()-nHorSBarHeight));
}

void SvImpLBox::Resize()
{
    aOutputSize = pView->Control::GetOutputSizePixel();
    if( aOutputSize.Width() <= 0 || aOutputSize.Height() <= 0 )
        return;
    nFlags |= LBoxFlags::InResize;
    InitScrollBarBox();

    if( pView->GetEntryHeight())
    {
        AdjustScrollBars( aOutputSize );
        UpdateAll(false);
    }
    // HACK, as in floating and docked windows the scrollbars might not be drawn
    // correctly/not be drawn at all after resizing!
    if( aHorSBar->IsVisible())
        aHorSBar->Invalidate();
    if( aVerSBar->IsVisible())
        aVerSBar->Invalidate();
    nFlags &= ~LBoxFlags::InResize;
}

void SvImpLBox::FillView()
{
    if( !pStartEntry )
    {
        sal_uLong nVisibleViewCount = pView->GetVisibleCount();
        long nTempThumb = aVerSBar->GetThumbPos();
        if( nTempThumb < 0 )
            nTempThumb = 0;
        else if( static_cast<unsigned long>(nTempThumb) >= nVisibleViewCount )
            nTempThumb = nVisibleViewCount == 0 ? 0 : nVisibleViewCount - 1;
        pStartEntry = pView->GetEntryAtVisPos(nTempThumb);
    }
    if( !pStartEntry )
        return;

    sal_uInt16 nLast = static_cast<sal_uInt16>(pView->GetVisiblePos(pView->LastVisible()));
    sal_uInt16 nThumb = static_cast<sal_uInt16>(pView->GetVisiblePos( pStartEntry ));
    sal_uLong nCurDispEntries = nLast-nThumb+1;
    if( nCurDispEntries >=  nVisibleCount )
        return;

    ShowCursor( false );
    // fill window by moving the thumb up incrementally
    bool bFound = false;
    SvTreeListEntry* pTemp = pStartEntry;
    while( nCurDispEntries < nVisibleCount && pTemp )
    {
        pTemp = pView->PrevVisible(pStartEntry);
        if( pTemp )
        {
            nThumb--;
            pStartEntry = pTemp;
            nCurDispEntries++;
            bFound = true;
        }
    }
    if( bFound )
    {
        aVerSBar->SetThumbPos( nThumb );
        ShowCursor( true ); // recalculate focus rectangle
        pView->Invalidate();
    }
}


void SvImpLBox::ShowVerSBar()
{
    bool bVerBar = ( pView->GetStyle() & WB_VSCROLL ) != 0;
    sal_uLong nVis = 0;
    if( !bVerBar )
        nVis = pView->GetVisibleCount();
    if( bVerBar || (nVisibleCount && nVis > static_cast<sal_uLong>(nVisibleCount-1)) )
    {
        if( !aVerSBar->IsVisible() )
        {
            pView->nFocusWidth = -1;
            AdjustScrollBars( aOutputSize );
            if( GetUpdateMode() )
                aVerSBar->Update();
        }
    }
    else
    {
        if( aVerSBar->IsVisible() )
        {
            pView->nFocusWidth = -1;
            AdjustScrollBars( aOutputSize );
        }
    }

    long nMaxRight = GetOutputSize().Width();
    Point aPos( pView->GetMapMode().GetOrigin() );
    aPos.setX( aPos.X() * -1 ); // convert document coordinates
    nMaxRight = nMaxRight + aPos.X() - 1;
    if( nMaxRight < nMostRight  )
    {
        if( !aHorSBar->IsVisible() )
        {
            pView->nFocusWidth = -1;
            AdjustScrollBars( aOutputSize );
            if( GetUpdateMode() )
                aHorSBar->Update();
        }
        else
        {
            Range aRange( aHorSBar->GetRange() );
            if( aRange.Max() < nMostRight+25 )
            {
                aRange.Max() = nMostRight+25;
                aHorSBar->SetRange( aRange );
            }
            else
            {
                pView->nFocusWidth = -1;
                AdjustScrollBars( aOutputSize );
            }
        }
    }
    else
    {
        if( aHorSBar->IsVisible() )
        {
            pView->nFocusWidth = -1;
            AdjustScrollBars( aOutputSize );
        }
    }
}


void SvImpLBox::SyncVerThumb()
{
    if( pStartEntry )
    {
        long nEntryPos = pView->GetVisiblePos( pStartEntry );
        aVerSBar->SetThumbPos( nEntryPos );
    }
    else
        aVerSBar->SetThumbPos( 0 );
}

bool SvImpLBox::IsEntryInView( SvTreeListEntry* pEntry ) const
{
    // parent collapsed
    if( !pView->IsEntryVisible(pEntry) )
        return false;
    long nY = GetEntryLine( pEntry );
    if( nY < 0 )
        return false;
    long nMax = nVisibleCount * pView->GetEntryHeight();
    return nY < nMax;
}


long SvImpLBox::GetEntryLine( SvTreeListEntry* pEntry ) const
{
    if(!pStartEntry )
        return -1; // invisible position

    long nFirstVisPos = pView->GetVisiblePos( pStartEntry );
    long nEntryVisPos = pView->GetVisiblePos( pEntry );
    nFirstVisPos = nEntryVisPos - nFirstVisPos;
    nFirstVisPos *= pView->GetEntryHeight();
    return nFirstVisPos;
}

void SvImpLBox::SetEntryHeight()
{
    SetNodeBmpWidth( GetExpandedNodeBmp() );
    SetNodeBmpWidth( GetCollapsedNodeBmp() );
    if(!pView->HasViewData()) // are we within the Clear?
    {
        Size aSize = pView->Control::GetOutputSizePixel();
        AdjustScrollBars( aSize );
    }
    else
    {
        Resize();
        if( GetUpdateMode() )
            pView->Invalidate();
    }
}


// ***********************************************************************
// Callback Functions
// ***********************************************************************

void SvImpLBox::EntryExpanded( SvTreeListEntry* pEntry )
{
    // SelAllDestrAnch( false, true ); //DeselectAll();
    if( !GetUpdateMode() )
        return;

    ShowCursor( false );
    long nY = GetEntryLine( pEntry );
    if( IsLineVisible(nY) )
    {
        InvalidateEntriesFrom( nY );
        FindMostRight( pEntry, nullptr  );
    }
    aVerSBar->SetRange( Range(0, pView->GetVisibleCount()-1 ) );
    // if we expanded before the thumb, the thumb's position has to be
    // corrected
    SyncVerThumb();
    ShowVerSBar();
    ShowCursor( true );
}

void SvImpLBox::EntryCollapsed( SvTreeListEntry* pEntry )
{
    if( !pView->IsEntryVisible( pEntry ) )
        return;

    ShowCursor( false );

    if( !pMostRightEntry || pTree->IsChild( pEntry,pMostRightEntry ) )
    {
        FindMostRight(nullptr);
    }

    if( pStartEntry )
    {
        long nOldThumbPos   = aVerSBar->GetThumbPos();
        sal_uLong nVisList      = pView->GetVisibleCount();
        aVerSBar->SetRange( Range(0, nVisList-1) );
        long nNewThumbPos   = aVerSBar->GetThumbPos();
        if( nNewThumbPos != nOldThumbPos  )
        {
            pStartEntry = pView->First();
            sal_uInt16 nDistance = static_cast<sal_uInt16>(nNewThumbPos);
            if( nDistance )
                pStartEntry = pView->NextVisible(pStartEntry, nDistance);
            if( GetUpdateMode() )
                pView->Invalidate();
        }
        else
            SyncVerThumb();
        ShowVerSBar();
    }
    // has the cursor been collapsed?
    if( pTree->IsChild( pEntry, pCursor ) )
        SetCursor( pEntry );
    if( GetUpdateMode() )
        ShowVerSBar();
    ShowCursor( true );
    if( GetUpdateMode() && pCursor )
        pView->Select( pCursor );
}

void SvImpLBox::CollapsingEntry( SvTreeListEntry* pEntry )
{
    if( !pView->IsEntryVisible( pEntry ) || !pStartEntry )
        return;

    SelAllDestrAnch( false ); // deselect all

    // is the collapsed cursor visible?
    long nY = GetEntryLine( pEntry );
    if( IsLineVisible(nY) )
    {
        if( GetUpdateMode() )
            InvalidateEntriesFrom( nY );
    }
    else
    {
        if( pTree->IsChild(pEntry, pStartEntry) )
        {
            pStartEntry = pEntry;
            if( GetUpdateMode() )
                pView->Invalidate();
        }
    }
}


void SvImpLBox::SetNodeBmpWidth( const Image& rBmp )
{
    const Size aSize( rBmp.GetSizePixel() );
    nNodeBmpWidth = aSize.Width();
}

void SvImpLBox::SetNodeBmpTabDistance()
{
    nNodeBmpTabDistance = -pView->GetIndent();
    if( pView->nContextBmpWidthMax )
    {
        // only if the first dynamic tab is centered (we currently assume that)
        Size aSize = GetExpandedNodeBmp().GetSizePixel();
        nNodeBmpTabDistance -= aSize.Width() / 2;
    }
}


// corrects the cursor when using SingleSelection

void SvImpLBox::EntrySelected( SvTreeListEntry* pEntry, bool bSelect )
{
    if( nFlags & LBoxFlags::IgnoreSelect )
        return;

    nFlags &= (~LBoxFlags::DeselectAll);
    if( bSelect &&
        aSelEng.GetSelectionMode() == SelectionMode::Single &&
        pEntry != pCursor )
    {
        SetCursor( pEntry );
        DBG_ASSERT(pView->GetSelectionCount()==1,"selection count?");
    }

    if( GetUpdateMode() && pView->IsEntryVisible(pEntry) )
    {
        long nY = GetEntryLine( pEntry );
        if( IsLineVisible( nY ) )
        {
            ShowCursor(false);
            InvalidateEntry(pEntry);
            ShowCursor(true);
        }
    }
}


void SvImpLBox::RemovingEntry( SvTreeListEntry* pEntry )
{
    CallEventListeners( VclEventId::ListboxItemRemoved , pEntry );

    DestroyAnchor();

    if( !pView->IsEntryVisible( pEntry ) )
    {
        // if parent is collapsed => bye!
        nFlags |= LBoxFlags::RemovedEntryInvisible;
        return;
    }

    if( pEntry == pMostRightEntry || (
        pEntry->HasChildren() && pView->IsExpanded(pEntry) &&
        pTree->IsChild(pEntry, pMostRightEntry)))
    {
        nFlags |= LBoxFlags::RemovedRecalcMostRight;
    }

    SvTreeListEntry* pOldStartEntry = pStartEntry;

    SvTreeListEntry* pParent = pView->GetModel()->GetParent(pEntry);

    if (pParent && pView->GetModel()->GetChildList(pParent).size() == 1)
    {
        DBG_ASSERT( pView->IsExpanded( pParent ), "Parent not expanded");
        pParent->SetFlags( pParent->GetFlags() | SvTLEntryFlags::NO_NODEBMP);
        InvalidateEntry( pParent );
    }

    if( pCursor && pTree->IsChild( pEntry, pCursor) )
        pCursor = pEntry;
    if( pStartEntry && pTree->IsChild(pEntry,pStartEntry) )
        pStartEntry = pEntry;

    SvTreeListEntry* pTemp;
    if( pCursor && pCursor == pEntry )
    {
        if( bSimpleTravel )
            pView->Select( pCursor, false );
        ShowCursor( false );    // focus rectangle gone
        // NextSibling, because we also delete the children of the cursor
        pTemp = pCursor->NextSibling();
        if( !pTemp )
            pTemp = pView->PrevVisible(pCursor);

        SetCursor( pTemp, true );
    }
    if( pStartEntry && pStartEntry == pEntry )
    {
        pTemp = pStartEntry->NextSibling();
        if( !pTemp )
            pTemp = pView->PrevVisible(pStartEntry);
        pStartEntry = pTemp;
    }
    if( GetUpdateMode())
    {
        // if it is the last one, we have to invalidate it, so the lines are
        // drawn correctly (in this case they're deleted)
        if( pStartEntry && (pStartEntry != pOldStartEntry || pEntry == pView->GetModel()->Last()) )
        {
            aVerSBar->SetThumbPos( pView->GetVisiblePos( pStartEntry ));
            pView->Invalidate( GetVisibleArea() );
        }
        else
            InvalidateEntriesFrom( GetEntryLine( pEntry ) );
    }
}

void SvImpLBox::EntryRemoved()
{
    if( nFlags & LBoxFlags::RemovedEntryInvisible )
    {
        nFlags &= (~LBoxFlags::RemovedEntryInvisible);
        return;
    }
    if( !pStartEntry )
        pStartEntry = pTree->First();
    if( !pCursor )
        SetCursor( pStartEntry, true );

    if( pCursor && (bSimpleTravel || !pView->GetSelectionCount() ))
        pView->Select( pCursor );

    if( GetUpdateMode())
    {
        if( nFlags & LBoxFlags::RemovedRecalcMostRight )
            FindMostRight(nullptr);
        aVerSBar->SetRange( Range(0, pView->GetVisibleCount()-1 ) );
        FillView();
        if( pStartEntry )
            // if something above the thumb was deleted
            aVerSBar->SetThumbPos( pView->GetVisiblePos( pStartEntry) );

        ShowVerSBar();
        if( pCursor && pView->HasFocus() && !pView->IsSelected(pCursor) )
        {
            if( pView->GetSelectionCount() )
            {
                // is a neighboring entry selected?
                SvTreeListEntry* pNextCursor = pView->PrevVisible( pCursor );
                if( !pNextCursor || !pView->IsSelected( pNextCursor ))
                    pNextCursor = pView->NextVisible( pCursor );
                if( !pNextCursor || !pView->IsSelected( pNextCursor ))
                    // no neighbor selected: use first selected
                    pNextCursor = pView->FirstSelected();
                SetCursor( pNextCursor );
                MakeVisible( pCursor );
            }
            else
                pView->Select( pCursor );
        }
        ShowCursor( true );
    }
    nFlags &= (~LBoxFlags::RemovedRecalcMostRight);
}


void SvImpLBox::MovingEntry( SvTreeListEntry* pEntry )
{
    bool bDeselAll(nFlags & LBoxFlags::DeselectAll);
    SelAllDestrAnch( false );  // DeselectAll();
    if( !bDeselAll )
        nFlags &= (~LBoxFlags::DeselectAll);

    if( pEntry == pCursor )
        ShowCursor( false );
    if( IsEntryInView( pEntry ) )
        pView->Invalidate();
    if( pEntry != pStartEntry )
        return;

    SvTreeListEntry* pNew = nullptr;
    if( !pEntry->HasChildren() )
    {
        pNew = pView->NextVisible(pStartEntry);
        if( !pNew )
            pNew = pView->PrevVisible(pStartEntry);
    }
    else
    {
        pNew = pEntry->NextSibling();
        if( !pNew )
            pNew = pEntry->PrevSibling();
    }
    pStartEntry = pNew;
}

void SvImpLBox::EntryMoved( SvTreeListEntry* pEntry )
{
    UpdateContextBmpWidthVectorFromMovedEntry( pEntry );

    if ( !pStartEntry )
        // this might happen if the only entry in the view is moved to its very same position
        // #i97346#
        pStartEntry = pView->First();

    aVerSBar->SetRange( Range(0, pView->GetVisibleCount()-1));
    sal_uInt16 nFirstPos = static_cast<sal_uInt16>(pTree->GetAbsPos( pStartEntry ));
    sal_uInt16 nNewPos = static_cast<sal_uInt16>(pTree->GetAbsPos( pEntry ));
    FindMostRight(nullptr);
    if( nNewPos < nFirstPos ) // HACK!
        pStartEntry = pEntry;
    SyncVerThumb();
    if( pEntry == pCursor )
    {
        if( pView->IsEntryVisible( pCursor ) )
            ShowCursor( true );
        else
        {
            SvTreeListEntry* pParent = pEntry;
            do {
                pParent = pTree->GetParent( pParent );
            }
            while( !pView->IsEntryVisible( pParent ) );
            SetCursor( pParent );
        }
    }
    if( IsEntryInView( pEntry ) )
        pView->Invalidate();
}


void SvImpLBox::EntryInserted( SvTreeListEntry* pEntry )
{
    if( !GetUpdateMode() )
        return;

    SvTreeListEntry* pParent = pTree->GetParent(pEntry);
    if (pParent && pTree->GetChildList(pParent).size() == 1)
        // draw plus sign
        pTree->InvalidateEntry( pParent );

    if( !pView->IsEntryVisible( pEntry ) )
        return;
    bool bDeselAll(nFlags & LBoxFlags::DeselectAll);
    if( bDeselAll )
        SelAllDestrAnch( false );
    else
        DestroyAnchor();
    //  nFlags &= (~LBoxFlags::DeselectAll);
//      ShowCursor( false ); // if cursor is moved lower
    long nY = GetEntryLine( pEntry );
    bool bEntryVisible = IsLineVisible( nY );
    if( bEntryVisible )
    {
        ShowCursor( false ); // if cursor is moved lower
        nY -= pView->GetEntryHeight(); // because of lines
        InvalidateEntriesFrom( nY );
    }
    else if( pStartEntry && nY < GetEntryLine(pStartEntry) )
    {
        // Check if the view is filled completely. If not, then adjust
        // pStartEntry and the Cursor (automatic scrolling).
        sal_uInt16 nLast = static_cast<sal_uInt16>(pView->GetVisiblePos(pView->LastVisible()));
        sal_uInt16 nThumb = static_cast<sal_uInt16>(pView->GetVisiblePos( pStartEntry ));
        sal_uInt16 nCurDispEntries = nLast-nThumb+1;
        if( nCurDispEntries < nVisibleCount )
        {
            // set at the next paint event
            pStartEntry = nullptr;
            SetCursor( nullptr );
            pView->Invalidate();
        }
    }
    else if( !pStartEntry )
        pView->Invalidate();

    SetMostRight( pEntry );
    aVerSBar->SetRange( Range(0, pView->GetVisibleCount()-1));
    SyncVerThumb(); // if something was inserted before the thumb
    ShowVerSBar();
    ShowCursor( true );
    if( pStartEntry != pView->First() && (nFlags & LBoxFlags::Filling) )
        pView->Update();
}


// ********************************************************************
// Event handler
// ********************************************************************


// ****** Control the control animation

bool SvImpLBox::ButtonDownCheckCtrl(const MouseEvent& rMEvt, SvTreeListEntry* pEntry)
{
    SvLBoxItem* pItem = pView->GetItem(pEntry,rMEvt.GetPosPixel().X(),&pActiveTab);
    if (pItem && pItem->GetType() == SvLBoxItemType::Button)
    {
        pActiveButton = static_cast<SvLBoxButton*>(pItem);
        pActiveEntry = pEntry;
        if( pCursor == pActiveEntry )
            pView->HideFocus();
        pView->CaptureMouse();
        pActiveButton->SetStateHilighted( true );
        InvalidateEntry(pActiveEntry);
        return true;
    }
    else
        pActiveButton = nullptr;
    return false;
}

bool SvImpLBox::MouseMoveCheckCtrl(const MouseEvent& rMEvt, SvTreeListEntry const * pEntry)
{
    if( pActiveButton )
    {
        long nMouseX = rMEvt.GetPosPixel().X();
        if( pEntry == pActiveEntry &&
             pView->GetItem(pActiveEntry, nMouseX) == pActiveButton )
        {
            if( !pActiveButton->IsStateHilighted() )
            {
                pActiveButton->SetStateHilighted(true );
                InvalidateEntry(pActiveEntry);
            }
        }
        else
        {
            if( pActiveButton->IsStateHilighted() )
            {
                pActiveButton->SetStateHilighted(false );
                InvalidateEntry(pActiveEntry);
            }
        }
        return true;
    }
    return false;
}

bool SvImpLBox::ButtonUpCheckCtrl( const MouseEvent& rMEvt )
{
    if( pActiveButton )
    {
        pView->ReleaseMouse();
        SvTreeListEntry* pEntry = GetClickedEntry( rMEvt.GetPosPixel() );
        pActiveButton->SetStateHilighted( false );
        long nMouseX = rMEvt.GetPosPixel().X();
        if (pEntry == pActiveEntry && pView->GetItem(pActiveEntry, nMouseX) == pActiveButton)
            pActiveButton->ClickHdl(pActiveEntry);
        InvalidateEntry(pActiveEntry);
        if (pCursor == pActiveEntry)
            ShowCursor(true);
        pActiveButton = nullptr;
        pActiveEntry = nullptr;
        pActiveTab = nullptr;
        return true;
    }
    return false;
}

// ******* Control plus/minus button for expanding/collapsing

// false == no expand/collapse button hit
bool SvImpLBox::IsNodeButton( const Point& rPosPixel, SvTreeListEntry* pEntry ) const
{
    if( !pEntry->HasChildren() && !pEntry->HasChildrenOnDemand() )
        return false;

    SvLBoxTab* pFirstDynamicTab = pView->GetFirstDynamicTab();
    if( !pFirstDynamicTab )
        return false;

    long nMouseX = rPosPixel.X();
    // convert to document coordinates
    Point aOrigin( pView->GetMapMode().GetOrigin() );
    nMouseX -= aOrigin.X();

    long nX = pView->GetTabPos( pEntry, pFirstDynamicTab);
    nX += nNodeBmpTabDistance;
    if( nMouseX < nX )
        return false;
    nX += nNodeBmpWidth;
    return nMouseX <= nX;
}

// false == hit no node button
bool SvImpLBox::ButtonDownCheckExpand( const MouseEvent& rMEvt, SvTreeListEntry* pEntry )
{
    bool bRet = false;

    if ( pView->IsEditingActive() && pEntry == pView->pEdEntry )
        // inplace editing -> nothing to do
        bRet = true;
    else if ( IsNodeButton( rMEvt.GetPosPixel(), pEntry ) )
    {
        if ( pView->IsExpanded( pEntry ) )
        {
            pView->EndEditing( true );
            pView->Collapse( pEntry );
        }
        else
        {
            // you can expand an entry, which is in editing
            pView->Expand( pEntry );
        }
        bRet = true;
    }

    return bRet;
}

void SvImpLBox::MouseButtonDown( const MouseEvent& rMEvt )
{
    if ( !rMEvt.IsLeft() && !rMEvt.IsRight())
        return;

    aEditIdle.Stop();
    Point aPos( rMEvt.GetPosPixel());

    if( aPos.X() > aOutputSize.Width() || aPos.Y() > aOutputSize.Height() )
        return;

    if( !pCursor )
        pCursor = pStartEntry;
    SvTreeListEntry* pEntry = GetEntry( aPos );
    if ( pEntry != pCursor )
        // new entry selected -> reset current tab position to first tab
        nCurTabPos = FIRST_ENTRY_TAB;
    nFlags &= (~LBoxFlags::Filling);
    pView->GrabFocus();
    //fdo#82270 Grabbing focus can invalidate the entries, re-fetch
    pEntry = GetEntry(aPos);
    // the entry can still be invalid!
    if( !pEntry || !pView->GetViewData( pEntry ))
        return;

    long nY = GetEntryLine( pEntry );
    // Node-Button?
    if( ButtonDownCheckExpand( rMEvt, pEntry ) )
        return;

    if( !EntryReallyHit(pEntry,aPos,nY))
        return;

    SvLBoxItem* pXItem = pView->GetItem( pEntry, aPos.X() );
    if( pXItem )
    {
        SvLBoxTab* pXTab = pView->GetTab( pEntry, pXItem );
        if ( !rMEvt.IsMod1() && !rMEvt.IsMod2() && rMEvt.IsLeft() && pXTab->IsEditable()
            && pEntry == pView->FirstSelected() && nullptr == pView->NextSelected( pEntry ) )
                // #i8234# FirstSelected() and NextSelected() ensures, that inplace editing is only triggered, when only one entry is selected
            nFlags |= LBoxFlags::StartEditTimer;
        if ( !pView->IsSelected( pEntry ) )
            nFlags &= ~LBoxFlags::StartEditTimer;
    }


    if( (rMEvt.GetClicks() % 2) == 0 )
    {
        nFlags &= (~LBoxFlags::StartEditTimer);
        pView->pHdlEntry = pEntry;
        if( !pView->DoubleClickHdl() )
        {
            // Handler signals nothing to be done anymore, bail out, 'this' may
            // even be dead and destroyed.
            return;
        }
        else
        {
            // if the entry was deleted within the handler
            pEntry = GetClickedEntry( aPos );
            if( !pEntry )
                return;
            if( pEntry != pView->pHdlEntry )
            {
                // select anew & bye
                if( !bSimpleTravel && !aSelEng.IsAlwaysAdding())
                    SelAllDestrAnch( false ); // DeselectAll();
                SetCursor( pEntry );

                return;
            }
            if( pEntry->HasChildren() || pEntry->HasChildrenOnDemand() )
            {
                if( bSubLstOpDblClick )
                {
                    if( pView->IsExpanded(pEntry) )
                        pView->Collapse( pEntry );
                    else
                        pView->Expand( pEntry );
                }
                if( pEntry == pCursor )  // only if Entryitem was clicked
                                          // (Nodebutton is not an Entryitem!)
                    pView->Select( pCursor );
                return;
            }
        }
    }
    else
    {
        // CheckButton? (TreeListBox: Check + Info)
        if( ButtonDownCheckCtrl(rMEvt, pEntry) )
            return;
        // Inplace-Editing?
    }
    if ( aSelEng.GetSelectionMode() != SelectionMode::NONE )
        aSelEng.SelMouseButtonDown( rMEvt );
}

void SvImpLBox::MouseButtonUp( const MouseEvent& rMEvt)
{
    if ( !ButtonUpCheckCtrl( rMEvt ) && ( aSelEng.GetSelectionMode() != SelectionMode::NONE ) )
        aSelEng.SelMouseButtonUp( rMEvt );
    EndScroll();
    if( nFlags & LBoxFlags::StartEditTimer )
    {
        nFlags &= (~LBoxFlags::StartEditTimer);
        aEditClickPos = rMEvt.GetPosPixel();
        aEditIdle.Start();
    }
}

void SvImpLBox::MouseMove( const MouseEvent& rMEvt)
{
    SvTreeListEntry* pEntry = GetClickedEntry( rMEvt.GetPosPixel() );
    if ( !MouseMoveCheckCtrl( rMEvt, pEntry ) && ( aSelEng.GetSelectionMode() != SelectionMode::NONE ) )
        aSelEng.SelMouseMove( rMEvt );
}

bool SvImpLBox::KeyInput( const KeyEvent& rKEvt)
{
    aEditIdle.Stop();
    const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();

    if( rKeyCode.IsMod2() )
        return false; // don't evaluate Alt key

    nFlags &= (~LBoxFlags::Filling);

    if( !pCursor )
        pCursor = pStartEntry;
    if( !pCursor )
        return false;

    bool bKeyUsed = true;

    sal_uInt16  nDelta = static_cast<sal_uInt16>(aVerSBar->GetPageSize());
    sal_uInt16  aCode = rKeyCode.GetCode();

    bool    bShift = rKeyCode.IsShift();
    bool    bMod1 = rKeyCode.IsMod1();

    SvTreeListEntry* pNewCursor;

    switch( aCode )
    {
        case KEY_UP:
            if( !IsEntryInView( pCursor ) )
                MakeVisible( pCursor );

            pNewCursor = pCursor;
            do
            {
                pNewCursor = pView->PrevVisible(pNewCursor);
            } while( pNewCursor && !IsSelectable(pNewCursor) );

            if ( pNewCursor )
                // new entry selected -> reset current tab position to first tab
                nCurTabPos = FIRST_ENTRY_TAB;
            // if there is no next entry, take the current one
            // this ensures that in case of _one_ entry in the list, this entry is selected when pressing
            // the cursor key
            if (!pNewCursor)
                pNewCursor = pCursor;

            aSelEng.CursorPosChanging( bShift, bMod1 );
            SetCursor( pNewCursor, bMod1 );     // no selection, when Ctrl is on
            if( !IsEntryInView( pNewCursor ) )
                KeyUp( false );
            break;

        case KEY_DOWN:
            if( !IsEntryInView( pCursor ) )
                MakeVisible( pCursor );

            pNewCursor = pCursor;
            do
            {
                pNewCursor = pView->NextVisible(pNewCursor);
            } while( pNewCursor && !IsSelectable(pNewCursor) );

            if ( pNewCursor )
                // new entry selected -> reset current tab position to first tab
                nCurTabPos = FIRST_ENTRY_TAB;

            // if there is no next entry, take the current one
            // this ensures that in case of _one_ entry in the list, this entry is selected when pressing
            // the cursor key
            // 06.09.20001 - 83416 - frank.schoenheit@sun.com
            if ( !pNewCursor && pCursor )
                pNewCursor = pCursor;

            if( pNewCursor )
            {
                aSelEng.CursorPosChanging( bShift, bMod1 );
                if( IsEntryInView( pNewCursor ) )
                    SetCursor( pNewCursor, bMod1 ); // no selection, when Ctrl is on
                else
                {
                    if( pCursor )
                        pView->Select( pCursor, false );
                    KeyDown( false );
                    SetCursor( pNewCursor, bMod1 ); // no selection, when Ctrl is on
                }
            }
            else
                KeyDown( false );   // because scrollbar range might still
                                        // allow scrolling
            break;

        case KEY_RIGHT:
        {
            if( bSubLstOpLR )
            {
                // only try to expand if sublist is expandable,
                // otherwise ignore the key press
                if( IsExpandable() && !pView->IsExpanded( pCursor ) )
                    pView->Expand( pCursor );
            }
            else if (bIsCellFocusEnabled)
            {
                if ( nCurTabPos < ( pView->TabCount() - 1 /*!2*/ ) )
                {
                    ++nCurTabPos;
                    ShowCursor( true );
                    CallEventListeners( VclEventId::ListboxSelect, pCursor );
                }
            }
            else if (aHorSBar->IsVisible())
            {
                long    nThumb = aHorSBar->GetThumbPos();
                nThumb += aHorSBar->GetLineSize();
                long    nOldThumb = aHorSBar->GetThumbPos();
                aHorSBar->SetThumbPos( nThumb );
                nThumb = nOldThumb;
                nThumb -= aHorSBar->GetThumbPos();
                nThumb *= -1;
                if( nThumb )
                {
                    KeyLeftRight( nThumb );
                    EndScroll();
                }
            }
            else
                bKeyUsed = false;
            break;
        }

        case KEY_LEFT:
        {
            if (bIsCellFocusEnabled)
            {
                if ( nCurTabPos > FIRST_ENTRY_TAB )
                {
                    --nCurTabPos;
                    ShowCursor( true );
                    CallEventListeners( VclEventId::ListboxSelect, pCursor );
                }
            }
            else if (aHorSBar->IsVisible())
            {
                long    nThumb = aHorSBar->GetThumbPos();
                nThumb -= aHorSBar->GetLineSize();
                long    nOldThumb = aHorSBar->GetThumbPos();
                aHorSBar->SetThumbPos( nThumb );
                nThumb = nOldThumb;
                nThumb -= aHorSBar->GetThumbPos();
                if( nThumb )
                {
                    KeyLeftRight( -nThumb );
                    EndScroll();
                }
                else if( bSubLstOpLR )
                {
                    if( IsExpandable() && pView->IsExpanded( pCursor ) )
                        pView->Collapse( pCursor );
                    else
                    {
                        pNewCursor = pView->GetParent( pCursor );
                        if( pNewCursor )
                            SetCursor( pNewCursor );
                    }
                }
            }
            else if( bSubLstOpLR )
            {
                if( IsExpandable() && pView->IsExpanded( pCursor ) )
                    pView->Collapse( pCursor );
                else
                {
                    pNewCursor = pView->GetParent( pCursor );
                    if( pNewCursor )
                        SetCursor( pNewCursor );
                }
            }
            else
                bKeyUsed = false;
            break;
        }

        case KEY_PAGEUP:
            if( !bMod1 )
            {
                pNewCursor = pView->PrevVisible(pCursor, nDelta);

                while( nDelta && pNewCursor && !IsSelectable(pNewCursor) )
                {
                    pNewCursor = pView->NextVisible(pNewCursor);
                    nDelta--;
                }

                if( nDelta )
                {
                    DBG_ASSERT(pNewCursor && pNewCursor!=pCursor, "Cursor?");
                    aSelEng.CursorPosChanging( bShift, bMod1 );
                    if( IsEntryInView( pNewCursor ) )
                        SetCursor( pNewCursor );
                    else
                    {
                        SetCursor( pNewCursor );
                        KeyUp( true );
                    }
                }
            }
            else
                bKeyUsed = false;
            break;

        case KEY_PAGEDOWN:
            if( !bMod1 )
            {
                pNewCursor= pView->NextVisible(pCursor, nDelta);

                while( nDelta && pNewCursor && !IsSelectable(pNewCursor) )
                {
                    pNewCursor = pView->PrevVisible(pNewCursor);
                    nDelta--;
                }

                if( nDelta && pNewCursor )
                {
                    DBG_ASSERT(pNewCursor && pNewCursor!=pCursor, "Cursor?");
                    aSelEng.CursorPosChanging( bShift, bMod1 );
                    if( IsEntryInView( pNewCursor ) )
                        SetCursor( pNewCursor );
                    else
                    {
                        SetCursor( pNewCursor );
                        KeyDown( true );
                    }
                }
                else
                    KeyDown( false ); // see also: KEY_DOWN
            }
            else
                bKeyUsed = false;
            break;

        case KEY_SPACE:
            if ( pView->GetSelectionMode() != SelectionMode::NONE )
            {
                if ( bMod1 )
                {
                    if ( pView->GetSelectionMode() == SelectionMode::Multiple && !bShift )
                        // toggle selection
                        pView->Select( pCursor, !pView->IsSelected( pCursor ) );
                }
                else if ( !bShift /*&& !bMod1*/ )
                {
                    if ( aSelEng.IsAddMode() )
                    {
                        // toggle selection
                        pView->Select( pCursor, !pView->IsSelected( pCursor ) );
                    }
                    else if ( !pView->IsSelected( pCursor ) )
                    {
                        SelAllDestrAnch( false );
                        pView->Select( pCursor );
                    }
                    else
                        bKeyUsed = false;
                }
                else
                    bKeyUsed = false;
            }
            else
                bKeyUsed = false;
            break;

        case KEY_RETURN:
            if( bSubLstOpRet && IsExpandable() )
            {
                if( pView->IsExpanded( pCursor ) )
                    pView->Collapse( pCursor );
                else
                    pView->Expand( pCursor );
            }
            else
                bKeyUsed = false;
            break;

        case KEY_F2:
            if( !bShift && !bMod1 )
            {
                aEditClickPos = Point( -1, -1 );
                EditTimerCall( nullptr );
            }
            else
                bKeyUsed = false;
            break;

        case KEY_F8:
            if( bShift && pView->GetSelectionMode()==SelectionMode::Multiple &&
                !(m_nStyle & WB_SIMPLEMODE))
            {
                if( aSelEng.IsAlwaysAdding() )
                    aSelEng.AddAlways( false );
                else
                    aSelEng.AddAlways( true );
            }
            else
                bKeyUsed = false;
            break;

        case KEY_ADD:
            if (!pView->IsExpanded(pCursor))
                pView->Expand(pCursor);
            if (bMod1)
            {
                sal_uInt16 nRefDepth = pTree->GetDepth(pCursor);
                SvTreeListEntry* pCur = pTree->Next(pCursor);
                while (pCur && pTree->GetDepth(pCur) > nRefDepth)
                {
                    if (pCur->HasChildren() && !pView->IsExpanded(pCur))
                        pView->Expand(pCur);
                    pCur = pTree->Next(pCur);
                }
            }
            break;

        case KEY_A:
            if( bMod1 )
                SelAllDestrAnch( true );
            else
                bKeyUsed = false;
            break;

        case KEY_SUBTRACT:
            if (pView->IsExpanded(pCursor))
                pView->Collapse(pCursor);
            if (bMod1)
            {
                // collapse all parents until we get to the root
                SvTreeListEntry* pParentToCollapse = pTree->GetRootLevelParent(pCursor);
                if (pParentToCollapse)
                {
                    sal_uInt16 nRefDepth;
                    // special case explorer: if the root only has a single
                    // entry, don't collapse the root entry
                    if (pTree->GetChildList(nullptr).size() < 2)
                    {
                        nRefDepth = 1;
                        pParentToCollapse = pCursor;
                        while (pTree->GetParent(pParentToCollapse)
                               && pTree->GetDepth(pTree->GetParent(pParentToCollapse)) > 0)
                        {
                            pParentToCollapse = pTree->GetParent(pParentToCollapse);
                        }
                    }
                    else
                        nRefDepth = 0;

                    if (pView->IsExpanded(pParentToCollapse))
                        pView->Collapse(pParentToCollapse);
                    SvTreeListEntry* pCur = pTree->Next(pParentToCollapse);
                    while (pCur && pTree->GetDepth(pCur) > nRefDepth)
                    {
                        if (pCur->HasChildren() && pView->IsExpanded(pCur))
                            pView->Collapse(pCur);
                        pCur = pTree->Next(pCur);
                    }
                }
            }
            break;

        case KEY_DIVIDE :
            if( bMod1 )
                SelAllDestrAnch( true );
            else
                bKeyUsed = false;
            break;

        case KEY_COMMA :
            if( bMod1 )
                SelAllDestrAnch( false );
            else
                bKeyUsed = false;
            break;

        case KEY_HOME :
            pNewCursor = pView->GetModel()->First();

            while( pNewCursor && !IsSelectable(pNewCursor) )
            {
                pNewCursor = pView->NextVisible(pNewCursor);
            }

            if( pNewCursor && pNewCursor != pCursor )
            {
//              SelAllDestrAnch( false );
                aSelEng.CursorPosChanging( bShift, bMod1 );
                SetCursor( pNewCursor );
                if( !IsEntryInView( pNewCursor ) )
                    MakeVisible( pNewCursor );
            }
            else
                bKeyUsed = false;
            break;

        case KEY_END :
            pNewCursor = pView->GetModel()->Last();

            while( pNewCursor && !IsSelectable(pNewCursor) )
            {
                pNewCursor = pView->PrevVisible(pNewCursor);
            }

            if( pNewCursor && pNewCursor != pCursor)
            {
//              SelAllDestrAnch( false );
                aSelEng.CursorPosChanging( bShift, bMod1 );
                SetCursor( pNewCursor );
                if( !IsEntryInView( pNewCursor ) )
                    MakeVisible( pNewCursor );
            }
            else
                bKeyUsed = false;
            break;

        case KEY_ESCAPE:
        case KEY_TAB:
        case KEY_DELETE:
        case KEY_BACKSPACE:
            // must not be handled because this quits dialogs and does other magic things...
            // if there are other single keys which should not be handled, they can be added here
            bKeyUsed = false;
            break;

        default:
            // is there any reason why we should eat the events here? The only place where this is called
            // is from SvTreeListBox::KeyInput. If we set bKeyUsed to true here, then the key input
            // is just silenced. However, we want SvLBox::KeyInput to get a chance, to do the QuickSelection
            // handling.
            // (The old code here which intentionally set bKeyUsed to sal_True said this was because of "quick search"
            // handling, but actually there was no quick search handling anymore. We just re-implemented it.)
            // #i31275# / 2009-06-16 / frank.schoenheit@sun.com
            bKeyUsed = false;
            break;
    }
    return bKeyUsed;
}

void SvImpLBox::GetFocus()
{
    if( pCursor )
    {
        pView->SetEntryFocus( pCursor, true );
        ShowCursor( true );
// auskommentiert wg. deselectall
//      if( bSimpleTravel && !pView->IsSelected(pCursor) )
//          pView->Select( pCursor, true );
    }
    if( m_nStyle & WB_HIDESELECTION )
    {
        SvTreeListEntry* pEntry = pView->FirstSelected();
        while( pEntry )
        {
            InvalidateEntry( pEntry );
            pEntry = pView->NextSelected( pEntry );
        }
    }
}

void SvImpLBox::LoseFocus()
{
    aEditIdle.Stop();
    if( pCursor )
        pView->SetEntryFocus( pCursor,false );
    ShowCursor( false );

    if( m_nStyle & WB_HIDESELECTION )
    {
        SvTreeListEntry* pEntry = pView ?  pView->FirstSelected() : nullptr;
        while( pEntry )
        {
            InvalidateEntry( pEntry );
            pEntry = pView->NextSelected( pEntry );
        }
    }
}


// ********************************************************************
// SelectionEngine
// ********************************************************************

void SvImpLBox::SelectEntry( SvTreeListEntry* pEntry, bool bSelect )
{
    pView->Select( pEntry, bSelect );
}

ImpLBSelEng::ImpLBSelEng( SvImpLBox* pImpl, SvTreeListBox* pV )
{
    pImp = pImpl;
    pView = pV;
}

ImpLBSelEng::~ImpLBSelEng()
{
}

void ImpLBSelEng::BeginDrag()
{
    pImp->BeginDrag();
}

void ImpLBSelEng::CreateAnchor()
{
    pImp->pAnchor = pImp->pCursor;
}

void ImpLBSelEng::DestroyAnchor()
{
    pImp->pAnchor = nullptr;
}

void ImpLBSelEng::SetCursorAtPoint(const Point& rPoint, bool bDontSelectAtCursor)
{
    SvTreeListEntry* pNewCursor = pImp->MakePointVisible( rPoint );
    if( pNewCursor != pImp->pCursor  )
        pImp->BeginScroll();

    if( pNewCursor )
    {
        // at SimpleTravel, the SetCursor is selected and the select handler is
        // called
        //if( !bDontSelectAtCursor && !pImp->bSimpleTravel )
        //  pImp->SelectEntry( pNewCursor, true );
        pImp->SetCursor( pNewCursor, bDontSelectAtCursor );
    }
}

bool ImpLBSelEng::IsSelectionAtPoint( const Point& rPoint )
{
    SvTreeListEntry* pEntry = pImp->MakePointVisible( rPoint );
    if( pEntry )
        return pView->IsSelected(pEntry);
    return false;
}

void ImpLBSelEng::DeselectAtPoint( const Point& rPoint )
{
    SvTreeListEntry* pEntry = pImp->MakePointVisible( rPoint );
    if( !pEntry )
        return;
    pImp->SelectEntry( pEntry, false );
}

void ImpLBSelEng::DeselectAll()
{
    pImp->SelAllDestrAnch( false, false ); // don't reset SelectionEngine!
    pImp->nFlags &= (~LBoxFlags::DeselectAll);
}

// ***********************************************************************
// Selection
// ***********************************************************************

void SvImpLBox::SetAnchorSelection(SvTreeListEntry* pOldCursor,SvTreeListEntry* pNewCursor)
{
    SvTreeListEntry* pEntry;
    sal_uLong nAnchorVisPos = pView->GetVisiblePos( pAnchor );
    sal_uLong nOldVisPos = pView->GetVisiblePos( pOldCursor );
    sal_uLong nNewVisPos = pView->GetVisiblePos( pNewCursor );

    if( nOldVisPos > nAnchorVisPos ||
        ( nAnchorVisPos==nOldVisPos && nNewVisPos > nAnchorVisPos) )
    {
        if( nNewVisPos > nOldVisPos )
        {
            pEntry = pOldCursor;
            while( pEntry && pEntry != pNewCursor )
            {
                pView->Select( pEntry );
                pEntry = pView->NextVisible(pEntry);
            }
            if( pEntry )
                pView->Select( pEntry );
            return;
        }

        if( nNewVisPos < nAnchorVisPos )
        {
            pEntry = pAnchor;
            while( pEntry && pEntry != pOldCursor )
            {
                pView->Select( pEntry, false );
                pEntry = pView->NextVisible(pEntry);
            }
            if( pEntry )
                pView->Select( pEntry, false );

            pEntry = pNewCursor;
            while( pEntry && pEntry != pAnchor )
            {
                pView->Select( pEntry );
                pEntry = pView->NextVisible(pEntry);
            }
            if( pEntry )
                pView->Select( pEntry );
            return;
        }

        if( nNewVisPos < nOldVisPos )
        {
            pEntry = pNewCursor;
            pEntry = pView->NextVisible(pEntry);
            while( pEntry && pEntry != pOldCursor )
            {
                pView->Select( pEntry, false );
                pEntry = pView->NextVisible(pEntry);
            }
            if( pEntry )
                pView->Select( pEntry, false );
            return;
        }
    }
    else
    {
        if( nNewVisPos < nOldVisPos )  // enlarge selection
        {
            pEntry = pNewCursor;
            while( pEntry && pEntry != pOldCursor )
            {
                pView->Select( pEntry );
                pEntry = pView->NextVisible(pEntry);
            }
            if( pEntry )
                pView->Select( pEntry );
            return;
        }

        if( nNewVisPos > nAnchorVisPos )
        {
            pEntry = pOldCursor;
            while( pEntry && pEntry != pAnchor )
            {
                pView->Select( pEntry, false );
                pEntry = pView->NextVisible(pEntry);
            }
            if( pEntry )
                pView->Select( pEntry, false );
            pEntry = pAnchor;
            while( pEntry && pEntry != pNewCursor )
            {
                pView->Select( pEntry );
                pEntry = pView->NextVisible(pEntry);
            }
            if( pEntry )
                pView->Select( pEntry );
            return;
        }

        if( nNewVisPos > nOldVisPos )
        {
            pEntry = pOldCursor;
            while( pEntry && pEntry != pNewCursor )
            {
                pView->Select( pEntry, false );
                pEntry = pView->NextVisible(pEntry);
            }
            return;
        }
    }
}

void SvImpLBox::SelAllDestrAnch(
    bool bSelect, bool bDestroyAnchor, bool bSingleSelToo )
{
    SvTreeListEntry* pEntry;
    nFlags &= (~LBoxFlags::DeselectAll);
    if( bSelect && bSimpleTravel )
    {
        if( pCursor && !pView->IsSelected( pCursor ))
        {
            pView->Select( pCursor );
        }
        return;
    }
    if( !bSelect && pView->GetSelectionCount() == 0 )
    {
        if( bSimpleTravel && ( !GetUpdateMode() || !pCursor) )
            nFlags |= LBoxFlags::DeselectAll;
        return;
    }
    if( bSelect && pView->GetSelectionCount() == pView->GetEntryCount())
        return;
    if( !bSingleSelToo && bSimpleTravel )
        return;

    if( !bSelect && pView->GetSelectionCount()==1 && pCursor &&
        pView->IsSelected( pCursor ))
    {
        pView->Select( pCursor, false );
        if( bDestroyAnchor )
            DestroyAnchor(); // delete anchor & reset SelectionEngine
        else
            pAnchor = nullptr; // always delete internal anchor
        return;
    }

    if( bSimpleTravel && !pCursor && !GetUpdateMode() )
        nFlags |= LBoxFlags::DeselectAll;

    ShowCursor( false );
    bool bUpdate = GetUpdateMode();

    nFlags |= LBoxFlags::IgnoreSelect; // EntryInserted should not do anything
    pEntry = pTree->First();
    while( pEntry )
    {
        if( pView->Select( pEntry, bSelect ) )
        {
            if( bUpdate && pView->IsEntryVisible(pEntry) )
            {
                long nY = GetEntryLine( pEntry );
                if( IsLineVisible( nY ) )
                    InvalidateEntry(pEntry);
            }
        }
        pEntry = pTree->Next( pEntry );
    }
    nFlags &= ~LBoxFlags::IgnoreSelect;

    if( bDestroyAnchor )
        DestroyAnchor(); // delete anchor & reset SelectionEngine
    else
        pAnchor = nullptr; // always delete internal anchor
    ShowCursor( true );
}

void SvImpLBox::SetSelectionMode( SelectionMode eSelMode  )
{
    aSelEng.SetSelectionMode( eSelMode);
    if( eSelMode == SelectionMode::Single )
        bSimpleTravel = true;
    else
        bSimpleTravel = false;
    if( (m_nStyle & WB_SIMPLEMODE) && (eSelMode == SelectionMode::Multiple) )
        aSelEng.AddAlways( true );
}

// ***********************************************************************
// Drag & Drop
// ***********************************************************************

void SvImpLBox::SetDragDropMode( DragDropMode eDDMode )
{
    if( eDDMode != DragDropMode::NONE && eDDMode != DragDropMode::APP_DROP )
    {
        aSelEng.ExpandSelectionOnMouseMove( false );
        aSelEng.EnableDrag( true );
    }
    else
    {
        aSelEng.ExpandSelectionOnMouseMove();
        aSelEng.EnableDrag( false );
    }
}

void SvImpLBox::BeginDrag()
{
    nFlags &= (~LBoxFlags::Filling);
    if( !bAsyncBeginDrag )
    {
        BeginScroll();
        pView->StartDrag( 0, aSelEng.GetMousePosPixel() );
        EndScroll();
    }
    else
    {
        aAsyncBeginDragPos = aSelEng.GetMousePosPixel();
        aAsyncBeginDragIdle.Start();
    }
}

IMPL_LINK_NOARG(SvImpLBox, BeginDragHdl, Timer *, void)
{
    pView->StartDrag( 0, aAsyncBeginDragPos );
}

void SvImpLBox::PaintDDCursor( SvTreeListEntry* pInsertionPos )
{
    long nY;
    if( pInsertionPos )
    {
        nY = GetEntryLine( pInsertionPos );
        nY += pView->GetEntryHeight();
    }
    else
        nY = 1;
    RasterOp eOldOp = pView->GetRasterOp();
    pView->SetRasterOp( RasterOp::Invert );
    Color aOldLineColor = pView->GetLineColor();
    pView->SetLineColor( COL_BLACK );
    pView->DrawLine( Point( 0, nY ), Point( aOutputSize.Width(), nY ) );
    pView->SetLineColor( aOldLineColor );
    pView->SetRasterOp( eOldOp );
}

void SvImpLBox::Command( const CommandEvent& rCEvt )
{
    CommandEventId   nCommand = rCEvt.GetCommand();

    if( nCommand == CommandEventId::ContextMenu )
        aEditIdle.Stop();

    // scroll mouse event?
    if( ( ( nCommand == CommandEventId::Wheel ) || ( nCommand == CommandEventId::StartAutoScroll ) || ( nCommand == CommandEventId::AutoScroll ) )
        && pView->HandleScrollCommand( rCEvt, aHorSBar.get(), aVerSBar.get() ) )
            return;

    if( bContextMenuHandling && nCommand == CommandEventId::ContextMenu )
    {
        Point   aPopupPos;
        bool    bClickedIsFreePlace = false;
        std::stack<SvTreeListEntry*> aSelRestore;

        if( rCEvt.IsMouseEvent() )
        {   // change selection, if mouse position doesn't fit to selection

            aPopupPos = rCEvt.GetMousePosPixel();

            SvTreeListEntry*    pClickedEntry = GetEntry( aPopupPos );
            if( pClickedEntry )
            {   // mouse in non empty area
                bool                bClickedIsSelected = false;

                // collect the currently selected entries
                SvTreeListEntry*        pSelected = pView->FirstSelected();
                while( pSelected )
                {
                    bClickedIsSelected |= ( pClickedEntry == pSelected );
                    pSelected = pView->NextSelected( pSelected );
                }

                // if the entry which the user clicked at is not selected
                if( !bClickedIsSelected )
                {   // deselect all other and select the clicked one
                    pView->SelectAll( false );
                    pView->SetCursor( pClickedEntry );
                }
            }
            else if( aSelEng.GetSelectionMode() == SelectionMode::Single )
            {
                bClickedIsFreePlace = true;
                sal_Int32               nSelectedEntries = pView->GetSelectionCount();
                SvTreeListEntry*        pSelected = pView->FirstSelected();
                for(sal_Int32 nSel = 0; nSel < nSelectedEntries; nSel++ )
                {
                    aSelRestore.push(pSelected);
                    pSelected = pView->NextSelected( pSelected );
                }
                pView->SelectAll( false );
            }
            else
            {   // deselect all
                pView->SelectAll( false );
            }


        }
        else
        {   // key event (or at least no mouse event)
            sal_Int32   nSelectionCount = pView->GetSelectionCount();

            if( nSelectionCount )
            {   // now always take first visible as base for positioning the menu
                SvTreeListEntry*    pSelected = pView->FirstSelected();
                while( pSelected )
                {
                    if( IsEntryInView( pSelected ) )
                        break;

                    pSelected = pView->NextSelected( pSelected );
                }

                if( !pSelected )
                {
                    // no one was visible
                    pSelected = pView->FirstSelected();
                    pView->MakeVisible( pSelected );
                }

                aPopupPos = pView->GetFocusRect( pSelected, pView->GetEntryPosition( pSelected ).Y() ).Center();
            }
            else
                aPopupPos = Point( 0, 0 );
        }

        {
            VclPtr<PopupMenu> pPopup = pView->CreateContextMenu();
            if (pPopup)
            {
                // do action for selected entry in popup menu
                sal_uInt16 nMenuAction = pPopup->Execute( pView, aPopupPos );
                if ( nMenuAction )
                    pView->ExecuteContextMenuAction( nMenuAction );
                pPopup.disposeAndClear();
            }
        }

        if( bClickedIsFreePlace )
        {
            while(!aSelRestore.empty())
            {
                SvTreeListEntry* pEntry = aSelRestore.top();
                //#i19717# the entry is maybe already deleted
                bool bFound = false;
                for(sal_uLong nEntry = 0; nEntry < pView->GetEntryCount(); nEntry++)
                    if(pEntry == pView->GetEntry(nEntry))
                    {
                        bFound = true;
                        break;
                    }
                if(bFound)
                    SetCurEntry( pEntry );
                aSelRestore.pop();
            }
        }
    }
    else
    {
        const Point& rPos = rCEvt.GetMousePosPixel();
        if( rPos.X() < aOutputSize.Width() && rPos.Y() < aOutputSize.Height() )
            aSelEng.Command( rCEvt );
    }
}

void SvImpLBox::BeginScroll()
{
    if( !(nFlags & LBoxFlags::InScrolling))
    {
        nFlags |= LBoxFlags::InScrolling;
    }
}

void SvImpLBox::EndScroll()
{
    if( nFlags & LBoxFlags::InScrolling)
    {
        pView->NotifyEndScroll();
        nFlags &= (~LBoxFlags::InScrolling);
    }
}


tools::Rectangle SvImpLBox::GetVisibleArea() const
{
    Point aPos( pView->GetMapMode().GetOrigin() );
    aPos.setX( aPos.X() * -1 );
    tools::Rectangle aRect( aPos, aOutputSize );
    return aRect;
}

void SvImpLBox::Invalidate()
{
    pView->SetClipRegion();
}

void SvImpLBox::SetCurEntry( SvTreeListEntry* pEntry )
{
    if  (  ( aSelEng.GetSelectionMode() != SelectionMode::Single )
        && ( aSelEng.GetSelectionMode() != SelectionMode::NONE )
        )
        SelAllDestrAnch( false );
    if ( pEntry )
        MakeVisible( pEntry );
    SetCursor( pEntry );
    if ( pEntry && ( aSelEng.GetSelectionMode() != SelectionMode::NONE ) )
        pView->Select( pEntry );
}

IMPL_LINK_NOARG(SvImpLBox, EditTimerCall, Timer *, void)
{
    if( !pView->IsInplaceEditingEnabled() )
        return;

    bool bIsMouseTriggered = aEditClickPos.X() >= 0;
    if ( bIsMouseTriggered )
    {
        Point aCurrentMousePos = pView->GetPointerPosPixel();
        if  (   ( std::abs( aCurrentMousePos.X() - aEditClickPos.X() ) > 5 )
            ||  ( std::abs( aCurrentMousePos.Y() - aEditClickPos.Y() ) > 5 )
            )
        {
            return;
        }
    }

    SvTreeListEntry* pEntry = GetCurEntry();
    if( pEntry )
    {
        ShowCursor( false );
        pView->ImplEditEntry( pEntry );
        ShowCursor( true );
    }
}

bool SvImpLBox::RequestHelp( const HelpEvent& rHEvt )
{
    if( rHEvt.GetMode() & HelpEventMode::QUICK )
    {
        Point aPos( pView->ScreenToOutputPixel( rHEvt.GetMousePosPixel() ));
        if( !GetVisibleArea().IsInside( aPos ))
            return false;

        SvTreeListEntry* pEntry = GetEntry( aPos );
        if( pEntry )
        {
            // recalculate text rectangle
            SvLBoxTab* pTab;
            SvLBoxItem* pItem = pView->GetItem( pEntry, aPos.X(), &pTab );
            if (!pItem || pItem->GetType() != SvLBoxItemType::String)
                return false;

            aPos = GetEntryPosition( pEntry );
            aPos.setX( pView->GetTabPos( pEntry, pTab ) ); //pTab->GetPos();
            Size aSize( pItem->GetSize( pView, pEntry ) );
            SvLBoxTab* pNextTab = NextTab( pTab );
            bool bItemClipped = false;
            // is the item cut off by its right neighbor?
            if( pNextTab && pView->GetTabPos(pEntry,pNextTab) < aPos.X()+aSize.Width() )
            {
                aSize.setWidth( pNextTab->GetPos() - pTab->GetPos() );
                bItemClipped = true;
            }
            tools::Rectangle aItemRect( aPos, aSize );

            tools::Rectangle aViewRect( GetVisibleArea() );

            if( bItemClipped || !aViewRect.IsInside( aItemRect ) )
            {
                // clip the right edge of the item at the edge of the view
                //if( aItemRect.Right() > aViewRect.Right() )
                //  aItemRect.Right() = aViewRect.Right();

                Point aPt = pView->OutputToScreenPixel( aItemRect.TopLeft() );
                aItemRect.SetLeft( aPt.X() );
                aItemRect.SetTop( aPt.Y() );
                aPt = pView->OutputToScreenPixel( aItemRect.BottomRight() );
                aItemRect.SetRight( aPt.X() );
                aItemRect.SetBottom( aPt.Y() );

                Help::ShowQuickHelp( pView, aItemRect,
                                     static_cast<SvLBoxString*>(pItem)->GetText(), QuickHelpFlags::Left | QuickHelpFlags::VCenter );
                return true;
            }
        }
    }
    return false;
}

SvLBoxTab* SvImpLBox::NextTab( SvLBoxTab const * pTab )
{
    sal_uInt16 nTabCount = pView->TabCount();
    if( nTabCount <= 1 )
        return nullptr;
    for( int nTab=0; nTab < (nTabCount-1); nTab++)
    {
        if( pView->aTabs[nTab].get() == pTab )
            return pView->aTabs[nTab+1].get();
    }
    return nullptr;
}

void SvImpLBox::EndSelection()
{
    DestroyAnchor();
    nFlags &=  ~LBoxFlags::StartEditTimer;
}

void SvImpLBox::SetUpdateMode( bool bMode )
{
    if( bUpdateMode != bMode )
    {
        bUpdateMode = bMode;
        if( bUpdateMode )
            UpdateAll( false );
    }
}

bool SvImpLBox::SetMostRight( SvTreeListEntry* pEntry )
{
    if( pView->nTreeFlags & SvTreeFlags::RECALCTABS )
    {
        nFlags |= LBoxFlags::IgnoreChangedTabs;
        pView->SetTabs();
        nFlags &= ~LBoxFlags::IgnoreChangedTabs;
    }

    sal_uInt16 nLastTab = pView->aTabs.size() - 1;
    sal_uInt16 nLastItem = pEntry->ItemCount() - 1;
    if( !pView->aTabs.empty() && nLastItem != USHRT_MAX )
    {
        if( nLastItem < nLastTab )
            nLastTab = nLastItem;

        SvLBoxTab* pTab = pView->aTabs[ nLastTab ].get();
        SvLBoxItem& rItem = pEntry->GetItem( nLastTab );

        long nTabPos = pView->GetTabPos( pEntry, pTab );

        long nMaxRight = GetOutputSize().Width();
        Point aPos( pView->GetMapMode().GetOrigin() );
        aPos.setX( aPos.X() * -1 ); // conversion document coordinates
        nMaxRight = nMaxRight + aPos.X() - 1;

        long nNextTab = nTabPos < nMaxRight ? nMaxRight : nMaxRight + 50;
        long nTabWidth = nNextTab - nTabPos + 1;
        long nItemSize = rItem.GetSize(pView,pEntry).Width();
        long nOffset = pTab->CalcOffset( nItemSize, nTabWidth );

        long nRight = nTabPos + nOffset + nItemSize;
        if( nRight > nMostRight )
        {
            nMostRight = nRight;
            pMostRightEntry = pEntry;
            return true;
        }
    }
    return false;
}

void SvImpLBox::FindMostRight( SvTreeListEntry const * pEntryToIgnore )
{
    nMostRight = -1;
    pMostRightEntry = nullptr;
    if( !pView->GetModel() )
        return;

    SvTreeListEntry* pEntry = pView->FirstVisible();
    while( pEntry )
    {
        if( pEntry != pEntryToIgnore )
            SetMostRight( pEntry );
        pEntry = pView->NextVisible( pEntry );
    }
}

void SvImpLBox::FindMostRight( SvTreeListEntry* pParent, SvTreeListEntry* pEntryToIgnore )
{
    if( !pParent )
        FindMostRight( pEntryToIgnore );
    else
        FindMostRight_Impl( pParent, pEntryToIgnore  );
}

void SvImpLBox::FindMostRight_Impl( SvTreeListEntry* pParent, SvTreeListEntry* pEntryToIgnore )
{
    SvTreeListEntries& rList = pTree->GetChildList( pParent );

    size_t nCount = rList.size();
    for( size_t nCur = 0; nCur < nCount; nCur++ )
    {
        SvTreeListEntry* pChild = rList[nCur].get();
        if( pChild != pEntryToIgnore )
        {
            SetMostRight( pChild );
            if( pChild->HasChildren() && pView->IsExpanded( pChild ))
                FindMostRight_Impl( pChild, pEntryToIgnore );
        }
    }
}

void SvImpLBox::NotifyTabsChanged()
{
    if( GetUpdateMode() && !(nFlags & LBoxFlags::IgnoreChangedTabs ) &&
        nCurUserEvent == nullptr )
    {
        nCurUserEvent = Application::PostUserEvent(LINK(this,SvImpLBox,MyUserEvent));
    }
}

bool SvImpLBox::IsExpandable() const
{
    return pCursor->HasChildren() || pCursor->HasChildrenOnDemand();
}

IMPL_LINK(SvImpLBox, MyUserEvent, void*, pArg, void )
{
    nCurUserEvent = nullptr;
    if( !pArg )
    {
        pView->Invalidate();
        pView->Update();
    }
    else
    {
        FindMostRight( nullptr );
        ShowVerSBar();
        pView->Invalidate( GetVisibleArea() );
    }
}


void SvImpLBox::StopUserEvent()
{
    if( nCurUserEvent != nullptr )
    {
        Application::RemoveUserEvent( nCurUserEvent );
        nCurUserEvent = nullptr;
    }
}

void SvImpLBox::ShowFocusRect( const SvTreeListEntry* pEntry )
{
    if( pEntry )
    {
        long nY = GetEntryLine( const_cast<SvTreeListEntry*>(pEntry) );
        tools::Rectangle aRect = pView->GetFocusRect( const_cast<SvTreeListEntry*>(pEntry), nY );
        vcl::Region aOldClip( pView->GetClipRegion());
        vcl::Region aClipRegion( GetClipRegionRect() );
        pView->SetClipRegion( aClipRegion );
        pView->ShowFocus( aRect );
        pView->SetClipRegion( aOldClip );

    }
    else
    {
        pView->HideFocus();
    }
}


void SvImpLBox::implInitDefaultNodeImages()
{
    if ( s_pDefCollapsed )
        // assume that all or nothing is initialized
        return;

    s_pDefCollapsed = new Image(BitmapEx(RID_BMP_TREENODE_COLLAPSED));
    s_pDefExpanded = new Image(BitmapEx(RID_BMP_TREENODE_EXPANDED));
}


const Image& SvImpLBox::GetDefaultExpandedNodeImage( )
{
    implInitDefaultNodeImages();
    return *s_pDefExpanded;
}


const Image& SvImpLBox::GetDefaultCollapsedNodeImage( )
{
    implInitDefaultNodeImages();
    return *s_pDefCollapsed;
}


void SvImpLBox::CallEventListeners( VclEventId nEvent, void* pData )
{
    if ( pView )
        pView->CallImplEventListeners( nEvent, pData);
}


bool SvImpLBox::SetCurrentTabPos( sal_uInt16 _nNewPos )
{
    bool bRet = false;

    if ( pView && _nNewPos < ( pView->TabCount() - 2 ) )
    {
        nCurTabPos = _nNewPos;
        ShowCursor( true );
        bRet = true;
    }

    return bRet;
}


bool SvImpLBox::IsSelectable( const SvTreeListEntry* pEntry )
{
    if( pEntry )
    {
        SvViewDataEntry* pViewDataNewCur = pView->GetViewDataEntry(pEntry);
        return (pViewDataNewCur == nullptr) || pViewDataNewCur->IsSelectable();
    }
    else
    {
        return false;
    }
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
