    /* -*- 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 <tools/debug.hxx>
    #include <vcl/builderfactory.hxx>
    #include <vcl/decoview.hxx>
    #include <vcl/svapp.hxx>
    #include <vcl/scrbar.hxx>
    #include <vcl/help.hxx>
    #include <vcl/settings.hxx>

    #include <com/sun/star/accessibility/AccessibleEventObject.hpp>
    #include <com/sun/star/accessibility/AccessibleEventId.hpp>
    #include <com/sun/star/accessibility/AccessibleStateType.hpp>
    #include <com/sun/star/lang/XComponent.hpp>
    #include <rtl/ustring.hxx>
    #include <sal/log.hxx>
    #include "valueimp.hxx"

    #include <svtools/valueset.hxx>

    using namespace css::uno;
    using namespace css::lang;
    using namespace css::accessibility;

    namespace
    {

    enum
    {
        ITEM_OFFSET = 4,
        ITEM_OFFSET_DOUBLE = 6,
        NAME_LINE_OFF_X = 2,
        NAME_LINE_OFF_Y = 2,
        NAME_LINE_HEIGHT = 2,
        NAME_OFFSET = 2,
        SCRBAR_OFFSET = 1,
        SCROLL_OFFSET = 4
    };

    }

    ValueSet::ValueSet( vcl::Window* pParent, WinBits nWinStyle ) :
        Control( pParent, nWinStyle ),
        maVirDev( VclPtr<VirtualDevice>::Create(*this) ),
        maColor( COL_TRANSPARENT )
    {
        mpNoneItem.reset(nullptr);
        mxScrollBar.reset(nullptr);

        mnItemWidth         = 0;
        mnItemHeight        = 0;
        mnTextOffset        = 0;
        mnVisLines          = 0;
        mnLines             = 0;
        mnUserItemWidth     = 0;
        mnUserItemHeight    = 0;
        mnFirstLine         = 0;
        mnSelItemId         = 0;
        mnHighItemId        = 0;
        mnCols              = 0;
        mnCurCol            = 0;
        mnUserCols          = 0;
        mnUserVisLines      = 0;
        mnSpacing           = 0;
        mnFrameStyle        = DrawFrameStyle::NONE;
        mbFormat            = true;
        mbHighlight         = false;
        mbSelection         = false;
        mbNoSelection       = true;
        mbDrawSelection     = true;
        mbBlackSel          = false;
        mbDoubleSel         = false;
        mbScroll            = false;
        mbFullMode          = true;
        mbEdgeBlending      = false;
        mbHasVisibleItems   = false;

        // #106446#, #106601# force mirroring of virtual device
        maVirDev->EnableRTL( GetParent()->IsRTLEnabled() );

        ImplInitSettings( true, true, true );
    }

    VCL_BUILDER_FACTORY_CONSTRUCTOR(ValueSet, WB_TABSTOP)

    ValueSet::~ValueSet()
    {
        disposeOnce();
    }

    void ValueSet::dispose()
    {
        Reference<XComponent> xComponent(GetAccessible(false), UNO_QUERY);
        if (xComponent.is())
            xComponent->dispose();

        ImplDeleteItems();
        mxScrollBar.disposeAndClear();
        Control::dispose();
    }

    void ValueSet::ImplDeleteItems()
    {
        const size_t n = mItemList.size();

        for ( size_t i = 0; i < n; ++i )
        {
            ValueSetItem* pItem = mItemList[i];
            if ( pItem->mbVisible && ImplHasAccessibleListeners() )
            {
                Any aOldAny;
                Any aNewAny;

                aOldAny <<= pItem->GetAccessible( false/*bIsTransientChildrenDisabled*/ );
                ImplFireAccessibleEvent(AccessibleEventId::CHILD, aOldAny, aNewAny);
            }

            delete pItem;
        }

        mItemList.clear();
    }

    void ValueSet::ApplySettings(vcl::RenderContext& rRenderContext)
    {
        const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();

        ApplyControlFont(rRenderContext, rStyleSettings.GetAppFont());
        ApplyControlForeground(rRenderContext, rStyleSettings.GetButtonTextColor());
        SetTextFillColor();
        Color aColor;
        if (GetStyle() & WB_MENUSTYLEVALUESET)
            aColor = rStyleSettings.GetMenuColor();
        else if (IsEnabled() && (GetStyle() & WB_FLATVALUESET))
            aColor = rStyleSettings.GetWindowColor();
        else
            aColor = rStyleSettings.GetFaceColor();
        ApplyControlBackground(rRenderContext, aColor);
    }

    void ValueSet::ImplInitSettings(bool bFont, bool bForeground, bool bBackground)
    {
        const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();

        if (bFont)
        {
            ApplyControlFont(*this, rStyleSettings.GetAppFont());
        }

        if (bForeground || bFont)
        {
            ApplyControlForeground(*this, rStyleSettings.GetButtonTextColor());
            SetTextFillColor();
        }

        if (bBackground)
        {
            Color aColor;
            if (GetStyle() & WB_MENUSTYLEVALUESET)
                aColor = rStyleSettings.GetMenuColor();
            else if (IsEnabled() && (GetStyle() & WB_FLATVALUESET))
                aColor = rStyleSettings.GetWindowColor();
            else
                aColor = rStyleSettings.GetFaceColor();
            ApplyControlBackground(*this, aColor);
        }
    }

    void ValueSet::ImplInitScrollBar()
    {
        if (!(GetStyle() & WB_VSCROLL))
            return;

        if (!mxScrollBar.get())
        {
            mxScrollBar.reset(VclPtr<ScrollBar>::Create(this, WB_VSCROLL | WB_DRAG));
            mxScrollBar->SetScrollHdl(LINK(this, ValueSet, ImplScrollHdl));
        }
        else
        {
            // adapt the width because of the changed settings
            long nScrBarWidth = Application::GetSettings().GetStyleSettings().GetScrollBarSize();
            mxScrollBar->setPosSizePixel(0, 0, nScrBarWidth, 0, PosSizeFlags::Width);
        }
    }

    void ValueSet::ImplFormatItem(vcl::RenderContext const & rRenderContext, ValueSetItem* pItem, tools::Rectangle aRect)
    {
        WinBits nStyle = GetStyle();
        if (nStyle & WB_ITEMBORDER)
        {
            aRect.AdjustLeft(1 );
            aRect.AdjustTop(1 );
            aRect.AdjustRight( -1 );
            aRect.AdjustBottom( -1 );

            if (nStyle & WB_FLATVALUESET)
            {
                sal_Int32 nBorder = (nStyle & WB_DOUBLEBORDER) ? 2 : 1;

                aRect.AdjustLeft(nBorder );
                aRect.AdjustTop(nBorder );
                aRect.AdjustRight( -nBorder );
                aRect.AdjustBottom( -nBorder );
            }
            else
            {
                DecorationView aView(maVirDev.get());
                aRect = aView.DrawFrame(aRect, mnFrameStyle);
            }
        }

        if (pItem == mpNoneItem.get())
            pItem->maText = GetText();

        if (!((aRect.GetHeight() > 0) && (aRect.GetWidth() > 0)))
            return;

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

        if (pItem == mpNoneItem.get())
        {
            maVirDev->SetFont(rRenderContext.GetFont());
            maVirDev->SetTextColor((nStyle & WB_MENUSTYLEVALUESET) ? rStyleSettings.GetMenuTextColor() : rStyleSettings.GetWindowTextColor());
            maVirDev->SetTextFillColor();
            maVirDev->SetFillColor((nStyle & WB_MENUSTYLEVALUESET) ? rStyleSettings.GetMenuColor() : rStyleSettings.GetWindowColor());
            maVirDev->DrawRect(aRect);
            Point aTxtPos(aRect.Left() + 2, aRect.Top());
            long nTxtWidth = rRenderContext.GetTextWidth(pItem->maText);
            if ((aTxtPos.X() + nTxtWidth) > aRect.Right())
            {
                maVirDev->SetClipRegion(vcl::Region(aRect));
                maVirDev->DrawText(aTxtPos, pItem->maText);
                maVirDev->SetClipRegion();
            }
            else
                maVirDev->DrawText(aTxtPos, pItem->maText);
        }
        else if (pItem->meType == VALUESETITEM_COLOR)
        {
            maVirDev->SetFillColor(pItem->maColor);
            maVirDev->DrawRect(aRect);
        }
        else
        {
            if (IsColor())
                maVirDev->SetFillColor(maColor);
            else if (nStyle & WB_MENUSTYLEVALUESET)
                maVirDev->SetFillColor(rStyleSettings.GetMenuColor());
            else if (IsEnabled())
                maVirDev->SetFillColor(rStyleSettings.GetWindowColor());
            else
                maVirDev->SetFillColor(rStyleSettings.GetFaceColor());
            maVirDev->DrawRect(aRect);

            if (pItem->meType == VALUESETITEM_USERDRAW)
            {
                UserDrawEvent aUDEvt(this, maVirDev.get(), aRect, pItem->mnId);
                UserDraw(aUDEvt);
            }
            else
            {
                Size aImageSize = pItem->maImage.GetSizePixel();
                Size  aRectSize = aRect.GetSize();
                Point aPos(aRect.Left(), aRect.Top());
                aPos.AdjustX((aRectSize.Width() - aImageSize.Width()) / 2 );

                if (pItem->meType != VALUESETITEM_IMAGE_AND_TEXT)
                    aPos.AdjustY((aRectSize.Height() - aImageSize.Height()) / 2 );

                DrawImageFlags  nImageStyle  = DrawImageFlags::NONE;
                if (!IsEnabled())
                    nImageStyle  |= DrawImageFlags::Disable;

                if (aImageSize.Width()  > aRectSize.Width() ||
                    aImageSize.Height() > aRectSize.Height())
                {
                    maVirDev->SetClipRegion(vcl::Region(aRect));
                    maVirDev->DrawImage(aPos, pItem->maImage, nImageStyle);
                    maVirDev->SetClipRegion();
                }
                else
                    maVirDev->DrawImage(aPos, pItem->maImage, nImageStyle);

                if (pItem->meType == VALUESETITEM_IMAGE_AND_TEXT)
                {
                    maVirDev->SetFont(rRenderContext.GetFont());
                    maVirDev->SetTextColor((nStyle & WB_MENUSTYLEVALUESET) ? rStyleSettings.GetMenuTextColor() : rStyleSettings.GetWindowTextColor());
                    maVirDev->SetTextFillColor();

                    long nTxtWidth = maVirDev->GetTextWidth(pItem->maText);

                    if (nTxtWidth > aRect.GetWidth())
                        maVirDev->SetClipRegion(vcl::Region(aRect));

                    maVirDev->DrawText(Point(aRect.Left() +
                                             (aRect.GetWidth() - nTxtWidth) / 2,
                                             aRect.Bottom() - maVirDev->GetTextHeight()),
                                       pItem->maText);

                    if (nTxtWidth > aRect.GetWidth())
                        maVirDev->SetClipRegion();
                }
            }
        }

        const sal_uInt16 nEdgeBlendingPercent(GetEdgeBlending() ? rStyleSettings.GetEdgeBlending() : 0);

        if (nEdgeBlendingPercent)
        {
            const Color& rTopLeft(rStyleSettings.GetEdgeBlendingTopLeftColor());
            const Color& rBottomRight(rStyleSettings.GetEdgeBlendingBottomRightColor());
            const sal_uInt8 nAlpha((nEdgeBlendingPercent * 255) / 100);
            const BitmapEx aBlendFrame(createBlendFrame(aRect.GetSize(), nAlpha, rTopLeft, rBottomRight));

            if (!aBlendFrame.IsEmpty())
            {
                maVirDev->DrawBitmapEx(aRect.TopLeft(), aBlendFrame);
            }
        }
    }

    Reference<XAccessible> ValueSet::CreateAccessible()
    {
        return new ValueSetAcc( this );
    }

    void ValueSet::Format(vcl::RenderContext const & rRenderContext)
    {
        Size aWinSize(GetOutputSizePixel());
        size_t nItemCount = mItemList.size();
        WinBits nStyle = GetStyle();
        long nTxtHeight = rRenderContext.GetTextHeight();
        long nOff;
        long nNoneHeight;
        long nNoneSpace;
        VclPtr<ScrollBar> xDeletedScrollBar;

        // consider the scrolling
        if (nStyle & WB_VSCROLL)
            ImplInitScrollBar();
        else
        {
            xDeletedScrollBar = mxScrollBar;
            mxScrollBar.clear();
        }

        // calculate item offset
        if (nStyle & WB_ITEMBORDER)
        {
            if (nStyle & WB_DOUBLEBORDER)
                nOff = ITEM_OFFSET_DOUBLE;
            else
                nOff = ITEM_OFFSET;
        }
        else
            nOff = 0;

        // consider size, if NameField does exist
        if (nStyle & WB_NAMEFIELD)
        {
            mnTextOffset = aWinSize.Height() - nTxtHeight - NAME_OFFSET;
            aWinSize.AdjustHeight( -(nTxtHeight + NAME_OFFSET) );

            if (!(nStyle & WB_FLATVALUESET))
            {
                mnTextOffset -= NAME_LINE_HEIGHT + NAME_LINE_OFF_Y;
                aWinSize.AdjustHeight( -(NAME_LINE_HEIGHT + NAME_LINE_OFF_Y) );
            }
        }
        else
            mnTextOffset = 0;

        // consider offset and size, if NoneField does exist
        if (nStyle & WB_NONEFIELD)
        {
            nNoneHeight = nTxtHeight + nOff;
            nNoneSpace = mnSpacing;
        }
        else
        {
            nNoneHeight = 0;
            nNoneSpace = 0;

            if (mpNoneItem.get())
                mpNoneItem.reset(nullptr);
        }

        // calculate ScrollBar width
        long nScrBarWidth = 0;
        if (mxScrollBar.get())
            nScrBarWidth = mxScrollBar->GetSizePixel().Width() + SCRBAR_OFFSET;

        // calculate number of columns
        if (!mnUserCols)
        {
            if (mnUserItemWidth)
            {
                mnCols = static_cast<sal_uInt16>((aWinSize.Width() - nScrBarWidth + mnSpacing) / (mnUserItemWidth + mnSpacing));
                if (mnCols <= 0)
                    mnCols = 1;
            }
            else
            {
                mnCols = 1;
            }
        }
        else
        {
            mnCols = mnUserCols;
        }

        // calculate number of rows
        mbScroll = false;

        // Floor( (M+N-1)/N )==Ceiling( M/N )
        mnLines = (static_cast<long>(nItemCount) + mnCols - 1) / mnCols;
        if (mnLines <= 0)
            mnLines = 1;

        long nCalcHeight = aWinSize.Height() - nNoneHeight;
        if (mnUserVisLines)
        {
            mnVisLines = mnUserVisLines;
        }
        else if (mnUserItemHeight)
        {
            mnVisLines = (nCalcHeight - nNoneSpace + mnSpacing) / (mnUserItemHeight + mnSpacing);
            if (!mnVisLines)
                mnVisLines = 1;
        }
        else
        {
            mnVisLines = mnLines;
        }

        if (mnLines > mnVisLines)
            mbScroll = true;

        if (mnLines <= mnVisLines)
        {
            mnFirstLine = 0;
        }
        else
        {
            if (mnFirstLine > static_cast<sal_uInt16>(mnLines - mnVisLines))
                mnFirstLine = static_cast<sal_uInt16>(mnLines - mnVisLines);
        }

        // calculate item size
        const long nColSpace  = (mnCols - 1) * static_cast<long>(mnSpacing);
        const long nLineSpace = ((mnVisLines - 1) * mnSpacing) + nNoneSpace;
        if (mnUserItemWidth && !mnUserCols)
        {
            mnItemWidth = mnUserItemWidth;
            if (mnItemWidth > aWinSize.Width() - nScrBarWidth - nColSpace)
                mnItemWidth = aWinSize.Width() - nScrBarWidth - nColSpace;
        }
        else
            mnItemWidth = (aWinSize.Width() - nScrBarWidth-nColSpace) / mnCols;
        if (mnUserItemHeight && !mnUserVisLines)
        {
            mnItemHeight = mnUserItemHeight;
            if (mnItemHeight > nCalcHeight - nNoneSpace)
                mnItemHeight = nCalcHeight - nNoneSpace;
        }
        else
        {
            nCalcHeight -= nLineSpace;
            mnItemHeight = nCalcHeight / mnVisLines;
        }

        // Init VirDev
        maVirDev->SetSettings(rRenderContext.GetSettings());
        maVirDev->SetBackground(rRenderContext.GetBackground());
        maVirDev->SetOutputSizePixel(aWinSize);

        // nothing is changed in case of too small items
        if ((mnItemWidth <= 0) ||
            (mnItemHeight <= ((nStyle & WB_ITEMBORDER) ? 4 : 2)) ||
            !nItemCount)
        {
            mbHasVisibleItems = false;

            if (nStyle & WB_NONEFIELD)
            {
                if (mpNoneItem.get())
                {
                    mpNoneItem->mbVisible = false;
                    mpNoneItem->maText = GetText();
                }
            }

            for (size_t i = 0; i < nItemCount; i++)
            {
                mItemList[i]->mbVisible = false;
            }

            if (mxScrollBar.get())
                mxScrollBar->Hide();
        }
        else
        {
            mbHasVisibleItems = true;

            // determine Frame-Style
            if (nStyle & WB_DOUBLEBORDER)
                mnFrameStyle = DrawFrameStyle::DoubleIn;
            else
                mnFrameStyle = DrawFrameStyle::In;

            // determine selected color and width
            // if necessary change the colors, to make the selection
            // better detectable
            const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
            Color aHighColor(rStyleSettings.GetHighlightColor());
            if (((aHighColor.GetRed() > 0x80) || (aHighColor.GetGreen() > 0x80) ||
                 (aHighColor.GetBlue() > 0x80)) ||
                ((aHighColor.GetRed() == 0x80) && (aHighColor.GetGreen() == 0x80) &&
                 (aHighColor.GetBlue() == 0x80)))
            {
                mbBlackSel = true;
            }
            else
            {
                mbBlackSel = false;
            }
            // draw the selection with double width if the items are bigger
            if ((nStyle & WB_DOUBLEBORDER) &&
                ((mnItemWidth >= 25) && (mnItemHeight >= 20)))
            {
                mbDoubleSel = true;
            }
            else
            {
                mbDoubleSel = false;
            }

            // calculate offsets
            long nStartX;
            long nStartY;
            if (mbFullMode)
            {
                long nAllItemWidth = (mnItemWidth * mnCols) + nColSpace;
                long nAllItemHeight = (mnItemHeight * mnVisLines) + nNoneHeight + nLineSpace;
                nStartX = (aWinSize.Width() - nScrBarWidth - nAllItemWidth) / 2;
                nStartY = (aWinSize.Height() - nAllItemHeight) / 2;
            }
            else
            {
                nStartX = 0;
                nStartY = 0;
            }

            // calculate and draw items
            maVirDev->SetLineColor();
            long x = nStartX;
            long y = nStartY;

            // create NoSelection field and show it
            if (nStyle & WB_NONEFIELD)
            {
                if (mpNoneItem.get() == nullptr)
                    mpNoneItem.reset(new ValueSetItem(*this));

                mpNoneItem->mnId = 0;
                mpNoneItem->meType = VALUESETITEM_NONE;
                mpNoneItem->mbVisible = true;
                maNoneItemRect.SetLeft( x );
                maNoneItemRect.SetTop( y );
                maNoneItemRect.SetRight( maNoneItemRect.Left() + aWinSize.Width() - x - 1 );
                maNoneItemRect.SetBottom( y + nNoneHeight - 1 );

                ImplFormatItem(rRenderContext, mpNoneItem.get(), maNoneItemRect);

                y += nNoneHeight + nNoneSpace;
            }

            // draw items
            sal_uLong nFirstItem = static_cast<sal_uLong>(mnFirstLine) * mnCols;
            sal_uLong nLastItem = nFirstItem + (mnVisLines * mnCols);

            maItemListRect.SetLeft( x );
            maItemListRect.SetTop( y );
            maItemListRect.SetRight( x + mnCols * (mnItemWidth + mnSpacing) - mnSpacing - 1 );
            maItemListRect.SetBottom( y + mnVisLines * (mnItemHeight + mnSpacing) - mnSpacing - 1 );

            if (!mbFullMode)
            {
                // If want also draw parts of items in the last line,
                // then we add one more line if parts of these line are
                // visible
                if (y + (mnVisLines * (mnItemHeight + mnSpacing)) < aWinSize.Height())
                    nLastItem += mnCols;
                maItemListRect.SetBottom( aWinSize.Height() - y );
            }
            for (size_t i = 0; i < nItemCount; i++)
            {
                ValueSetItem* pItem = mItemList[i];

                if (i >= nFirstItem && i < nLastItem)
                {
                    if (!pItem->mbVisible && ImplHasAccessibleListeners())
                    {
                        Any aOldAny;
                        Any aNewAny;

                        aNewAny <<= pItem->GetAccessible(false/*bIsTransientChildrenDisabled*/);
                        ImplFireAccessibleEvent(AccessibleEventId::CHILD, aOldAny, aNewAny);
                    }

                    pItem->mbVisible = true;
                    ImplFormatItem(rRenderContext, pItem, tools::Rectangle(Point(x, y), Size(mnItemWidth, mnItemHeight)));

                    if (!((i + 1) % mnCols))
                    {
                        x = nStartX;
                        y += mnItemHeight + mnSpacing;
                    }
                    else
                        x += mnItemWidth + mnSpacing;
                }
                else
                {
                    if (pItem->mbVisible && ImplHasAccessibleListeners())
                    {
                        Any aOldAny;
                        Any aNewAny;

                        aOldAny <<= pItem->GetAccessible(false/*bIsTransientChildrenDisabled*/);
                        ImplFireAccessibleEvent(AccessibleEventId::CHILD, aOldAny, aNewAny);
                    }

                    pItem->mbVisible = false;
                }
            }

            // arrange ScrollBar, set values and show it
            if (mxScrollBar.get())
            {
                Point   aPos(aWinSize.Width() - nScrBarWidth + SCRBAR_OFFSET, 0);
                Size    aSize(nScrBarWidth - SCRBAR_OFFSET, aWinSize.Height());
                // If a none field is visible, then we center the scrollbar
                if (nStyle & WB_NONEFIELD)
                {
                    aPos.setY( nStartY + nNoneHeight + 1 );
                    aSize.setHeight( ((mnItemHeight + mnSpacing) * mnVisLines) - 2 - mnSpacing );
                }
                mxScrollBar->SetPosSizePixel(aPos, aSize);
                mxScrollBar->SetRangeMax(mnLines);
                mxScrollBar->SetVisibleSize(mnVisLines);
                mxScrollBar->SetThumbPos(static_cast<long>(mnFirstLine));
                long nPageSize = mnVisLines;
                if (nPageSize < 1)
                    nPageSize = 1;
                mxScrollBar->SetPageSize(nPageSize);
                mxScrollBar->Show();
            }
        }

        // waiting for the next since the formatting is finished
        mbFormat = false;

        xDeletedScrollBar.disposeAndClear();
    }

    void ValueSet::ImplDrawItemText(vcl::RenderContext& rRenderContext, const OUString& rText)
    {
        if (!(GetStyle() & WB_NAMEFIELD))
            return;

        Size aWinSize(GetOutputSizePixel());
        long nTxtWidth = rRenderContext.GetTextWidth(rText);
        long nTxtOffset = mnTextOffset;

        // delete rectangle and show text
        if (GetStyle() & WB_FLATVALUESET)
        {
            const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
            rRenderContext.SetLineColor();
            rRenderContext.SetFillColor(rStyleSettings.GetFaceColor());
            rRenderContext.DrawRect(tools::Rectangle(Point(0, nTxtOffset), Point(aWinSize.Width(), aWinSize.Height())));
            rRenderContext.SetTextColor(rStyleSettings.GetButtonTextColor());
        }
        else
        {
            nTxtOffset += NAME_LINE_HEIGHT+NAME_LINE_OFF_Y;
            rRenderContext.Erase(tools::Rectangle(Point(0, nTxtOffset), Point(aWinSize.Width(), aWinSize.Height())));
        }
        rRenderContext.DrawText(Point((aWinSize.Width() - nTxtWidth) / 2, nTxtOffset + (NAME_OFFSET / 2)), rText);
    }

    void ValueSet::ImplDrawSelect(vcl::RenderContext& rRenderContext)
    {
        if (!IsReallyVisible())
            return;

        const bool bFocus = HasFocus();
        const bool bDrawSel = !((mbNoSelection && !mbHighlight) || (!mbDrawSelection && mbHighlight));

        if (!bFocus && !bDrawSel)
        {
            ImplDrawItemText(rRenderContext, OUString());
            return;
        }

        ImplDrawSelect(rRenderContext, mnSelItemId, bFocus, bDrawSel);
        if (mbHighlight)
        {
            ImplDrawSelect(rRenderContext, mnHighItemId, bFocus, bDrawSel);
        }
    }

    void ValueSet::ImplDrawSelect(vcl::RenderContext& rRenderContext, sal_uInt16 nItemId, const bool bFocus, const bool bDrawSel )
    {
        ValueSetItem* pItem;
        tools::Rectangle aRect;
        if (nItemId)
        {
            const size_t nPos = GetItemPos( nItemId );
            pItem = mItemList[ nPos ];
            aRect = ImplGetItemRect( nPos );
        }
        else if (mpNoneItem.get())
        {
            pItem = mpNoneItem.get();
            aRect = maNoneItemRect;
        }
        else if (bFocus && (pItem = ImplGetFirstItem()))
        {
            aRect = ImplGetItemRect(0);
        }
        else
        {
            return;
        }

        if (!pItem->mbVisible)
            return;

        // draw selection
        const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
        rRenderContext.SetFillColor();

        Color aDoubleColor(rStyleSettings.GetHighlightColor());
        Color aSingleColor(rStyleSettings.GetHighlightTextColor());
        if (!mbDoubleSel)
        {
            /*
            *  #99777# contrast enhancement for thin mode
            */
            const Wallpaper& rWall = GetDisplayBackground();
            if (!rWall.IsBitmap() && ! rWall.IsGradient())
            {
                const Color& rBack = rWall.GetColor();
                if (rBack.IsDark() && ! aDoubleColor.IsBright())
                {
                    aDoubleColor = COL_WHITE;
                    aSingleColor = COL_BLACK;
                }
                else if (rBack.IsBright() && ! aDoubleColor.IsDark())
                {
                    aDoubleColor = COL_BLACK;
                    aSingleColor = COL_WHITE;
                }
            }
        }

        // specify selection output
        WinBits nStyle = GetStyle();
        if (nStyle & WB_MENUSTYLEVALUESET)
        {
            if (bFocus)
                ShowFocus(aRect);

            if (bDrawSel)
            {
                rRenderContext.SetLineColor(mbBlackSel ? COL_BLACK : aDoubleColor);
                rRenderContext.DrawRect(aRect);
            }
        }
        else
        {
            if (bDrawSel)
            {
                rRenderContext.SetLineColor(mbBlackSel ? COL_BLACK : aDoubleColor);
                rRenderContext.DrawRect(aRect);
            }
            if (mbDoubleSel)
            {
                aRect.AdjustLeft( 1 );
                aRect.AdjustTop( 1 );
                aRect.AdjustRight( -1 );
                aRect.AdjustBottom( -1 );
                if (bDrawSel)
                    rRenderContext.DrawRect(aRect);
            }
            aRect.AdjustLeft( 1 );
            aRect.AdjustTop( 1 );
            aRect.AdjustRight( -1 );
            aRect.AdjustBottom( -1 );
            tools::Rectangle aRect2 = aRect;
            aRect.AdjustLeft( 1 );
            aRect.AdjustTop( 1 );
            aRect.AdjustRight( -1 );
            aRect.AdjustBottom( -1 );
            if (bDrawSel)
                rRenderContext.DrawRect(aRect);
            if (mbDoubleSel)
            {
                aRect.AdjustLeft( 1 );
                aRect.AdjustTop( 1 );
                aRect.AdjustRight( -1 );
                aRect.AdjustBottom( -1 );
                if (bDrawSel)
                    rRenderContext.DrawRect(aRect);
            }

            if (bDrawSel)
            {
                rRenderContext.SetLineColor(mbBlackSel ? COL_WHITE : aSingleColor);
            }
            else
            {
                rRenderContext.SetLineColor(COL_LIGHTGRAY);
            }
            rRenderContext.DrawRect(aRect2);

            if (bFocus)
                ShowFocus(aRect2);
        }

        ImplDrawItemText(rRenderContext, pItem->maText);
    }

    void ValueSet::ImplHideSelect( sal_uInt16 nItemId )
    {
        tools::Rectangle aRect;

        const size_t nItemPos = GetItemPos( nItemId );
        if ( nItemPos != VALUESET_ITEM_NOTFOUND )
        {
            if ( !mItemList[nItemPos]->mbVisible )
            {
                return;
            }
            aRect = ImplGetItemRect(nItemPos);
        }
        else
        {
            if (mpNoneItem.get() == nullptr)
            {
                return;
            }
            aRect = maNoneItemRect;
        }

        HideFocus();
        const Point aPos  = aRect.TopLeft();
        const Size  aSize = aRect.GetSize();
        DrawOutDev( aPos, aSize, aPos, aSize, *maVirDev.get() );
    }

    void ValueSet::ImplHighlightItem( sal_uInt16 nItemId, bool bIsSelection )
    {
        if ( mnHighItemId == nItemId )
            return;

        // remember the old item to delete the previous selection
        sal_uInt16 nOldItem = mnHighItemId;
        mnHighItemId = nItemId;

        // don't draw the selection if nothing is selected
        if ( !bIsSelection && mbNoSelection )
            mbDrawSelection = false;

        // remove the old selection and draw the new one
        ImplHideSelect( nOldItem );
        Invalidate();
        mbDrawSelection = true;
    }

    void ValueSet::ImplDraw(vcl::RenderContext& rRenderContext)
    {
        if (mbFormat)
            Format(rRenderContext);

        HideFocus();

        Point aDefPos;
        Size aSize = maVirDev->GetOutputSizePixel();

        if (mxScrollBar.get() && mxScrollBar->IsVisible())
        {
            Point aScrPos = mxScrollBar->GetPosPixel();
            Size aScrSize = mxScrollBar->GetSizePixel();
            Point aTempPos(0, aScrPos.Y());
            Size aTempSize(aSize.Width(), aScrPos.Y());

            rRenderContext.DrawOutDev(aDefPos, aTempSize, aDefPos, aTempSize, *maVirDev.get());
            aTempSize.setWidth( aScrPos.X() - 1 );
            aTempSize.setHeight( aScrSize.Height() );
            rRenderContext.DrawOutDev(aTempPos, aTempSize, aTempPos, aTempSize, *maVirDev.get());
            aTempPos.setY( aScrPos.Y() + aScrSize.Height() );
            aTempSize.setWidth( aSize.Width() );
            aTempSize.setHeight( aSize.Height() - aTempPos.Y() );
            rRenderContext.DrawOutDev(aTempPos, aTempSize, aTempPos, aTempSize, *maVirDev.get());
        }
        else
            rRenderContext.DrawOutDev(aDefPos, aSize, aDefPos, aSize, *maVirDev.get());

        // draw parting line to the Namefield
        if (GetStyle() & WB_NAMEFIELD)
        {
            if (!(GetStyle() & WB_FLATVALUESET))
            {
                const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
                Size aWinSize(GetOutputSizePixel());
                Point aPos1(NAME_LINE_OFF_X, mnTextOffset + NAME_LINE_OFF_Y);
                Point aPos2(aWinSize.Width() - (NAME_LINE_OFF_X * 2), mnTextOffset + NAME_LINE_OFF_Y);
                if (!(rStyleSettings.GetOptions() & StyleSettingsOptions::Mono))
                {
                    rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
                    rRenderContext.DrawLine(aPos1, aPos2);
                    aPos1.AdjustY( 1 );
                    aPos2.AdjustY( 1 );
                    rRenderContext.SetLineColor(rStyleSettings.GetLightColor());
                }
                else
                    rRenderContext.SetLineColor(rStyleSettings.GetWindowTextColor());
                rRenderContext.DrawLine(aPos1, aPos2);
            }
        }

        ImplDrawSelect(rRenderContext);
    }

    bool ValueSet::ImplScroll(const Point& rPos)
    {
        if (!mbScroll || !maItemListRect.IsInside(rPos))
            return false;

        const long nScrollOffset = (mnItemHeight <= 16) ? SCROLL_OFFSET / 2 : SCROLL_OFFSET;
        bool bScroll = false;

        if (rPos.Y() <= maItemListRect.Top() + nScrollOffset)
        {
            if (mnFirstLine > 0)
            {
                --mnFirstLine;
                bScroll = true;
            }
        }
        else if (rPos.Y() >= maItemListRect.Bottom() - nScrollOffset)
        {
            if (mnFirstLine < static_cast<sal_uInt16>(mnLines - mnVisLines))
            {
                ++mnFirstLine;
                bScroll = true;
            }
        }

        if (!bScroll)
            return false;

        mbFormat = true;
        Invalidate();
        return true;
    }

    size_t ValueSet::ImplGetItem( const Point& rPos ) const
    {
        if (!mbHasVisibleItems)
        {
            return VALUESET_ITEM_NOTFOUND;
        }

        if (mpNoneItem.get() && maNoneItemRect.IsInside(rPos))
        {
            return VALUESET_ITEM_NONEITEM;
        }

        if (maItemListRect.IsInside(rPos))
        {
            const int xc = rPos.X() - maItemListRect.Left();
            const int yc = rPos.Y() - maItemListRect.Top();
            // The point is inside the area of item list,
            // let's find the containing item.
            const int col = xc / (mnItemWidth + mnSpacing);
            const int x = xc % (mnItemWidth + mnSpacing);
            const int row = yc / (mnItemHeight + mnSpacing);
            const int y = yc % (mnItemHeight + mnSpacing);

            if (x < mnItemWidth && y < mnItemHeight)
            {
                // the point is inside item rect and not inside spacing
                const size_t item = (mnFirstLine + row) * static_cast<size_t>(mnCols) + col;
                if (item < mItemList.size())
                {
                    return item;
                }
            }
        }

        return VALUESET_ITEM_NOTFOUND;
    }

    ValueSetItem* ValueSet::ImplGetItem( size_t nPos )
    {
        if (nPos == VALUESET_ITEM_NONEITEM)
            return mpNoneItem.get();
        else
            return (nPos < mItemList.size()) ? mItemList[nPos] : nullptr;
    }

    ValueSetItem* ValueSet::ImplGetFirstItem()
    {
        return mItemList.size() ? mItemList[0] : nullptr;
    }

    sal_uInt16 ValueSet::ImplGetVisibleItemCount() const
    {
        sal_uInt16 nRet = 0;
        const size_t nItemCount = mItemList.size();

        for ( size_t n = 0; n < nItemCount; ++n )
        {
            if ( mItemList[n]->mbVisible )
                ++nRet;
        }

        return nRet;
    }

    void ValueSet::ImplFireAccessibleEvent( short nEventId, const Any& rOldValue, const Any& rNewValue )
    {
        ValueSetAcc* pAcc = ValueSetAcc::getImplementation( GetAccessible( false ) );

        if( pAcc )
            pAcc->FireAccessibleEvent( nEventId, rOldValue, rNewValue );
    }

    bool ValueSet::ImplHasAccessibleListeners()
    {
        ValueSetAcc* pAcc = ValueSetAcc::getImplementation( GetAccessible( false ) );
        return( pAcc && pAcc->HasAccessibleListeners() );
    }

    IMPL_LINK( ValueSet,ImplScrollHdl, ScrollBar*, pScrollBar, void )
    {
        sal_uInt16 nNewFirstLine = static_cast<sal_uInt16>(pScrollBar->GetThumbPos());
        if ( nNewFirstLine != mnFirstLine )
        {
            mnFirstLine = nNewFirstLine;
            mbFormat = true;
            Invalidate();
        }
    }

    IMPL_LINK_NOARG(ValueSet, ImplTimerHdl, Timer *, void)
    {
        ImplTracking( GetPointerPosPixel(), true );
    }

    void ValueSet::ImplTracking( const Point& rPos, bool bRepeat )
    {
        if ( bRepeat || mbSelection )
        {
            if ( ImplScroll( rPos ) )
            {
                if ( mbSelection )
                {
                    maTimer.SetInvokeHandler( LINK( this, ValueSet, ImplTimerHdl ) );
                    maTimer.SetTimeout( GetSettings().GetMouseSettings().GetScrollRepeat() );
                    maTimer.Start();
                }
            }
        }

        ValueSetItem* pItem = ImplGetItem( ImplGetItem( rPos ) );
        if ( pItem )
        {
            if( GetStyle() & WB_MENUSTYLEVALUESET || GetStyle() & WB_FLATVALUESET )
                mbHighlight = true;

            ImplHighlightItem( pItem->mnId );
        }
        else
        {
            if( GetStyle() & WB_MENUSTYLEVALUESET || GetStyle() & WB_FLATVALUESET )
                mbHighlight = true;

            ImplHighlightItem( mnSelItemId, false );
        }
    }

    void ValueSet::ImplEndTracking( const Point& rPos, bool bCancel )
    {
        ValueSetItem* pItem;

        // restore the old status in case of termination
        if ( bCancel )
            pItem = nullptr;
        else
            pItem = ImplGetItem( ImplGetItem( rPos ) );

        if ( pItem )
        {
            SelectItem( pItem->mnId );
            if ( !mbSelection && !(GetStyle() & WB_NOPOINTERFOCUS) )
                GrabFocus();
            mbHighlight = false;
            mbSelection = false;
            Select();
        }
        else
        {
            ImplHighlightItem( mnSelItemId, false );
            mbHighlight = false;
            mbSelection = false;
        }
    }

    void ValueSet::MouseButtonDown( const MouseEvent& rMouseEvent )
    {
        if ( rMouseEvent.IsLeft() )
        {
            ValueSetItem* pItem = ImplGetItem( ImplGetItem( rMouseEvent.GetPosPixel() ) );
            if ( mbSelection )
            {
                mbHighlight = true;
                if ( pItem )
                {
                    mnHighItemId = mnSelItemId;
                    ImplHighlightItem( pItem->mnId );
                }

                return;
            }
            else
            {
                if ( pItem && !rMouseEvent.IsMod2() )
                {
                    if ( rMouseEvent.GetClicks() == 1 )
                    {
                        mbHighlight  = true;
                        mnHighItemId = mnSelItemId;
                        ImplHighlightItem( pItem->mnId );
                        StartTracking( StartTrackingFlags::ScrollRepeat );
                    }
                    else if ( rMouseEvent.GetClicks() == 2 )
                        maDoubleClickHdl.Call( this );

                    return;
                }
            }
        }

        Control::MouseButtonDown( rMouseEvent );
    }

    void ValueSet::MouseButtonUp( const MouseEvent& rMouseEvent )
    {
        // because of SelectionMode
        if ( rMouseEvent.IsLeft() && mbSelection )
            ImplEndTracking( rMouseEvent.GetPosPixel(), false );
        else
            Control::MouseButtonUp( rMouseEvent );
    }

    void ValueSet::MouseMove( const MouseEvent& rMouseEvent )
    {
        // because of SelectionMode
        if ( mbSelection || (GetStyle() & WB_MENUSTYLEVALUESET) || (GetStyle() & WB_FLATVALUESET))
            ImplTracking( rMouseEvent.GetPosPixel(), false );
        Control::MouseMove( rMouseEvent );
    }

    void ValueSet::Tracking( const TrackingEvent& rTrackingEvent )
    {
        Point aMousePos = rTrackingEvent.GetMouseEvent().GetPosPixel();

        if ( rTrackingEvent.IsTrackingEnded() )
            ImplEndTracking( aMousePos, rTrackingEvent.IsTrackingCanceled() );
        else
            ImplTracking( aMousePos, rTrackingEvent.IsTrackingRepeat() );
    }

    void ValueSet::KeyInput( const KeyEvent& rKeyEvent )
    {
        size_t nLastItem = mItemList.size();

        if ( !nLastItem || !ImplGetFirstItem() )
        {
            Control::KeyInput( rKeyEvent );
            return;
        }

        if (mbFormat)
            Invalidate();

        --nLastItem;

        const size_t nCurPos = mnSelItemId ? GetItemPos(mnSelItemId)
                                           : (mpNoneItem.get() ? VALUESET_ITEM_NONEITEM : 0);
        size_t nItemPos = VALUESET_ITEM_NOTFOUND;
        size_t nVStep = mnCols;

        switch (rKeyEvent.GetKeyCode().GetCode())
        {
            case KEY_HOME:
                nItemPos = mpNoneItem.get() ? VALUESET_ITEM_NONEITEM : 0;
                break;

            case KEY_END:
                nItemPos = nLastItem;
                break;

            case KEY_LEFT:
                if (nCurPos != VALUESET_ITEM_NONEITEM)
                {
                    if (nCurPos)
                    {
                        nItemPos = nCurPos-1;
                    }
                    else if (mpNoneItem.get())
                    {
                        nItemPos = VALUESET_ITEM_NONEITEM;
                    }
                }
                break;

            case KEY_RIGHT:
                if (nCurPos < nLastItem)
                {
                    if (nCurPos == VALUESET_ITEM_NONEITEM)
                    {
                        nItemPos = 0;
                    }
                    else
                    {
                        nItemPos = nCurPos+1;
                    }
                }
                break;

            case KEY_PAGEUP:
                if (rKeyEvent.GetKeyCode().IsShift() || rKeyEvent.GetKeyCode().IsMod1() || rKeyEvent.GetKeyCode().IsMod2())
                {
                    Control::KeyInput( rKeyEvent );
                    return;
                }
                nVStep *= mnVisLines;
                SAL_FALLTHROUGH;
            case KEY_UP:
                if (nCurPos != VALUESET_ITEM_NONEITEM)
                {
                    if (nCurPos == nLastItem)
                    {
                        const size_t nCol = mnCols ? nLastItem % mnCols : 0;
                        if (nCol < mnCurCol)
                        {
                            // Move to previous row/page, keeping the old column
                            nVStep -= mnCurCol - nCol;
                        }
                    }
                    if (nCurPos >= nVStep)
                    {
                        // Go up of a whole page
                        nItemPos = nCurPos-nVStep;
                    }
                    else if (mpNoneItem.get())
                    {
                        nItemPos = VALUESET_ITEM_NONEITEM;
                    }
                    else if (nCurPos > mnCols)
                    {
                        // Go to same column in first row
                        nItemPos = nCurPos % mnCols;
                    }
                }
                break;

            case KEY_PAGEDOWN:
                if (rKeyEvent.GetKeyCode().IsShift() || rKeyEvent.GetKeyCode().IsMod1() || rKeyEvent.GetKeyCode().IsMod2())
                {
                    Control::KeyInput( rKeyEvent );
                    return;
                }
                nVStep *= mnVisLines;
                SAL_FALLTHROUGH;
            case KEY_DOWN:
                if (nCurPos != nLastItem)
                {
                    if (nCurPos == VALUESET_ITEM_NONEITEM)
                    {
                        nItemPos = nVStep-mnCols+mnCurCol;
                    }
                    else
                    {
                        nItemPos = nCurPos+nVStep;
                    }
                    if (nItemPos > nLastItem)
                    {
                        nItemPos = nLastItem;
                    }
                }
                break;

            case KEY_RETURN:
                if (GetStyle() & WB_NO_DIRECTSELECT)
                {
                    Select();
                    break;
                }
                SAL_FALLTHROUGH;
            default:
                Control::KeyInput( rKeyEvent );
                return;
        }

        // This point is reached only if key travelling was used,
        // in which case selection mode should be switched off
        EndSelection();

        if ( nItemPos == VALUESET_ITEM_NOTFOUND )
            return;

        if ( nItemPos!=VALUESET_ITEM_NONEITEM && nItemPos<nLastItem )
        {
            // update current column only in case of a new position
            // which is also not a "specially" handled one.
            mnCurCol = mnCols ? nItemPos % mnCols : 0;
        }
        const sal_uInt16 nItemId = (nItemPos != VALUESET_ITEM_NONEITEM) ? GetItemId( nItemPos ) : 0;
        if ( nItemId != mnSelItemId )
        {
            SelectItem( nItemId );
            if (!(GetStyle() & WB_NO_DIRECTSELECT))
            {
                // select only if WB_NO_DIRECTSELECT is not set
                Select();
            }
        }
    }

    void ValueSet::Command( const CommandEvent& rCommandEvent )
    {
        if ( rCommandEvent.GetCommand() == CommandEventId::Wheel ||
             rCommandEvent.GetCommand() == CommandEventId::StartAutoScroll ||
             rCommandEvent.GetCommand() == CommandEventId::AutoScroll )
        {
            if ( HandleScrollCommand( rCommandEvent, nullptr, mxScrollBar.get() ) )
                return;
        }

        Control::Command( rCommandEvent );
    }

    void ValueSet::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
    {
        if (GetStyle() & WB_FLATVALUESET)
        {
            const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
            rRenderContext.SetLineColor();
            rRenderContext.SetFillColor(rStyleSettings.GetFaceColor());
            long nOffY = maVirDev->GetOutputSizePixel().Height();
            Size aWinSize(GetOutputSizePixel());
            rRenderContext.DrawRect(tools::Rectangle(Point(0, nOffY ), Point( aWinSize.Width(), aWinSize.Height())));
        }

        ImplDraw(rRenderContext);
    }

    void ValueSet::GetFocus()
    {
        SAL_INFO("svtools", "value set getting focus");
        Invalidate();
        Control::GetFocus();

        // Tell the accessible object that we got the focus.
        ValueSetAcc* pAcc = ValueSetAcc::getImplementation(GetAccessible(false));
        if (pAcc)
            pAcc->GetFocus();
    }

    void ValueSet::LoseFocus()
    {
        SAL_INFO("svtools", "value set losing focus");
        if ( mbNoSelection && mnSelItemId )
            ImplHideSelect( mnSelItemId );
        else
            HideFocus();
        Control::LoseFocus();

        // Tell the accessible object that we lost the focus.
        ValueSetAcc* pAcc = ValueSetAcc::getImplementation( GetAccessible( false ) );
        if( pAcc )
            pAcc->LoseFocus();
    }

    void ValueSet::Resize()
    {
        mbFormat = true;
        if ( IsReallyVisible() && IsUpdateMode() )
            Invalidate();
        Control::Resize();
    }

    void ValueSet::RequestHelp( const HelpEvent& rHelpEvent )
    {
        if (rHelpEvent.GetMode() & (HelpEventMode::QUICK | HelpEventMode::BALLOON))
        {
            Point aPos = ScreenToOutputPixel( rHelpEvent.GetMousePosPixel() );
            size_t nItemPos = ImplGetItem( aPos );
            if ( nItemPos != VALUESET_ITEM_NOTFOUND )
            {
                tools::Rectangle aItemRect = ImplGetItemRect( nItemPos );
                Point aPt = OutputToScreenPixel( aItemRect.TopLeft() );
                aItemRect.SetLeft( aPt.X() );
                aItemRect.SetTop( aPt.Y() );
                aPt = OutputToScreenPixel( aItemRect.BottomRight() );
                aItemRect.SetRight( aPt.X() );
                aItemRect.SetBottom( aPt.Y() );
                Help::ShowQuickHelp( this, aItemRect, GetItemText( ImplGetItem( nItemPos )->mnId ) );
                return;
            }
        }

        Control::RequestHelp( rHelpEvent );
    }

    void ValueSet::StateChanged(StateChangedType nType)
    {
        Control::StateChanged(nType);

        if (nType == StateChangedType::InitShow)
        {
            if (mbFormat)
                Invalidate();
        }
        else if (nType == StateChangedType::UpdateMode)
        {
            if (IsReallyVisible() && IsUpdateMode())
                Invalidate();
        }
        else if (nType == StateChangedType::Text)
        {
            if (mpNoneItem.get() && !mbFormat && IsReallyVisible() && IsUpdateMode())
            {
                Invalidate(maNoneItemRect);
            }
        }
        else if ((nType == StateChangedType::Zoom) ||
                 (nType == StateChangedType::ControlFont))
        {
            ImplInitSettings(true, false, false);
            Invalidate();
        }
        else if (nType == StateChangedType::ControlForeground)
        {
            ImplInitSettings(false, true, false);
            Invalidate();
        }
        else if (nType == StateChangedType::ControlBackground)
        {
            ImplInitSettings(false, false, true);
            Invalidate();
        }
        else if ((nType == StateChangedType::Style) || (nType == StateChangedType::Enable))
        {
            mbFormat = true;
            ImplInitSettings(false, false, true);
            Invalidate();
        }
    }

    void ValueSet::DataChanged( const DataChangedEvent& rDataChangedEvent )
    {
        Control::DataChanged( rDataChangedEvent );

        if ( rDataChangedEvent.GetType() == DataChangedEventType::FONTS ||
             rDataChangedEvent.GetType() == DataChangedEventType::DISPLAY ||
             rDataChangedEvent.GetType() == DataChangedEventType::FONTSUBSTITUTION ||
             (rDataChangedEvent.GetType() == DataChangedEventType::SETTINGS &&
              rDataChangedEvent.GetFlags() & AllSettingsFlags::STYLE) )
        {
            mbFormat = true;
            ImplInitSettings( true, true, true );
            Invalidate();
        }
    }

    void ValueSet::Select()
    {
        maSelectHdl.Call( this );
    }

    void ValueSet::UserDraw( const UserDrawEvent& )
    {
    }

    void ValueSet::InsertItem( sal_uInt16 nItemId, const Image& rImage )
    {
        ValueSetItem* pItem = new ValueSetItem( *this );
        pItem->mnId     = nItemId;
        pItem->meType   = VALUESETITEM_IMAGE;
        pItem->maImage  = rImage;
        ImplInsertItem( pItem, VALUESET_APPEND );
    }

    void ValueSet::InsertItem( sal_uInt16 nItemId, const Image& rImage,
                               const OUString& rText, size_t nPos,
                               bool bShowLegend )
    {
        ValueSetItem* pItem = new ValueSetItem( *this );
        pItem->mnId     = nItemId;
        pItem->meType   = bShowLegend ? VALUESETITEM_IMAGE_AND_TEXT : VALUESETITEM_IMAGE;
        pItem->maImage  = rImage;
        pItem->maText   = rText;
        ImplInsertItem( pItem, nPos );
    }

    void ValueSet::InsertItem( sal_uInt16 nItemId, const Color& rColor,
                               const OUString& rText )
    {
        ValueSetItem* pItem = new ValueSetItem( *this );
        pItem->mnId     = nItemId;
        pItem->meType   = VALUESETITEM_COLOR;
        pItem->maColor  = rColor;
        pItem->maText   = rText;
        ImplInsertItem( pItem, VALUESET_APPEND );
    }

    void ValueSet::InsertItem( sal_uInt16 nItemId, size_t nPos )
    {
        ValueSetItem* pItem = new ValueSetItem( *this );
        pItem->mnId     = nItemId;
        pItem->meType   = VALUESETITEM_USERDRAW;
        ImplInsertItem( pItem, nPos );
    }

    void ValueSet::ImplInsertItem( ValueSetItem *const pItem, const size_t nPos )
    {
        DBG_ASSERT( pItem->mnId, "ValueSet::InsertItem(): ItemId == 0" );
        DBG_ASSERT( GetItemPos( pItem->mnId ) == VALUESET_ITEM_NOTFOUND,
                    "ValueSet::InsertItem(): ItemId already exists" );

        if ( nPos < mItemList.size() ) {
            ValueItemList::iterator it = mItemList.begin();
            ::std::advance( it, nPos );
            mItemList.insert( it, pItem );
        } else {
            mItemList.push_back( pItem );
        }

        queue_resize();

        mbFormat = true;
        if ( IsReallyVisible() && IsUpdateMode() )
            Invalidate();
    }

    tools::Rectangle ValueSet::ImplGetItemRect( size_t nPos ) const
    {
        const size_t nVisibleBegin = static_cast<size_t>(mnFirstLine)*mnCols;
        const size_t nVisibleEnd = nVisibleBegin + static_cast<size_t>(mnVisLines)*mnCols;

        // Check if the item is inside the range of the displayed ones,
        // taking into account that last row could be incomplete
        if ( nPos<nVisibleBegin || nPos>=nVisibleEnd || nPos>=mItemList.size() )
            return tools::Rectangle();

        nPos -= nVisibleBegin;

        const size_t row = mnCols ? nPos/mnCols : 0;
        const size_t col = mnCols ? nPos%mnCols : 0;
        const long x = maItemListRect.Left()+col*(mnItemWidth+mnSpacing);
        const long y = maItemListRect.Top()+row*(mnItemHeight+mnSpacing);

        return tools::Rectangle( Point(x, y), Size(mnItemWidth, mnItemHeight) );
    }

    void ValueSet::RemoveItem( sal_uInt16 nItemId )
    {
        size_t nPos = GetItemPos( nItemId );

        if ( nPos == VALUESET_ITEM_NOTFOUND )
            return;

        if ( nPos < mItemList.size() ) {
            ValueItemList::iterator it = mItemList.begin();
            ::std::advance( it, nPos );
            delete *it;
            mItemList.erase( it );
        }

        // reset variables
        if ( (mnHighItemId == nItemId) || (mnSelItemId == nItemId) )
        {
            mnCurCol        = 0;
            mnHighItemId    = 0;
            mnSelItemId     = 0;
            mbNoSelection   = true;
        }

        queue_resize();

        mbFormat = true;
        if ( IsReallyVisible() && IsUpdateMode() )
            Invalidate();
    }

    void ValueSet::Clear()
    {
        ImplDeleteItems();

        // reset variables
        mnFirstLine     = 0;
        mnCurCol        = 0;
        mnHighItemId    = 0;
        mnSelItemId     = 0;
        mbNoSelection   = true;

        mbFormat = true;
        if ( IsReallyVisible() && IsUpdateMode() )
            Invalidate();
    }

    size_t ValueSet::GetItemCount() const
    {
        return mItemList.size();
    }

    size_t ValueSet::GetItemPos( sal_uInt16 nItemId ) const
    {
        for ( size_t i = 0, n = mItemList.size(); i < n; ++i ) {
            if ( mItemList[i]->mnId == nItemId ) {
                return i;
            }
        }
        return VALUESET_ITEM_NOTFOUND;
    }

    sal_uInt16 ValueSet::GetItemId( size_t nPos ) const
    {
        return ( nPos < mItemList.size() ) ? mItemList[nPos]->mnId : 0 ;
    }

    sal_uInt16 ValueSet::GetItemId( const Point& rPos ) const
    {
        size_t nItemPos = ImplGetItem( rPos );
        if ( nItemPos != VALUESET_ITEM_NOTFOUND )
            return GetItemId( nItemPos );

        return 0;
    }

    tools::Rectangle ValueSet::GetItemRect( sal_uInt16 nItemId ) const
    {
        const size_t nPos = GetItemPos( nItemId );

        if ( nPos!=VALUESET_ITEM_NOTFOUND && mItemList[nPos]->mbVisible )
            return ImplGetItemRect( nPos );

        return tools::Rectangle();
    }

    void ValueSet::EnableFullItemMode( bool bFullMode )
    {
        mbFullMode = bFullMode;
    }

    void ValueSet::SetColCount( sal_uInt16 nNewCols )
    {
        if ( mnUserCols != nNewCols )
        {
            mnUserCols = nNewCols;
            mbFormat = true;
            queue_resize();
            if ( IsReallyVisible() && IsUpdateMode() )
                Invalidate();
        }
    }

    void ValueSet::SetLineCount( sal_uInt16 nNewLines )
    {
        if ( mnUserVisLines != nNewLines )
        {
            mnUserVisLines = nNewLines;
            mbFormat = true;
            queue_resize();
            if ( IsReallyVisible() && IsUpdateMode() )
                Invalidate();
        }
    }

    void ValueSet::SetItemWidth( long nNewItemWidth )
    {
        if ( mnUserItemWidth != nNewItemWidth )
        {
            mnUserItemWidth = nNewItemWidth;
            mbFormat = true;
            queue_resize();
            if ( IsReallyVisible() && IsUpdateMode() )
                Invalidate();
        }
    }

    void ValueSet::SetItemHeight( long nNewItemHeight )
    {
        if ( mnUserItemHeight != nNewItemHeight )
        {
            mnUserItemHeight = nNewItemHeight;
            mbFormat = true;
            queue_resize();
            if ( IsReallyVisible() && IsUpdateMode() )
                Invalidate();
        }
    }

    /**
     * An inelegant method; sets the item width & height such that
     * all of the included items and their labels fit; if we can
     * calculate that.
     */
    void ValueSet::RecalculateItemSizes()
    {
        Size aLargestItem = GetLargestItemSize();

        if ( mnUserItemWidth != aLargestItem.Width() ||
             mnUserItemHeight != aLargestItem.Height() )
        {
            mnUserItemWidth = aLargestItem.Width();
            mnUserItemHeight = aLargestItem.Height();
            mbFormat = true;
            queue_resize();
            if ( IsReallyVisible() && IsUpdateMode() )
                Invalidate();
        }
    }

    void ValueSet::SelectItem( sal_uInt16 nItemId )
    {
        size_t nItemPos = 0;

        if ( nItemId )
        {
            nItemPos = GetItemPos( nItemId );
            if ( nItemPos == VALUESET_ITEM_NOTFOUND )
                return;
        }

        if ( !((mnSelItemId != nItemId) || mbNoSelection) )
            return;

        sal_uInt16 nOldItem = mnSelItemId ? mnSelItemId : 1;
        mnSelItemId = nItemId;
        mbNoSelection = false;

        bool bNewOut = !mbFormat && IsReallyVisible() && IsUpdateMode();
        bool bNewLine = false;

        // if necessary scroll to the visible area
        if (mbScroll && nItemId && mnCols)
        {
            sal_uInt16 nNewLine = static_cast<sal_uInt16>(nItemPos / mnCols);
            if ( nNewLine < mnFirstLine )
            {
                mnFirstLine = nNewLine;
                bNewLine = true;
            }
            else if ( nNewLine > static_cast<sal_uInt16>(mnFirstLine+mnVisLines-1) )
            {
                mnFirstLine = static_cast<sal_uInt16>(nNewLine-mnVisLines+1);
                bNewLine = true;
            }
        }

        if ( bNewOut )
        {
            if ( bNewLine )
            {
                // redraw everything if the visible area has changed
                mbFormat = true;
                Invalidate();
            }
            else
            {
                // remove old selection and draw the new one
                ImplHideSelect( nOldItem );
                Invalidate();
            }
        }

        if( ImplHasAccessibleListeners() )
        {
            // focus event (deselect)
            if( nOldItem )
            {
                const size_t nPos = GetItemPos( nItemId );

                if( nPos != VALUESET_ITEM_NOTFOUND )
                {
                    ValueItemAcc* pItemAcc = ValueItemAcc::getImplementation(
                        mItemList[nPos]->GetAccessible( false/*bIsTransientChildrenDisabled*/ ) );

                    if( pItemAcc )
                    {
                        Any aOldAny;
                        Any aNewAny;
                        aOldAny <<= Reference<XInterface>(static_cast<cppu::OWeakObject*>(pItemAcc));
                        ImplFireAccessibleEvent(AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, aOldAny, aNewAny );
                    }
                }
            }

            // focus event (select)
            const size_t nPos = GetItemPos( mnSelItemId );

            ValueSetItem* pItem;
            if( nPos != VALUESET_ITEM_NOTFOUND )
                pItem = mItemList[nPos];
            else
                pItem = mpNoneItem.get();

            ValueItemAcc* pItemAcc = nullptr;
            if (pItem != nullptr)
                pItemAcc = ValueItemAcc::getImplementation( pItem->GetAccessible( false/*bIsTransientChildrenDisabled*/ ) );

            if( pItemAcc )
            {
                Any aOldAny;
                Any aNewAny;
                aNewAny <<= Reference<XInterface>(static_cast<cppu::OWeakObject*>(pItemAcc));
                ImplFireAccessibleEvent(AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, aOldAny, aNewAny);
            }

            // selection event
            Any aOldAny;
            Any aNewAny;
            ImplFireAccessibleEvent(AccessibleEventId::SELECTION_CHANGED, aOldAny, aNewAny);
        }
        maHighlightHdl.Call(this);
    }

    void ValueSet::SetNoSelection()
    {
        mbNoSelection   = true;
        mbHighlight     = false;
        mbSelection     = false;

        if (IsReallyVisible() && IsUpdateMode())
            Invalidate();
    }

    void ValueSet::SetItemImage( sal_uInt16 nItemId, const Image& rImage )
    {
        size_t nPos = GetItemPos( nItemId );

        if ( nPos == VALUESET_ITEM_NOTFOUND )
            return;

        ValueSetItem* pItem = mItemList[nPos];
        pItem->meType  = VALUESETITEM_IMAGE;
        pItem->maImage = rImage;

        if ( !mbFormat && IsReallyVisible() && IsUpdateMode() )
        {
            const tools::Rectangle aRect = ImplGetItemRect(nPos);
            Invalidate(aRect);
        }
        else
            mbFormat = true;
    }

    Image ValueSet::GetItemImage( sal_uInt16 nItemId ) const
    {
        size_t nPos = GetItemPos( nItemId );

        if ( nPos != VALUESET_ITEM_NOTFOUND )
            return mItemList[nPos]->maImage;
        else
            return Image();
    }

    void ValueSet::SetItemColor( sal_uInt16 nItemId, const Color& rColor )
    {
        size_t nPos = GetItemPos( nItemId );

        if ( nPos == VALUESET_ITEM_NOTFOUND )
            return;

        ValueSetItem* pItem = mItemList[nPos];
        pItem->meType  = VALUESETITEM_COLOR;
        pItem->maColor = rColor;

        if ( !mbFormat && IsReallyVisible() && IsUpdateMode() )
        {
            const tools::Rectangle aRect = ImplGetItemRect(nPos);
            Invalidate( aRect );
        }
        else
            mbFormat = true;
    }

    Color ValueSet::GetItemColor( sal_uInt16 nItemId ) const
    {
        size_t nPos = GetItemPos( nItemId );

        if ( nPos != VALUESET_ITEM_NOTFOUND )
            return mItemList[nPos]->maColor;
        else
            return Color();
    }

    void ValueSet::SetItemData( sal_uInt16 nItemId, void* pData )
    {
        size_t nPos = GetItemPos( nItemId );

        if ( nPos == VALUESET_ITEM_NOTFOUND )
            return;

        ValueSetItem* pItem = mItemList[nPos];
        pItem->mpData = pData;

        if ( pItem->meType == VALUESETITEM_USERDRAW )
        {
            if ( !mbFormat && IsReallyVisible() && IsUpdateMode() )
            {
                const tools::Rectangle aRect = ImplGetItemRect(nPos);
                Invalidate(aRect);
            }
            else
                mbFormat = true;
        }
    }

    void* ValueSet::GetItemData( sal_uInt16 nItemId ) const
    {
        size_t nPos = GetItemPos( nItemId );

        if ( nPos != VALUESET_ITEM_NOTFOUND )
            return mItemList[nPos]->mpData;
        else
            return nullptr;
    }

    void ValueSet::SetItemText(sal_uInt16 nItemId, const OUString& rText)
    {
        size_t nPos = GetItemPos( nItemId );

        if ( nPos == VALUESET_ITEM_NOTFOUND )
            return;

        ValueSetItem* pItem = mItemList[nPos];

        // Remember old and new name for accessibility event.
        Any aOldName;
        Any aNewName;
        OUString sString (pItem->maText);
        aOldName <<= sString;
        sString = rText;
        aNewName <<= sString;

        pItem->maText = rText;

        if (!mbFormat && IsReallyVisible() && IsUpdateMode())
        {
            sal_uInt16 nTempId = mnSelItemId;

            if (mbHighlight)
                nTempId = mnHighItemId;

            if (nTempId == nItemId)
                Invalidate();
        }

        if (ImplHasAccessibleListeners())
        {
            Reference<XAccessible> xAccessible(pItem->GetAccessible( false/*bIsTransientChildrenDisabled*/));
            ValueItemAcc* pValueItemAcc = static_cast<ValueItemAcc*>(xAccessible.get());
            pValueItemAcc->FireAccessibleEvent(AccessibleEventId::NAME_CHANGED, aOldName, aNewName);
        }
    }

    OUString ValueSet::GetItemText( sal_uInt16 nItemId ) const
    {
        size_t nPos = GetItemPos( nItemId );

        if ( nPos != VALUESET_ITEM_NOTFOUND )
            return mItemList[nPos]->maText;

        return OUString();
    }

    void ValueSet::SetColor( const Color& rColor )
    {
        maColor  = rColor;
        mbFormat = true;
        if (IsReallyVisible() && IsUpdateMode())
            Invalidate();
    }

    void ValueSet::SetExtraSpacing( sal_uInt16 nNewSpacing )
    {
        if ( GetStyle() & WB_ITEMBORDER )
        {
            mnSpacing = nNewSpacing;

            mbFormat = true;
            queue_resize();
            if ( IsReallyVisible() && IsUpdateMode() )
                Invalidate();
        }
    }

    void ValueSet::StartSelection()
    {
        mbHighlight  = true;
        mbSelection  = true;
        mnHighItemId = mnSelItemId;
    }

    void ValueSet::EndSelection()
    {
        if ( mbHighlight )
        {
            if ( IsTracking() )
                EndTracking( TrackingEventFlags::Cancel );

            ImplHighlightItem( mnSelItemId );
            mbHighlight = false;
        }
        mbSelection = false;
    }

    void ValueSet::SetFormat()
    {
        mbFormat = true;
    }

    void ValueSet::StartDrag( const CommandEvent& rEvent, vcl::Region& rRegion )
    {
        if ( rEvent.GetCommand() != CommandEventId::StartDrag )
            return;

        // if necessary abort an existing action
        EndSelection();

        // Check out if the clicked on page is selected. If this is not the
        // case set it as the current item. We only check mouse actions since
        // drag-and-drop can also be triggered by the keyboard
        sal_uInt16 nSelId;
        if ( rEvent.IsMouseEvent() )
            nSelId = GetItemId( rEvent.GetMousePosPixel() );
        else
            nSelId = mnSelItemId;

        // don't activate dragging if no item was clicked on
        if ( !nSelId )
            return;

        // Check out if the page was selected. If not set as current page and
        // call select.
        if ( nSelId != mnSelItemId )
        {
            SelectItem( nSelId );
            Update();
            Select();
        }

        vcl::Region aRegion;

        // assign region
        rRegion = aRegion;
    }

    Size ValueSet::CalcWindowSizePixel( const Size& rItemSize, sal_uInt16 nDesireCols,
                                        sal_uInt16 nDesireLines ) const
    {
        size_t nCalcCols = nDesireCols;
        size_t nCalcLines = nDesireLines;

        if ( !nCalcCols )
        {
            if ( mnUserCols )
                nCalcCols = mnUserCols;
            else
                nCalcCols = 1;
        }

        if ( !nCalcLines )
        {
            nCalcLines = mnVisLines;

            if ( mbFormat )
            {
                if ( mnUserVisLines )
                    nCalcLines = mnUserVisLines;
                else
                {
                    // Floor( (M+N-1)/N )==Ceiling( M/N )
                    nCalcLines = (mItemList.size()+nCalcCols-1) / nCalcCols;
                    if ( !nCalcLines )
                        nCalcLines = 1;
                }
            }
        }

        Size        aSize( rItemSize.Width() * nCalcCols, rItemSize.Height() * nCalcLines );
        WinBits     nStyle = GetStyle();
        long        nTxtHeight = GetTextHeight();
        long        n;

        if ( nStyle & WB_ITEMBORDER )
        {
            if ( nStyle & WB_DOUBLEBORDER )
                n = ITEM_OFFSET_DOUBLE;
            else
                n = ITEM_OFFSET;

            aSize.AdjustWidth(n * nCalcCols );
            aSize.AdjustHeight(n * nCalcLines );
        }
        else
            n = 0;

        if ( mnSpacing )
        {
            aSize.AdjustWidth(mnSpacing * (nCalcCols - 1) );
            aSize.AdjustHeight(mnSpacing * (nCalcLines - 1) );
        }

        if ( nStyle & WB_NAMEFIELD )
        {
            aSize.AdjustHeight(nTxtHeight + NAME_OFFSET );
            if ( !(nStyle & WB_FLATVALUESET) )
                aSize.AdjustHeight(NAME_LINE_HEIGHT + NAME_LINE_OFF_Y );
        }

        if ( nStyle & WB_NONEFIELD )
        {
            aSize.AdjustHeight(nTxtHeight + n + mnSpacing );
        }

        // sum possible ScrollBar width
        aSize.AdjustWidth(GetScrollWidth() );

        return aSize;
    }

    Size ValueSet::CalcItemSizePixel( const Size& rItemSize) const
    {
        Size aSize = rItemSize;

        WinBits nStyle = GetStyle();
        if ( nStyle & WB_ITEMBORDER )
        {
            long n;

            if ( nStyle & WB_DOUBLEBORDER )
                n = ITEM_OFFSET_DOUBLE;
            else
                n = ITEM_OFFSET;

            aSize.AdjustWidth(n );
            aSize.AdjustHeight(n );
        }

        return aSize;
    }

    long ValueSet::GetScrollWidth() const
    {
        if (GetStyle() & WB_VSCROLL)
        {
            ValueSet* pValueSet = const_cast<ValueSet*>(this);
            if (!mxScrollBar)
            {
                pValueSet->ImplInitScrollBar();
            }
            pValueSet->Invalidate();
            return mxScrollBar->GetSizePixel().Width() + SCRBAR_OFFSET;
        }
        else
            return 0;
    }

    void ValueSet::SetHighlightHdl( const Link<ValueSet*,void>& rLink )
    {
        maHighlightHdl = rLink;
    }

    Size ValueSet::GetLargestItemSize()
    {
        Size aLargestItem;

        for (ValueSetItem* pItem : mItemList)
        {
            if (!pItem->mbVisible)
                continue;

            if (pItem->meType != VALUESETITEM_IMAGE &&
                pItem->meType != VALUESETITEM_IMAGE_AND_TEXT)
            {
                // handle determining an optimal size for this case
                continue;
            }

            Size aSize = pItem->maImage.GetSizePixel();
            if (pItem->meType == VALUESETITEM_IMAGE_AND_TEXT)
            {
                aSize.AdjustHeight(3 * NAME_LINE_HEIGHT +
                    maVirDev->GetTextHeight() );
                aSize.setWidth( std::max(aSize.Width(),
                                         maVirDev->GetTextWidth(pItem->maText) + NAME_OFFSET) );
            }

            aLargestItem.setWidth( std::max(aLargestItem.Width(), aSize.Width()) );
            aLargestItem.setHeight( std::max(aLargestItem.Height(), aSize.Height()) );
        }

        return aLargestItem;
    }

    Size ValueSet::GetOptimalSize() const
    {
        return CalcWindowSizePixel(
            const_cast<ValueSet *>(this)->GetLargestItemSize());
    }

    void ValueSet::SetEdgeBlending(bool bNew)
    {
        if(mbEdgeBlending != bNew)
        {
            mbEdgeBlending = bNew;
            mbFormat = true;

            if(IsReallyVisible() && IsUpdateMode())
            {
                Invalidate();
            }
        }
    }

    SvtValueSet::SvtValueSet(std::unique_ptr<weld::ScrolledWindow> pScrolledWindow)
        : maVirDev( VclPtr<VirtualDevice>::Create())
        , mxScrolledWindow(std::move(pScrolledWindow))
        , mnHighItemId(0)
        , maColor(COL_TRANSPARENT)
        , mnStyle(0)
        , mbFormat(true)
        , mbHighlight(false)
    {
        maVirDev->SetBackground(Application::GetSettings().GetStyleSettings().GetFaceColor());

        mpNoneItem.reset(nullptr);

        mnItemWidth         = 0;
        mnItemHeight        = 0;
        mnTextOffset        = 0;
        mnVisLines          = 0;
        mnLines             = 0;
        mnUserItemWidth     = 0;
        mnUserItemHeight    = 0;
        mnFirstLine         = 0;
        mnSelItemId         = 0;
        mnCols              = 0;
        mnCurCol            = 0;
        mnUserCols          = 0;
        mnUserVisLines      = 0;
        mnSpacing           = 0;
        mnFrameStyle        = DrawFrameStyle::NONE;
        mbNoSelection       = true;
        mbDrawSelection     = true;
        mbBlackSel          = false;
        mbDoubleSel         = false;
        mbScroll            = false;
        mbFullMode          = true;
        mbEdgeBlending      = false;
        mbHasVisibleItems   = false;

        if (mxScrolledWindow)
        {
            mxScrolledWindow->set_user_managed_scrolling();
            mxScrolledWindow->connect_vadjustment_changed(LINK(this, SvtValueSet, ImplScrollHdl));
        }
    }

    void SvtValueSet::SetDrawingArea(weld::DrawingArea* pDrawingArea)
    {
        CustomWidgetController::SetDrawingArea(pDrawingArea);
        // #106446#, #106601# force mirroring of virtual device
        maVirDev->EnableRTL(pDrawingArea->get_direction());
    }

    Reference<XAccessible> SvtValueSet::CreateAccessible()
    {
        if (!mxAccessible)
            mxAccessible.set(new SvtValueSetAcc(this));
        return mxAccessible;
    }

    SvtValueSet::~SvtValueSet()
    {
        Reference<XComponent> xComponent(mxAccessible, UNO_QUERY);
        if (xComponent.is())
            xComponent->dispose();

        ImplDeleteItems();
    }

    void SvtValueSet::ImplDeleteItems()
    {
        const size_t n = mItemList.size();

        for ( size_t i = 0; i < n; ++i )
        {
            SvtValueSetItem* pItem = mItemList[i];
            if ( pItem->mbVisible && ImplHasAccessibleListeners() )
            {
                Any aOldAny;
                Any aNewAny;

                aOldAny <<= pItem->GetAccessible( false/*bIsTransientChildrenDisabled*/ );
                ImplFireAccessibleEvent(AccessibleEventId::CHILD, aOldAny, aNewAny);
            }

            delete pItem;
        }

        mItemList.clear();
    }

    void SvtValueSet::Select()
    {
        maSelectHdl.Call( this );
    }

    void SvtValueSet::UserDraw( const UserDrawEvent& )
    {
    }

    size_t SvtValueSet::ImplGetItem( const Point& rPos ) const
    {
        if (!mbHasVisibleItems)
        {
            return VALUESET_ITEM_NOTFOUND;
        }

        if (mpNoneItem.get() && maNoneItemRect.IsInside(rPos))
        {
            return VALUESET_ITEM_NONEITEM;
        }

        if (maItemListRect.IsInside(rPos))
        {
            const int xc = rPos.X() - maItemListRect.Left();
            const int yc = rPos.Y() - maItemListRect.Top();
            // The point is inside the area of item list,
            // let's find the containing item.
            const int col = xc / (mnItemWidth + mnSpacing);
            const int x = xc % (mnItemWidth + mnSpacing);
            const int row = yc / (mnItemHeight + mnSpacing);
            const int y = yc % (mnItemHeight + mnSpacing);

            if (x < mnItemWidth && y < mnItemHeight)
            {
                // the point is inside item rect and not inside spacing
                const size_t item = (mnFirstLine + row) * static_cast<size_t>(mnCols) + col;
                if (item < mItemList.size())
                {
                    return item;
                }
            }
        }

        return VALUESET_ITEM_NOTFOUND;
    }

    SvtValueSetItem* SvtValueSet::ImplGetItem( size_t nPos )
    {
        if (nPos == VALUESET_ITEM_NONEITEM)
            return mpNoneItem.get();
        else
            return (nPos < mItemList.size()) ? mItemList[nPos] : nullptr;
    }

    SvtValueSetItem* SvtValueSet::ImplGetFirstItem()
    {
        return mItemList.size() ? mItemList[0] : nullptr;
    }

    sal_uInt16 SvtValueSet::ImplGetVisibleItemCount() const
    {
        sal_uInt16 nRet = 0;
        const size_t nItemCount = mItemList.size();

        for ( size_t n = 0; n < nItemCount; ++n )
        {
            if ( mItemList[n]->mbVisible )
                ++nRet;
        }

        return nRet;
    }

    void SvtValueSet::ImplFireAccessibleEvent( short nEventId, const Any& rOldValue, const Any& rNewValue )
    {
        SvtValueSetAcc* pAcc = SvtValueSetAcc::getImplementation(mxAccessible);

        if( pAcc )
            pAcc->FireAccessibleEvent( nEventId, rOldValue, rNewValue );
    }

    bool SvtValueSet::ImplHasAccessibleListeners()
    {
        SvtValueSetAcc* pAcc = SvtValueSetAcc::getImplementation(mxAccessible);
        return( pAcc && pAcc->HasAccessibleListeners() );
    }

    IMPL_LINK(SvtValueSet, ImplScrollHdl, weld::ScrolledWindow&, rScrollWin, void)
    {
        auto nNewFirstLine = rScrollWin.vadjustment_get_value();
        if ( nNewFirstLine != mnFirstLine )
        {
            mnFirstLine = nNewFirstLine;
            mbFormat = true;
            Invalidate();
        }
    }

    void SvtValueSet::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
    {
        if (GetStyle() & WB_FLATVALUESET)
        {
            const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
            rRenderContext.SetLineColor();
            rRenderContext.SetFillColor(rStyleSettings.GetFaceColor());
            long nOffY = maVirDev->GetOutputSizePixel().Height();
            Size aWinSize(GetOutputSizePixel());
            rRenderContext.DrawRect(tools::Rectangle(Point(0, nOffY ), Point( aWinSize.Width(), aWinSize.Height())));
        }

        ImplDraw(rRenderContext);
    }

    void SvtValueSet::GetFocus()
    {
        SAL_INFO("svtools", "value set getting focus");
        Invalidate();
        CustomWidgetController::GetFocus();

        // Tell the accessible object that we got the focus.
        SvtValueSetAcc* pAcc = SvtValueSetAcc::getImplementation(mxAccessible);
        if (pAcc)
            pAcc->GetFocus();
    }

    void SvtValueSet::LoseFocus()
    {
        SAL_INFO("svtools", "value set losing focus");
        Invalidate();
        CustomWidgetController::LoseFocus();

        // Tell the accessible object that we lost the focus.
        SvtValueSetAcc* pAcc = SvtValueSetAcc::getImplementation(mxAccessible);
        if( pAcc )
            pAcc->LoseFocus();
    }

    void SvtValueSet::Resize()
    {
        mbFormat = true;
        if ( IsReallyVisible() && IsUpdateMode() )
            Invalidate();
        CustomWidgetController::Resize();
    }

    bool SvtValueSet::KeyInput( const KeyEvent& rKeyEvent )
    {
        size_t nLastItem = mItemList.size();

        if ( !nLastItem || !ImplGetFirstItem() )
            return CustomWidgetController::KeyInput(rKeyEvent);

        if (mbFormat)
            Invalidate();

        --nLastItem;

        const size_t nCurPos = mnSelItemId ? GetItemPos(mnSelItemId)
                                           : (mpNoneItem.get() ? VALUESET_ITEM_NONEITEM : 0);
        size_t nItemPos = VALUESET_ITEM_NOTFOUND;
        size_t nVStep = mnCols;

        switch (rKeyEvent.GetKeyCode().GetCode())
        {
            case KEY_HOME:
                nItemPos = mpNoneItem.get() ? VALUESET_ITEM_NONEITEM : 0;
                break;

            case KEY_END:
                nItemPos = nLastItem;
                break;

            case KEY_LEFT:
                if (nCurPos != VALUESET_ITEM_NONEITEM)
                {
                    if (nCurPos)
                    {
                        nItemPos = nCurPos-1;
                    }
                    else if (mpNoneItem.get())
                    {
                        nItemPos = VALUESET_ITEM_NONEITEM;
                    }
                }
                break;

            case KEY_RIGHT:
                if (nCurPos < nLastItem)
                {
                    if (nCurPos == VALUESET_ITEM_NONEITEM)
                    {
                        nItemPos = 0;
                    }
                    else
                    {
                        nItemPos = nCurPos+1;
                    }
                }
                break;

            case KEY_PAGEUP:
                if (rKeyEvent.GetKeyCode().IsShift() || rKeyEvent.GetKeyCode().IsMod1() || rKeyEvent.GetKeyCode().IsMod2())
                {
                    return CustomWidgetController::KeyInput(rKeyEvent);
                }
                nVStep *= mnVisLines;
                SAL_FALLTHROUGH;
            case KEY_UP:
                if (nCurPos != VALUESET_ITEM_NONEITEM)
                {
                    if (nCurPos == nLastItem)
                    {
                        const size_t nCol = mnCols ? nLastItem % mnCols : 0;
                        if (nCol < mnCurCol)
                        {
                            // Move to previous row/page, keeping the old column
                            nVStep -= mnCurCol - nCol;
                        }
                    }
                    if (nCurPos >= nVStep)
                    {
                        // Go up of a whole page
                        nItemPos = nCurPos-nVStep;
                    }
                    else if (mpNoneItem.get())
                    {
                        nItemPos = VALUESET_ITEM_NONEITEM;
                    }
                    else if (nCurPos > mnCols)
                    {
                        // Go to same column in first row
                        nItemPos = nCurPos % mnCols;
                    }
                }
                break;

            case KEY_PAGEDOWN:
                if (rKeyEvent.GetKeyCode().IsShift() || rKeyEvent.GetKeyCode().IsMod1() || rKeyEvent.GetKeyCode().IsMod2())
                {
                    return CustomWidgetController::KeyInput(rKeyEvent);
                }
                nVStep *= mnVisLines;
                SAL_FALLTHROUGH;
            case KEY_DOWN:
                if (nCurPos != nLastItem)
                {
                    if (nCurPos == VALUESET_ITEM_NONEITEM)
                    {
                        nItemPos = nVStep-mnCols+mnCurCol;
                    }
                    else
                    {
                        nItemPos = nCurPos+nVStep;
                    }
                    if (nItemPos > nLastItem)
                    {
                        nItemPos = nLastItem;
                    }
                }
                break;

            case KEY_RETURN:
                if (GetStyle() & WB_NO_DIRECTSELECT)
                {
                    Select();
                    break;
                }
                SAL_FALLTHROUGH;
            default:
                return CustomWidgetController::KeyInput(rKeyEvent);
        }

        if ( nItemPos == VALUESET_ITEM_NOTFOUND )
            return false;

        if ( nItemPos!=VALUESET_ITEM_NONEITEM && nItemPos<nLastItem )
        {
            // update current column only in case of a new position
            // which is also not a "specially" handled one.
            mnCurCol = mnCols ? nItemPos % mnCols : 0;
        }
        const sal_uInt16 nItemId = (nItemPos != VALUESET_ITEM_NONEITEM) ? GetItemId( nItemPos ) : 0;
        if ( nItemId != mnSelItemId )
        {
            SelectItem( nItemId );
            if (!(GetStyle() & WB_NO_DIRECTSELECT))
            {
                // select only if WB_NO_DIRECTSELECT is not set
                Select();
            }
        }

        return true;
    }

    void SvtValueSet::ImplTracking(const Point& rPos)
    {
        SvtValueSetItem* pItem = ImplGetItem( ImplGetItem( rPos ) );
        if ( pItem )
        {
            if( GetStyle() & WB_MENUSTYLEVALUESET || GetStyle() & WB_FLATVALUESET )
                mbHighlight = true;

            ImplHighlightItem( pItem->mnId );
        }
        else
        {
            if( GetStyle() & WB_MENUSTYLEVALUESET || GetStyle() & WB_FLATVALUESET )
                mbHighlight = true;

            ImplHighlightItem( mnSelItemId, false );
        }
    }

    void SvtValueSet::MouseButtonDown( const MouseEvent& rMouseEvent )
    {
        if ( rMouseEvent.IsLeft() )
        {
            SvtValueSetItem* pItem = ImplGetItem( ImplGetItem( rMouseEvent.GetPosPixel() ) );
            if (pItem && !rMouseEvent.IsMod2())
            {
                if (rMouseEvent.GetClicks() == 1)
                {
                    SelectItem( pItem->mnId );
                    if (!(GetStyle() & WB_NOPOINTERFOCUS))
                        GrabFocus();
                    Select();
                }
                else if ( rMouseEvent.GetClicks() == 2 )
                    maDoubleClickHdl.Call( this );

                return;
            }
        }

        CustomWidgetController::MouseButtonDown( rMouseEvent );
    }

    void SvtValueSet::MouseMove(const MouseEvent& rMouseEvent)
    {
        // because of SelectionMode
        if ((GetStyle() & WB_MENUSTYLEVALUESET) || (GetStyle() & WB_FLATVALUESET))
            ImplTracking(rMouseEvent.GetPosPixel());
        CustomWidgetController::MouseMove(rMouseEvent);
    }

    void SvtValueSet::RemoveItem( sal_uInt16 nItemId )
    {
        size_t nPos = GetItemPos( nItemId );

        if ( nPos == VALUESET_ITEM_NOTFOUND )
            return;

        if ( nPos < mItemList.size() ) {
            SvtValueItemList::iterator it = mItemList.begin();
            ::std::advance( it, nPos );
            delete *it;
            mItemList.erase( it );
        }

        // reset variables
        if (mnSelItemId == nItemId)
        {
            mnCurCol        = 0;
            mnSelItemId     = 0;
            mbNoSelection   = true;
        }

        queue_resize();

        mbFormat = true;
        if ( IsReallyVisible() && IsUpdateMode() )
            Invalidate();
    }

    void SvtValueSet::Clear()
    {
        ImplDeleteItems();

        // reset variables
        mnFirstLine     = 0;
        mnCurCol        = 0;
        mnSelItemId     = 0;
        mbNoSelection   = true;

        mbFormat = true;
        if (IsReallyVisible() && IsUpdateMode())
            Invalidate();
    }

    size_t SvtValueSet::GetItemCount() const
    {
        return mItemList.size();
    }

    size_t SvtValueSet::GetItemPos( sal_uInt16 nItemId ) const
    {
        for ( size_t i = 0, n = mItemList.size(); i < n; ++i ) {
            if ( mItemList[i]->mnId == nItemId ) {
                return i;
            }
        }
        return VALUESET_ITEM_NOTFOUND;
    }

    sal_uInt16 SvtValueSet::GetItemId( size_t nPos ) const
    {
        return ( nPos < mItemList.size() ) ? mItemList[nPos]->mnId : 0 ;
    }

    sal_uInt16 SvtValueSet::GetItemId( const Point& rPos ) const
    {
        size_t nItemPos = ImplGetItem( rPos );
        if ( nItemPos != VALUESET_ITEM_NOTFOUND )
            return GetItemId( nItemPos );

        return 0;
    }

    tools::Rectangle SvtValueSet::GetItemRect( sal_uInt16 nItemId ) const
    {
        const size_t nPos = GetItemPos( nItemId );

        if ( nPos!=VALUESET_ITEM_NOTFOUND && mItemList[nPos]->mbVisible )
            return ImplGetItemRect( nPos );

        return tools::Rectangle();
    }

    tools::Rectangle SvtValueSet::ImplGetItemRect( size_t nPos ) const
    {
        const size_t nVisibleBegin = static_cast<size_t>(mnFirstLine)*mnCols;
        const size_t nVisibleEnd = nVisibleBegin + static_cast<size_t>(mnVisLines)*mnCols;

        // Check if the item is inside the range of the displayed ones,
        // taking into account that last row could be incomplete
        if ( nPos<nVisibleBegin || nPos>=nVisibleEnd || nPos>=mItemList.size() )
            return tools::Rectangle();

        nPos -= nVisibleBegin;

        const size_t row = mnCols ? nPos/mnCols : 0;
        const size_t col = mnCols ? nPos%mnCols : 0;
        const long x = maItemListRect.Left()+col*(mnItemWidth+mnSpacing);
        const long y = maItemListRect.Top()+row*(mnItemHeight+mnSpacing);

        return tools::Rectangle( Point(x, y), Size(mnItemWidth, mnItemHeight) );
    }

    void SvtValueSet::ImplHighlightItem( sal_uInt16 nItemId, bool bIsSelection )
    {
        if ( mnHighItemId == nItemId )
            return;

        // remember the old item to delete the previous selection
        mnHighItemId = nItemId;

        // don't draw the selection if nothing is selected
        if ( !bIsSelection && mbNoSelection )
            mbDrawSelection = false;

        // remove the old selection and draw the new one
        Invalidate();
        mbDrawSelection = true;
    }

    void SvtValueSet::ImplDraw(vcl::RenderContext& rRenderContext)
    {
        if (mbFormat)
            Format(rRenderContext);

        Point aDefPos;
        Size aSize = maVirDev->GetOutputSizePixel();

        rRenderContext.DrawOutDev(aDefPos, aSize, aDefPos, aSize, *maVirDev.get());

        // draw parting line to the Namefield
        if (GetStyle() & WB_NAMEFIELD)
        {
            if (!(GetStyle() & WB_FLATVALUESET))
            {
                const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
                Size aWinSize(GetOutputSizePixel());
                Point aPos1(NAME_LINE_OFF_X, mnTextOffset + NAME_LINE_OFF_Y);
                Point aPos2(aWinSize.Width() - (NAME_LINE_OFF_X * 2), mnTextOffset + NAME_LINE_OFF_Y);
                if (!(rStyleSettings.GetOptions() & StyleSettingsOptions::Mono))
                {
                    rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
                    rRenderContext.DrawLine(aPos1, aPos2);
                    aPos1.AdjustY( 1 );
                    aPos2.AdjustY( 1 );
                    rRenderContext.SetLineColor(rStyleSettings.GetLightColor());
                }
                else
                    rRenderContext.SetLineColor(rStyleSettings.GetWindowTextColor());
                rRenderContext.DrawLine(aPos1, aPos2);
            }
        }

        ImplDrawSelect(rRenderContext);
    }

    void SvtValueSet::SelectItem( sal_uInt16 nItemId )
    {
        size_t nItemPos = 0;

        if ( nItemId )
        {
            nItemPos = GetItemPos( nItemId );
            if ( nItemPos == VALUESET_ITEM_NOTFOUND )
                return;
        }

        if ( !((mnSelItemId != nItemId) || mbNoSelection) )
            return;

        sal_uInt16 nOldItem = mnSelItemId ? mnSelItemId : 1;
        mnSelItemId = nItemId;
        mbNoSelection = false;

        bool bNewOut = !mbFormat && IsReallyVisible() && IsUpdateMode();
        bool bNewLine = false;

        // if necessary scroll to the visible area
        if (mbScroll && nItemId && mnCols)
        {
            sal_uInt16 nNewLine = static_cast<sal_uInt16>(nItemPos / mnCols);
            if ( nNewLine < mnFirstLine )
            {
                mnFirstLine = nNewLine;
                bNewLine = true;
            }
            else if ( nNewLine > static_cast<sal_uInt16>(mnFirstLine+mnVisLines-1) )
            {
                mnFirstLine = static_cast<sal_uInt16>(nNewLine-mnVisLines+1);
                bNewLine = true;
            }
        }

        if ( bNewOut )
        {
            if ( bNewLine )
            {
                // redraw everything if the visible area has changed
                mbFormat = true;
            }
            Invalidate();
        }

        if( ImplHasAccessibleListeners() )
        {
            // focus event (deselect)
            if( nOldItem )
            {
                const size_t nPos = GetItemPos( nItemId );

                if( nPos != VALUESET_ITEM_NOTFOUND )
                {
                    SvtValueItemAcc* pItemAcc = SvtValueItemAcc::getImplementation(
                        mItemList[nPos]->GetAccessible( false/*bIsTransientChildrenDisabled*/ ) );

                    if( pItemAcc )
                    {
                        Any aOldAny;
                        Any aNewAny;
                        aOldAny <<= Reference<XInterface>(static_cast<cppu::OWeakObject*>(pItemAcc));
                        ImplFireAccessibleEvent(AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, aOldAny, aNewAny );
                    }
                }
            }

            // focus event (select)
            const size_t nPos = GetItemPos( mnSelItemId );

            SvtValueSetItem* pItem;
            if( nPos != VALUESET_ITEM_NOTFOUND )
                pItem = mItemList[nPos];
            else
                pItem = mpNoneItem.get();

            SvtValueItemAcc* pItemAcc = nullptr;
            if (pItem != nullptr)
                pItemAcc = SvtValueItemAcc::getImplementation( pItem->GetAccessible( false/*bIsTransientChildrenDisabled*/ ) );

            if( pItemAcc )
            {
                Any aOldAny;
                Any aNewAny;
                aNewAny <<= Reference<XInterface>(static_cast<cppu::OWeakObject*>(pItemAcc));
                ImplFireAccessibleEvent(AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, aOldAny, aNewAny);
            }

            // selection event
            Any aOldAny;
            Any aNewAny;
            ImplFireAccessibleEvent(AccessibleEventId::SELECTION_CHANGED, aOldAny, aNewAny);
        }
    }

    void SvtValueSet::SetNoSelection()
    {
        mbNoSelection   = true;
        mbHighlight     = false;

        if (IsReallyVisible() && IsUpdateMode())
            Invalidate();
    }

    void SvtValueSet::SetStyle(WinBits nStyle)
    {
        if (nStyle != mnStyle)
        {
            mnStyle = nStyle;
            mbFormat = true;
            Invalidate();
        }
    }

    void SvtValueSet::Format(vcl::RenderContext const & rRenderContext)
    {
        Size aWinSize(GetOutputSizePixel());
        size_t nItemCount = mItemList.size();
        WinBits nStyle = GetStyle();
        long nTxtHeight = rRenderContext.GetTextHeight();
        long nOff;
        long nNoneHeight;
        long nNoneSpace;

        if (mxScrolledWindow && !(nStyle & WB_VSCROLL) && mxScrolledWindow->get_vpolicy() != VclPolicyType::NEVER)
        {
            mxScrolledWindow->set_vpolicy(VclPolicyType::NEVER);
            Size aPrefSize(GetDrawingArea()->get_preferred_size());
            GetDrawingArea()->set_size_request(aPrefSize.Width() + GetScrollWidth(), aPrefSize.Height());
        }

        // calculate item offset
        if (nStyle & WB_ITEMBORDER)
        {
            if (nStyle & WB_DOUBLEBORDER)
                nOff = ITEM_OFFSET_DOUBLE;
            else
                nOff = ITEM_OFFSET;
        }
        else
            nOff = 0;

        // consider size, if NameField does exist
        if (nStyle & WB_NAMEFIELD)
        {
            mnTextOffset = aWinSize.Height() - nTxtHeight - NAME_OFFSET;
            aWinSize.AdjustHeight( -(nTxtHeight + NAME_OFFSET) );

            if (!(nStyle & WB_FLATVALUESET))
            {
                mnTextOffset -= NAME_LINE_HEIGHT + NAME_LINE_OFF_Y;
                aWinSize.AdjustHeight( -(NAME_LINE_HEIGHT + NAME_LINE_OFF_Y) );
            }
        }
        else
            mnTextOffset = 0;

        // consider offset and size, if NoneField does exist
        if (nStyle & WB_NONEFIELD)
        {
            nNoneHeight = nTxtHeight + nOff;
            nNoneSpace = mnSpacing;
        }
        else
        {
            nNoneHeight = 0;
            nNoneSpace = 0;

            if (mpNoneItem.get())
                mpNoneItem.reset(nullptr);
        }

        // calculate number of columns
        if (!mnUserCols)
        {
            if (mnUserItemWidth)
            {
                mnCols = static_cast<sal_uInt16>((aWinSize.Width() - mnSpacing) / (mnUserItemWidth + mnSpacing));
                if (mnCols <= 0)
                    mnCols = 1;
            }
            else
            {
                mnCols = 1;
            }
        }
        else
        {
            mnCols = mnUserCols;
        }

        // calculate number of rows
        mbScroll = false;

        // Floor( (M+N-1)/N )==Ceiling( M/N )
        mnLines = (static_cast<long>(nItemCount) + mnCols - 1) / mnCols;
        if (mnLines <= 0)
            mnLines = 1;

        long nCalcHeight = aWinSize.Height() - nNoneHeight;
        if (mnUserVisLines)
        {
            mnVisLines = mnUserVisLines;
        }
        else if (mnUserItemHeight)
        {
            mnVisLines = (nCalcHeight - nNoneSpace + mnSpacing) / (mnUserItemHeight + mnSpacing);
            if (!mnVisLines)
                mnVisLines = 1;
        }
        else
        {
            mnVisLines = mnLines;
        }

        if (mnLines > mnVisLines)
            mbScroll = true;

        if (mnLines <= mnVisLines)
        {
            mnFirstLine = 0;
        }
        else
        {
            if (mnFirstLine > static_cast<sal_uInt16>(mnLines - mnVisLines))
                mnFirstLine = static_cast<sal_uInt16>(mnLines - mnVisLines);
        }

        // calculate item size
        const long nColSpace  = (mnCols - 1) * static_cast<long>(mnSpacing);
        const long nLineSpace = ((mnVisLines - 1) * mnSpacing) + nNoneSpace;
        if (mnUserItemWidth && !mnUserCols)
        {
            mnItemWidth = mnUserItemWidth;
            if (mnItemWidth > aWinSize.Width() - nColSpace)
                mnItemWidth = aWinSize.Width() - nColSpace;
        }
        else
            mnItemWidth = (aWinSize.Width() - nColSpace) / mnCols;
        if (mnUserItemHeight && !mnUserVisLines)
        {
            mnItemHeight = mnUserItemHeight;
            if (mnItemHeight > nCalcHeight - nNoneSpace)
                mnItemHeight = nCalcHeight - nNoneSpace;
        }
        else
        {
            nCalcHeight -= nLineSpace;
            mnItemHeight = nCalcHeight / mnVisLines;
        }

        // Init VirDev
        maVirDev->SetSettings(rRenderContext.GetSettings());
        maVirDev->SetOutputSizePixel(aWinSize);

        // nothing is changed in case of too small items
        if ((mnItemWidth <= 0) ||
            (mnItemHeight <= ((nStyle & WB_ITEMBORDER) ? 4 : 2)) ||
            !nItemCount)
        {
            mbHasVisibleItems = false;

            if (nStyle & WB_NONEFIELD)
            {
                if (mpNoneItem.get())
                {
                    mpNoneItem->mbVisible = false;
                    mpNoneItem->maText = GetText();
                }
            }

            for (size_t i = 0; i < nItemCount; i++)
            {
                mItemList[i]->mbVisible = false;
            }

            if (mxScrolledWindow && mxScrolledWindow->get_vpolicy() != VclPolicyType::NEVER)
            {
                mxScrolledWindow->set_vpolicy(VclPolicyType::NEVER);
                Size aPrefSize(GetDrawingArea()->get_preferred_size());
                GetDrawingArea()->set_size_request(aPrefSize.Width() + GetScrollWidth(), aPrefSize.Height());
            }
        }
        else
        {
            mbHasVisibleItems = true;

            // determine Frame-Style
            if (nStyle & WB_DOUBLEBORDER)
                mnFrameStyle = DrawFrameStyle::DoubleIn;
            else
                mnFrameStyle = DrawFrameStyle::In;

            // determine selected color and width
            // if necessary change the colors, to make the selection
            // better detectable
            const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
            Color aHighColor(rStyleSettings.GetHighlightColor());
            if (((aHighColor.GetRed() > 0x80) || (aHighColor.GetGreen() > 0x80) ||
                 (aHighColor.GetBlue() > 0x80)) ||
                ((aHighColor.GetRed() == 0x80) && (aHighColor.GetGreen() == 0x80) &&
                 (aHighColor.GetBlue() == 0x80)))
            {
                mbBlackSel = true;
            }
            else
            {
                mbBlackSel = false;
            }
            // draw the selection with double width if the items are bigger
            if ((nStyle & WB_DOUBLEBORDER) &&
                ((mnItemWidth >= 25) && (mnItemHeight >= 20)))
            {
                mbDoubleSel = true;
            }
            else
            {
                mbDoubleSel = false;
            }

            // calculate offsets
            long nStartX;
            long nStartY;
            if (mbFullMode)
            {
                long nAllItemWidth = (mnItemWidth * mnCols) + nColSpace;
                long nAllItemHeight = (mnItemHeight * mnVisLines) + nNoneHeight + nLineSpace;
                nStartX = (aWinSize.Width() - nAllItemWidth) / 2;
                nStartY = (aWinSize.Height() - nAllItemHeight) / 2;
            }
            else
            {
                nStartX = 0;
                nStartY = 0;
            }

            // calculate and draw items
            maVirDev->SetLineColor();
            long x = nStartX;
            long y = nStartY;

            // create NoSelection field and show it
            if (nStyle & WB_NONEFIELD)
            {
                if (!mpNoneItem)
                    mpNoneItem.reset(new SvtValueSetItem(*this));

                mpNoneItem->mnId = 0;
                mpNoneItem->meType = VALUESETITEM_NONE;
                mpNoneItem->mbVisible = true;
                maNoneItemRect.SetLeft( x );
                maNoneItemRect.SetTop( y );
                maNoneItemRect.SetRight( maNoneItemRect.Left() + aWinSize.Width() - x - 1 );
                maNoneItemRect.SetBottom( y + nNoneHeight - 1 );

                ImplFormatItem(rRenderContext, mpNoneItem.get(), maNoneItemRect);

                y += nNoneHeight + nNoneSpace;
            }

            // draw items
            sal_uLong nFirstItem = static_cast<sal_uLong>(mnFirstLine) * mnCols;
            sal_uLong nLastItem = nFirstItem + (mnVisLines * mnCols);

            maItemListRect.SetLeft( x );
            maItemListRect.SetTop( y );
            maItemListRect.SetRight( x + mnCols * (mnItemWidth + mnSpacing) - mnSpacing - 1 );
            maItemListRect.SetBottom( y + mnVisLines * (mnItemHeight + mnSpacing) - mnSpacing - 1 );

            if (!mbFullMode)
            {
                // If want also draw parts of items in the last line,
                // then we add one more line if parts of these line are
                // visible
                if (y + (mnVisLines * (mnItemHeight + mnSpacing)) < aWinSize.Height())
                    nLastItem += mnCols;
                maItemListRect.SetBottom( aWinSize.Height() - y );
            }
            for (size_t i = 0; i < nItemCount; i++)
            {
                SvtValueSetItem* pItem = mItemList[i];

                if (i >= nFirstItem && i < nLastItem)
                {
                    if (!pItem->mbVisible && ImplHasAccessibleListeners())
                    {
                        Any aOldAny;
                        Any aNewAny;

                        aNewAny <<= pItem->GetAccessible(false/*bIsTransientChildrenDisabled*/);
                        ImplFireAccessibleEvent(AccessibleEventId::CHILD, aOldAny, aNewAny);
                    }

                    pItem->mbVisible = true;
                    ImplFormatItem(rRenderContext, pItem, tools::Rectangle(Point(x, y), Size(mnItemWidth, mnItemHeight)));

                    if (!((i + 1) % mnCols))
                    {
                        x = nStartX;
                        y += mnItemHeight + mnSpacing;
                    }
                    else
                        x += mnItemWidth + mnSpacing;
                }
                else
                {
                    if (pItem->mbVisible && ImplHasAccessibleListeners())
                    {
                        Any aOldAny;
                        Any aNewAny;

                        aOldAny <<= pItem->GetAccessible(false/*bIsTransientChildrenDisabled*/);
                        ImplFireAccessibleEvent(AccessibleEventId::CHILD, aOldAny, aNewAny);
                    }

                    pItem->mbVisible = false;
                }
            }

            // arrange ScrollBar, set values and show it
            if (mxScrolledWindow && (nStyle & WB_VSCROLL) && mxScrolledWindow->get_vpolicy() != VclPolicyType::ALWAYS)
            {
                long nPageSize = mnVisLines;
                if (nPageSize < 1)
                    nPageSize = 1;
                mxScrolledWindow->vadjustment_configure(mnFirstLine, 0, mnLines, 1,
                                                        mnVisLines, nPageSize);
                mxScrolledWindow->set_vpolicy(VclPolicyType::ALWAYS);
                Size aPrefSize(GetDrawingArea()->get_preferred_size());
                GetDrawingArea()->set_size_request(aPrefSize.Width() - GetScrollWidth(), aPrefSize.Height());
            }
        }

        // waiting for the next since the formatting is finished
        mbFormat = false;
    }

    void SvtValueSet::ImplDrawSelect(vcl::RenderContext& rRenderContext)
    {
        if (!IsReallyVisible())
            return;

        const bool bFocus = HasFocus();
        const bool bDrawSel = !((mbNoSelection && !mbHighlight) || (!mbDrawSelection && mbHighlight));

        if (!bFocus && !bDrawSel)
        {
            ImplDrawItemText(rRenderContext, OUString());
            return;
        }

        ImplDrawSelect(rRenderContext, mnSelItemId, bFocus, bDrawSel);
        if (mbHighlight)
        {
            ImplDrawSelect(rRenderContext, mnHighItemId, bFocus, bDrawSel);
        }
    }

    void SvtValueSet::ImplDrawSelect(vcl::RenderContext& rRenderContext, sal_uInt16 nItemId, const bool bFocus, const bool bDrawSel )
    {
        SvtValueSetItem* pItem;
        tools::Rectangle aRect;
        if (nItemId)
        {
            const size_t nPos = GetItemPos( nItemId );
            pItem = mItemList[ nPos ];
            aRect = ImplGetItemRect( nPos );
        }
        else if (mpNoneItem.get())
        {
            pItem = mpNoneItem.get();
            aRect = maNoneItemRect;
        }
        else if (bFocus && (pItem = ImplGetFirstItem()))
        {
            aRect = ImplGetItemRect(0);
        }
        else
        {
            return;
        }

        if (!pItem->mbVisible)
            return;

        // draw selection
        const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
        rRenderContext.SetFillColor();

        Color aDoubleColor(rStyleSettings.GetHighlightColor());
        Color aSingleColor(rStyleSettings.GetHighlightTextColor());
        if (!mbDoubleSel)
        {
            /*
            *  #99777# contrast enhancement for thin mode
            */
            const Wallpaper& rWall = maVirDev->GetBackground();
            if (!rWall.IsBitmap() && ! rWall.IsGradient())
            {
                const Color& rBack = rWall.GetColor();
                if (rBack.IsDark() && ! aDoubleColor.IsBright())
                {
                    aDoubleColor = COL_WHITE;
                    aSingleColor = COL_BLACK;
                }
                else if (rBack.IsBright() && !aDoubleColor.IsDark())
                {
                    aDoubleColor = COL_BLACK;
                    aSingleColor = COL_WHITE;
                }
            }
        }

        // specify selection output
        WinBits nStyle = GetStyle();
        if (nStyle & WB_MENUSTYLEVALUESET)
        {
            if (bFocus)
                DrawFocusRect(rRenderContext, aRect);
            if (bDrawSel)
            {
                rRenderContext.SetLineColor(mbBlackSel ? COL_BLACK : aDoubleColor);
                rRenderContext.DrawRect(aRect);
            }
        }
        else
        {
            if (bDrawSel)
            {
                rRenderContext.SetLineColor(mbBlackSel ? COL_BLACK : aDoubleColor);
                rRenderContext.DrawRect(aRect);
            }
            if (mbDoubleSel)
            {
                aRect.AdjustLeft( 1 );
                aRect.AdjustTop( 1 );
                aRect.AdjustRight( -1 );
                aRect.AdjustBottom( -1 );
                if (bDrawSel)
                    rRenderContext.DrawRect(aRect);
            }
            aRect.AdjustLeft( 1 );
            aRect.AdjustTop( 1 );
            aRect.AdjustRight( -1 );
            aRect.AdjustBottom( -1 );
            tools::Rectangle aRect2 = aRect;
            aRect.AdjustLeft( 1 );
            aRect.AdjustTop( 1 );
            aRect.AdjustRight( -1 );
            aRect.AdjustBottom( -1 );
            if (bDrawSel)
                rRenderContext.DrawRect(aRect);
            if (mbDoubleSel)
            {
                aRect.AdjustLeft( 1 );
                aRect.AdjustTop( 1 );
                aRect.AdjustRight( -1 );
                aRect.AdjustBottom( -1 );
                if (bDrawSel)
                    rRenderContext.DrawRect(aRect);
            }

            if (bDrawSel)
            {
                rRenderContext.SetLineColor(mbBlackSel ? COL_WHITE : aSingleColor);
            }
            else
            {
                rRenderContext.SetLineColor(COL_LIGHTGRAY);
            }
            rRenderContext.DrawRect(aRect2);
            if (bFocus)
                DrawFocusRect(rRenderContext, aRect2);
        }

        ImplDrawItemText(rRenderContext, pItem->maText);
    }

    void SvtValueSet::ImplFormatItem(vcl::RenderContext const & rRenderContext, SvtValueSetItem* pItem, tools::Rectangle aRect)
    {
        WinBits nStyle = GetStyle();
        if (nStyle & WB_ITEMBORDER)
        {
            aRect.AdjustLeft(1 );
            aRect.AdjustTop(1 );
            aRect.AdjustRight( -1 );
            aRect.AdjustBottom( -1 );

            if (nStyle & WB_FLATVALUESET)
            {
                sal_Int32 nBorder = (nStyle & WB_DOUBLEBORDER) ? 2 : 1;

                aRect.AdjustLeft(nBorder );
                aRect.AdjustTop(nBorder );
                aRect.AdjustRight( -nBorder );
                aRect.AdjustBottom( -nBorder );
            }
            else
            {
                DecorationView aView(maVirDev.get());
                aRect = aView.DrawFrame(aRect, mnFrameStyle);
            }
        }

        if (pItem == mpNoneItem.get())
            pItem->maText = GetText();

        if (!((aRect.GetHeight() > 0) && (aRect.GetWidth() > 0)))
            return;

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

        if (pItem == mpNoneItem.get())
        {
            maVirDev->SetFont(rRenderContext.GetFont());
            maVirDev->SetTextColor((nStyle & WB_MENUSTYLEVALUESET) ? rStyleSettings.GetMenuTextColor() : rStyleSettings.GetWindowTextColor());
            maVirDev->SetTextFillColor();
            maVirDev->SetFillColor((nStyle & WB_MENUSTYLEVALUESET) ? rStyleSettings.GetMenuColor() : rStyleSettings.GetWindowColor());
            maVirDev->DrawRect(aRect);
            Point aTxtPos(aRect.Left() + 2, aRect.Top());
            long nTxtWidth = rRenderContext.GetTextWidth(pItem->maText);
            if ((aTxtPos.X() + nTxtWidth) > aRect.Right())
            {
                maVirDev->SetClipRegion(vcl::Region(aRect));
                maVirDev->DrawText(aTxtPos, pItem->maText);
                maVirDev->SetClipRegion();
            }
            else
                maVirDev->DrawText(aTxtPos, pItem->maText);
        }
        else if (pItem->meType == VALUESETITEM_COLOR)
        {
            maVirDev->SetFillColor(pItem->maColor);
            maVirDev->DrawRect(aRect);
        }
        else
        {
            if (IsColor())
                maVirDev->SetFillColor(maColor);
            else if (nStyle & WB_MENUSTYLEVALUESET)
                maVirDev->SetFillColor(rStyleSettings.GetMenuColor());
            else if (IsEnabled())
                maVirDev->SetFillColor(rStyleSettings.GetWindowColor());
            else
                maVirDev->SetFillColor(rStyleSettings.GetFaceColor());
            maVirDev->DrawRect(aRect);

            if (pItem->meType == VALUESETITEM_USERDRAW)
            {
                UserDrawEvent aUDEvt(nullptr, maVirDev.get(), aRect, pItem->mnId);
                UserDraw(aUDEvt);
            }
            else
            {
                Size aImageSize = pItem->maImage.GetSizePixel();
                Size  aRectSize = aRect.GetSize();
                Point aPos(aRect.Left(), aRect.Top());
                aPos.AdjustX((aRectSize.Width() - aImageSize.Width()) / 2 );

                if (pItem->meType != VALUESETITEM_IMAGE_AND_TEXT)
                    aPos.AdjustY((aRectSize.Height() - aImageSize.Height()) / 2 );

                DrawImageFlags  nImageStyle  = DrawImageFlags::NONE;
                if (!IsEnabled())
                    nImageStyle  |= DrawImageFlags::Disable;

                if (aImageSize.Width()  > aRectSize.Width() ||
                    aImageSize.Height() > aRectSize.Height())
                {
                    maVirDev->SetClipRegion(vcl::Region(aRect));
                    maVirDev->DrawImage(aPos, pItem->maImage, nImageStyle);
                    maVirDev->SetClipRegion();
                }
                else
                    maVirDev->DrawImage(aPos, pItem->maImage, nImageStyle);

                if (pItem->meType == VALUESETITEM_IMAGE_AND_TEXT)
                {
                    maVirDev->SetFont(rRenderContext.GetFont());
                    maVirDev->SetTextColor((nStyle & WB_MENUSTYLEVALUESET) ? rStyleSettings.GetMenuTextColor() : rStyleSettings.GetWindowTextColor());
                    maVirDev->SetTextFillColor();

                    long nTxtWidth = maVirDev->GetTextWidth(pItem->maText);

                    if (nTxtWidth > aRect.GetWidth())
                        maVirDev->SetClipRegion(vcl::Region(aRect));

                    maVirDev->DrawText(Point(aRect.Left() +
                                             (aRect.GetWidth() - nTxtWidth) / 2,
                                             aRect.Bottom() - maVirDev->GetTextHeight()),
                                       pItem->maText);

                    if (nTxtWidth > aRect.GetWidth())
                        maVirDev->SetClipRegion();
                }
            }
        }

        const sal_uInt16 nEdgeBlendingPercent(GetEdgeBlending() ? rStyleSettings.GetEdgeBlending() : 0);

        if (nEdgeBlendingPercent)
        {
            const Color& rTopLeft(rStyleSettings.GetEdgeBlendingTopLeftColor());
            const Color& rBottomRight(rStyleSettings.GetEdgeBlendingBottomRightColor());
            const sal_uInt8 nAlpha((nEdgeBlendingPercent * 255) / 100);
            const BitmapEx aBlendFrame(createBlendFrame(aRect.GetSize(), nAlpha, rTopLeft, rBottomRight));

            if (!aBlendFrame.IsEmpty())
            {
                maVirDev->DrawBitmapEx(aRect.TopLeft(), aBlendFrame);
            }
        }
    }

    void SvtValueSet::ImplDrawItemText(vcl::RenderContext& rRenderContext, const OUString& rText)
    {
        if (!(GetStyle() & WB_NAMEFIELD))
            return;

        Size aWinSize(GetOutputSizePixel());
        long nTxtWidth = rRenderContext.GetTextWidth(rText);
        long nTxtOffset = mnTextOffset;

        // delete rectangle and show text
        if (GetStyle() & WB_FLATVALUESET)
        {
            const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
            rRenderContext.SetLineColor();
            rRenderContext.SetFillColor(rStyleSettings.GetFaceColor());
            rRenderContext.DrawRect(tools::Rectangle(Point(0, nTxtOffset), Point(aWinSize.Width(), aWinSize.Height())));
            rRenderContext.SetTextColor(rStyleSettings.GetButtonTextColor());
        }
        else
        {
            nTxtOffset += NAME_LINE_HEIGHT+NAME_LINE_OFF_Y;
            rRenderContext.SetBackground(Application::GetSettings().GetStyleSettings().GetFaceColor());
            rRenderContext.Erase(tools::Rectangle(Point(0, nTxtOffset), Point(aWinSize.Width(), aWinSize.Height())));
        }
        rRenderContext.DrawText(Point((aWinSize.Width() - nTxtWidth) / 2, nTxtOffset + (NAME_OFFSET / 2)), rText);
    }

    void SvtValueSet::StyleUpdated()
    {
        mbFormat = true;
        CustomWidgetController::StyleUpdated();
    }

    void SvtValueSet::SetColCount( sal_uInt16 nNewCols )
    {
        if ( mnUserCols != nNewCols )
        {
            mnUserCols = nNewCols;
            mbFormat = true;
            queue_resize();
            if ( IsReallyVisible() && IsUpdateMode() )
                Invalidate();
        }
    }

    void SvtValueSet::SetItemImage( sal_uInt16 nItemId, const Image& rImage )
    {
        size_t nPos = GetItemPos( nItemId );

        if ( nPos == VALUESET_ITEM_NOTFOUND )
            return;

        SvtValueSetItem* pItem = mItemList[nPos];
        pItem->meType  = VALUESETITEM_IMAGE;
        pItem->maImage = rImage;

        if ( !mbFormat && IsReallyVisible() && IsUpdateMode() )
        {
            const tools::Rectangle aRect = ImplGetItemRect(nPos);
            Invalidate(aRect);
        }
        else
            mbFormat = true;
    }

    Color SvtValueSet::GetItemColor( sal_uInt16 nItemId ) const
    {
        size_t nPos = GetItemPos( nItemId );

        if ( nPos != VALUESET_ITEM_NOTFOUND )
            return mItemList[nPos]->maColor;
        else
            return Color();
    }

    Size SvtValueSet::CalcWindowSizePixel( const Size& rItemSize, sal_uInt16 nDesireCols,
                                        sal_uInt16 nDesireLines ) const
    {
        size_t nCalcCols = nDesireCols;
        size_t nCalcLines = nDesireLines;

        if ( !nCalcCols )
        {
            if ( mnUserCols )
                nCalcCols = mnUserCols;
            else
                nCalcCols = 1;
        }

        if ( !nCalcLines )
        {
            nCalcLines = mnVisLines;

            if ( mbFormat )
            {
                if ( mnUserVisLines )
                    nCalcLines = mnUserVisLines;
                else
                {
                    // Floor( (M+N-1)/N )==Ceiling( M/N )
                    nCalcLines = (mItemList.size()+nCalcCols-1) / nCalcCols;
                    if ( !nCalcLines )
                        nCalcLines = 1;
                }
            }
        }

        Size        aSize( rItemSize.Width() * nCalcCols, rItemSize.Height() * nCalcLines );
        WinBits     nStyle = GetStyle();
        long        nTxtHeight = GetTextHeight();
        long        n;

        if ( nStyle & WB_ITEMBORDER )
        {
            if ( nStyle & WB_DOUBLEBORDER )
                n = ITEM_OFFSET_DOUBLE;
            else
                n = ITEM_OFFSET;

            aSize.AdjustWidth(n * nCalcCols );
            aSize.AdjustHeight(n * nCalcLines );
        }
        else
            n = 0;

        if ( mnSpacing )
        {
            aSize.AdjustWidth(mnSpacing * (nCalcCols - 1) );
            aSize.AdjustHeight(mnSpacing * (nCalcLines - 1) );
        }

        if ( nStyle & WB_NAMEFIELD )
        {
            aSize.AdjustHeight(nTxtHeight + NAME_OFFSET );
            if ( !(nStyle & WB_FLATVALUESET) )
                aSize.AdjustHeight(NAME_LINE_HEIGHT + NAME_LINE_OFF_Y );
        }

        if ( nStyle & WB_NONEFIELD )
        {
            aSize.AdjustHeight(nTxtHeight + n + mnSpacing );
        }

        return aSize;
    }

    void SvtValueSet::InsertItem( sal_uInt16 nItemId, const Image& rImage,
                               const OUString& rText, size_t nPos )
    {
        SvtValueSetItem* pItem = new SvtValueSetItem( *this );
        pItem->mnId     = nItemId;
        pItem->meType   = VALUESETITEM_IMAGE;
        pItem->maImage  = rImage;
        pItem->maText   = rText;
        ImplInsertItem( pItem, nPos );
    }

    void SvtValueSet::InsertItem( sal_uInt16 nItemId, size_t nPos )
    {
        SvtValueSetItem* pItem = new SvtValueSetItem( *this );
        pItem->mnId     = nItemId;
        pItem->meType   = VALUESETITEM_USERDRAW;
        ImplInsertItem( pItem, nPos );
    }

    void SvtValueSet::InsertItem( sal_uInt16 nItemId, const Color& rColor,
                               const OUString& rText )
    {
        SvtValueSetItem* pItem = new SvtValueSetItem( *this );
        pItem->mnId     = nItemId;
        pItem->meType   = VALUESETITEM_COLOR;
        pItem->maColor  = rColor;
        pItem->maText   = rText;
        ImplInsertItem( pItem, VALUESET_APPEND );
    }

    void SvtValueSet::ImplInsertItem( SvtValueSetItem *const pItem, const size_t nPos )
    {
        DBG_ASSERT( pItem->mnId, "ValueSet::InsertItem(): ItemId == 0" );
        DBG_ASSERT( GetItemPos( pItem->mnId ) == VALUESET_ITEM_NOTFOUND,
                    "ValueSet::InsertItem(): ItemId already exists" );

        if ( nPos < mItemList.size() ) {
            SvtValueItemList::iterator it = mItemList.begin();
            ::std::advance( it, nPos );
            mItemList.insert( it, pItem );
        } else {
            mItemList.push_back( pItem );
        }

        queue_resize();

        mbFormat = true;
        if ( IsReallyVisible() && IsUpdateMode() )
            Invalidate();
    }

    int SvtValueSet::GetScrollWidth() const
    {
        if (mxScrolledWindow)
            return mxScrolledWindow->get_vscroll_width();
        return 0;
    }

    void SvtValueSet::SetEdgeBlending(bool bNew)
    {
        if(mbEdgeBlending != bNew)
        {
            mbEdgeBlending = bNew;
            mbFormat = true;

            if (GetDrawingArea() && IsReallyVisible() && IsUpdateMode())
            {
                Invalidate();
            }
        }
    }

    Size SvtValueSet::CalcItemSizePixel( const Size& rItemSize) const
    {
        Size aSize = rItemSize;

        WinBits nStyle = GetStyle();
        if ( nStyle & WB_ITEMBORDER )
        {
            long n;

            if ( nStyle & WB_DOUBLEBORDER )
                n = ITEM_OFFSET_DOUBLE;
            else
                n = ITEM_OFFSET;

            aSize.AdjustWidth(n );
            aSize.AdjustHeight(n );
        }

        return aSize;
    }

    void SvtValueSet::SetLineCount( sal_uInt16 nNewLines )
    {
        if ( mnUserVisLines != nNewLines )
        {
            mnUserVisLines = nNewLines;
            mbFormat = true;
            queue_resize();
            if ( IsReallyVisible() && IsUpdateMode() )
                Invalidate();
        }
    }

    void SvtValueSet::SetItemWidth( long nNewItemWidth )
    {
        if ( mnUserItemWidth != nNewItemWidth )
        {
            mnUserItemWidth = nNewItemWidth;
            mbFormat = true;
            queue_resize();
            if ( IsReallyVisible() && IsUpdateMode() )
                Invalidate();
        }
    }

    //method to set accessible when the style is user draw.
    void SvtValueSet::InsertItem( sal_uInt16 nItemId, const OUString& rText, size_t nPos  )
    {
        DBG_ASSERT( nItemId, "ValueSet::InsertItem(): ItemId == 0" );
        DBG_ASSERT( GetItemPos( nItemId ) == VALUESET_ITEM_NOTFOUND,
                    "ValueSet::InsertItem(): ItemId already exists" );
        SvtValueSetItem* pItem = new SvtValueSetItem( *this );
        pItem->mnId     = nItemId;
        pItem->meType   = VALUESETITEM_USERDRAW;
        pItem->maText   = rText;
        ImplInsertItem( pItem, nPos );
    }

    void SvtValueSet::SetItemHeight( long nNewItemHeight )
    {
        if ( mnUserItemHeight != nNewItemHeight )
        {
            mnUserItemHeight = nNewItemHeight;
            mbFormat = true;
            queue_resize();
            if ( IsReallyVisible() && IsUpdateMode() )
                Invalidate();
        }
    }

    OUString SvtValueSet::RequestHelp(tools::Rectangle& rHelpRect)
    {
        Point aPos = rHelpRect.TopLeft();
        const size_t nItemPos = ImplGetItem( aPos );
        OUString sRet;
        if (nItemPos != VALUESET_ITEM_NOTFOUND)
        {
            rHelpRect = ImplGetItemRect(nItemPos);
            sRet = GetItemText(ImplGetItem(nItemPos)->mnId);
        }
        return sRet;
    }

    OUString SvtValueSet::GetItemText(sal_uInt16 nItemId) const
    {
        const size_t nPos = GetItemPos(nItemId);

        if ( nPos != VALUESET_ITEM_NOTFOUND )
            return mItemList[nPos]->maText;

        return OUString();
    }

    void SvtValueSet::SetExtraSpacing( sal_uInt16 nNewSpacing )
    {
        if ( GetStyle() & WB_ITEMBORDER )
        {
            mnSpacing = nNewSpacing;

            mbFormat = true;
            queue_resize();
            if ( IsReallyVisible() && IsUpdateMode() )
                Invalidate();
        }
    }

    void SvtValueSet::SetFormat()
    {
        mbFormat = true;
    }

    void SvtValueSet::SetItemText(sal_uInt16 nItemId, const OUString& rText)
    {
        size_t nPos = GetItemPos( nItemId );

        if ( nPos == VALUESET_ITEM_NOTFOUND )
            return;

        SvtValueSetItem* pItem = mItemList[nPos];

        // Remember old and new name for accessibility event.
        Any aOldName;
        Any aNewName;
        OUString sString (pItem->maText);
        aOldName <<= sString;
        sString = rText;
        aNewName <<= sString;

        pItem->maText = rText;

        if (!mbFormat && IsReallyVisible() && IsUpdateMode())
        {
            sal_uInt16 nTempId = mnSelItemId;

            if (mbHighlight)
                nTempId = mnHighItemId;

            if (nTempId == nItemId)
                Invalidate();
        }

        if (ImplHasAccessibleListeners())
        {
            Reference<XAccessible> xAccessible(pItem->GetAccessible( false/*bIsTransientChildrenDisabled*/));
            SvtValueItemAcc* pValueItemAcc = static_cast<SvtValueItemAcc*>(xAccessible.get());
            pValueItemAcc->FireAccessibleEvent(AccessibleEventId::NAME_CHANGED, aOldName, aNewName);
        }
    }

    Size SvtValueSet::GetLargestItemSize()
    {
        Size aLargestItem;

        for (SvtValueSetItem* pItem : mItemList)
        {
            if (!pItem->mbVisible)
                continue;

            if (pItem->meType != VALUESETITEM_IMAGE &&
                pItem->meType != VALUESETITEM_IMAGE_AND_TEXT)
            {
                // handle determining an optimal size for this case
                continue;
            }

            Size aSize = pItem->maImage.GetSizePixel();
            if (pItem->meType == VALUESETITEM_IMAGE_AND_TEXT)
            {
                aSize.AdjustHeight(3 * NAME_LINE_HEIGHT +
                    maVirDev->GetTextHeight() );
                aSize.setWidth( std::max(aSize.Width(),
                                         maVirDev->GetTextWidth(pItem->maText) + NAME_OFFSET) );
            }

            aLargestItem.setWidth( std::max(aLargestItem.Width(), aSize.Width()) );
            aLargestItem.setHeight( std::max(aLargestItem.Height(), aSize.Height()) );
        }

        return aLargestItem;
    }

    void SvtValueSet::SetOptimalSize()
    {
        Size aLargestSize(GetLargestItemSize());
        aLargestSize.setWidth(std::max(aLargestSize.Width(), mnUserItemWidth));
        aLargestSize.setHeight(std::max(aLargestSize.Height(), mnUserItemHeight));
        Size aPrefSize(CalcWindowSizePixel(aLargestSize));
        GetDrawingArea()->set_size_request(aPrefSize.Width(), aPrefSize.Height());
    }

    Image SvtValueSet::GetItemImage(sal_uInt16 nItemId) const
    {
        size_t nPos = GetItemPos( nItemId );

        if ( nPos != VALUESET_ITEM_NOTFOUND )
            return mItemList[nPos]->maImage;
        else
            return Image();
    }

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

↑ V547 Expression 'nOldItem' is always true.

↑ V547 Expression 'nOldItem' is always true.