/* -*- 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 <limits.h>
#include <osl/diagnose.h>
#include <tools/debug.hxx>
#include <vcl/wall.hxx>
#include <vcl/help.hxx>
#include <vcl/decoview.hxx>
#include <vcl/svapp.hxx>
#include <tools/poly.hxx>
#include <vcl/lineinfo.hxx>
#include <vcl/i18nhelp.hxx>
#include <vcl/mnemonic.hxx>
#include <vcl/settings.hxx>
#include <vcl/commandevent.hxx>

#include <vcl/ivctrl.hxx>
#include "imivctl.hxx"

#include <algorithm>
#include <memory>
#include <vcl/idle.hxx>

static constexpr auto DRAWTEXT_FLAGS_ICON =
    DrawTextFlags::Center | DrawTextFlags::Top | DrawTextFlags::EndEllipsis |
    DrawTextFlags::Clip | DrawTextFlags::MultiLine | DrawTextFlags::WordBreak | DrawTextFlags::Mnemonic;

#define DRAWTEXT_FLAGS_SMALLICON (DrawTextFlags::Left|DrawTextFlags::EndEllipsis|DrawTextFlags::Clip)

#define EVENTID_SHOW_CURSOR             (reinterpret_cast<void*>(1))
#define EVENTID_ADJUST_SCROLLBARS       (reinterpret_cast<void*>(2))

SvxIconChoiceCtrl_Impl::SvxIconChoiceCtrl_Impl(
    SvtIconChoiceCtrl* pCurView,
    WinBits nWinStyle
) :
    bChooseWithCursor(false),
    aVerSBar( VclPtr<ScrollBar>::Create(pCurView, WB_DRAG | WB_VSCROLL) ),
    aHorSBar( VclPtr<ScrollBar>::Create(pCurView, WB_DRAG | WB_HSCROLL) ),
    aScrBarBox( VclPtr<ScrollBarBox>::Create(pCurView) ),
    aAutoArrangeIdle ( "svtools contnr SvxIconChoiceCtrl_Impl AutoArrange" ),
    aDocRectChangedIdle ( "svtools contnr SvxIconChoiceCtrl_Impl DocRectChanged" ),
    aVisRectChangedIdle ( "svtools contnr SvxIconChoiceCtrl_Impl VisRectChanged" ),
    aCallSelectHdlIdle ( "svtools contnr SvxIconChoiceCtrl_Impl CallSelectHdl" ),
    aImageSize( 32 * pCurView->GetDPIScaleFactor(), 32 * pCurView->GetDPIScaleFactor()),
    pView(pCurView), nMaxVirtWidth(DEFAULT_MAX_VIRT_WIDTH), nMaxVirtHeight(DEFAULT_MAX_VIRT_HEIGHT),
    nFlags(IconChoiceFlags::NONE), nUserEventAdjustScrBars(nullptr),
    pCurHighlightFrame(nullptr), bHighlightFramePressed(false), pHead(nullptr), pCursor(nullptr),
    pHdlEntry(nullptr),
    pAnchor(nullptr), eTextMode(SvxIconChoiceCtrlTextMode::Short),
    eSelectionMode(SelectionMode::Multiple), ePositionMode(SvxIconChoiceCtrlPositionMode::Free),
    bUpdateMode(true)
{
    SetStyle( nWinStyle );
    pImpCursor.reset( new IcnCursor_Impl( this ) );
    pGridMap.reset( new IcnGridMap_Impl( this ) );

    aVerSBar->SetScrollHdl( LINK( this, SvxIconChoiceCtrl_Impl, ScrollUpDownHdl ) );
    aHorSBar->SetScrollHdl( LINK( this, SvxIconChoiceCtrl_Impl, ScrollLeftRightHdl ) );

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

    aAutoArrangeIdle.SetPriority( TaskPriority::HIGH_IDLE );
    aAutoArrangeIdle.SetInvokeHandler(LINK(this,SvxIconChoiceCtrl_Impl,AutoArrangeHdl));
    aAutoArrangeIdle.SetDebugName( "svtools::SvxIconChoiceCtrl_Impl aAutoArrangeIdle" );

    aCallSelectHdlIdle.SetPriority( TaskPriority::LOWEST );
    aCallSelectHdlIdle.SetInvokeHandler( LINK(this,SvxIconChoiceCtrl_Impl,CallSelectHdlHdl));
    aCallSelectHdlIdle.SetDebugName( "svtools::SvxIconChoiceCtrl_Impl aCallSelectHdlIdle" );

    aDocRectChangedIdle.SetPriority( TaskPriority::HIGH_IDLE );
    aDocRectChangedIdle.SetInvokeHandler(LINK(this,SvxIconChoiceCtrl_Impl,DocRectChangedHdl));
    aDocRectChangedIdle.SetDebugName( "svtools::SvxIconChoiceCtrl_Impl aDocRectChangedIdle" );

    aVisRectChangedIdle.SetPriority( TaskPriority::HIGH_IDLE );
    aVisRectChangedIdle.SetInvokeHandler(LINK(this,SvxIconChoiceCtrl_Impl,VisRectChangedHdl));
    aVisRectChangedIdle.SetDebugName( "svtools::SvxIconChoiceCtrl_Impl aVisRectChangedIdle" );

    Clear( true );
    Size gridSize(100,70);
    if(pView->GetDPIScaleFactor() > 1)
    {
      gridSize.setHeight( gridSize.Height() * ( pView->GetDPIScaleFactor()) );
    }
    SetGrid(gridSize);
}

SvxIconChoiceCtrl_Impl::~SvxIconChoiceCtrl_Impl()
{
    Clear(false);
    CancelUserEvents();
    pImpCursor.reset();
    pGridMap.reset();
    ClearSelectedRectList();
    m_pColumns.reset();
    aVerSBar.disposeAndClear();
    aHorSBar.disposeAndClear();
    aScrBarBox.disposeAndClear();
}

void SvxIconChoiceCtrl_Impl::Clear( bool bInCtor )
{
    nSelectionCount = 0;
    pCurHighlightFrame = nullptr;
    CancelUserEvents();
    ShowCursor( false );
    bBoundRectsDirty = false;
    nMaxBoundHeight = 0;

    pCursor = nullptr;
    if( !bInCtor )
    {
        pImpCursor->Clear();
        pGridMap->Clear();
        aVirtOutputSize.setWidth( 0 );
        aVirtOutputSize.setHeight( 0 );
        Size aSize( pView->GetOutputSizePixel() );
        nMaxVirtWidth = aSize.Width() - nVerSBarWidth;
        if( nMaxVirtWidth <= 0 )
            nMaxVirtWidth = DEFAULT_MAX_VIRT_WIDTH;
        nMaxVirtHeight = aSize.Height() - nHorSBarHeight;
        if( nMaxVirtHeight <= 0 )
            nMaxVirtHeight = DEFAULT_MAX_VIRT_HEIGHT;
        maZOrderList.clear();
        SetOrigin( Point() );
        if( bUpdateMode )
            pView->Invalidate(InvalidateFlags::NoChildren);
    }
    AdjustScrollBars();
    maEntries.clear();
    DocRectChanged();
    VisRectChanged();
}

void SvxIconChoiceCtrl_Impl::SetStyle( WinBits nWinStyle )
{
    nWinBits = nWinStyle;
    nCurTextDrawFlags = DRAWTEXT_FLAGS_ICON;
    if( nWinBits & (WB_SMALLICON | WB_DETAILS) )
        nCurTextDrawFlags = DRAWTEXT_FLAGS_SMALLICON;
    if( nWinBits & WB_NOSELECTION )
        eSelectionMode = SelectionMode::NONE;
    if( !(nWinStyle & (WB_ALIGN_TOP | WB_ALIGN_LEFT)))
        nWinBits |= WB_ALIGN_LEFT;
    if( nWinStyle & WB_DETAILS )
    {
        if (!m_pColumns)
            SetColumn( 0, SvxIconChoiceCtrlColumnInfo() );
    }
}

IMPL_LINK( SvxIconChoiceCtrl_Impl, ScrollUpDownHdl, ScrollBar*, pScrollBar, void )
{
    // arrow up: delta=-1; arrow down: delta=+1
    Scroll( 0, pScrollBar->GetDelta() );
}

IMPL_LINK( SvxIconChoiceCtrl_Impl, ScrollLeftRightHdl, ScrollBar*, pScrollBar, void )
{
    // arrow left: delta=-1; arrow right: delta=+1
    Scroll( pScrollBar->GetDelta(), 0 );
}

void SvxIconChoiceCtrl_Impl::FontModified()
{
    SetDefaultTextSize();
    ShowCursor( false );
    ShowCursor( true );
}

void SvxIconChoiceCtrl_Impl::InsertEntry( std::unique_ptr<SvxIconChoiceCtrlEntry> pEntry1, size_t nPos)
{
    auto pEntry = pEntry1.get();

    if ( nPos < maEntries.size() ) {
        maEntries.insert( maEntries.begin() + nPos, std::move(pEntry1) );
    } else {
        maEntries.push_back( std::move(pEntry1) );
    }

    if( pHead )
        pEntry->SetBacklink( pHead->pblink );

    if( (nFlags & IconChoiceFlags::EntryListPosValid) && nPos >= maEntries.size() - 1 )
        pEntry->nPos = maEntries.size() - 1;
    else
        nFlags &= ~IconChoiceFlags::EntryListPosValid;

    maZOrderList.push_back( pEntry );
    pImpCursor->Clear();

    // If the UpdateMode is true, don't set all bounding rectangles to
    // 'to be checked', but only the bounding rectangle of the new entry.
    // Thus, don't call InvalidateBoundingRect!
    pEntry->aRect.SetRight( LONG_MAX );
    if( bUpdateMode )
    {
        FindBoundingRect( pEntry );
        tools::Rectangle aOutputArea( GetOutputRect() );
        pGridMap->OccupyGrids( pEntry );
        if( !aOutputArea.IsOver( pEntry->aRect ) )
            return; // is invisible
        pView->Invalidate( pEntry->aRect );
    }
    else
        InvalidateBoundingRect( pEntry->aRect );
}

void SvxIconChoiceCtrl_Impl::RemoveEntry(size_t nPos)
{
    pImpCursor->Clear();
    maEntries.erase(maEntries.begin() + nPos);
    RecalcAllBoundingRectsSmart();
}

void SvxIconChoiceCtrl_Impl::CreateAutoMnemonics( MnemonicGenerator* _pGenerator )
{
    std::unique_ptr< MnemonicGenerator > pAutoDeleteOwnGenerator;
    if ( !_pGenerator )
    {
        _pGenerator = new MnemonicGenerator;
        pAutoDeleteOwnGenerator.reset( _pGenerator );
    }

    sal_uLong   nEntryCount = GetEntryCount();
    sal_uLong   i;

    // insert texts in generator
    for( i = 0; i < nEntryCount; ++i )
    {
        DBG_ASSERT( GetEntry( i ), "-SvxIconChoiceCtrl_Impl::CreateAutoMnemonics(): more expected than provided!" );

        _pGenerator->RegisterMnemonic( GetEntry( i )->GetText() );
    }

    // exchange texts with generated mnemonics
    for( i = 0; i < nEntryCount; ++i )
    {
        SvxIconChoiceCtrlEntry* pEntry = GetEntry( i );
        OUString                aTxt = pEntry->GetText();

        OUString aNewText = _pGenerator->CreateMnemonic( aTxt );
        if( aNewText != aTxt )
            pEntry->SetText( aNewText );
    }
}

tools::Rectangle SvxIconChoiceCtrl_Impl::GetOutputRect() const
{
    Point aOrigin( pView->GetMapMode().GetOrigin() );
    aOrigin *= -1;
    return tools::Rectangle( aOrigin, aOutputSize );
}

void SvxIconChoiceCtrl_Impl::SetListPositions()
{
    if( nFlags & IconChoiceFlags::EntryListPosValid )
        return;

    size_t nCount = maEntries.size();
    for( size_t nCur = 0; nCur < nCount; nCur++ )
    {
        maEntries[ nCur ]->nPos = nCur;
    }
    nFlags |= IconChoiceFlags::EntryListPosValid;
}

void SvxIconChoiceCtrl_Impl::SelectEntry( SvxIconChoiceCtrlEntry* pEntry, bool bSelect,
    bool bAdd )
{
    if( eSelectionMode == SelectionMode::NONE )
        return;

    if( !bAdd )
    {
        if ( !( nFlags & IconChoiceFlags::ClearingSelection ) )
        {
            nFlags |= IconChoiceFlags::ClearingSelection;
            DeselectAllBut( pEntry );
            nFlags &= ~IconChoiceFlags::ClearingSelection;
        }
    }
    if( pEntry->IsSelected() == bSelect )
        return;

    pHdlEntry = pEntry;
    SvxIconViewFlags nEntryFlags = pEntry->GetFlags();
    if( bSelect )
    {
        nEntryFlags |= SvxIconViewFlags::SELECTED;
        pEntry->AssignFlags( nEntryFlags );
        nSelectionCount++;
        CallSelectHandler();
    }
    else
    {
        nEntryFlags &= ~SvxIconViewFlags::SELECTED;
        pEntry->AssignFlags( nEntryFlags );
        nSelectionCount--;
        CallSelectHandler();
    }
    EntrySelected( pEntry, bSelect );
}

void SvxIconChoiceCtrl_Impl::EntrySelected(SvxIconChoiceCtrlEntry* pEntry, bool bSelect)
{
    // When using SingleSelection, make sure that the cursor is always placed
    // over the (only) selected entry. (But only if a cursor exists.)
    if (bSelect && pCursor &&
        eSelectionMode == SelectionMode::Single &&
        pEntry != pCursor)
    {
        SetCursor(pEntry);
    }

    // Not when dragging though, else the loop in SelectRect doesn't work
    // correctly!
    if (!(nFlags & IconChoiceFlags::SelectingRect))
        ToTop(pEntry);
    if (bUpdateMode)
    {
        if (pEntry == pCursor)
            ShowCursor(false);
        pView->Invalidate(CalcFocusRect(pEntry));
        if (pEntry == pCursor)
            ShowCursor(true);
    }

    // #i101012# emit vcl event LISTBOX_SELECT only in case that the given entry is selected.
    if (bSelect)
    {
        CallEventListeners(VclEventId::ListboxSelect, pEntry);
    }
}

void SvxIconChoiceCtrl_Impl::ResetVirtSize()
{
    aVirtOutputSize.setWidth( 0 );
    aVirtOutputSize.setHeight( 0 );
    const size_t nCount = maEntries.size();
    for( size_t nCur = 0; nCur < nCount; nCur++ )
    {
        SvxIconChoiceCtrlEntry* pCur = maEntries[ nCur ].get();
        pCur->ClearFlags( SvxIconViewFlags::POS_MOVED );
        if( pCur->IsPosLocked() )
        {
            // adapt (among others) VirtSize
            if( !IsBoundingRectValid( pCur->aRect ) )
                FindBoundingRect( pCur );
            else
                AdjustVirtSize( pCur->aRect );
        }
        else
            InvalidateBoundingRect( pCur->aRect );
    }

    if( !(nWinBits & (WB_NOVSCROLL | WB_NOHSCROLL)) )
    {
        Size aRealOutputSize( pView->GetOutputSizePixel() );
        if( aVirtOutputSize.Width() < aRealOutputSize.Width() ||
            aVirtOutputSize.Height() < aRealOutputSize.Height() )
        {
            sal_uLong nGridCount = IcnGridMap_Impl::GetGridCount(
                aRealOutputSize, static_cast<sal_uInt16>(nGridDX), static_cast<sal_uInt16>(nGridDY) );
            if( nGridCount < nCount )
            {
                if( nWinBits & WB_ALIGN_TOP )
                    nMaxVirtWidth = aRealOutputSize.Width() - nVerSBarWidth;
                else // WB_ALIGN_LEFT
                    nMaxVirtHeight = aRealOutputSize.Height() - nHorSBarHeight;
            }
        }
    }

    pImpCursor->Clear();
    pGridMap->Clear();
    VisRectChanged();
}

void SvxIconChoiceCtrl_Impl::AdjustVirtSize( const tools::Rectangle& rRect )
{
    long nHeightOffs = 0;
    long nWidthOffs = 0;

    if( aVirtOutputSize.Width() < (rRect.Right()+LROFFS_WINBORDER) )
        nWidthOffs = (rRect.Right()+LROFFS_WINBORDER) - aVirtOutputSize.Width();

    if( aVirtOutputSize.Height() < (rRect.Bottom()+TBOFFS_WINBORDER) )
        nHeightOffs = (rRect.Bottom()+TBOFFS_WINBORDER) - aVirtOutputSize.Height();

    if( !(nWidthOffs || nHeightOffs) )
        return;

    Range aRange;
    aVirtOutputSize.AdjustWidth(nWidthOffs );
    aRange.Max() = aVirtOutputSize.Width();
    aHorSBar->SetRange( aRange );

    aVirtOutputSize.AdjustHeight(nHeightOffs );
    aRange.Max() = aVirtOutputSize.Height();
    aVerSBar->SetRange( aRange );

    pImpCursor->Clear();
    pGridMap->OutputSizeChanged();
    AdjustScrollBars();
    DocRectChanged();
}

void SvxIconChoiceCtrl_Impl::InitPredecessors()
{
    DBG_ASSERT(!pHead,"SvxIconChoiceCtrl_Impl::InitPredecessors() >> Already initialized");
    size_t nCount = maEntries.size();
    if( nCount )
    {
        SvxIconChoiceCtrlEntry* pPrev = maEntries[ 0 ].get();
        for( size_t nCur = 1; nCur <= nCount; nCur++ )
        {
            pPrev->ClearFlags( SvxIconViewFlags::POS_LOCKED | SvxIconViewFlags::POS_MOVED );

            SvxIconChoiceCtrlEntry* pNext;
            if( nCur == nCount )
                pNext = maEntries[ 0 ].get();
            else
                pNext = maEntries[ nCur ].get();
            pPrev->pflink = pNext;
            pNext->pblink = pPrev;
            pPrev = pNext;
        }
        pHead = maEntries[ 0 ].get();
    }
    else
        pHead = nullptr;
}

void SvxIconChoiceCtrl_Impl::ClearPredecessors()
{
    if( pHead )
    {
        size_t nCount = maEntries.size();
        for( size_t nCur = 0; nCur < nCount; nCur++ )
        {
            SvxIconChoiceCtrlEntry* pCur = maEntries[ nCur ].get();
            pCur->pflink = nullptr;
            pCur->pblink = nullptr;
        }
        pHead = nullptr;
    }
}

void SvxIconChoiceCtrl_Impl::Arrange( bool bKeepPredecessors, long nSetMaxVirtWidth, long nSetMaxVirtHeight )
{
    if ( nSetMaxVirtWidth != 0 )
        nMaxVirtWidth = nSetMaxVirtWidth;
    else
        nMaxVirtWidth = aOutputSize.Width();

    if ( nSetMaxVirtHeight != 0 )
        nMaxVirtHeight = nSetMaxVirtHeight;
    else
        nMaxVirtHeight = aOutputSize.Height();

    ImpArrange( bKeepPredecessors );
}

void SvxIconChoiceCtrl_Impl::ImpArrange( bool bKeepPredecessors )
{
    static Point aEmptyPoint;

    bool bOldUpdate = bUpdateMode;
    tools::Rectangle aCurOutputArea( GetOutputRect() );
    if( (nWinBits & WB_SMART_ARRANGE) && aCurOutputArea.TopLeft() != aEmptyPoint )
        bUpdateMode = false;
    aAutoArrangeIdle.Stop();
    nFlags |= IconChoiceFlags::Arranging;
    ShowCursor( false );
    ResetVirtSize();
    if( !bKeepPredecessors )
        ClearPredecessors();
    bBoundRectsDirty = false;
    SetOrigin( Point() );
    VisRectChanged();
    RecalcAllBoundingRectsSmart();
    // TODO: the invalidation in the detail view should be more intelligent
    //if( !(nWinBits & WB_DETAILS ))
    pView->Invalidate( InvalidateFlags::NoChildren );
    nFlags &= ~IconChoiceFlags::Arranging;
    if( (nWinBits & WB_SMART_ARRANGE) && aCurOutputArea.TopLeft() != aEmptyPoint )
    {
        MakeVisible( aCurOutputArea );
        SetUpdateMode( bOldUpdate );
    }
    ShowCursor( true );
}

void SvxIconChoiceCtrl_Impl::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
{
#if defined(OV_DRAWGRID)
    Color aOldColor (rRenderContext.GetLineColor());
    Color aCOL_BLACK);
    rRenderContext.SetLineColor( aColor );
    Point aOffs(rRenderContext.GetMapMode().GetOrigin());
    Size aXSize(GetOutputSizePixel());
    {
        Point aStart(LROFFS_WINBORDER, 0);
        Point aEnd(LROFFS_WINBORDER, aXSize.Height());
        aStart -= aOffs;
        aEnd -= aOffs;
        rRenderContext.DrawLine(aStart, aEnd);
    }
    {
        Point aStart(0, TBOFFS_WINBORDER);
        Point aEnd(aXSize.Width(), TBOFFS_WINBORDER);
        aStart -= aOffs;
        aEnd -= aOffs;
        rRenderContext.DrawLine(aStart, aEnd);
    }

    for (long nDX = nGridDX; nDX <= aXSize.Width(); nDX += nGridDX)
    {
        Point aStart( nDX+LROFFS_WINBORDER, 0 );
        Point aEnd( nDX+LROFFS_WINBORDER, aXSize.Height());
        aStart -= aOffs;
        aEnd -= aOffs;
        rRenderContext.DrawLine(aStart, aEnd);
    }
    for (long nDY = nGridDY; nDY <= aXSize.Height(); nDY += nGridDY)
    {
        Point aStart(0, nDY + TBOFFS_WINBORDER);
        Point aEnd(aXSize.Width(), nDY + TBOFFS_WINBORDER);
        aStart -= aOffs;
        aEnd -= aOffs;
        rRenderContext.DrawLine(aStart, aEnd);
    }
    rRenderContext.SetLineColor(aOldColor);
#endif

    if (!maEntries.size())
        return;
    if (!pCursor)
    {
        // set cursor to item with focus-flag
        bool bfound = false;
        for (sal_Int32 i = 0; i < pView->GetEntryCount() && !bfound; i++)
        {
            SvxIconChoiceCtrlEntry* pEntry = pView->GetEntry(i);
            if (pEntry->IsFocused())
            {
                pCursor = pEntry;
                bfound = true;
            }
        }

        if (!bfound)
            pCursor = maEntries[ 0 ].get();
    }

    size_t nCount = maZOrderList.size();
    if (!nCount)
        return;

    rRenderContext.Push(PushFlags::CLIPREGION);
    rRenderContext.SetClipRegion(vcl::Region(rRect));

    std::vector< SvxIconChoiceCtrlEntry* > aNewZOrderList;
    std::vector< SvxIconChoiceCtrlEntry* > aPaintedEntries;

    size_t nPos = 0;
    while(nCount)
    {
        SvxIconChoiceCtrlEntry* pEntry = maZOrderList[nPos];
        const tools::Rectangle& rBoundRect = GetEntryBoundRect(pEntry);
        if (rRect.IsOver(rBoundRect))
        {
            PaintEntry(pEntry, rBoundRect.TopLeft(), rRenderContext);
            // set entries to Top if they are being repainted
            aPaintedEntries.push_back(pEntry);
        }
        else
            aNewZOrderList.push_back(pEntry);

        nCount--;
        nPos++;
    }
    maZOrderList = std::move( aNewZOrderList );
    maZOrderList.insert(maZOrderList.end(), aPaintedEntries.begin(), aPaintedEntries.end());

    rRenderContext.Pop();
}

void SvxIconChoiceCtrl_Impl::RepaintSelectedEntries()
{
    const size_t nCount = maZOrderList.size();
    if (!nCount)
        return;

    tools::Rectangle aOutRect(GetOutputRect());
    for (size_t nCur = 0; nCur < nCount; nCur++)
    {
        SvxIconChoiceCtrlEntry* pEntry = maZOrderList[nCur];
        if (pEntry->GetFlags() & SvxIconViewFlags::SELECTED)
        {
            const tools::Rectangle& rBoundRect = GetEntryBoundRect(pEntry);
            if (aOutRect.IsOver(rBoundRect))
                pView->Invalidate(rBoundRect);
        }
    }
}

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

bool SvxIconChoiceCtrl_Impl::MouseButtonDown( const MouseEvent& rMEvt)
{
    bool bHandled = true;
    bHighlightFramePressed = false;
    bool bGotFocus = (!pView->HasFocus() && !(nWinBits & WB_NOPOINTERFOCUS));
    if( !(nWinBits & WB_NOPOINTERFOCUS) )
        pView->GrabFocus();

    Point aDocPos( rMEvt.GetPosPixel() );
    if(aDocPos.X()>=aOutputSize.Width() || aDocPos.Y()>=aOutputSize.Height())
        return false;
    ToDocPos( aDocPos );
    SvxIconChoiceCtrlEntry* pEntry = GetEntry( aDocPos, true );
    if( pEntry )
        MakeEntryVisible( pEntry, false );

    if( rMEvt.IsShift() && eSelectionMode != SelectionMode::Single )
    {
        if( pEntry )
            SetCursor_Impl( pCursor, pEntry, rMEvt.IsMod1(), rMEvt.IsShift() );
        return true;
    }

    if( pAnchor && (rMEvt.IsShift() || rMEvt.IsMod1())) // keyboard selection?
    {
        DBG_ASSERT(eSelectionMode != SelectionMode::Single,"Invalid selection mode");
        if( rMEvt.IsMod1() )
            nFlags |= IconChoiceFlags::AddMode;

        if( rMEvt.IsShift() )
        {
            tools::Rectangle aRect( GetEntryBoundRect( pAnchor ));
            if( pEntry )
                aRect.Union( GetEntryBoundRect( pEntry ) );
            else
            {
                tools::Rectangle aTempRect( aDocPos, Size(1,1));
                aRect.Union( aTempRect );
            }
            aCurSelectionRect = aRect;
            SelectRect( aRect, bool(nFlags & IconChoiceFlags::AddMode), &aSelectedRectList );
        }
        else if( rMEvt.IsMod1() )
        {
            AddSelectedRect( aCurSelectionRect );
            pAnchor = nullptr;
            aCurSelectionRect.SetPos( aDocPos );
        }

        if( !pEntry && !(nWinBits & WB_NODRAGSELECTION))
            pView->StartTracking( StartTrackingFlags::ScrollRepeat );
        return true;
    }
    else
    {
        if( !pEntry )
        {
            if( eSelectionMode == SelectionMode::Multiple )
            {
                if( !rMEvt.IsMod1() )  // Ctrl
                {
                    if( !bGotFocus )
                    {
                        SetNoSelection();
                        ClearSelectedRectList();
                    }
                }
                else
                    nFlags |= IconChoiceFlags::AddMode;
                aCurSelectionRect.SetPos( aDocPos );
                pView->StartTracking( StartTrackingFlags::ScrollRepeat );
            }
            else
                bHandled = false;
            return bHandled;
        }
    }
    bool bSelected = pEntry->IsSelected();

    if( rMEvt.GetClicks() == 2 )
    {
        DeselectAllBut( pEntry );
        SelectEntry( pEntry, true, false );
        pHdlEntry = pEntry;
        pView->ClickIcon();
    }
    else
    {
        // Inplace-Editing ?
        if( rMEvt.IsMod2() )  // Alt?
        {
        }
        else if( eSelectionMode == SelectionMode::Single )
        {
            DeselectAllBut( pEntry );
            SetCursor( pEntry );
        }
        else if( eSelectionMode == SelectionMode::NONE )
        {
            if( rMEvt.IsLeft() && (nWinBits & WB_HIGHLIGHTFRAME) )
            {
                pCurHighlightFrame = nullptr; // force repaint of frame
                bHighlightFramePressed = true;
                SetEntryHighlightFrame( pEntry, true );
            }
        }
        else
        {
            if( !rMEvt.GetModifier() && rMEvt.IsLeft() )
            {
                if( !bSelected )
                {
                    DeselectAllBut( pEntry );
                    SetCursor( pEntry );
                    SelectEntry( pEntry, true, false );
                }
                else
                {
                    // deselect only in the Up, if the Move happened via D&D!
                    nFlags |= IconChoiceFlags::DownDeselect;
                }
            }
            else if( rMEvt.IsMod1() )
                nFlags |= IconChoiceFlags::DownCtrl;
        }
    }
    return bHandled;
}

bool SvxIconChoiceCtrl_Impl::MouseButtonUp( const MouseEvent& rMEvt )
{
    bool bHandled = false;
    if( rMEvt.IsRight() && (nFlags & (IconChoiceFlags::DownCtrl | IconChoiceFlags::DownDeselect) ))
    {
        nFlags &= ~IconChoiceFlags(IconChoiceFlags::DownCtrl | IconChoiceFlags::DownDeselect);
        bHandled = true;
    }

    Point aDocPos( rMEvt.GetPosPixel() );
    ToDocPos( aDocPos );
    SvxIconChoiceCtrlEntry* pDocEntry = GetEntry( aDocPos );
    if( pDocEntry )
    {
        if( nFlags & IconChoiceFlags::DownCtrl )
        {
            // Ctrl & MultiSelection
            ToggleSelection( pDocEntry );
            SetCursor( pDocEntry );
            bHandled = true;
        }
        else if( nFlags & IconChoiceFlags::DownDeselect )
        {
            DeselectAllBut( pDocEntry );
            SetCursor( pDocEntry );
            SelectEntry( pDocEntry, true, false );
            bHandled = true;
        }
    }

    nFlags &= ~IconChoiceFlags(IconChoiceFlags::DownCtrl | IconChoiceFlags::DownDeselect);

    if((nWinBits & WB_HIGHLIGHTFRAME) && bHighlightFramePressed && pCurHighlightFrame)
    {
        bHandled = true;
        SvxIconChoiceCtrlEntry* pEntry = pCurHighlightFrame;
        pCurHighlightFrame = nullptr; // force repaint of frame
        bHighlightFramePressed = false;
        SetEntryHighlightFrame( pEntry, true );

        pHdlEntry = pCurHighlightFrame;
        pView->ClickIcon();

        // set focus on Icon
        SvxIconChoiceCtrlEntry* pOldCursor = pCursor;
        SetCursor_Impl( pOldCursor, pHdlEntry, false, false );

        pHdlEntry = nullptr;
    }
    return bHandled;
}

bool SvxIconChoiceCtrl_Impl::MouseMove( const MouseEvent& rMEvt )
{
    const Point aDocPos( pView->PixelToLogic(rMEvt.GetPosPixel()) );

    if( pView->IsTracking() )
        return false;
    else if( nWinBits & WB_HIGHLIGHTFRAME )
    {
        SvxIconChoiceCtrlEntry* pEntry = GetEntry( aDocPos, true );
        SetEntryHighlightFrame( pEntry, false );
    }
    else
        return false;
    return true;
}

void SvxIconChoiceCtrl_Impl::SetCursor_Impl( SvxIconChoiceCtrlEntry* pOldCursor,
    SvxIconChoiceCtrlEntry* pNewCursor, bool bMod1, bool bShift )
{
    if( !pNewCursor )
        return;

    SvxIconChoiceCtrlEntry* pFilterEntry = nullptr;
    bool bDeselectAll = false;
    if( eSelectionMode != SelectionMode::Single )
    {
        if( !bMod1 && !bShift )
            bDeselectAll = true;
        else if( bShift && !bMod1 && !pAnchor )
        {
            bDeselectAll = true;
            pFilterEntry = pOldCursor;
        }
    }
    if( bDeselectAll )
        DeselectAllBut( pFilterEntry );
    ShowCursor( false );
    MakeEntryVisible( pNewCursor );
    SetCursor( pNewCursor );
    if( bMod1 && !bShift )
    {
        if( pAnchor )
        {
            AddSelectedRect( pAnchor, pOldCursor );
            pAnchor = nullptr;
        }
    }
    else if( bShift )
    {
        if( !pAnchor )
            pAnchor = pOldCursor;
        if ( nWinBits & WB_ALIGN_LEFT )
            SelectRange( pAnchor, pNewCursor, bool(nFlags & IconChoiceFlags::AddMode) );
        else
            SelectRect(pAnchor,pNewCursor, bool(nFlags & IconChoiceFlags::AddMode), &aSelectedRectList);
    }
    else
    {
        SelectEntry( pCursor, true, false );
        aCurSelectionRect = GetEntryBoundRect( pCursor );
        CallEventListeners( VclEventId::ListboxSelect, pCursor );
    }
}

bool SvxIconChoiceCtrl_Impl::KeyInput( const KeyEvent& rKEvt )
{
    bool bMod2 = rKEvt.GetKeyCode().IsMod2();
    sal_Unicode cChar = rKEvt.GetCharCode();
    sal_uLong nPos = sal_uLong(-1);
    if ( bMod2 && cChar && IsMnemonicChar( cChar, nPos ) )
    {
        // shortcut is clicked
        SvxIconChoiceCtrlEntry* pNewCursor = GetEntry( nPos );
        SvxIconChoiceCtrlEntry* pOldCursor = pCursor;
        if ( pNewCursor != pOldCursor )
            SetCursor_Impl( pOldCursor, pNewCursor, false, false );
        return true;
    }

    if ( bMod2 )
        // no actions with <ALT>
        return false;

    bool bKeyUsed = true;
    bool bMod1 = rKEvt.GetKeyCode().IsMod1();
    bool bShift = rKEvt.GetKeyCode().IsShift();

    if( eSelectionMode == SelectionMode::Single || eSelectionMode == SelectionMode::NONE)
    {
        bShift = false;
        bMod1 = false;
    }

    if( bMod1 )
        nFlags |= IconChoiceFlags::AddMode;

    SvxIconChoiceCtrlEntry* pNewCursor;
    SvxIconChoiceCtrlEntry* pOldCursor = pCursor;

    sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode();
    switch( nCode )
    {
        case KEY_UP:
        case KEY_PAGEUP:
            if( pCursor )
            {
                MakeEntryVisible( pCursor );
                if( nCode == KEY_UP )
                    pNewCursor = pImpCursor->GoUpDown(pCursor,false);
                else
                    pNewCursor = pImpCursor->GoPageUpDown(pCursor,false);
                SetCursor_Impl( pOldCursor, pNewCursor, bMod1, bShift );
                if( !pNewCursor )
                {
                    tools::Rectangle aRect( GetEntryBoundRect( pCursor ) );
                    if( aRect.Top())
                    {
                        aRect.AdjustBottom( -(aRect.Top()) );
                        aRect.SetTop( 0 );
                        MakeVisible( aRect );
                    }
                }

                if ( bChooseWithCursor && pNewCursor != nullptr )
                {
                    pHdlEntry = pNewCursor;//GetCurEntry();
                    pCurHighlightFrame = pHdlEntry;
                    pView->ClickIcon();
                    pCurHighlightFrame = nullptr;
                }
            }
            break;

        case KEY_DOWN:
        case KEY_PAGEDOWN:
            if( pCursor )
            {
                if( nCode == KEY_DOWN )
                    pNewCursor=pImpCursor->GoUpDown( pCursor,true );
                else
                    pNewCursor=pImpCursor->GoPageUpDown( pCursor,true );
                SetCursor_Impl( pOldCursor, pNewCursor, bMod1, bShift );

                if ( bChooseWithCursor && pNewCursor != nullptr)
                {
                    pHdlEntry = pNewCursor;//GetCurEntry();
                    pCurHighlightFrame = pHdlEntry;
                    pView->ClickIcon();
                    pCurHighlightFrame = nullptr;
                }
            }
            break;

        case KEY_RIGHT:
            if( pCursor )
            {
                pNewCursor=pImpCursor->GoLeftRight(pCursor,true );
                SetCursor_Impl( pOldCursor, pNewCursor, bMod1, bShift );
            }
            break;

        case KEY_LEFT:
            if( pCursor )
            {
                MakeEntryVisible( pCursor );
                pNewCursor = pImpCursor->GoLeftRight(pCursor,false );
                SetCursor_Impl( pOldCursor, pNewCursor, bMod1, bShift );
                if( !pNewCursor )
                {
                    tools::Rectangle aRect( GetEntryBoundRect(pCursor));
                    if( aRect.Left() )
                    {
                        aRect.AdjustRight( -(aRect.Left()) );
                        aRect.SetLeft( 0 );
                        MakeVisible( aRect );
                    }
                }
            }
            break;

        case KEY_F2:
            if( bMod1 || bShift )
                bKeyUsed = false;
            break;

        case KEY_F8:
            if( rKEvt.GetKeyCode().IsShift() )
            {
                if( nFlags & IconChoiceFlags::AddMode )
                    nFlags &= ~IconChoiceFlags::AddMode;
                else
                    nFlags |= IconChoiceFlags::AddMode;
            }
            else
                bKeyUsed = false;
            break;

        case KEY_SPACE:
            if( pCursor && eSelectionMode != SelectionMode::Single )
            {
                if( !bMod1 )
                {
                    //SelectAll( false );
                    SetNoSelection();
                    ClearSelectedRectList();

                    // click Icon with spacebar
                    SetEntryHighlightFrame( GetCurEntry(), true );
                    pView->ClickIcon();
                    pHdlEntry = pCurHighlightFrame;
                    pCurHighlightFrame=nullptr;
                }
                else
                    ToggleSelection( pCursor );
            }
            break;

#ifdef DBG_UTIL
        case KEY_F10:
            if( rKEvt.GetKeyCode().IsShift() )
            {
                if( pCursor )
                    pView->SetEntryTextMode( SvxIconChoiceCtrlTextMode::Full, pCursor );
            }
            if( rKEvt.GetKeyCode().IsMod1() )
            {
                if( pCursor )
                    pView->SetEntryTextMode( SvxIconChoiceCtrlTextMode::Short, pCursor );
            }
            break;
#endif

        case KEY_ADD:
        case KEY_DIVIDE :
        case KEY_A:
            if( bMod1 && (eSelectionMode != SelectionMode::Single))
                SelectAll();
            else
                bKeyUsed = false;
            break;

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

        case KEY_RETURN:
            if( !bMod1 )
                bKeyUsed = false;
            break;

        case KEY_END:
            if( pCursor )
            {
                pNewCursor = maEntries.back().get();
                SetCursor_Impl( pOldCursor, pNewCursor, bMod1, bShift );
            }
            break;

        case KEY_HOME:
            if( pCursor )
            {
                pNewCursor = maEntries[ 0 ].get();
                SetCursor_Impl( pOldCursor, pNewCursor, bMod1, bShift );
            }
            break;

        default:
            bKeyUsed = false;

    }
    return bKeyUsed;
}

// recalculate TopLeft of scrollbars (but not their sizes!)
void SvxIconChoiceCtrl_Impl::PositionScrollBars( long nRealWidth, long nRealHeight )
{
    // horizontal scrollbar
    Point aPos( 0, nRealHeight );
    aPos.AdjustY( -nHorSBarHeight );

    if( aHorSBar->GetPosPixel() != aPos )
        aHorSBar->SetPosPixel( aPos );

    // vertical scrollbar
    aPos.setX( nRealWidth ); aPos.setY( 0 );
    aPos.AdjustX( -nVerSBarWidth );
    aPos.AdjustX( 1 );
    aPos.AdjustY( -1 );

    if( aVerSBar->GetPosPixel() != aPos )
        aVerSBar->SetPosPixel( aPos );
}

void SvxIconChoiceCtrl_Impl::AdjustScrollBars()
{
    long nVirtHeight = aVirtOutputSize.Height();
    long nVirtWidth = aVirtOutputSize.Width();

    Size aOSize( pView->Control::GetOutputSizePixel() );
    long nRealHeight = aOSize.Height();
    long nRealWidth = aOSize.Width();

    PositionScrollBars( nRealWidth, nRealHeight );

    const MapMode& rMapMode = pView->GetMapMode();
    Point aOrigin( rMapMode.GetOrigin() );

    long nVisibleWidth;
    if( nRealWidth > nVirtWidth )
        nVisibleWidth = nVirtWidth + aOrigin.X();
    else
        nVisibleWidth = nRealWidth;

    long nVisibleHeight;
    if( nRealHeight > nVirtHeight )
        nVisibleHeight = nVirtHeight + aOrigin.Y();
    else
        nVisibleHeight = nRealHeight;

    bool bVerSBar = ( nWinBits & WB_VSCROLL ) != 0;
    bool bHorSBar = ( nWinBits & WB_HSCROLL ) != 0;
    bool bNoVerSBar = ( nWinBits & WB_NOVSCROLL ) != 0;
    bool bNoHorSBar = ( nWinBits & WB_NOHSCROLL ) != 0;

    sal_uInt16 nResult = 0;
    if( nVirtHeight )
    {
        // activate vertical scrollbar?
        if( !bNoVerSBar && (bVerSBar || ( nVirtHeight > nVisibleHeight)) )
        {
            nResult = 0x0001;
            nRealWidth -= nVerSBarWidth;

            if( nRealWidth > nVirtWidth )
                nVisibleWidth = nVirtWidth + aOrigin.X();
            else
                nVisibleWidth = nRealWidth;
        }
        // activate horizontal scrollbar?
        if( !bNoHorSBar && (bHorSBar || (nVirtWidth > nVisibleWidth)) )
        {
            nResult |= 0x0002;
            nRealHeight -= nHorSBarHeight;

            if( nRealHeight > nVirtHeight )
                nVisibleHeight = nVirtHeight + aOrigin.Y();
            else
                nVisibleHeight = nRealHeight;

            // do we need a vertical scrollbar after all?
            if( !(nResult & 0x0001) &&  // only if not already there
                ( !bNoVerSBar && ((nVirtHeight > nVisibleHeight) || bVerSBar)) )
            {
                nResult = 3; // both turned on
                nRealWidth -= nVerSBarWidth;

                if( nRealWidth > nVirtWidth )
                    nVisibleWidth = nVirtWidth + aOrigin.X();
                else
                    nVisibleWidth = nRealWidth;
            }
        }
    }

    // size vertical scrollbar
    long nThumb = aVerSBar->GetThumbPos();
    Size aSize( nVerSBarWidth, nRealHeight );
    aSize.AdjustHeight(2 );
    if( aSize != aVerSBar->GetSizePixel() )
        aVerSBar->SetSizePixel( aSize );
    aVerSBar->SetVisibleSize( nVisibleHeight );
    aVerSBar->SetPageSize( GetScrollBarPageSize( nVisibleHeight ));

    if( nResult & 0x0001 )
    {
        aVerSBar->SetThumbPos( nThumb );
        aVerSBar->Show();
    }
    else
    {
        aVerSBar->SetThumbPos( 0 );
        aVerSBar->Hide();
    }

    // size horizontal scrollbar
    nThumb = aHorSBar->GetThumbPos();
    aSize.setWidth( nRealWidth );
    aSize.setHeight( nHorSBarHeight );
    aSize.AdjustWidth( 1 );
    if( nResult & 0x0001 ) // vertical scrollbar?
    {
        aSize.AdjustWidth( 1 );
        nRealWidth++;
    }
    if( aSize != aHorSBar->GetSizePixel() )
        aHorSBar->SetSizePixel( aSize );
    aHorSBar->SetVisibleSize( nVisibleWidth );
    aHorSBar->SetPageSize( GetScrollBarPageSize(nVisibleWidth ));
    if( nResult & 0x0002 )
    {
        aHorSBar->SetThumbPos( nThumb );
        aHorSBar->Show();
    }
    else
    {
        aHorSBar->SetThumbPos( 0 );
        aHorSBar->Hide();
    }

    aOutputSize.setWidth( nRealWidth );
    if( nResult & 0x0002 ) // horizontal scrollbar ?
        nRealHeight++; // because lower border is clipped
    aOutputSize.setHeight( nRealHeight );

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

void SvxIconChoiceCtrl_Impl::Resize()
{
    InitScrollBarBox();
    aOutputSize = pView->GetOutputSizePixel();
    pImpCursor->Clear();
    pGridMap->OutputSizeChanged();

    const Size& rSize = pView->Control::GetOutputSizePixel();
    PositionScrollBars( rSize.Width(), rSize.Height() );
    // The scrollbars are shown/hidden asynchronously, so derived classes can
    // do an Arrange during Resize, without the scrollbars suddenly turning
    // on and off again.
    // If an event is already underway, we don't need to send a new one, at least
    // as long as there is only one event type.
    if ( ! nUserEventAdjustScrBars )
        nUserEventAdjustScrBars =
            Application::PostUserEvent( LINK( this, SvxIconChoiceCtrl_Impl, UserEventHdl),
                                        EVENTID_ADJUST_SCROLLBARS);

    VisRectChanged();
}

bool SvxIconChoiceCtrl_Impl::CheckHorScrollBar()
{
    if( maZOrderList.empty() || !aHorSBar->IsVisible() )
        return false;
    const MapMode& rMapMode = pView->GetMapMode();
    Point aOrigin( rMapMode.GetOrigin() );
    if(!( nWinBits & WB_HSCROLL) && !aOrigin.X() )
    {
        long nWidth = aOutputSize.Width();
        const size_t nCount = maZOrderList.size();
        long nMostRight = 0;
        for( size_t nCur = 0; nCur < nCount; nCur++ )
        {
            SvxIconChoiceCtrlEntry* pEntry = maZOrderList[ nCur ];
            long nRight = GetEntryBoundRect(pEntry).Right();
            if( nRight > nWidth )
                return false;
            if( nRight > nMostRight )
                nMostRight = nRight;
        }
        aHorSBar->Hide();
        aOutputSize.AdjustHeight(nHorSBarHeight );
        aVirtOutputSize.setWidth( nMostRight );
        aHorSBar->SetThumbPos( 0 );
        Range aRange;
        aRange.Max() = nMostRight - 1;
        aHorSBar->SetRange( aRange  );
        if( aVerSBar->IsVisible() )
        {
            Size aSize( aVerSBar->GetSizePixel());
            aSize.AdjustHeight(nHorSBarHeight );
            aVerSBar->SetSizePixel( aSize );
        }
        return true;
    }
    return false;
}

bool SvxIconChoiceCtrl_Impl::CheckVerScrollBar()
{
    if( maZOrderList.empty() || !aVerSBar->IsVisible() )
        return false;
    const MapMode& rMapMode = pView->GetMapMode();
    Point aOrigin( rMapMode.GetOrigin() );
    if(!( nWinBits & WB_VSCROLL) && !aOrigin.Y() )
    {
        long nDeepest = 0;
        long nHeight = aOutputSize.Height();
        const size_t nCount = maZOrderList.size();
        for( size_t nCur = 0; nCur < nCount; nCur++ )
        {
            SvxIconChoiceCtrlEntry* pEntry = maZOrderList[ nCur ];
            long nBottom = GetEntryBoundRect(pEntry).Bottom();
            if( nBottom > nHeight )
                return false;
            if( nBottom > nDeepest )
                nDeepest = nBottom;
        }
        aVerSBar->Hide();
        aOutputSize.AdjustWidth(nVerSBarWidth );
        aVirtOutputSize.setHeight( nDeepest );
        aVerSBar->SetThumbPos( 0 );
        Range aRange;
        aRange.Max() = nDeepest - 1;
        aVerSBar->SetRange( aRange  );
        if( aHorSBar->IsVisible() )
        {
            Size aSize( aHorSBar->GetSizePixel());
            aSize.AdjustWidth(nVerSBarWidth );
            aHorSBar->SetSizePixel( aSize );
        }
        return true;
    }
    return false;
}


// hides scrollbars if they're unnecessary
void SvxIconChoiceCtrl_Impl::CheckScrollBars()
{
    CheckVerScrollBar();
    if( CheckHorScrollBar() )
        CheckVerScrollBar();
    if( aVerSBar->IsVisible() && aHorSBar->IsVisible() )
        aScrBarBox->Show();
    else
        aScrBarBox->Hide();
}


void SvxIconChoiceCtrl_Impl::GetFocus()
{
    RepaintSelectedEntries();
    if( pCursor )
    {
        pCursor->SetFlags( SvxIconViewFlags::FOCUSED );
        ShowCursor( true );
    }
}

void SvxIconChoiceCtrl_Impl::LoseFocus()
{
    if( pCursor )
        pCursor->ClearFlags( SvxIconViewFlags::FOCUSED );
    ShowCursor( false );

//  HideFocus ();
//  pView->Invalidate ( aFocus.aRect );

    RepaintSelectedEntries();
}

void SvxIconChoiceCtrl_Impl::SetUpdateMode( bool bUpdate )
{
    if( bUpdate != bUpdateMode )
    {
        bUpdateMode = bUpdate;
        if( bUpdate )
        {
            AdjustScrollBars();
            pImpCursor->Clear();
            pGridMap->Clear();
            pView->Invalidate(InvalidateFlags::NoChildren);
        }
    }
}

// priorities of the emphasis:  bSelected
void SvxIconChoiceCtrl_Impl::PaintEmphasis(const tools::Rectangle& rTextRect, bool bSelected,
                                           vcl::RenderContext& rRenderContext)
{
    Color aOldFillColor(rRenderContext.GetFillColor());

    bool bSolidTextRect = false;

    if (!bSelected)
    {
        const Color& rFillColor = rRenderContext.GetFont().GetFillColor();
        rRenderContext.SetFillColor(rFillColor);
        if (rFillColor != COL_TRANSPARENT)
            bSolidTextRect = true;
    }

    // draw text rectangle
    if (bSolidTextRect)
    {
        rRenderContext.DrawRect(rTextRect);
    }

    rRenderContext.SetFillColor(aOldFillColor);
}


void SvxIconChoiceCtrl_Impl::PaintItem(const tools::Rectangle& rRect,
    IcnViewFieldType eItem, SvxIconChoiceCtrlEntry* pEntry, sal_uInt16 nPaintFlags,
    vcl::RenderContext& rRenderContext )
{
    if (eItem == IcnViewFieldType::Text)
    {
        OUString aText = SvtIconChoiceCtrl::GetEntryText(pEntry);

        rRenderContext.DrawText(rRect, aText, nCurTextDrawFlags);

        if (pEntry->IsFocused())
        {
            tools::Rectangle aRect (CalcFocusRect(pEntry));
            ShowFocus(aRect);
            DrawFocusRect(rRenderContext);
        }
    }
    else
    {
        Point aPos(rRect.TopLeft());
        if (nPaintFlags & PAINTFLAG_HOR_CENTERED)
            aPos.AdjustX((rRect.GetWidth() - aImageSize.Width()) / 2 );
        if (nPaintFlags & PAINTFLAG_VER_CENTERED)
            aPos.AdjustY((rRect.GetHeight() - aImageSize.Height()) / 2 );
        SvtIconChoiceCtrl::DrawEntryImage(pEntry, aPos, rRenderContext);
    }
}

void SvxIconChoiceCtrl_Impl::PaintEntry(SvxIconChoiceCtrlEntry* pEntry, const Point& rPos, vcl::RenderContext& rRenderContext)
{
    bool bSelected = false;

    if (eSelectionMode != SelectionMode::NONE)
        bSelected = pEntry->IsSelected();

    rRenderContext.Push(PushFlags::FONT | PushFlags::TEXTCOLOR);

    OUString aEntryText(SvtIconChoiceCtrl::GetEntryText(pEntry));
    tools::Rectangle aTextRect(CalcTextRect(pEntry, &rPos, &aEntryText));
    tools::Rectangle aBmpRect(CalcBmpRect(pEntry, &rPos));

    bool bShowSelection = (bSelected && (eSelectionMode != SelectionMode::NONE));

    bool bActiveSelection = (0 != (nWinBits & WB_NOHIDESELECTION)) || pView->HasFocus();

    if (bShowSelection)
    {
        const StyleSettings& rSettings = rRenderContext.GetSettings().GetStyleSettings();
        vcl::Font aNewFont(rRenderContext.GetFont());

        // font fill colors that are attributed "hard" need corresponding "hard"
        // attributed highlight colors
        if ((nWinBits & WB_NOHIDESELECTION) || pView->HasFocus())
            aNewFont.SetFillColor(rSettings.GetHighlightColor());
        else
            aNewFont.SetFillColor(rSettings.GetDeactiveColor());

        Color aWinCol = rSettings.GetWindowTextColor();
        if (!bActiveSelection && rSettings.GetFaceColor().IsBright() == aWinCol.IsBright())
            aNewFont.SetColor(rSettings.GetWindowTextColor());
        else
            aNewFont.SetColor(rSettings.GetHighlightTextColor());

        rRenderContext.SetFont(aNewFont);

        rRenderContext.SetFillColor(rRenderContext.GetBackground().GetColor());
        rRenderContext.DrawRect(CalcFocusRect(pEntry));
        rRenderContext.SetFillColor();
    }

    bool bResetClipRegion = false;
    if (!rRenderContext.IsClipRegion() && (aVerSBar->IsVisible() || aHorSBar->IsVisible()))
    {
        tools::Rectangle aOutputArea(GetOutputRect());
        if (aOutputArea.IsOver(aTextRect) || aOutputArea.IsOver(aBmpRect))
        {
            rRenderContext.SetClipRegion(vcl::Region(aOutputArea));
            bResetClipRegion = true;
        }
    }

    bool bLargeIconMode = WB_ICON == ( nWinBits & VIEWMODE_MASK );
    sal_uInt16 nBmpPaintFlags = PAINTFLAG_VER_CENTERED;
    if (bLargeIconMode)
        nBmpPaintFlags |= PAINTFLAG_HOR_CENTERED;
    sal_uInt16 nTextPaintFlags = bLargeIconMode ? PAINTFLAG_HOR_CENTERED : PAINTFLAG_VER_CENTERED;

    PaintEmphasis(aTextRect, bSelected, rRenderContext);

    if ( bShowSelection )
        vcl::RenderTools::DrawSelectionBackground(rRenderContext, *pView, CalcFocusRect(pEntry),
                                                  bActiveSelection ? 1 : 2, false, true, false);


    PaintItem(aBmpRect, IcnViewFieldType::Image, pEntry, nBmpPaintFlags, rRenderContext);

    PaintItem(aTextRect, IcnViewFieldType::Text, pEntry, nTextPaintFlags, rRenderContext);

    // draw highlight frame
    if (pEntry == pCurHighlightFrame)
        DrawHighlightFrame(rRenderContext, CalcFocusRect(pEntry));

    rRenderContext.Pop();
    if (bResetClipRegion)
        rRenderContext.SetClipRegion();
}

void SvxIconChoiceCtrl_Impl::SetEntryPos( SvxIconChoiceCtrlEntry* pEntry, const Point& rPos )
{
    ShowCursor( false );
    tools::Rectangle aBoundRect( GetEntryBoundRect( pEntry ));
    pView->Invalidate( aBoundRect );
    ToTop( pEntry );
    if( !IsAutoArrange() )
    {
        bool bAdjustVirtSize = false;
        if( rPos != aBoundRect.TopLeft() )
        {
            Point aGridOffs(
                pEntry->aGridRect.TopLeft() - pEntry->aRect.TopLeft() );
            pImpCursor->Clear();
            pGridMap->Clear();
            aBoundRect.SetPos( rPos );
            pEntry->aRect = aBoundRect;
            pEntry->aGridRect.SetPos( rPos + aGridOffs );
            bAdjustVirtSize = true;
        }
        if( bAdjustVirtSize )
            AdjustVirtSize( pEntry->aRect );

        pView->Invalidate( pEntry->aRect );
        pGridMap->OccupyGrids( pEntry );
    }
    else
    {
        SvxIconChoiceCtrlEntry* pPrev = FindEntryPredecessor( pEntry, rPos );
        SetEntryPredecessor( pEntry, pPrev );
        aAutoArrangeIdle.Start();
    }
    ShowCursor( true );
}

void SvxIconChoiceCtrl_Impl::SetNoSelection()
{
    // block recursive calls via SelectEntry
    if( !(nFlags & IconChoiceFlags::ClearingSelection ))
    {
        nFlags |= IconChoiceFlags::ClearingSelection;
        DeselectAllBut( nullptr );
        nFlags &= ~IconChoiceFlags::ClearingSelection;
    }
}

SvxIconChoiceCtrlEntry* SvxIconChoiceCtrl_Impl::GetEntry( const Point& rDocPos, bool bHit )
{
    CheckBoundingRects();
    // search through z-order list from the end
    size_t nCount = maZOrderList.size();
    while( nCount )
    {
        nCount--;
        SvxIconChoiceCtrlEntry* pEntry = maZOrderList[ nCount ];
        if( pEntry->aRect.IsInside( rDocPos ) )
        {
            if( bHit )
            {
                tools::Rectangle aRect = CalcBmpRect( pEntry );
                aRect.AdjustTop( -3 );
                aRect.AdjustBottom(3 );
                aRect.AdjustLeft( -3 );
                aRect.AdjustRight(3 );
                if( aRect.IsInside( rDocPos ) )
                    return pEntry;
                aRect = CalcTextRect( pEntry );
                if( aRect.IsInside( rDocPos ) )
                    return pEntry;
            }
            else
                return pEntry;
        }
    }
    return nullptr;
}

void SvxIconChoiceCtrl_Impl::MakeEntryVisible( SvxIconChoiceCtrlEntry* pEntry, bool bBound )
{
    if ( bBound )
    {
        const tools::Rectangle& rRect = GetEntryBoundRect( pEntry );
        MakeVisible( rRect );
    }
    else
    {
        tools::Rectangle aRect = CalcBmpRect( pEntry );
        aRect.Union( CalcTextRect( pEntry ) );
        aRect.AdjustTop(TBOFFS_BOUND );
        aRect.AdjustBottom(TBOFFS_BOUND );
        aRect.AdjustLeft(LROFFS_BOUND );
        aRect.AdjustRight(LROFFS_BOUND );
        MakeVisible( aRect );
    }
}

const tools::Rectangle& SvxIconChoiceCtrl_Impl::GetEntryBoundRect( SvxIconChoiceCtrlEntry* pEntry )
{
    if( !IsBoundingRectValid( pEntry->aRect ))
        FindBoundingRect( pEntry );
    return pEntry->aRect;
}

tools::Rectangle SvxIconChoiceCtrl_Impl::CalcBmpRect( SvxIconChoiceCtrlEntry* pEntry, const Point* pPos )
{
    tools::Rectangle aBound = GetEntryBoundRect( pEntry );
    if( pPos )
        aBound.SetPos( *pPos );
    Point aPos( aBound.TopLeft() );

    switch( nWinBits & VIEWMODE_MASK )
    {
        case WB_ICON:
        {
            aPos.AdjustX(( aBound.GetWidth() - aImageSize.Width() ) / 2 );
            return tools::Rectangle( aPos, aImageSize );
        }

        case WB_SMALLICON:
        case WB_DETAILS:
            aPos.AdjustY(( aBound.GetHeight() - aImageSize.Height() ) / 2 );
            //TODO: determine horizontal distance to bounding rectangle
            return tools::Rectangle( aPos, aImageSize );

        default:
            OSL_FAIL("IconView: Viewmode not set");
            return aBound;
    }
}

tools::Rectangle SvxIconChoiceCtrl_Impl::CalcTextRect( SvxIconChoiceCtrlEntry* pEntry,
    const Point* pEntryPos, const OUString* pStr )
{
    OUString aEntryText;
    if( !pStr )
        aEntryText = SvtIconChoiceCtrl::GetEntryText( pEntry );
    else
        aEntryText = *pStr;

    const tools::Rectangle aMaxTextRect( CalcMaxTextRect( pEntry ) );
    tools::Rectangle aBound( GetEntryBoundRect( pEntry ) );
    if( pEntryPos )
        aBound.SetPos( *pEntryPos );

    tools::Rectangle aTextRect = pView->GetTextRect( aMaxTextRect, aEntryText, nCurTextDrawFlags );

    Size aTextSize( aTextRect.GetSize() );

    Point aPos( aBound.TopLeft() );
    long nBoundWidth = aBound.GetWidth();
    long nBoundHeight = aBound.GetHeight();

    switch( nWinBits & VIEWMODE_MASK )
    {
        case WB_ICON:
            aPos.AdjustY(aImageSize.Height() );
            aPos.AdjustY(VER_DIST_BMP_STRING );
            aPos.AdjustX((nBoundWidth - aTextSize.Width()) / 2 );
            break;

        case WB_SMALLICON:
        case WB_DETAILS:
            aPos.AdjustX(aImageSize.Width() );
            aPos.AdjustX(HOR_DIST_BMP_STRING );
            aPos.AdjustY((nBoundHeight - aTextSize.Height()) / 2 );
            break;
    }
    return tools::Rectangle( aPos, aTextSize );
}


long SvxIconChoiceCtrl_Impl::CalcBoundingWidth() const
{
    long nStringWidth = GetItemSize( IcnViewFieldType::Text ).Width();
    long nWidth = 0;

    switch( nWinBits & VIEWMODE_MASK )
    {
        case WB_ICON:
            nWidth = std::max( nStringWidth, aImageSize.Width() );
            break;

        case WB_SMALLICON:
        case WB_DETAILS:
            nWidth = aImageSize.Width();
            nWidth += HOR_DIST_BMP_STRING;
            nWidth += nStringWidth;
            break;
    }
    return nWidth;
}

long SvxIconChoiceCtrl_Impl::CalcBoundingHeight() const
{
    long nStringHeight = GetItemSize(IcnViewFieldType::Text).Height();
    long nHeight = 0;

    switch( nWinBits & VIEWMODE_MASK )
    {
        case WB_ICON:
            nHeight = aImageSize.Height();
            nHeight += VER_DIST_BMP_STRING;
            nHeight += nStringHeight;
            break;

        case WB_SMALLICON:
        case WB_DETAILS:
            nHeight = std::max( aImageSize.Height(), nStringHeight );
            break;
    }
    if( nHeight > nMaxBoundHeight )
    {
        const_cast<SvxIconChoiceCtrl_Impl*>(this)->nMaxBoundHeight = nHeight;
        const_cast<SvxIconChoiceCtrl_Impl*>(this)->aHorSBar->SetLineSize( GetScrollBarLineSize() );
        const_cast<SvxIconChoiceCtrl_Impl*>(this)->aVerSBar->SetLineSize( GetScrollBarLineSize() );
    }
    return nHeight;
}

Size SvxIconChoiceCtrl_Impl::CalcBoundingSize() const
{
    return Size( CalcBoundingWidth(), CalcBoundingHeight() );
}

void SvxIconChoiceCtrl_Impl::RecalcAllBoundingRectsSmart()
{
    nMaxBoundHeight = 0;
    maZOrderList.clear();
    size_t nCur;
    SvxIconChoiceCtrlEntry* pEntry;
    const size_t nCount = maEntries.size();

    if( !IsAutoArrange() || !pHead )
    {
        for( nCur = 0; nCur < nCount; nCur++ )
        {
            pEntry = maEntries[ nCur ].get();
            if( IsBoundingRectValid( pEntry->aRect ))
            {
                Size aBoundSize( pEntry->aRect.GetSize() );
                if( aBoundSize.Height() > nMaxBoundHeight )
                    nMaxBoundHeight = aBoundSize.Height();
            }
            else
                FindBoundingRect( pEntry );
            maZOrderList.push_back( pEntry );
        }
    }
    else
    {
        nCur = 0;
        pEntry = pHead;
        while( nCur != nCount )
        {
            DBG_ASSERT(pEntry->pflink&&pEntry->pblink,"SvxIconChoiceCtrl_Impl::RecalcAllBoundingRect > Bad link(s)");
            if( IsBoundingRectValid( pEntry->aRect ))
            {
                Size aBoundSize( pEntry->aRect.GetSize() );
                if( aBoundSize.Height() > nMaxBoundHeight )
                    nMaxBoundHeight = aBoundSize.Height();
            }
            else
                FindBoundingRect( pEntry );
            maZOrderList.push_back( pEntry );
            pEntry = pEntry->pflink;
            nCur++;
        }
    }
    AdjustScrollBars();
}

void SvxIconChoiceCtrl_Impl::FindBoundingRect( SvxIconChoiceCtrlEntry* pEntry )
{
    DBG_ASSERT(!pEntry->IsPosLocked(),"Locked entry pos in FindBoundingRect");
    if( pEntry->IsPosLocked() && IsBoundingRectValid( pEntry->aRect) )
    {
        AdjustVirtSize( pEntry->aRect );
        return;
    }
    Size aSize( CalcBoundingSize() );
    Point aPos(pGridMap->GetGridRect(pGridMap->GetUnoccupiedGrid()).TopLeft());
    SetBoundingRect_Impl( pEntry, aPos, aSize );
}

void SvxIconChoiceCtrl_Impl::SetBoundingRect_Impl( SvxIconChoiceCtrlEntry* pEntry, const Point& rPos,
    const Size& /*rBoundingSize*/ )
{
    tools::Rectangle aGridRect( rPos, Size(nGridDX, nGridDY) );
    pEntry->aGridRect = aGridRect;
    Center( pEntry );
    AdjustVirtSize( pEntry->aRect );
    pGridMap->OccupyGrids( pEntry );
}


void SvxIconChoiceCtrl_Impl::SetCursor( SvxIconChoiceCtrlEntry* pEntry )
{
    if( pEntry == pCursor )
    {
        if( pCursor && eSelectionMode == SelectionMode::Single &&
                !pCursor->IsSelected() )
            SelectEntry( pCursor, true );
        return;
    }
    ShowCursor( false );
    SvxIconChoiceCtrlEntry* pOldCursor = pCursor;
    pCursor = pEntry;
    if( pOldCursor )
    {
        pOldCursor->ClearFlags( SvxIconViewFlags::FOCUSED );
        if( eSelectionMode == SelectionMode::Single )
            SelectEntry( pOldCursor, false ); // deselect old cursor
    }
    if( pCursor )
    {
        ToTop( pCursor );
        pCursor->SetFlags( SvxIconViewFlags::FOCUSED );
        if( eSelectionMode == SelectionMode::Single )
            SelectEntry( pCursor, true );
        ShowCursor( true );
    }
}


void SvxIconChoiceCtrl_Impl::ShowCursor( bool bShow )
{
    if( !pCursor || !bShow || !pView->HasFocus() )
    {
        pView->HideFocus();
        return;
    }
    tools::Rectangle aRect ( CalcFocusRect( pCursor ) );
    /*pView->*/ShowFocus( aRect );
}


void SvxIconChoiceCtrl_Impl::HideDDIcon()
{
    pView->PaintImmediately();
}

bool SvxIconChoiceCtrl_Impl::HandleScrollCommand( const CommandEvent& rCmd )
{
    tools::Rectangle aDocRect( Point(), aVirtOutputSize );
    tools::Rectangle aVisRect( GetOutputRect() );
    if( aVisRect.IsInside( aDocRect ))
        return false;
    Size aDocSize( aDocRect.GetSize() );
    Size aVisSize( aVisRect.GetSize() );
    bool bHor = aDocSize.Width() > aVisSize.Width();
    bool bVer = aDocSize.Height() > aVisSize.Height();

    long nScrollDX = 0, nScrollDY = 0;

    switch( rCmd.GetCommand() )
    {
        case CommandEventId::StartAutoScroll:
        {
            pView->EndTracking();
            StartAutoScrollFlags nScrollFlags = StartAutoScrollFlags::NONE;
            if( bHor )
                nScrollFlags |= StartAutoScrollFlags::Horz;
            if( bVer )
                nScrollFlags |= StartAutoScrollFlags::Vert;
            if( nScrollFlags != StartAutoScrollFlags::NONE )
            {
                pView->StartAutoScroll( nScrollFlags );
                return true;
            }
        }
        break;

        case CommandEventId::Wheel:
        {
            const CommandWheelData* pData = rCmd.GetWheelData();
            if( pData && (CommandWheelMode::SCROLL == pData->GetMode()) && !pData->IsHorz() )
            {
                sal_uLong nScrollLines = pData->GetScrollLines();
                if( nScrollLines == COMMAND_WHEEL_PAGESCROLL )
                {
                    nScrollDY = GetScrollBarPageSize( aVisSize.Width() );
                    if( pData->GetDelta() < 0 )
                        nScrollDY *= -1;
                }
                else
                {
                    nScrollDY = pData->GetNotchDelta() * static_cast<long>(nScrollLines);
                    nScrollDY *= GetScrollBarLineSize();
                }
            }
        }
        break;

        case CommandEventId::AutoScroll:
        {
            const CommandScrollData* pData = rCmd.GetAutoScrollData();
            if( pData )
            {
                nScrollDX = pData->GetDeltaX() * GetScrollBarLineSize();
                nScrollDY = pData->GetDeltaY() * GetScrollBarLineSize();
            }
        }
        break;

        default: break;
    }

    if( nScrollDX || nScrollDY )
    {
        aVisRect.AdjustTop( -nScrollDY );
        aVisRect.AdjustBottom( -nScrollDY );
        aVisRect.AdjustLeft( -nScrollDX );
        aVisRect.AdjustRight( -nScrollDX );
        MakeVisible( aVisRect );
        return true;
    }
    return false;
}


void SvxIconChoiceCtrl_Impl::Command( const CommandEvent& rCEvt )
{
    // scroll mouse event?
    if( (rCEvt.GetCommand() == CommandEventId::Wheel) ||
        (rCEvt.GetCommand() == CommandEventId::StartAutoScroll) ||
        (rCEvt.GetCommand() == CommandEventId::AutoScroll) )
    {
        if( HandleScrollCommand( rCEvt ) )
            return;
    }
}

void SvxIconChoiceCtrl_Impl::ToTop( SvxIconChoiceCtrlEntry* pEntry )
{
    if( maZOrderList.empty() || pEntry == maZOrderList.back())
        return;

    auto it = std::find(maZOrderList.begin(), maZOrderList.end(), pEntry);
    if (it != maZOrderList.end())
    {
        maZOrderList.erase( it );
        maZOrderList.push_back( pEntry );
    }
}

void SvxIconChoiceCtrl_Impl::ClipAtVirtOutRect( tools::Rectangle& rRect ) const
{
    if( rRect.Bottom() >= aVirtOutputSize.Height() )
        rRect.SetBottom( aVirtOutputSize.Height() - 1 );
    if( rRect.Right() >= aVirtOutputSize.Width() )
        rRect.SetRight( aVirtOutputSize.Width() - 1 );
    if( rRect.Top() < 0 )
        rRect.SetTop( 0 );
    if( rRect.Left() < 0 )
        rRect.SetLeft( 0 );
}

// rRect: area of the document (in document coordinates) that we want to make
// visible
// bScrBar == true: rectangle was calculated because of a scrollbar event

void SvxIconChoiceCtrl_Impl::MakeVisible( const tools::Rectangle& rRect, bool bScrBar )
{
    tools::Rectangle aVirtRect( rRect );
    ClipAtVirtOutRect( aVirtRect );
    Point aOrigin( pView->GetMapMode().GetOrigin() );
    // convert to document coordinate
    aOrigin *= -1;
    tools::Rectangle aOutputArea( GetOutputRect() );
    if( aOutputArea.IsInside( aVirtRect ) )
        return; // is already visible

    long nDy;
    if( aVirtRect.Top() < aOutputArea.Top() )
    {
        // scroll up (nDy < 0)
        nDy = aVirtRect.Top() - aOutputArea.Top();
    }
    else if( aVirtRect.Bottom() > aOutputArea.Bottom() )
    {
        // scroll down (nDy > 0)
        nDy = aVirtRect.Bottom() - aOutputArea.Bottom();
    }
    else
        nDy = 0;

    long nDx;
    if( aVirtRect.Left() < aOutputArea.Left() )
    {
        // scroll to the left (nDx < 0)
        nDx = aVirtRect.Left() - aOutputArea.Left();
    }
    else if( aVirtRect.Right() > aOutputArea.Right() )
    {
        // scroll to the right (nDx > 0)
        nDx = aVirtRect.Right() - aOutputArea.Right();
    }
    else
        nDx = 0;

    aOrigin.AdjustX(nDx );
    aOrigin.AdjustY(nDy );
    aOutputArea.SetPos( aOrigin );
    if( GetUpdateMode() )
    {
        HideDDIcon();
        pView->PaintImmediately();
        ShowCursor( false );
    }

    // invert origin for SV (so we can scroll/paint using document coordinates)
    aOrigin *= -1;
    SetOrigin( aOrigin );

    bool bScrollable = pView->GetBackground().IsScrollable();

    if( bScrollable && GetUpdateMode() )
    {
        // scroll in reverse direction!
        pView->Control::Scroll( -nDx, -nDy, aOutputArea,
            ScrollFlags::NoChildren | ScrollFlags::UseClipRegion | ScrollFlags::Clip );
    }
    else
        pView->Invalidate(InvalidateFlags::NoChildren);

    if( aHorSBar->IsVisible() || aVerSBar->IsVisible() )
    {
        if( !bScrBar )
        {
            aOrigin *= -1;
            // correct thumbs
            if(aHorSBar->IsVisible() && aHorSBar->GetThumbPos() != aOrigin.X())
                aHorSBar->SetThumbPos( aOrigin.X() );
            if(aVerSBar->IsVisible() && aVerSBar->GetThumbPos() != aOrigin.Y())
                aVerSBar->SetThumbPos( aOrigin.Y() );
        }
    }

    if( GetUpdateMode() )
        ShowCursor( true );

    // check if we still need scrollbars
    CheckScrollBars();
    if( bScrollable && GetUpdateMode() )
        pView->PaintImmediately();

    // If the requested area can not be made completely visible, the
    // Vis-Rect-Changed handler is called in any case. This case may occur e.g.
    // if only few pixels of the lower border are invisible, but a scrollbar has
    // a larger line size.
    VisRectChanged();
}

sal_Int32 SvxIconChoiceCtrl_Impl::GetSelectionCount() const
{
    if( (nWinBits & WB_HIGHLIGHTFRAME) && pCurHighlightFrame )
        return 1;
    return nSelectionCount;
}

void SvxIconChoiceCtrl_Impl::ToggleSelection( SvxIconChoiceCtrlEntry* pEntry )
{
    bool bSel;
    bSel = !pEntry->IsSelected();
    SelectEntry( pEntry, bSel, true );
}

void SvxIconChoiceCtrl_Impl::DeselectAllBut( SvxIconChoiceCtrlEntry const * pThisEntryNot )
{
    ClearSelectedRectList();

    // TODO: work through z-order list, if necessary!

    size_t nCount = maEntries.size();
    for( size_t nCur = 0; nCur < nCount; nCur++ )
    {
        SvxIconChoiceCtrlEntry* pEntry = maEntries[ nCur ].get();
        if( pEntry != pThisEntryNot && pEntry->IsSelected() )
            SelectEntry( pEntry, false, true );
    }
    pAnchor = nullptr;
    nFlags &= ~IconChoiceFlags::AddMode;
}

Size SvxIconChoiceCtrl_Impl::GetMinGrid() const
{
    Size aMinSize( aImageSize );
    aMinSize.AdjustWidth(2 * LROFFS_BOUND );
    aMinSize.AdjustHeight(TBOFFS_BOUND );  // single offset is enough (FileDlg)
    OUString const aStrDummy( "XXX" );
    Size aTextSize( pView->GetTextWidth( aStrDummy ), pView->GetTextHeight() );
    if( nWinBits & WB_ICON )
    {
        aMinSize.AdjustHeight(VER_DIST_BMP_STRING );
        aMinSize.AdjustHeight(aTextSize.Height() );
    }
    else
    {
        aMinSize.AdjustWidth(HOR_DIST_BMP_STRING );
        aMinSize.AdjustWidth(aTextSize.Width() );
    }
    return aMinSize;
}

void SvxIconChoiceCtrl_Impl::SetGrid( const Size& rSize )
{
    Size aSize( rSize );
    Size aMinSize( GetMinGrid() );
    if( aSize.Width() < aMinSize.Width() )
        aSize.setWidth( aMinSize.Width() );
    if( aSize.Height() < aMinSize.Height() )
        aSize.setHeight( aMinSize.Height() );

    nGridDX = aSize.Width();
    // HACK: Detail mode is not yet fully implemented, this workaround makes it
    // fly with a single column
    if( nWinBits & WB_DETAILS )
    {
        const SvxIconChoiceCtrlColumnInfo* pCol = GetColumn( 0 );
        if( pCol )
            const_cast<SvxIconChoiceCtrlColumnInfo*>(pCol)->SetWidth( nGridDX );
    }
    nGridDY = aSize.Height();
    SetDefaultTextSize();
}

// Calculates the maximum size that the text rectangle may use within its
// bounding rectangle. In WB_ICON mode with SvxIconChoiceCtrlTextMode::Full, Bottom is set to
// LONG_MAX.

tools::Rectangle SvxIconChoiceCtrl_Impl::CalcMaxTextRect( const SvxIconChoiceCtrlEntry* pEntry ) const
{
    tools::Rectangle aBoundRect;
    // avoid infinite recursion: don't calculate the bounding rectangle here
    if( IsBoundingRectValid( pEntry->aRect ) )
        aBoundRect = pEntry->aRect;
    else
        aBoundRect = pEntry->aGridRect;

    tools::Rectangle aBmpRect( const_cast<SvxIconChoiceCtrl_Impl*>(this)->CalcBmpRect(
        const_cast<SvxIconChoiceCtrlEntry*>(pEntry) ) );
    if( nWinBits & WB_ICON )
    {
        aBoundRect.SetTop( aBmpRect.Bottom() );
        aBoundRect.AdjustTop(VER_DIST_BMP_STRING );
        if( aBoundRect.Top() > aBoundRect.Bottom())
            aBoundRect.SetTop( aBoundRect.Bottom() );
        aBoundRect.AdjustLeft(LROFFS_BOUND );
        aBoundRect.AdjustLeft( 1 );
        aBoundRect.AdjustRight( -(LROFFS_BOUND) );
        aBoundRect.AdjustRight( -1 );
        if( aBoundRect.Left() > aBoundRect.Right())
            aBoundRect.SetLeft( aBoundRect.Right() );
        if( pEntry->GetTextMode() == SvxIconChoiceCtrlTextMode::Full )
            aBoundRect.SetBottom( LONG_MAX );
    }
    else
    {
        aBoundRect.SetLeft( aBmpRect.Right() );
        aBoundRect.AdjustLeft(HOR_DIST_BMP_STRING );
        aBoundRect.AdjustRight( -(LROFFS_BOUND) );
        if( aBoundRect.Left() > aBoundRect.Right() )
            aBoundRect.SetLeft( aBoundRect.Right() );
        long nHeight = aBoundRect.GetSize().Height();
        nHeight = nHeight - aDefaultTextSize.Height();
        nHeight /= 2;
        aBoundRect.AdjustTop(nHeight );
        aBoundRect.AdjustBottom( -nHeight );
    }
    return aBoundRect;
}

void SvxIconChoiceCtrl_Impl::SetDefaultTextSize()
{
    long nDY = nGridDY;
    nDY -= aImageSize.Height();
    nDY -= VER_DIST_BMP_STRING;
    nDY -= 2 * TBOFFS_BOUND;
    if (nDY <= 0)
        nDY = 2;

    long nDX = nGridDX;
    nDX -= 2 * LROFFS_BOUND;
    nDX -= 2;
    if (nDX <= 0)
        nDX = 2;

    long nHeight = pView->GetTextHeight();
    if (nDY < nHeight)
        nDY = nHeight;
    if(pView->GetDPIScaleFactor() > 1)
    {
      nDY*=2;
    }
    aDefaultTextSize = Size(nDX, nDY);
}


void SvxIconChoiceCtrl_Impl::Center( SvxIconChoiceCtrlEntry* pEntry ) const
{
    pEntry->aRect = pEntry->aGridRect;
    Size aSize( CalcBoundingSize() );
    if( nWinBits & WB_ICON )
    {
        // center horizontally
        long nBorder = pEntry->aGridRect.GetWidth() - aSize.Width();
        pEntry->aRect.AdjustLeft(nBorder / 2 );
        pEntry->aRect.AdjustRight( -(nBorder / 2) );
    }
    // center vertically
    pEntry->aRect.SetBottom( pEntry->aRect.Top() + aSize.Height() );
}


// The deltas are the offsets by which the view is moved on the document.
// left, up: offsets < 0
// right, down: offsets > 0
void SvxIconChoiceCtrl_Impl::Scroll( long nDeltaX, long nDeltaY )
{
    const MapMode& rMapMode = pView->GetMapMode();
    Point aOrigin( rMapMode.GetOrigin() );
    // convert to document coordinate
    aOrigin *= -1;
    aOrigin.AdjustY(nDeltaY );
    aOrigin.AdjustX(nDeltaX );
    tools::Rectangle aRect( aOrigin, aOutputSize );
    MakeVisible( aRect, true/*bScrollBar*/ );
}


const Size& SvxIconChoiceCtrl_Impl::GetItemSize( IcnViewFieldType eItem ) const
{
    if (eItem == IcnViewFieldType::Text)
        return aDefaultTextSize;
    return aImageSize; // IcnViewFieldType::Image
}

tools::Rectangle SvxIconChoiceCtrl_Impl::CalcFocusRect( SvxIconChoiceCtrlEntry* pEntry )
{
    tools::Rectangle aTextRect( CalcTextRect( pEntry ) );
    tools::Rectangle aBoundRect( GetEntryBoundRect( pEntry ) );
    return tools::Rectangle(
        aBoundRect.Left(), aBoundRect.Top() - 1, aBoundRect.Right() - 1,
        aTextRect.Bottom() + 1);
}

// the hot spot is the inner 50% of the rectangle
static tools::Rectangle GetHotSpot( const tools::Rectangle& rRect )
{
    tools::Rectangle aResult( rRect );
    aResult.Justify();
    Size aSize( rRect.GetSize() );
    long nDelta = aSize.Width() / 4;
    aResult.AdjustLeft(nDelta );
    aResult.AdjustRight( -nDelta );
    nDelta = aSize.Height() / 4;
    aResult.AdjustTop(nDelta );
    aResult.AdjustBottom( -nDelta );
    return aResult;
}

void SvxIconChoiceCtrl_Impl::SelectRect( SvxIconChoiceCtrlEntry* pEntry1, SvxIconChoiceCtrlEntry* pEntry2,
    bool bAdd, std::vector<tools::Rectangle>* pOtherRects )
{
    DBG_ASSERT(pEntry1 && pEntry2,"SelectEntry: Invalid Entry-Ptr");
    tools::Rectangle aRect( GetEntryBoundRect( pEntry1 ) );
    aRect.Union( GetEntryBoundRect( pEntry2 ) );
    SelectRect( aRect, bAdd, pOtherRects );
}

void SvxIconChoiceCtrl_Impl::SelectRect( const tools::Rectangle& rRect, bool bAdd,
    std::vector<tools::Rectangle>* pOtherRects )
{
    aCurSelectionRect = rRect;
    if( maZOrderList.empty() )
        return;

    // set flag, so ToTop won't be called in Select
    bool bAlreadySelectingRect(nFlags & IconChoiceFlags::SelectingRect);
    nFlags |= IconChoiceFlags::SelectingRect;

    CheckBoundingRects();
    pView->PaintImmediately();
    const size_t nCount = maZOrderList.size();

    tools::Rectangle aRect( rRect );
    aRect.Justify();
    bool bCalcOverlap = (bAdd && pOtherRects && !pOtherRects->empty());

    bool bResetClipRegion = false;
    if( !pView->IsClipRegion() )
    {
        bResetClipRegion = true;
        pView->SetClipRegion(vcl::Region(GetOutputRect()));
    }

    for( size_t nPos = 0; nPos < nCount; nPos++ )
    {
        SvxIconChoiceCtrlEntry* pEntry = maZOrderList[ nPos ];

        if( !IsBoundingRectValid( pEntry->aRect ))
            FindBoundingRect( pEntry );
        tools::Rectangle aBoundRect( GetHotSpot( pEntry->aRect ) );
        bool bSelected = pEntry->IsSelected();

        bool bOverlaps;
        if( bCalcOverlap )
            bOverlaps = IsOver( pOtherRects, aBoundRect );
        else
            bOverlaps = false;
        bool bOver = aRect.IsOver( aBoundRect );

        if( bOver && !bOverlaps )
        {
            // is inside the new selection rectangle and outside of any old one
            // => select
            if( !bSelected )
                SelectEntry( pEntry, true, true );
        }
        else if( !bAdd )
        {
            // is outside of the selection rectangle
            // => deselect
            if( bSelected )
                SelectEntry( pEntry, false, true );
        }
        else if (bOverlaps)
        {
            // The entry is inside an old (=>span multiple rectangles with Ctrl)
            // selection rectangle.

            // There is still a bug here! The selection status of an entry in a
            // previous rectangle has to be restored, if it was touched by the
            // current selection rectangle but is not inside it any more.
            // For simplicity's sake, let's assume that all entries in the old
            // rectangles were correctly selected. It is wrong to just deselect
            // the intersection.
            // Possible solution: remember a snapshot of the selection before
            // spanning the rectangle.
            if( aBoundRect.IsOver( rRect))
            {
                // deselect intersection between old rectangles and current rectangle
                if( bSelected )
                    SelectEntry( pEntry, false, true );
            }
            else
            {
                // select entry of an old rectangle
                if( !bSelected )
                    SelectEntry( pEntry, true, true );
            }
        }
        else if( !bOver && bSelected )
        {
            // this entry is completely outside the rectangle => deselect it
            SelectEntry( pEntry, false, true );
        }
    }

    if( !bAlreadySelectingRect )
        nFlags &= ~IconChoiceFlags::SelectingRect;

    pView->PaintImmediately();
    if( bResetClipRegion )
        pView->SetClipRegion();
}

void SvxIconChoiceCtrl_Impl::SelectRange(
                        SvxIconChoiceCtrlEntry const * pStart,
                        SvxIconChoiceCtrlEntry const * pEnd,
                        bool bAdd )
{
    sal_uLong nFront = GetEntryListPos( pStart );
    sal_uLong nBack  = GetEntryListPos( pEnd );
    sal_uLong nFirst = std::min( nFront, nBack );
    sal_uLong nLast  = std::max( nFront, nBack );
    sal_uLong i;
    SvxIconChoiceCtrlEntry* pEntry;

    if ( ! bAdd )
    {
        // deselect everything before the first entry if not in
        // adding mode
        for ( i=0; i<nFirst; i++ )
        {
            pEntry = GetEntry( i );
            if( pEntry->IsSelected() )
                SelectEntry( pEntry, false, true );
        }
    }

    // select everything between nFirst and nLast
    for ( i=nFirst; i<=nLast; i++ )
    {
        pEntry = GetEntry( i );
        if( ! pEntry->IsSelected() )
            SelectEntry( pEntry, true, true );
    }

    if ( ! bAdd )
    {
        // deselect everything behind the last entry if not in
        // adding mode
        sal_uLong nEnd = GetEntryCount();
        for ( ; i<nEnd; i++ )
        {
            pEntry = GetEntry( i );
            if( pEntry->IsSelected() )
                SelectEntry( pEntry, false, true );
        }
    }
}

bool SvxIconChoiceCtrl_Impl::IsOver( std::vector<tools::Rectangle>* pRectList, const tools::Rectangle& rBoundRect )
{
    const sal_uInt16 nCount = pRectList->size();
    for( sal_uInt16 nCur = 0; nCur < nCount; nCur++ )
    {
        tools::Rectangle& rRect = (*pRectList)[ nCur ];
        if( rBoundRect.IsOver( rRect ))
            return true;
    }
    return false;
}

void SvxIconChoiceCtrl_Impl::AddSelectedRect( SvxIconChoiceCtrlEntry* pEntry1,
    SvxIconChoiceCtrlEntry* pEntry2 )
{
    DBG_ASSERT(pEntry1 && pEntry2,"SelectEntry: Invalid Entry-Ptr");
    tools::Rectangle aRect( GetEntryBoundRect( pEntry1 ) );
    aRect.Union( GetEntryBoundRect( pEntry2 ) );
    AddSelectedRect( aRect );
}

void SvxIconChoiceCtrl_Impl::AddSelectedRect( const tools::Rectangle& rRect )
{
    tools::Rectangle newRect = rRect;
    newRect.Justify();
    aSelectedRectList.push_back( newRect );
}

void SvxIconChoiceCtrl_Impl::ClearSelectedRectList()
{
    aSelectedRectList.clear();
}

IMPL_LINK_NOARG(SvxIconChoiceCtrl_Impl, AutoArrangeHdl, Timer *, void)
{
    aAutoArrangeIdle.Stop();
    Arrange( IsAutoArrange(), 0, 0 );
}

IMPL_LINK_NOARG(SvxIconChoiceCtrl_Impl, VisRectChangedHdl, Timer *, void)
{
    aVisRectChangedIdle.Stop();
}

IMPL_LINK_NOARG(SvxIconChoiceCtrl_Impl, DocRectChangedHdl, Timer *, void)
{
    aDocRectChangedIdle.Stop();
}

#ifdef DBG_UTIL
void SvxIconChoiceCtrl_Impl::SetEntryTextMode( SvxIconChoiceCtrlTextMode eMode, SvxIconChoiceCtrlEntry* pEntry )
{
    if( !pEntry )
    {
        if( eTextMode != eMode )
        {
            eTextMode = eMode;
            Arrange( true, 0, 0 );
        }
    }
    else
    {
        if( pEntry->eTextMode != eMode )
        {
            pEntry->eTextMode = eMode;
            InvalidateEntry( pEntry );
            pView->Invalidate( GetEntryBoundRect( pEntry ) );
            AdjustVirtSize( pEntry->aRect );
        }
    }
}
#endif

// Draw my own focusrect, because the focusrect of the outputdevice has got the inverted color
// of the background. But what will we see, if the backgroundcolor is gray ? - We will see
// a gray focusrect on a gray background !!!

void SvxIconChoiceCtrl_Impl::ShowFocus ( tools::Rectangle const & rRect )
{
    Color aBkgColor(pView->GetBackground().GetColor());
    Color aPenColor;
    sal_uInt16 nColor = ( aBkgColor.GetRed() + aBkgColor.GetGreen() + aBkgColor.GetBlue() ) / 3;
    if (nColor > 128)
        aPenColor = COL_BLACK;
    else
        aPenColor = COL_WHITE;

    aFocus.aPenColor = aPenColor;
    aFocus.aRect = rRect;
}

void SvxIconChoiceCtrl_Impl::DrawFocusRect(vcl::RenderContext& rRenderContext)
{
    rRenderContext.SetLineColor(aFocus.aPenColor);
    rRenderContext.SetFillColor();
    tools::Polygon aPolygon (aFocus.aRect);

    LineInfo aLineInfo(LineStyle::Dash);

    aLineInfo.SetDashLen(1);
    aLineInfo.SetDotLen(1);
    aLineInfo.SetDistance(1);
    aLineInfo.SetDotCount(1);

    rRenderContext.DrawPolyLine(aPolygon, aLineInfo);
}

bool SvxIconChoiceCtrl_Impl::IsMnemonicChar( sal_Unicode cChar, sal_uLong& rPos ) const
{
    bool bRet = false;
    const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetUILocaleI18nHelper();
    size_t nEntryCount = GetEntryCount();
    for ( size_t i = 0; i < nEntryCount; ++i )
    {
        if ( rI18nHelper.MatchMnemonic( GetEntry( i )->GetText(), cChar ) )
        {
            bRet = true;
            rPos = i;
            break;
        }
    }

    return bRet;
}


IMPL_LINK(SvxIconChoiceCtrl_Impl, UserEventHdl, void*, nId, void )
{
    if( nId == EVENTID_ADJUST_SCROLLBARS )
    {
        nUserEventAdjustScrBars = nullptr;
        AdjustScrollBars();
    }
    else if( nId == EVENTID_SHOW_CURSOR )
    {
        ShowCursor( true );
    }
}

void SvxIconChoiceCtrl_Impl::CancelUserEvents()
{
    if( nUserEventAdjustScrBars )
    {
        Application::RemoveUserEvent( nUserEventAdjustScrBars );
        nUserEventAdjustScrBars = nullptr;
    }
}

void SvxIconChoiceCtrl_Impl::InvalidateEntry( SvxIconChoiceCtrlEntry* pEntry )
{
    if( pEntry == pCursor )
        ShowCursor( false );
    pView->Invalidate( pEntry->aRect );
    Center( pEntry );
    pView->Invalidate( pEntry->aRect );
    if( pEntry == pCursor )
        ShowCursor( true );
}

SvxIconChoiceCtrlEntry* SvxIconChoiceCtrl_Impl::GetFirstSelectedEntry() const
{
    if( !GetSelectionCount() )
        return nullptr;

    if( (nWinBits & WB_HIGHLIGHTFRAME) && (eSelectionMode == SelectionMode::NONE) )
    {
        return pCurHighlightFrame;
    }

    size_t nCount = maEntries.size();
    if( !pHead )
    {
        for( size_t nCur = 0; nCur < nCount; nCur++ )
        {
            SvxIconChoiceCtrlEntry* pEntry = maEntries[ nCur ].get();
            if( pEntry->IsSelected() )
            {
                return pEntry;
            }
        }
    }
    else
    {
        SvxIconChoiceCtrlEntry* pEntry = pHead;
        while( nCount-- )
        {
            if( pEntry->IsSelected() )
            {
                return pEntry;
            }
            pEntry = pEntry->pflink;
            if( nCount && pEntry == pHead )
            {
                OSL_FAIL("SvxIconChoiceCtrl_Impl::GetFirstSelectedEntry > infinite loop!");
                return nullptr;
            }
        }
    }
    return nullptr;
}

void SvxIconChoiceCtrl_Impl::SelectAll()
{
    size_t nCount = maEntries.size();
    for( size_t nCur = 0; nCur < nCount; nCur++ )
    {
        SvxIconChoiceCtrlEntry* pEntry = maEntries[ nCur ].get();
        SelectEntry( pEntry, true/*bSelect*/, true );
    }
    nFlags &= ~IconChoiceFlags::AddMode;
    pAnchor = nullptr;
}




sal_Int32 SvxIconChoiceCtrl_Impl::GetEntryListPos( SvxIconChoiceCtrlEntry const * pEntry ) const
{
    if( !(nFlags & IconChoiceFlags::EntryListPosValid ))
        const_cast<SvxIconChoiceCtrl_Impl*>(this)->SetListPositions();
    return pEntry->nPos;
}

void SvxIconChoiceCtrl_Impl::InitSettings()
{
    const StyleSettings& rStyleSettings = pView->GetSettings().GetStyleSettings();

    // unit (from settings) is Point
    vcl::Font aFont( rStyleSettings.GetFieldFont() );
    aFont.SetColor( rStyleSettings.GetWindowTextColor() );
    pView->SetPointFont( aFont );
    SetDefaultTextSize();

    pView->SetTextColor( rStyleSettings.GetFieldTextColor() );
    pView->SetTextFillColor();

    pView->SetBackground( rStyleSettings.GetFieldColor());

    long nScrBarSize = rStyleSettings.GetScrollBarSize();
    if( nScrBarSize == nHorSBarHeight && nScrBarSize == nVerSBarWidth )
        return;

    nHorSBarHeight = nScrBarSize;
    Size aSize( aHorSBar->GetSizePixel() );
    aSize.setHeight( nScrBarSize );
    aHorSBar->Hide();
    aHorSBar->SetSizePixel( aSize );

    nVerSBarWidth = nScrBarSize;
    aSize = aVerSBar->GetSizePixel();
    aSize.setWidth( nScrBarSize );
    aVerSBar->Hide();
    aVerSBar->SetSizePixel( aSize );

    Size aOSize( pView->Control::GetOutputSizePixel() );
    PositionScrollBars( aOSize.Width(), aOSize.Height() );
    AdjustScrollBars();
}

void SvxIconChoiceCtrl_Impl::SetPositionMode( SvxIconChoiceCtrlPositionMode eMode )
{
    if( eMode == ePositionMode )
        return;

    SvxIconChoiceCtrlPositionMode eOldMode = ePositionMode;
    ePositionMode = eMode;
    size_t nCount = maEntries.size();

    if( eOldMode == SvxIconChoiceCtrlPositionMode::AutoArrange )
    {
        // when positioning moved entries "hard", there are problems with
        // unwanted overlaps, as these entries aren't taken into account in
        // Arrange.
        if( maEntries.size() )
            aAutoArrangeIdle.Start();
        return;
    }

    if( ePositionMode == SvxIconChoiceCtrlPositionMode::AutoArrange )
    {
        for( size_t nCur = 0; nCur < nCount; nCur++ )
        {
            SvxIconChoiceCtrlEntry* pEntry = maEntries[ nCur ].get();
            if( pEntry->GetFlags() & SvxIconViewFlags(SvxIconViewFlags::POS_LOCKED | SvxIconViewFlags::POS_MOVED))
                SetEntryPos(pEntry, GetEntryBoundRect( pEntry ).TopLeft());
        }

        if( maEntries.size() )
            aAutoArrangeIdle.Start();
    }
}

void SvxIconChoiceCtrl_Impl::SetEntryPredecessor( SvxIconChoiceCtrlEntry* pEntry,
    SvxIconChoiceCtrlEntry* pPredecessor )
{
    if( !IsAutoArrange() )
        return;

    if( pEntry == pPredecessor )
        return;

    sal_uLong nPos1 = GetEntryListPos( pEntry );
    if( !pHead )
    {
        if( pPredecessor )
        {
            sal_uLong nPos2 = GetEntryListPos( pPredecessor );
            if( nPos1 == (nPos2 + 1) )
                return; // is already the predecessor
        }
        else if( !nPos1 )
            return;

        InitPredecessors();
    }

    if( !pPredecessor && pHead == pEntry )
        return; // is already the first one

    bool bSetHead = false;
    if( !pPredecessor )
    {
        bSetHead = true;
        pPredecessor = pHead->pblink;
    }
    if( pEntry == pHead )
    {
        pHead = pHead->pflink;
        bSetHead = false;
    }
    if( pEntry != pPredecessor )
    {
        pEntry->Unlink();
        pEntry->SetBacklink( pPredecessor );
    }
    if( bSetHead )
        pHead = pEntry;
    aAutoArrangeIdle.Start();
}

SvxIconChoiceCtrlEntry* SvxIconChoiceCtrl_Impl::FindEntryPredecessor( SvxIconChoiceCtrlEntry* pEntry,
    const Point& rPosTopLeft )
{
    Point aPos( rPosTopLeft ); //TopLeft
    tools::Rectangle aCenterRect( CalcBmpRect( pEntry, &aPos ));
    Point aNewPos( aCenterRect.Center() );
    sal_uLong nGrid = GetPredecessorGrid( aNewPos );
    size_t nCount = maEntries.size();
    if( nGrid == ULONG_MAX )
        return nullptr;
    if( nGrid >= nCount )
        nGrid = nCount - 1;
    if( !pHead )
        return maEntries[ nGrid ].get();

    SvxIconChoiceCtrlEntry* pCur = pHead; // Grid 0
    // TODO: go through list from the end if nGrid > nCount/2
    for( sal_uLong nCur = 0; nCur < nGrid; nCur++ )
        pCur = pCur->pflink;

    return pCur;
}

sal_uLong SvxIconChoiceCtrl_Impl::GetPredecessorGrid( const Point& rPos) const
{
    Point aPos( rPos );
    aPos.AdjustX( -(LROFFS_WINBORDER) );
    aPos.AdjustY( -(TBOFFS_WINBORDER) );
    long nMaxCol = aVirtOutputSize.Width() / nGridDX;
    if( nMaxCol )
        nMaxCol--;
    long nGridX = aPos.X() / nGridDX;
    if( nGridX > nMaxCol )
        nGridX = nMaxCol;
    long nGridY = aPos.Y() / nGridDY;
    long nGridsX = aOutputSize.Width() / nGridDX;
    sal_uLong nGrid = (nGridY * nGridsX) + nGridX;
    long nMiddle = (nGridX * nGridDX) + (nGridDX / 2);
    if( rPos.X() < nMiddle )
    {
        if( !nGrid )
            nGrid = ULONG_MAX;
        else
            nGrid--;
    }
    return nGrid;
}

bool SvxIconChoiceCtrl_Impl::RequestHelp( const HelpEvent& rHEvt )
{
    if ( !(rHEvt.GetMode() & HelpEventMode::QUICK ) )
        return false;

    Point aPos( pView->ScreenToOutputPixel(rHEvt.GetMousePosPixel() ) );
    aPos -= pView->GetMapMode().GetOrigin();
    SvxIconChoiceCtrlEntry* pEntry = GetEntry( aPos, true );

    if ( !pEntry )
        return false;

    OUString sQuickHelpText = pEntry->GetQuickHelpText();
    OUString aEntryText( SvtIconChoiceCtrl::GetEntryText( pEntry ) );
    tools::Rectangle aTextRect( CalcTextRect( pEntry, nullptr, &aEntryText ) );
    if ( ( !aTextRect.IsInside( aPos ) || aEntryText.isEmpty() ) && sQuickHelpText.isEmpty() )
        return false;

    tools::Rectangle aOptTextRect( aTextRect );
    aOptTextRect.SetBottom( LONG_MAX );
    DrawTextFlags nNewFlags = nCurTextDrawFlags;
    nNewFlags &= ~DrawTextFlags( DrawTextFlags::Clip | DrawTextFlags::EndEllipsis );
    aOptTextRect = pView->GetTextRect( aOptTextRect, aEntryText, nNewFlags );
    if ( aOptTextRect != aTextRect || !sQuickHelpText.isEmpty() )
    {
        //aTextRect.Right() = aTextRect.Left() + aRealSize.Width() + 4;
        Point aPt( aOptTextRect.TopLeft() );
        aPt += pView->GetMapMode().GetOrigin();
        aPt = pView->OutputToScreenPixel( aPt );
        // subtract border of tooltip help
        aPt.AdjustY( -1 );
        aPt.AdjustX( -3 );
        aOptTextRect.SetPos( aPt );
        OUString sHelpText;
        if ( !sQuickHelpText.isEmpty() )
            sHelpText = sQuickHelpText;
        else
            sHelpText = aEntryText;
        Help::ShowQuickHelp( static_cast<vcl::Window*>(pView), aOptTextRect, sHelpText, QuickHelpFlags::Left | QuickHelpFlags::VCenter );
    }

    return true;
}

void SvxIconChoiceCtrl_Impl::SetColumn( sal_uInt16 nIndex, const SvxIconChoiceCtrlColumnInfo& rInfo)
{
    if (!m_pColumns)
        m_pColumns.reset(new SvxIconChoiceCtrlColumnInfoMap);

    SvxIconChoiceCtrlColumnInfo* pInfo = new SvxIconChoiceCtrlColumnInfo( rInfo );
    m_pColumns->insert(std::make_pair(nIndex, std::unique_ptr<SvxIconChoiceCtrlColumnInfo>(pInfo)));

    // HACK: Detail mode is not yet fully implemented, this workaround makes it
    // fly with a single column
    if( !nIndex && (nWinBits & WB_DETAILS) )
        nGridDX = pInfo->GetWidth();

    if( GetUpdateMode() )
        Arrange( IsAutoArrange(), 0, 0 );
}

const SvxIconChoiceCtrlColumnInfo* SvxIconChoiceCtrl_Impl::GetColumn( sal_uInt16 nIndex ) const
{
    if (!m_pColumns)
        return nullptr;
    auto const it = m_pColumns->find( nIndex );
    if (it == m_pColumns->end())
        return nullptr;
    return it->second.get();
}

void SvxIconChoiceCtrl_Impl::DrawHighlightFrame(vcl::RenderContext& rRenderContext, const tools::Rectangle& rBmpRect)
{
    tools::Rectangle aBmpRect(rBmpRect);
    long nBorder = 2;
    if (aImageSize.Width() < 32)
        nBorder = 1;
    aBmpRect.AdjustRight(nBorder );
    aBmpRect.AdjustLeft( -nBorder );
    aBmpRect.AdjustBottom(nBorder );
    aBmpRect.AdjustTop( -nBorder );

    DecorationView aDecoView(&rRenderContext);
    DrawHighlightFrameStyle nDecoFlags;
    if (bHighlightFramePressed)
        nDecoFlags = DrawHighlightFrameStyle::In;
    else
        nDecoFlags = DrawHighlightFrameStyle::Out;
    aDecoView.DrawHighlightFrame(aBmpRect, nDecoFlags);
}

void SvxIconChoiceCtrl_Impl::SetEntryHighlightFrame( SvxIconChoiceCtrlEntry* pEntry,
    bool bKeepHighlightFlags )
{
    if( pEntry == pCurHighlightFrame )
        return;

    if( !bKeepHighlightFlags )
        bHighlightFramePressed = false;

    if (pCurHighlightFrame)
    {
        tools::Rectangle aInvalidationRect(GetEntryBoundRect(pCurHighlightFrame));
        aInvalidationRect.expand(5);
        pCurHighlightFrame = nullptr;
        pView->Invalidate(aInvalidationRect);
    }

    pCurHighlightFrame = pEntry;
    if (pEntry)
    {
        tools::Rectangle aInvalidationRect(GetEntryBoundRect(pEntry));
        aInvalidationRect.expand(5);
        pView->Invalidate(aInvalidationRect);
    }
}

void SvxIconChoiceCtrl_Impl::CallSelectHandler()
{
    // When single-click mode is active, the selection handler should be called
    // synchronously, as the selection is automatically taken away once the
    // mouse cursor doesn't touch the object any more. Else, we might run into
    // missing calls to Select if the object is selected from a mouse movement,
    // because when starting the timer, the mouse cursor might have already left
    // the object.
    // In special cases (=>SfxFileDialog!), synchronous calls can be forced via
    // WB_NOASYNCSELECTHDL.
    if( nWinBits & (WB_NOASYNCSELECTHDL | WB_HIGHLIGHTFRAME) )
    {
        pHdlEntry = nullptr;
        pView->ClickIcon();
        //pView->Select();
    }
    else
        aCallSelectHdlIdle.Start();
}

IMPL_LINK_NOARG(SvxIconChoiceCtrl_Impl, CallSelectHdlHdl, Timer *, void)
{
    pHdlEntry = nullptr;
    pView->ClickIcon();
    //pView->Select();
}

void SvxIconChoiceCtrl_Impl::SetOrigin( const Point& rPos )
{
    MapMode aMapMode( pView->GetMapMode() );
    aMapMode.SetOrigin( rPos );
    pView->SetMapMode( aMapMode );
}

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


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