/* -*- 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 <string>
#include <typeinfo>
#include <utility>

#include <comphelper/propertysequence.hxx>
#include <tools/color.hxx>
#include <svl/poolitem.hxx>
#include <svl/eitem.hxx>
#include <svl/itemset.hxx>
#include <vcl/commandinfoprovider.hxx>
#include <vcl/toolbox.hxx>
#include <vcl/bitmapaccess.hxx>
#include <vcl/menubtn.hxx>
#include <vcl/vclptr.hxx>
#include <svtools/valueset.hxx>
#include <svtools/ctrlbox.hxx>
#include <svl/style.hxx>
#include <svtools/ctrltool.hxx>
#include <svtools/borderhelper.hxx>
#include <svl/stritem.hxx>
#include <sfx2/tplpitem.hxx>
#include <sfx2/dispatch.hxx>
#include <sfx2/viewsh.hxx>
#include <sfx2/docfac.hxx>
#include <sfx2/templdlg.hxx>
#include <svl/isethint.hxx>
#include <sfx2/sfxstatuslistener.hxx>
#include <toolkit/helper/vclunohelper.hxx>
#include <tools/urlobj.hxx>
#include <sfx2/childwin.hxx>
#include <sfx2/viewfrm.hxx>
#include <unotools/fontoptions.hxx>
#include <vcl/builderfactory.hxx>
#include <vcl/mnemonic.hxx>
#include <vcl/svapp.hxx>
#include <vcl/settings.hxx>
#include <vcl/virdev.hxx>
#include <svtools/colorcfg.hxx>
#include <com/sun/star/table/BorderLine2.hpp>
#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
#include <svx/strings.hrc>
#include <svx/svxitems.hrc>
#include <svx/svxids.hrc>
#include <helpids.h>
#include <sfx2/htmlmode.hxx>
#include <sfx2/sidebar/Sidebar.hxx>
#include <sfx2/sidebar/SidebarToolBox.hxx>
#include <svx/xtable.hxx>
#include <editeng/editids.hrc>
#include <editeng/fontitem.hxx>
#include <editeng/fhgtitem.hxx>
#include <editeng/boxitem.hxx>
#include <editeng/charreliefitem.hxx>
#include <editeng/contouritem.hxx>
#include <editeng/colritem.hxx>
#include <editeng/crossedoutitem.hxx>
#include <editeng/emphasismarkitem.hxx>
#include <editeng/flstitem.hxx>
#include <editeng/lineitem.hxx>
#include <editeng/postitem.hxx>
#include <editeng/shdditem.hxx>
#include <editeng/udlnitem.hxx>
#include <editeng/wghtitem.hxx>
#include <editeng/svxfont.hxx>
#include <editeng/cmapitem.hxx>
#include <svx/colorwindow.hxx>
#include <svx/colorbox.hxx>
#include <svx/drawitem.hxx>
#include <svx/tbcontrl.hxx>
#include <svx/dlgutil.hxx>
#include <svx/dialmgr.hxx>
#include <svx/PaletteManager.hxx>
#include <memory>

#include <svx/framelink.hxx>
#include <svx/tbxcolorupdate.hxx>
#include <editeng/eerdll.hxx>
#include <editeng/editrids.hrc>
#include <svx/xlnclit.hxx>
#include <svx/xfillit0.hxx>
#include <svx/xflclit.hxx>
#include <svl/currencytable.hxx>
#include <svtools/langtab.hxx>
#include <cppu/unotype.hxx>
#include <cppuhelper/supportsservice.hxx>
#include <officecfg/Office/Common.hxx>
#include <o3tl/typed_flags_set.hxx>
#include <bitmaps.hlst>
#include <sal/log.hxx>
#include <unotools/collatorwrapper.hxx>

#define MAX_MRU_FONTNAME_ENTRIES    5

// don't make more than 15 entries visible at once
#define MAX_STYLES_ENTRIES          15

// namespaces
using namespace ::editeng;
using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::frame;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::lang;

SFX_IMPL_TOOLBOX_CONTROL( SvxStyleToolBoxControl, SfxTemplateItem );
SFX_IMPL_TOOLBOX_CONTROL( SvxSimpleUndoRedoController, SfxStringItem );

class SvxStyleBox_Impl : public ComboBox
{
    using Window::IsVisible;
public:
    SvxStyleBox_Impl( vcl::Window* pParent, const OUString& rCommand, SfxStyleFamily eFamily, const Reference< XDispatchProvider >& rDispatchProvider,
                        const Reference< XFrame >& _xFrame,const OUString& rClearFormatKey, const OUString& rMoreKey, bool bInSpecialMode );
    virtual ~SvxStyleBox_Impl() override;
    virtual void dispose() override;

    void            SetFamily( SfxStyleFamily eNewFamily );
    bool            IsVisible() const { return bVisible; }

    virtual bool    PreNotify( NotifyEvent& rNEvt ) override;
    virtual bool    EventNotify( NotifyEvent& rNEvt ) override;
    virtual void    DataChanged( const DataChangedEvent& rDCEvt ) override;
    virtual void    StateChanged( StateChangedType nStateChange ) override;

    virtual void    UserDraw( const UserDrawEvent& rUDEvt ) override;

    void            SetVisibilityListener( const Link<SvxStyleBox_Impl&,void>& aVisListener ) { aVisibilityListener = aVisListener; }

    void            SetDefaultStyle( const OUString& rDefault ) { sDefaultStyle = rDefault; }

protected:
    /// Calculate the optimal width of the dropdown.  Very expensive operation, triggers lots of font measurement.
    DECL_LINK(CalcOptimalExtraUserWidth, VclWindowEvent&, void);

    virtual void    Select() override;

private:
    SfxStyleFamily                  eStyleFamily;
    sal_Int32                       nCurSel;
    bool                            bRelease;
    Size const                      aLogicalSize;
    Link<SvxStyleBox_Impl&,void>    aVisibilityListener;
    bool                            bVisible;
    Reference< XDispatchProvider >  m_xDispatchProvider;
    Reference< XFrame >             m_xFrame;
    OUString const                  m_aCommand;
    OUString const                  aClearFormatKey;
    OUString const                  aMoreKey;
    OUString                        sDefaultStyle;
    bool const                      bInSpecialMode;
    VclPtr<MenuButton>              m_pButtons[MAX_STYLES_ENTRIES];
    VclBuilder                      m_aBuilder;
    VclPtr<PopupMenu>               m_pMenu;

    void            ReleaseFocus();
    static Color    TestColorsVisible(const Color &FontCol, const Color &BackCol);
    static void     UserDrawEntry(const UserDrawEvent& rUDEvt, const OUString &rStyleName);
    void            SetupEntry(vcl::RenderContext& rRenderContext, vcl::Window* pParent, sal_Int32 nItem, const tools::Rectangle& rRect, const OUString& rStyleName, bool bIsNotSelected);
    static bool     AdjustFontForItemHeight(OutputDevice* pDevice, tools::Rectangle const & rTextRect, long nHeight);
    void            SetOptimalSize();
    DECL_LINK( MenuSelectHdl, Menu *, bool );
    DECL_STATIC_LINK(SvxStyleBox_Impl, ShowMoreHdl, void*, void);
};

class SvxFontNameBox_Impl : public FontNameBox
{
    using Window::Update;
private:
    const FontList*                pFontList;
    ::std::unique_ptr<FontList>    m_aOwnFontList;
    vcl::Font                      aCurFont;
    Size const                     aLogicalSize;
    OUString                       aCurText;
    sal_uInt16                     nFtCount;
    bool                           bRelease;
    Reference< XDispatchProvider > m_xDispatchProvider;
    Reference< XFrame >            m_xFrame;
    bool            mbEndPreview;
    bool            mbCheckingUnknownFont;

    void            ReleaseFocus_Impl();
    void            EnableControls_Impl();

    void            EndPreview()
    {
        Sequence< PropertyValue > aArgs;
        SfxToolBoxControl::Dispatch( m_xDispatchProvider,
                                         ".uno:CharEndPreviewFontName",
                                         aArgs );
    }
    DECL_LINK( CheckAndMarkUnknownFont, VclWindowEvent&, void );

    void            SetOptimalSize();

protected:
    virtual void    Select() override;
    virtual void    DataChanged( const DataChangedEvent& rDCEvt ) override;

public:
    SvxFontNameBox_Impl( vcl::Window* pParent, const Reference< XDispatchProvider >& rDispatchProvider,const Reference< XFrame >& _xFrame
        , WinBits nStyle
        );
    virtual ~SvxFontNameBox_Impl() override;
    virtual void dispose() override;

    void            FillList();
    void            Update( const css::awt::FontDescriptor* pFontDesc );
    sal_uInt16      GetListCount() { return nFtCount; }
    void            Clear() { FontNameBox::Clear(); nFtCount = 0; }
    void            Fill( const FontList* pList )
                        { FontNameBox::Fill( pList );
                          nFtCount = pList->GetFontNameCount(); }
    virtual void    UserDraw( const UserDrawEvent& rUDEvt ) override;
    virtual bool    PreNotify( NotifyEvent& rNEvt ) override;
    virtual bool    EventNotify( NotifyEvent& rNEvt ) override;
    virtual Reference< css::accessibility::XAccessible > CreateAccessible() override;
    void     SetOwnFontList(::std::unique_ptr<FontList> && _aOwnFontList) { m_aOwnFontList = std::move(_aOwnFontList); }
};

// SelectHdl needs the Modifiers, get them in MouseButtonUp
class SvxFrmValueSet_Impl : public ValueSet
{
    sal_uInt16          nModifier;
    virtual void    MouseButtonUp( const MouseEvent& rMEvt ) override;
public:
    SvxFrmValueSet_Impl(vcl::Window* pParent,  WinBits nWinStyle)
        : ValueSet(pParent, nWinStyle), nModifier(0) {}
    sal_uInt16          GetModifier() const {return nModifier;}

};

void SvxFrmValueSet_Impl::MouseButtonUp( const MouseEvent& rMEvt )
{
    nModifier = rMEvt.GetModifier();
    ValueSet::MouseButtonUp(rMEvt);
}

class SvxFrameWindow_Impl : public svtools::ToolbarPopup
{
private:
    VclPtr<SvxFrmValueSet_Impl> aFrameSet;
    svt::ToolboxController&     mrController;
    std::vector<BitmapEx>       aImgVec;
    bool                        bParagraphMode;

    void InitImageList();
    void CalcSizeValueSet();
    DECL_LINK( SelectHdl, ValueSet*, void );

protected:
    virtual void    GetFocus() override;
    virtual void    KeyInput( const KeyEvent& rKEvt ) override;

public:
    SvxFrameWindow_Impl( svt::ToolboxController& rController, vcl::Window* pParentWindow );
    virtual ~SvxFrameWindow_Impl() override;
    virtual void dispose() override;

    virtual void    statusChanged( const css::frame::FeatureStateEvent& rEvent ) override;
    virtual void    DataChanged( const DataChangedEvent& rDCEvt ) override;
};

namespace
{
    class LineListBox final : public ListBox
    {
    public:
        typedef Color (*ColorFunc)(Color);
        typedef Color (*ColorDistFunc)(Color, Color);

                        LineListBox( vcl::Window* pParent, WinBits nWinStyle = WB_BORDER );
        virtual         ~LineListBox() override;
        virtual void    dispose() override;

        /** Set the width in Twips */
        void SetWidth( long nWidth )
        {
            long nOldWidth = m_nWidth;
            m_nWidth = nWidth;
            UpdateEntries( nOldWidth );
        }

        void SetNone( const OUString& sNone )
        {
            m_sNone = sNone;
        }

        using ListBox::InsertEntry;
        /** Insert a listbox entry with all widths in Twips. */
        void            InsertEntry(const BorderWidthImpl& rWidthImpl,
                            SvxBorderLineStyle nStyle, long nMinWidth = 0,
                            ColorFunc pColor1Fn = &sameColor,
                            ColorFunc pColor2Fn = &sameColor,
                            ColorDistFunc pColorDistFn = &sameDistColor);

        SvxBorderLineStyle GetEntryStyle( sal_Int32 nPos ) const;

        SvxBorderLineStyle GetSelectEntryStyle() const;

        void            SetSourceUnit( FieldUnit eNewUnit ) { eSourceUnit = eNewUnit; }

        const Color&    GetColor() const { return aColor; }

    private:

        void         ImpGetLine(long nLine1, long nLine2, long nDistance,
                                Color nColor1, Color nColor2, Color nColorDist,
                                SvxBorderLineStyle nStyle, BitmapEx& rBmp);
        using Window::ImplInit;
        void            UpdatePaintLineColor();       // returns sal_True if maPaintCol has changed
        virtual void    DataChanged( const DataChangedEvent& rDCEvt ) override;

        void            UpdateEntries( long nOldWidth );
        sal_Int32       GetStylePos( sal_Int32  nListPos, long nWidth );

        const Color& GetPaintColor() const
        {
            return maPaintCol;
        }

        Color   GetColorLine1( sal_Int32  nPos );
        Color   GetColorLine2( sal_Int32  nPos );
        Color   GetColorDist( sal_Int32  nPos );

                        LineListBox( const LineListBox& ) = delete;
        LineListBox&    operator =( const LineListBox& ) = delete;

        std::vector<std::unique_ptr<ImpLineListData>> m_vLineList;
        long            m_nWidth;
        OUString        m_sNone;
        ScopedVclPtr<VirtualDevice>   aVirDev;
        Size            aTxtSize;
        Color const     aColor;
        Color           maPaintCol;
        FieldUnit       eSourceUnit;
    };

    SvxBorderLineStyle LineListBox::GetSelectEntryStyle() const
    {
        SvxBorderLineStyle nStyle = SvxBorderLineStyle::SOLID;
        sal_Int32 nPos = GetSelectedEntryPos();
        if ( nPos != LISTBOX_ENTRY_NOTFOUND )
        {
            if (!m_sNone.isEmpty())
                nPos--;
            nStyle = GetEntryStyle( nPos );
        }

        return nStyle;
    }

    void LineListBox::ImpGetLine( long nLine1, long nLine2, long nDistance,
                                Color aColor1, Color aColor2, Color aColorDist,
                                SvxBorderLineStyle nStyle, BitmapEx& rBmp )
    {
        //TODO, rather than including the " " text to force
        //the line height, better would be do drop
        //this calculation and draw a bitmap of height
        //equal to normal text line and center the
        //line within that
        long nMinWidth = GetTextWidth("----------");
        Size aSize = CalcSubEditSize();
        aSize.setWidth( std::max(nMinWidth, aSize.Width()) );
        aSize.AdjustWidth( -(aTxtSize.Width()) );
        aSize.AdjustWidth( -6 );
        aSize.setHeight( aTxtSize.Height() );

        // SourceUnit to Twips
        if ( eSourceUnit == FieldUnit::POINT )
        {
            nLine1      /= 5;
            nLine2      /= 5;
            nDistance   /= 5;
        }

        // Paint the lines
        aSize = aVirDev->PixelToLogic( aSize );
        long nPix = aVirDev->PixelToLogic( Size( 0, 1 ) ).Height();
        sal_uInt32 n1 = nLine1;
        sal_uInt32 n2 = nLine2;
        long nDist  = nDistance;
        n1 += nPix-1;
        n1 -= n1%nPix;
        if ( n2 )
        {
            nDist += nPix-1;
            nDist -= nDist%nPix;
            n2    += nPix-1;
            n2    -= n2%nPix;
        }
        long nVirHeight = n1+nDist+n2;
        if ( nVirHeight > aSize.Height() )
            aSize.setHeight( nVirHeight );
        // negative width should not be drawn
        if ( aSize.Width() <= 0 )
            return;

        Size aVirSize = aVirDev->LogicToPixel( aSize );
        if ( aVirDev->GetOutputSizePixel() != aVirSize )
            aVirDev->SetOutputSizePixel( aVirSize );
        aVirDev->SetFillColor( aColorDist );
        aVirDev->DrawRect( tools::Rectangle( Point(), aSize ) );

        aVirDev->SetFillColor( aColor1 );

        double y1 = double( n1 ) / 2;
        svtools::DrawLine( *aVirDev, basegfx::B2DPoint( 0, y1 ), basegfx::B2DPoint( aSize.Width( ), y1 ), n1, nStyle );

        if ( n2 )
        {
            double y2 =  n1 + nDist + double( n2 ) / 2;
            aVirDev->SetFillColor( aColor2 );
            svtools::DrawLine( *aVirDev, basegfx::B2DPoint( 0, y2 ), basegfx::B2DPoint( aSize.Width(), y2 ), n2, SvxBorderLineStyle::SOLID );
        }
        rBmp = aVirDev->GetBitmapEx( Point(), Size( aSize.Width(), n1+nDist+n2 ) );
    }

    LineListBox::LineListBox( vcl::Window* pParent, WinBits nWinStyle ) :
        ListBox( pParent, nWinStyle ),
        m_nWidth( 5 ),
        m_sNone( ),
        aVirDev( VclPtr<VirtualDevice>::Create() ),
        aColor( COL_BLACK ),
        maPaintCol( COL_BLACK )
    {
        aTxtSize.setWidth( GetTextWidth( " " ) );
        aTxtSize.setHeight( GetTextHeight() );
        eSourceUnit = FieldUnit::POINT;

        aVirDev->SetLineColor();
        aVirDev->SetMapMode( MapMode( MapUnit::MapTwip ) );

        UpdatePaintLineColor();
    }

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

    void LineListBox::dispose()
    {
        m_vLineList.clear();
        ListBox::dispose();
    }

    sal_Int32 LineListBox::GetStylePos( sal_Int32 nListPos, long nWidth )
    {
        sal_Int32 nPos = LISTBOX_ENTRY_NOTFOUND;
        if (!m_sNone.isEmpty())
            nListPos--;

        sal_Int32 n = 0;
        size_t i = 0;
        size_t nCount = m_vLineList.size();
        while ( nPos == LISTBOX_ENTRY_NOTFOUND && i < nCount )
        {
            auto& pData = m_vLineList[ i ];
            if ( pData->GetMinWidth() <= nWidth )
            {
                if ( nListPos == n )
                    nPos = static_cast<sal_Int32>(i);
                n++;
            }
            i++;
        }

        return nPos;
    }

    void LineListBox::InsertEntry(
        const BorderWidthImpl& rWidthImpl, SvxBorderLineStyle nStyle, long nMinWidth,
        ColorFunc pColor1Fn, ColorFunc pColor2Fn, ColorDistFunc pColorDistFn )
    {
        m_vLineList.emplace_back(new ImpLineListData(
            rWidthImpl, nStyle, nMinWidth, pColor1Fn, pColor2Fn, pColorDistFn));
    }

    SvxBorderLineStyle LineListBox::GetEntryStyle( sal_Int32 nPos ) const
    {
        ImpLineListData* pData = (0 <= nPos && static_cast<size_t>(nPos) < m_vLineList.size()) ? m_vLineList[ nPos ].get() : nullptr;
        return pData ? pData->GetStyle() : SvxBorderLineStyle::NONE;
    }

    void LineListBox::UpdatePaintLineColor()
    {
        const StyleSettings&    rSettings = GetSettings().GetStyleSettings();
        Color                   aNewCol( rSettings.GetWindowColor().IsDark()? rSettings.GetLabelTextColor() : aColor );

        bool bRet = aNewCol != maPaintCol;

        if( bRet )
            maPaintCol = aNewCol;
    }

    void LineListBox::UpdateEntries( long nOldWidth )
    {
        SetUpdateMode( false );

        UpdatePaintLineColor( );

        sal_Int32      nSelEntry = GetSelectedEntryPos();
        sal_Int32       nTypePos = GetStylePos( nSelEntry, nOldWidth );

        // Remove the old entries
        while ( GetEntryCount( ) > 0 )
            ListBox::RemoveEntry( 0 );

        // Add the new entries based on the defined width
        if (!m_sNone.isEmpty())
            ListBox::InsertEntry( m_sNone );

        sal_uInt16 n = 0;
        sal_uInt16 nCount = m_vLineList.size( );
        while ( n < nCount )
        {
            auto& pData = m_vLineList[ n ];
            if ( pData->GetMinWidth() <= m_nWidth )
            {
                BitmapEx aBmp;
                ImpGetLine( pData->GetLine1ForWidth( m_nWidth ),
                        pData->GetLine2ForWidth( m_nWidth ),
                        pData->GetDistForWidth( m_nWidth ),
                        GetColorLine1( GetEntryCount( ) ),
                        GetColorLine2( GetEntryCount( ) ),
                        GetColorDist( GetEntryCount( ) ),
                        pData->GetStyle(), aBmp );
                ListBox::InsertEntry(" ", Image(aBmp));
                if ( n == nTypePos )
                    SelectEntryPos( GetEntryCount() - 1 );
            }
            else if ( n == nTypePos )
                SetNoSelection();
            n++;
        }

        SetUpdateMode( true );
        Invalidate();
    }

    Color LineListBox::GetColorLine1( sal_Int32 nPos )
    {
        sal_Int32 nStyle = GetStylePos( nPos, m_nWidth );
        if (nStyle == LISTBOX_ENTRY_NOTFOUND)
            return GetPaintColor( );
        auto& pData = m_vLineList[ nStyle ];
        return pData->GetColorLine1( GetColor( ) );
    }

    Color LineListBox::GetColorLine2( sal_Int32 nPos )
    {
        sal_Int32 nStyle = GetStylePos( nPos, m_nWidth );
        if (nStyle == LISTBOX_ENTRY_NOTFOUND)
            return GetPaintColor( );
        auto& pData = m_vLineList[ nStyle ];
        return pData->GetColorLine2( GetColor( ) );
    }

    Color LineListBox::GetColorDist( sal_Int32 nPos )
    {
        Color rResult = GetSettings().GetStyleSettings().GetFieldColor();

        sal_Int32 nStyle = GetStylePos( nPos, m_nWidth );
        if (nStyle == LISTBOX_ENTRY_NOTFOUND)
            return rResult;
        auto& pData = m_vLineList[ nStyle ];
        return pData->GetColorDist( GetColor( ), rResult );
    }

    void LineListBox::DataChanged( const DataChangedEvent& rDCEvt )
    {
        ListBox::DataChanged( rDCEvt );

        if( ( rDCEvt.GetType() == DataChangedEventType::SETTINGS ) && ( rDCEvt.GetFlags() & AllSettingsFlags::STYLE ) )
            UpdateEntries( m_nWidth );
    }
}

class SvxLineWindow_Impl : public svtools::ToolbarPopup
{
private:
    VclPtr<LineListBox> m_aLineStyleLb;
    svt::ToolboxController& m_rController;
    bool                m_bIsWriter;

    DECL_LINK( SelectHdl, ListBox&, void );

protected:
    virtual void    Resize() override;
    virtual void    GetFocus() override;
public:
    SvxLineWindow_Impl( svt::ToolboxController& rController, vcl::Window* pParentWindow );
    virtual ~SvxLineWindow_Impl() override { disposeOnce(); }
    virtual void dispose() override { m_aLineStyleLb.disposeAndClear(); ToolbarPopup::dispose(); }
};

class SvxCurrencyList_Impl : public svtools::ToolbarPopup
{
private:
    VclPtr<ListBox> m_pCurrencyLb;
    rtl::Reference<SvxCurrencyToolBoxControl> m_xControl;
    OUString&       m_rSelectedFormat;
    LanguageType&   m_eSelectedLanguage;

    std::vector<OUString> m_aFormatEntries;
    LanguageType          m_eFormatLanguage;
    DECL_LINK( SelectHdl, ListBox&, void );

public:
    SvxCurrencyList_Impl( SvxCurrencyToolBoxControl* pControl,
                          vcl::Window* pParentWindow,
                          OUString&     rSelectFormat,
                          LanguageType& eSelectLanguage );
    virtual ~SvxCurrencyList_Impl() override { disposeOnce(); }
    virtual void dispose() override;
};

class SvxStyleToolBoxControl;

class SfxStyleControllerItem_Impl : public SfxStatusListener
{
    public:
        SfxStyleControllerItem_Impl( const Reference< XDispatchProvider >& rDispatchProvider,
                                     sal_uInt16 nSlotId,
                                     const OUString& rCommand,
                                     SvxStyleToolBoxControl& rTbxCtl );

    protected:
        virtual void StateChanged( sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState ) override;

    private:
        SvxStyleToolBoxControl& rControl;
};

#define BUTTON_WIDTH 20
#define BUTTON_PADDING 10
#define ITEM_HEIGHT 30

SvxStyleBox_Impl::SvxStyleBox_Impl(vcl::Window* pParent,
                                   const OUString& rCommand,
                                   SfxStyleFamily eFamily,
                                   const Reference< XDispatchProvider >& rDispatchProvider,
                                   const Reference< XFrame >& _xFrame,
                                   const OUString& rClearFormatKey,
                                   const OUString& rMoreKey,
                                   bool bInSpec)
    : ComboBox(pParent, WB_SORT | WB_BORDER | WB_HIDE | WB_DROPDOWN | WB_AUTOHSCROLL)
    , eStyleFamily( eFamily )
    , nCurSel(0)
    , bRelease( true )
    , aLogicalSize(60, 86)
    , bVisible(false)
    , m_xDispatchProvider( rDispatchProvider )
    , m_xFrame(_xFrame)
    , m_aCommand( rCommand )
    , aClearFormatKey( rClearFormatKey )
    , aMoreKey( rMoreKey )
    , bInSpecialMode( bInSpec )
    , m_aBuilder(nullptr, VclBuilderContainer::getUIRootDir(), "svx/ui/stylemenu.ui", "")
    , m_pMenu(m_aBuilder.get_menu("menu"))
{
    SetHelpId(HID_STYLE_LISTBOX);
    m_pMenu->SetSelectHdl( LINK( this, SvxStyleBox_Impl, MenuSelectHdl ) );
    for(VclPtr<MenuButton> & rpButton : m_pButtons)
        rpButton = nullptr;
    SetOptimalSize();
    EnableAutocomplete( true );
    EnableUserDraw( true );
    AddEventListener(LINK(this, SvxStyleBox_Impl, CalcOptimalExtraUserWidth));
    SetUserItemSize( Size( 0, ITEM_HEIGHT ) );
}

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

void SvxStyleBox_Impl::dispose()
{
    RemoveEventListener(LINK(this, SvxStyleBox_Impl, CalcOptimalExtraUserWidth));

    for (VclPtr<MenuButton>& rButton : m_pButtons)
    {
        rButton.disposeAndClear();
    }

    m_pMenu.clear();
    m_aBuilder.disposeBuilder();

    ComboBox::dispose();
}

void SvxStyleBox_Impl::ReleaseFocus()
{
    if ( !bRelease )
    {
        bRelease = true;
        return;
    }
    if ( m_xFrame.is() && m_xFrame->getContainerWindow().is() )
        m_xFrame->getContainerWindow()->setFocus();
}

IMPL_LINK( SvxStyleBox_Impl, MenuSelectHdl, Menu*, pMenu, bool)
{
    OUString sEntry = GetSelectedEntry();
    OString sMenuIdent = pMenu->GetCurItemIdent();
    ReleaseFocus(); // It must be after getting entry pos!
    if (IsInDropDown())
        ToggleDropDown();
    Sequence< PropertyValue > aArgs( 2 );
    aArgs[0].Name   = "Param";
    aArgs[0].Value  <<= sEntry;
    aArgs[1].Name   = "Family";
    aArgs[1].Value  <<= sal_Int16( eStyleFamily );

    if (sMenuIdent == "update")
    {
        SfxToolBoxControl::Dispatch( m_xDispatchProvider,
            ".uno:StyleUpdateByExample", aArgs );
    }
    else if (sMenuIdent == "edit")
    {
        SfxToolBoxControl::Dispatch( m_xDispatchProvider,
            ".uno:EditStyle", aArgs );
    }

    return false;
}

IMPL_STATIC_LINK_NOARG(SvxStyleBox_Impl, ShowMoreHdl, void*, void)
{
    SfxViewFrame* pViewFrm = SfxViewFrame::Current();
    DBG_ASSERT( pViewFrm, "SvxStyleBox_Impl::Select(): no viewframe" );
    if (!pViewFrm)
        return;
    pViewFrm->ShowChildWindow(SID_SIDEBAR);
    ::sfx2::sidebar::Sidebar::ShowPanel("StyleListPanel", pViewFrm->GetFrame().GetFrameInterface());
}

void SvxStyleBox_Impl::Select()
{
    // Tell base class about selection so that AT get informed about it.
    ComboBox::Select();

    if ( IsTravelSelect() )
        return;

    OUString aSearchEntry( GetText() );
    bool bDoIt = true, bClear = false;
    if( bInSpecialMode )
    {
        if( aSearchEntry == aClearFormatKey && GetSelectedEntryPos() == 0 )
        {
            aSearchEntry = sDefaultStyle;
            bClear = true;
            //not only apply default style but also call 'ClearFormatting'
            Sequence< PropertyValue > aEmptyVals;
            SfxToolBoxControl::Dispatch( m_xDispatchProvider, ".uno:ResetAttributes",
                aEmptyVals);
        }
        else if( aSearchEntry == aMoreKey && GetSelectedEntryPos() == ( GetEntryCount() - 1 ) )
        {
            Application::PostUserEvent(LINK(nullptr, SvxStyleBox_Impl, ShowMoreHdl));
            //tdf#113214 change text back to previous entry
            SetText(GetSavedValue());
            bDoIt = false;
        }
    }

    //Do we need to create a new style?
    SfxObjectShell *pShell = SfxObjectShell::Current();
    SfxStyleSheetBasePool* pPool = pShell->GetStyleSheetPool();
    SfxStyleSheetBase* pStyle = nullptr;

    bool bCreateNew = false;

    if ( pPool )
    {
        pPool->SetSearchMask( eStyleFamily );

        pStyle = pPool->First();
        while ( pStyle && pStyle->GetName() != aSearchEntry )
            pStyle = pPool->Next();
    }

    if ( !pStyle )
    {
        // cannot find the style for whatever reason
        // therefore create a new style
        bCreateNew = true;
    }

    /*  #i33380# DR 2004-09-03 Moved the following line above the Dispatch() call.
        This instance may be deleted in the meantime (i.e. when a dialog is opened
        while in Dispatch()), accessing members will crash in this case. */
    ReleaseFocus();

    if( bDoIt )
    {
        if ( bClear )
            SetText( aSearchEntry );
        SaveValue();

        Sequence< PropertyValue > aArgs( 2 );
        aArgs[0].Value  <<= aSearchEntry;
        aArgs[1].Name   = "Family";
        aArgs[1].Value  <<= sal_Int16( eStyleFamily );
        if( bCreateNew )
        {
            aArgs[0].Name   = "Param";
            SfxToolBoxControl::Dispatch( m_xDispatchProvider, ".uno:StyleNewByExample", aArgs);
        }
        else
        {
            aArgs[0].Name   = "Template";
            SfxToolBoxControl::Dispatch( m_xDispatchProvider, m_aCommand, aArgs );
        }
    }
}

void SvxStyleBox_Impl::SetFamily( SfxStyleFamily eNewFamily )
{
    eStyleFamily = eNewFamily;
}

bool SvxStyleBox_Impl::PreNotify( NotifyEvent& rNEvt )
{
    MouseNotifyEvent nType = rNEvt.GetType();

    if ( MouseNotifyEvent::MOUSEBUTTONDOWN == nType || MouseNotifyEvent::GETFOCUS == nType )
        nCurSel = GetSelectedEntryPos();
    else if ( MouseNotifyEvent::LOSEFOCUS == nType )
    {
        // don't handle before our Select() is called
        if (!HasFocus() && !HasChildPathFocus() && !IsChild(rNEvt.GetWindow()))
            SetText( GetSavedValue() );
    }
    return ComboBox::PreNotify( rNEvt );
}

bool SvxStyleBox_Impl::EventNotify( NotifyEvent& rNEvt )
{
    bool bHandled = false;

    if ( rNEvt.GetType() == MouseNotifyEvent::KEYINPUT )
    {
        sal_uInt16 nCode = rNEvt.GetKeyEvent()->GetKeyCode().GetCode();

        switch ( nCode )
        {
            case KEY_CONTEXTMENU:
            {
                if(IsInDropDown())
                {
                    const sal_Int32 nItem = GetSelectedEntryPos() - 1;
                    if(nItem < MAX_STYLES_ENTRIES)
                        m_pButtons[nItem]->ExecuteMenu();
                    bHandled = true;
                }
                break;
            }
            case KEY_RETURN:
            case KEY_TAB:
            {
                if ( KEY_TAB == nCode )
                    bRelease = false;
                else
                    bHandled = true;
                Select();
                break;
            }

            case KEY_ESCAPE:
                SelectEntryPos( nCurSel );
                ReleaseFocus();
                bHandled = true;
                break;
        }
    }
    return bHandled || ComboBox::EventNotify( rNEvt );
}

void SvxStyleBox_Impl::DataChanged( const DataChangedEvent& rDCEvt )
{
    if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
         (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) )
    {
        SetOptimalSize();
    }

    ComboBox::DataChanged( rDCEvt );
}

void SvxStyleBox_Impl::StateChanged( StateChangedType nStateChange )
{
    ComboBox::StateChanged( nStateChange );

    if ( nStateChange == StateChangedType::Visible )
    {
        bVisible = IsReallyVisible();
        aVisibilityListener.Call( *this );
    }
    else if ( nStateChange == StateChangedType::InitShow )
    {
        bVisible = true;
        aVisibilityListener.Call( *this );
    }
}

bool SvxStyleBox_Impl::AdjustFontForItemHeight(OutputDevice* pDevice, tools::Rectangle const & rTextRect, long nHeight)
{
    if (rTextRect.Bottom() > nHeight)
    {
        // the text does not fit, adjust the font size
        double ratio = static_cast< double >( nHeight ) / rTextRect.Bottom();
        vcl::Font aFont(pDevice->GetFont());
        Size aPixelSize(aFont.GetFontSize());
        aPixelSize.setWidth( aPixelSize.Width() * ratio );
        aPixelSize.setHeight( aPixelSize.Height() * ratio );
        aFont.SetFontSize(aPixelSize);
        pDevice->SetFont(aFont);
        return true;
    }
    return false;
}

void SvxStyleBox_Impl::SetOptimalSize()
{
    Size aSize(LogicToPixel(aLogicalSize, MapMode(MapUnit::MapAppFont)));
    set_width_request(aSize.Width());
    set_height_request(aSize.Height());
    SetSizePixel(aSize);
}

void SvxStyleBox_Impl::UserDrawEntry(const UserDrawEvent& rUDEvt, const OUString &rStyleName)
{
    vcl::RenderContext *pDevice = rUDEvt.GetRenderContext();

    // IMG_TXT_DISTANCE in ilstbox.hxx is 6, then 1 is added as
    // nBorder, and we are adding 1 in order to look better when
    // italics is present
    const int nLeftDistance = 8;

    tools::Rectangle aTextRect;
    pDevice->GetTextBoundRect(aTextRect, rStyleName);

    Point aPos( rUDEvt.GetRect().TopLeft() );
    aPos.AdjustX(nLeftDistance );

    if (!AdjustFontForItemHeight(pDevice, aTextRect, rUDEvt.GetRect().GetHeight()))
        aPos.AdjustY(( rUDEvt.GetRect().GetHeight() - aTextRect.Bottom() ) / 2 );

    pDevice->DrawText(aPos, rStyleName);
}

void SvxStyleBox_Impl::SetupEntry(vcl::RenderContext& rRenderContext, vcl::Window* pParent, sal_Int32 nItem, const tools::Rectangle& rRect, const OUString& rStyleName, bool bIsNotSelected)
{
    unsigned int nId = rRect.GetHeight() != 0 ? (rRect.getY() / rRect.GetHeight()) : MAX_STYLES_ENTRIES;
    if (nItem == 0 || nItem == GetEntryCount() - 1)
    {
        if(nId < MAX_STYLES_ENTRIES && m_pButtons[nId])
            m_pButtons[nId]->Hide();
    }
    else
    {
        SfxObjectShell *pShell = SfxObjectShell::Current();
        SfxStyleSheetBasePool* pPool = pShell->GetStyleSheetPool();
        SfxStyleSheetBase* pStyle = nullptr;

        if ( pPool )
        {
            pPool->SetSearchMask( eStyleFamily );

            pStyle = pPool->First();
            while (pStyle && pStyle->GetName() != rStyleName)
                pStyle = pPool->Next();
        }

        if (pStyle )
        {
            std::unique_ptr<const SfxItemSet> const pItemSet(pStyle->GetItemSetForPreview());
            if (!pItemSet) return;

            const SvxFontItem * const pFontItem =
                pItemSet->GetItem<SvxFontItem>(SID_ATTR_CHAR_FONT);
            const SvxFontHeightItem * const pFontHeightItem =
                pItemSet->GetItem<SvxFontHeightItem>(SID_ATTR_CHAR_FONTHEIGHT);

            if ( pFontItem && pFontHeightItem )
            {
                Size aFontSize( 0, pFontHeightItem->GetHeight() );
                Size aPixelSize(rRenderContext.LogicToPixel(aFontSize, MapMode(pShell->GetMapUnit())));

                // setup the font properties
                SvxFont aFont;
                aFont.SetFamilyName(pFontItem->GetFamilyName());
                aFont.SetStyleName(pFontItem->GetStyleName());
                aFont.SetFontSize(aPixelSize);

                const SfxPoolItem *pItem = pItemSet->GetItem( SID_ATTR_CHAR_WEIGHT );
                if ( pItem )
                    aFont.SetWeight( static_cast< const SvxWeightItem* >( pItem )->GetWeight() );

                pItem = pItemSet->GetItem( SID_ATTR_CHAR_POSTURE );
                if ( pItem )
                    aFont.SetItalic( static_cast< const SvxPostureItem* >( pItem )->GetPosture() );

                pItem = pItemSet->GetItem( SID_ATTR_CHAR_CONTOUR );
                if ( pItem )
                    aFont.SetOutline( static_cast< const SvxContourItem* >( pItem )->GetValue() );

                pItem = pItemSet->GetItem( SID_ATTR_CHAR_SHADOWED );
                if ( pItem )
                    aFont.SetShadow( static_cast< const SvxShadowedItem* >( pItem )->GetValue() );

                pItem = pItemSet->GetItem( SID_ATTR_CHAR_RELIEF );
                if ( pItem )
                    aFont.SetRelief( static_cast< const SvxCharReliefItem* >( pItem )->GetValue() );

                pItem = pItemSet->GetItem( SID_ATTR_CHAR_UNDERLINE );
                if ( pItem )
                    aFont.SetUnderline( static_cast< const SvxUnderlineItem* >( pItem )->GetLineStyle() );

                pItem = pItemSet->GetItem( SID_ATTR_CHAR_OVERLINE );
                if ( pItem )
                    aFont.SetOverline( static_cast< const SvxOverlineItem* >( pItem )->GetValue() );

                pItem = pItemSet->GetItem( SID_ATTR_CHAR_STRIKEOUT );
                if ( pItem )
                    aFont.SetStrikeout( static_cast< const SvxCrossedOutItem* >( pItem )->GetStrikeout() );

                pItem = pItemSet->GetItem( SID_ATTR_CHAR_CASEMAP );
                if ( pItem )
                    aFont.SetCaseMap(static_cast<const SvxCaseMapItem*>(pItem)->GetCaseMap());

                pItem = pItemSet->GetItem( SID_ATTR_CHAR_EMPHASISMARK );
                if ( pItem )
                    aFont.SetEmphasisMark( static_cast< const SvxEmphasisMarkItem* >( pItem )->GetEmphasisMark() );

                // setup the device & draw
                vcl::Font aOldFont(rRenderContext.GetFont());

                Color aFontCol = COL_AUTO, aBackCol = COL_AUTO;

                rRenderContext.SetFont(aFont);

                pItem = pItemSet->GetItem( SID_ATTR_CHAR_COLOR );
                // text color, when nothing is selected
                if ( (nullptr != pItem) && bIsNotSelected)
                    aFontCol = static_cast< const SvxColorItem* >( pItem )->GetValue();

                drawing::FillStyle style = drawing::FillStyle_NONE;
                // which kind of Fill style is selected
                pItem = pItemSet->GetItem( XATTR_FILLSTYLE );
                // only when ok and not selected
                if ( (nullptr != pItem) && bIsNotSelected)
                    style = static_cast< const XFillStyleItem* >( pItem )->GetValue();

                switch(style)
                {
                    case drawing::FillStyle_SOLID:
                    {
                        // set background color
                        pItem = pItemSet->GetItem( XATTR_FILLCOLOR );
                        if ( nullptr != pItem )
                            aBackCol = static_cast< const XFillColorItem* >( pItem )->GetColorValue();

                        if ( aBackCol != COL_AUTO )
                        {
                            rRenderContext.SetFillColor(aBackCol);
                            rRenderContext.DrawRect(rRect);
                        }
                    }
                    break;

                    default: break;
                    //TODO Draw the other background styles: gradient, hatching and bitmap
                }

                // when the font and background color are too similar, adjust the Font-Color
                if( (aFontCol != COL_AUTO) || (aBackCol != COL_AUTO) )
                    aFontCol = TestColorsVisible(aFontCol, (aBackCol != COL_AUTO) ? aBackCol : rRenderContext.GetBackground().GetColor());

                // set text color
                if ( aFontCol != COL_AUTO )
                    rRenderContext.SetTextColor(aFontCol);

                // handle the push-button
                if (bIsNotSelected)
                {
                    if (nId < MAX_STYLES_ENTRIES && m_pButtons[nId])
                        m_pButtons[nId]->Hide();
                }
                else
                {
                    if (nId < MAX_STYLES_ENTRIES)
                    {
                        if (!m_pButtons[nId] && pParent)
                        {
                            m_pButtons[nId] = VclPtr<MenuButton>::Create(pParent, WB_FLATBUTTON | WB_NOPOINTERFOCUS);
                            m_pButtons[nId]->SetSizePixel(Size(BUTTON_WIDTH, rRect.GetHeight()));
                            m_pButtons[nId]->SetPopupMenu(m_pMenu);
                        }
                        m_pButtons[nId]->SetPosPixel(Point(rRect.GetWidth() - BUTTON_WIDTH, rRect.getY()));
                        m_pButtons[nId]->Show();
                    }
                }
            }
        }
    }
}

void SvxStyleBox_Impl::UserDraw( const UserDrawEvent& rUDEvt )
{
    sal_uInt16 nItem = rUDEvt.GetItemId();
    OUString aStyleName( GetEntry( nItem ) );

    vcl::RenderContext *pDevice = rUDEvt.GetRenderContext();
    pDevice->Push(PushFlags::FILLCOLOR | PushFlags::FONT | PushFlags::TEXTCOLOR);

    const tools::Rectangle& rRect(rUDEvt.GetRect());
    bool bIsNotSelected = rUDEvt.GetItemId() != GetSelectedEntryPos();

    SetupEntry(*pDevice, rUDEvt.GetWindow(), nItem, rRect, aStyleName, bIsNotSelected);

    UserDrawEntry(rUDEvt, aStyleName);

    pDevice->Pop();
    // draw separator, if present
    DrawEntry( rUDEvt, false, false );
}

IMPL_LINK(SvxStyleBox_Impl, CalcOptimalExtraUserWidth, VclWindowEvent&, event, void)
{
    // perform the calculation only when we are opening the dropdown
    if (event.GetId() != VclEventId::DropdownPreOpen)
        return;

    long nMaxNormalFontWidth = 0;
    sal_Int32 nEntryCount = GetEntryCount();
    for (sal_Int32 i = 0; i < nEntryCount; ++i)
    {
        OUString sStyleName(GetEntry(i));
        tools::Rectangle aTextRectForDefaultFont;
        GetTextBoundRect(aTextRectForDefaultFont, sStyleName);

        const long nWidth = aTextRectForDefaultFont.GetWidth();

        nMaxNormalFontWidth = std::max(nWidth, nMaxNormalFontWidth);
    }

    long nMaxUserDrawFontWidth = nMaxNormalFontWidth;
    for (sal_Int32 i = 1; i < nEntryCount-1; ++i)
    {
        OUString sStyleName(GetEntry(i));

        Push(PushFlags::FILLCOLOR | PushFlags::FONT | PushFlags::TEXTCOLOR);
        SetupEntry(*this /*FIXME rendercontext*/, this, i, tools::Rectangle(0, 0, RECT_MAX, ITEM_HEIGHT), sStyleName, true);
        tools::Rectangle aTextRectForActualFont;
        GetTextBoundRect(aTextRectForActualFont, sStyleName);
        if (AdjustFontForItemHeight(this, aTextRectForActualFont, ITEM_HEIGHT))
        {
            //Font didn't fit, so it was changed, refetch with final font size
            GetTextBoundRect(aTextRectForActualFont, sStyleName);
        }
        Pop();

        const long nWidth = aTextRectForActualFont.GetWidth() + BUTTON_WIDTH + BUTTON_PADDING;

        nMaxUserDrawFontWidth = std::max(nWidth, nMaxUserDrawFontWidth);
    }

    SetUserItemSize(Size(nMaxUserDrawFontWidth - nMaxNormalFontWidth, ITEM_HEIGHT));
}

// test is the color between Font- and background-color to be identify
// return is always the Font-Color
//        when both light or dark, change the Contrast
//        in other case do not change the origin color
//        when the color is R=G=B=128 the DecreaseContrast make 128 the need a exception
Color SvxStyleBox_Impl::TestColorsVisible(const Color &FontCol, const Color &BackCol)
{
    const sal_uInt8  ChgVal = 60;       // increase/decrease the Contrast

    Color  retCol = FontCol;
    if ((FontCol.IsDark() == BackCol.IsDark()) && (FontCol.IsBright() == BackCol.IsBright()))
    {
        sal_uInt8 lumi = retCol.GetLuminance();

        if((lumi > 120) && (lumi < 140))
            retCol.DecreaseLuminance(ChgVal / 2);
        else
            retCol.DecreaseContrast(ChgVal);
    }

    return retCol;
}


static bool lcl_GetDocFontList( const FontList** ppFontList, SvxFontNameBox_Impl* pBox )
{
    bool bChanged = false;
    const SfxObjectShell* pDocSh = SfxObjectShell::Current();
    const SvxFontListItem* pFontListItem = nullptr;

    if ( pDocSh )
        pFontListItem =
            static_cast<const SvxFontListItem*>(pDocSh->GetItem( SID_ATTR_CHAR_FONTLIST ));
    else
    {
        ::std::unique_ptr<FontList> aFontList(new FontList( pBox->GetParent() ));
        *ppFontList = aFontList.get();
        pBox->SetOwnFontList(std::move(aFontList));
        bChanged = true;
    }

    if ( pFontListItem )
    {
        const FontList* pNewFontList = pFontListItem->GetFontList();
        DBG_ASSERT( pNewFontList, "Doc-FontList not available!" );

        // No old list, but a new list
        if ( !*ppFontList && pNewFontList )
        {
            // => take over
            *ppFontList = pNewFontList;
            bChanged = true;
        }
        else
        {
            // Comparing the font lists is not perfect.
            // When you change the font list in the Doc, you can track
            // changes here only on the Listbox, because ppFontList
            // has already been updated.
            bChanged =
                ( ( *ppFontList != pNewFontList ) ||
                  pBox->GetListCount() != pNewFontList->GetFontNameCount() );
            // HACK: Comparing is incomplete

            if ( bChanged )
                *ppFontList = pNewFontList;
        }

        if ( pBox )
            pBox->Enable();
    }
    else if ( pBox && ( pDocSh || !ppFontList ))
    {
        // Disable box only when we have a SfxObjectShell and didn't get a font list OR
        // we don't have a SfxObjectShell and no current font list.
        // It's possible that we currently have no SfxObjectShell, but a current font list.
        // See #i58471: When a user set the focus into the font name combo box and opens
        // the help window with F1. After closing the help window, we disable the font name
        // combo box. The SfxObjectShell::Current() method returns in that case zero. But the
        // font list hasn't changed and therefore the combo box shouldn't be disabled!
        pBox->Disable();
    }

    // Fill the FontBox, also the new list if necessary
    if ( pBox && bChanged )
    {
        if ( *ppFontList )
            pBox->Fill( *ppFontList );
        else
            pBox->Clear();
    }
    return bChanged;
}

SvxFontNameBox_Impl::SvxFontNameBox_Impl( vcl::Window* pParent, const Reference< XDispatchProvider >& rDispatchProvider,const Reference< XFrame >& _xFrame, WinBits nStyle ) :

    FontNameBox        ( pParent, nStyle | WinBits( WB_DROPDOWN | WB_AUTOHSCROLL ) ),
    pFontList          ( nullptr ),
    aLogicalSize       ( 60,160 ),
    nFtCount           ( 0 ),
    bRelease           ( true ),
    m_xDispatchProvider( rDispatchProvider ),
    m_xFrame (_xFrame),
    mbEndPreview(false),
    mbCheckingUnknownFont(false)
{
    SetOptimalSize();
    EnableControls_Impl();
    GetSubEdit()->AddEventListener( LINK( this, SvxFontNameBox_Impl, CheckAndMarkUnknownFont ));
}

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

void SvxFontNameBox_Impl::dispose()
{
    GetSubEdit()->RemoveEventListener( LINK( this, SvxFontNameBox_Impl, CheckAndMarkUnknownFont ));
    FontNameBox::dispose();
}

void SvxFontNameBox_Impl::FillList()
{
    // Save old Selection, set back in the end
    Selection aOldSel = GetSelection();
    // Did Doc-Fontlist change?
    lcl_GetDocFontList( &pFontList, this );
    aCurText = GetText();
    SetSelection( aOldSel );
}

IMPL_LINK( SvxFontNameBox_Impl, CheckAndMarkUnknownFont, VclWindowEvent&, event, void )
{
    if( event.GetId() != VclEventId::EditModify )
        return;
    if (mbCheckingUnknownFont) //tdf#117537 block rentry
        return;
    mbCheckingUnknownFont = true;
    OUString fontname = GetSubEdit()->GetText();
    lcl_GetDocFontList( &pFontList, this );
    // If the font is unknown, show it in italic.
    vcl::Font font = GetControlFont();
    if( pFontList != nullptr && pFontList->IsAvailable( fontname ))
    {
        if( font.GetItalic() != ITALIC_NONE )
        {
            font.SetItalic( ITALIC_NONE );
            SetControlFont( font );
            SetQuickHelpText( SvxResId( RID_SVXSTR_CHARFONTNAME ));
        }
    }
    else
    {
        if( font.GetItalic() != ITALIC_NORMAL )
        {
            font.SetItalic( ITALIC_NORMAL );
            SetControlFont( font );
            SetQuickHelpText( SvxResId( RID_SVXSTR_CHARFONTNAME_NOTAVAILABLE ));
        }
    }
    mbCheckingUnknownFont = false;
}

void SvxFontNameBox_Impl::Update( const css::awt::FontDescriptor* pFontDesc )
{
    if ( pFontDesc )
    {
        aCurFont.SetFamilyName  ( pFontDesc->Name );
        aCurFont.SetFamily      ( FontFamily( pFontDesc->Family ) );
        aCurFont.SetStyleName   ( pFontDesc->StyleName );
        aCurFont.SetPitch       ( FontPitch( pFontDesc->Pitch ) );
        aCurFont.SetCharSet     ( rtl_TextEncoding( pFontDesc->CharSet ) );
    }
    OUString aCurName = aCurFont.GetFamilyName();
    if ( GetText() != aCurName )
        SetText( aCurName );
}

bool SvxFontNameBox_Impl::PreNotify( NotifyEvent& rNEvt )
{
    MouseNotifyEvent nType = rNEvt.GetType();

    if ( MouseNotifyEvent::MOUSEBUTTONDOWN == nType || MouseNotifyEvent::GETFOCUS == nType )
    {
        EnableControls_Impl();
        FillList();
    }
    return FontNameBox::PreNotify( rNEvt );
}

bool SvxFontNameBox_Impl::EventNotify( NotifyEvent& rNEvt )
{
    bool bHandled = false;
    mbEndPreview = false;
    if ( rNEvt.GetType() == MouseNotifyEvent::KEYUP )
        mbEndPreview = true;

    if ( rNEvt.GetType() == MouseNotifyEvent::KEYINPUT )
    {
        sal_uInt16 nCode = rNEvt.GetKeyEvent()->GetKeyCode().GetCode();

        switch ( nCode )
        {
            case KEY_RETURN:
            case KEY_TAB:
            {
                if ( KEY_TAB == nCode )
                    bRelease = false;
                else
                    bHandled = true;
                Select();
                break;
            }

            case KEY_ESCAPE:
                SetText( aCurText );
                ReleaseFocus_Impl();
                EndPreview();
                break;
        }
    }
    else if ( MouseNotifyEvent::LOSEFOCUS == rNEvt.GetType() )
    {
        vcl::Window* pFocusWin = Application::GetFocusWindow();
        if ( !HasFocus() && GetSubEdit() != pFocusWin )
            SetText( GetSavedValue() );
        // send EndPreview
        EndPreview();
    }

    return bHandled || FontNameBox::EventNotify( rNEvt );
}

void SvxFontNameBox_Impl::SetOptimalSize()
{
    Size aSize(LogicToPixel(aLogicalSize, MapMode(MapUnit::MapAppFont)));
    set_width_request(aSize.Width());
    set_height_request(aSize.Height());
    SetSizePixel(aSize);
}

void SvxFontNameBox_Impl::DataChanged( const DataChangedEvent& rDCEvt )
{
    if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
         (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) )
    {
        SetOptimalSize();
    }
    else if ( ( rDCEvt.GetType() == DataChangedEventType::FONTS ) ||
              ( rDCEvt.GetType() == DataChangedEventType::DISPLAY ) )
    {
        // The old font list in shell has likely been destroyed at this point, so we need to get
        // the new one before doing anything further.
        lcl_GetDocFontList( &pFontList, this );
    }

    FontNameBox::DataChanged( rDCEvt );
}

void SvxFontNameBox_Impl::ReleaseFocus_Impl()
{
    if ( !bRelease )
    {
        bRelease = true;
        return;
    }
    if ( m_xFrame.is() && m_xFrame->getContainerWindow().is() )
        m_xFrame->getContainerWindow()->setFocus();
}

void SvxFontNameBox_Impl::EnableControls_Impl()
{
    SvtFontOptions aFontOpt;
    bool bEnable = aFontOpt.IsFontHistoryEnabled();
    sal_uInt16 nEntries = bEnable ? MAX_MRU_FONTNAME_ENTRIES : 0;
    if ( GetMaxMRUCount() != nEntries )
    {
        // refill in the next GetFocus-Handler
        pFontList = nullptr;
        Clear();
        SetMaxMRUCount( nEntries );
    }

    bEnable = aFontOpt.IsFontWYSIWYGEnabled();
    EnableWYSIWYG( bEnable );
}

void SvxFontNameBox_Impl::UserDraw( const UserDrawEvent& rUDEvt )
{
    FontNameBox::UserDraw( rUDEvt );

    // Hack - GetStyle now contains the currently
    // selected item in the list box
    // ItemId contains the id of the current item to draw
    // or select
    if (  rUDEvt.GetItemId() == rUDEvt.GetStyle() )
    {
        OUString fontName(GetText());
        if (IsInDropDown())
        {
            /*
             * when in dropdown mode the selected item should be
             * used and not the current selection
             */
             fontName = GetEntry(rUDEvt.GetItemId());
        }
        Sequence< PropertyValue > aArgs( 1 );
        FontMetric aFontMetric( pFontList->Get( fontName,
            aCurFont.GetWeight(),
            aCurFont.GetItalic() ) );

        SvxFontItem aFontItem( aFontMetric.GetFamilyType(),
            aFontMetric.GetFamilyName(),
            aFontMetric.GetStyleName(),
            aFontMetric.GetPitch(),
            aFontMetric.GetCharSet(),
            SID_ATTR_CHAR_FONT );
        aFontItem.QueryValue( aArgs[0].Value );
        aArgs[0].Name   = "CharPreviewFontName";
        SfxToolBoxControl::Dispatch( m_xDispatchProvider,
            ".uno:CharPreviewFontName",
                aArgs );
    }
}

void SvxFontNameBox_Impl::Select()
{
    FontNameBox::Select();

    Sequence< PropertyValue > aArgs( 1 );
    std::unique_ptr<SvxFontItem> pFontItem;
    if ( pFontList )
    {
        FontMetric aFontMetric( pFontList->Get( GetText(),
            aCurFont.GetWeight(),
            aCurFont.GetItalic() ) );
        aCurFont = aFontMetric;

        pFontItem.reset( new SvxFontItem( aFontMetric.GetFamilyType(),
            aFontMetric.GetFamilyName(),
            aFontMetric.GetStyleName(),
            aFontMetric.GetPitch(),
            aFontMetric.GetCharSet(),
            SID_ATTR_CHAR_FONT ) );

        Any a;
        pFontItem->QueryValue( a );
        aArgs[0].Value  = a;
    }
    if ( !IsTravelSelect() )
    {
        //  #i33380# DR 2004-09-03 Moved the following line above the Dispatch() call.
        //  This instance may be deleted in the meantime (i.e. when a dialog is opened
        //  while in Dispatch()), accessing members will crash in this case.
        ReleaseFocus_Impl();
        EndPreview();
        if (pFontItem)
        {
            aArgs[0].Name   = "CharFontName";
            SfxToolBoxControl::Dispatch( m_xDispatchProvider,
                                         ".uno:CharFontName",
                                         aArgs );
        }
    }
    else
    {
        if ( mbEndPreview )
        {
            EndPreview();
            return;
        }
        if (pFontItem)
        {
            aArgs[0].Name   = "CharPreviewFontName";
            SfxToolBoxControl::Dispatch( m_xDispatchProvider,
                                         ".uno:CharPreviewFontName",
                                         aArgs );
        }
    }
}

SvxColorWindow::SvxColorWindow(const OUString&            rCommand,
                               std::shared_ptr<PaletteManager> const & rPaletteManager,
                               ColorStatus&               rColorStatus,
                               sal_uInt16                 nSlotId,
                               const Reference< XFrame >& rFrame,
                               vcl::Window*               pParentWindow,
                               bool                       bReuseParentForPicker,
                               ColorSelectFunction const & aFunction):

    ToolbarPopup( rFrame, pParentWindow, "palette_popup_window", "svx/ui/oldcolorwindow.ui" ),
    theSlotId( nSlotId ),
    maCommand( rCommand ),
    mxParentWindow(pParentWindow),
    mxPaletteManager( rPaletteManager ),
    mrColorStatus( rColorStatus ),
    maColorSelectFunction(aFunction),
    mbReuseParentForPicker(bReuseParentForPicker)
{
    get(mpPaletteListBox,     "palette_listbox");
    get(mpButtonAutoColor,    "auto_color_button");
    get(mpButtonNoneColor,    "none_color_button");
    get(mpButtonPicker,       "color_picker_button");
    get(mpColorSet,           "colorset");
    get(mpRecentColorSet,     "recent_colorset");
    get(mpAutomaticSeparator, "separator4");

    mpColorSet->SetStyle( WinBits(WB_FLATVALUESET | WB_ITEMBORDER | WB_3DLOOK | WB_NO_DIRECTSELECT | WB_TABSTOP) );
    mpRecentColorSet->SetStyle( WinBits(WB_FLATVALUESET | WB_ITEMBORDER | WB_3DLOOK | WB_NO_DIRECTSELECT | WB_TABSTOP) );

    switch ( theSlotId )
    {
        case SID_ATTR_CHAR_COLOR_BACKGROUND:
        case SID_BACKGROUND_COLOR:
        case SID_ATTR_CHAR_BACK_COLOR:
        case SID_TABLE_CELL_BACKGROUND_COLOR:
        {
            mpButtonAutoColor->SetText( SvxResId( RID_SVXSTR_NOFILL ) );
            break;
        }
        case SID_AUTHOR_COLOR:
        {
            mpButtonAutoColor->SetText( SvxResId( RID_SVXSTR_BY_AUTHOR ) );
            break;
        }
        case SID_BMPMASK_COLOR:
        {
            mpButtonAutoColor->SetText( SvxResId( RID_SVXSTR_TRANSPARENT ) );
            break;
        }
        case SID_ATTR_CHAR_COLOR:
        case SID_ATTR_CHAR_COLOR2:
        case SID_EXTRUSION_3D_COLOR:
        {
            mpButtonAutoColor->SetText(EditResId(RID_SVXSTR_AUTOMATIC));
            break;
        }
        case SID_FM_CTL_PROPERTIES:
        {
            mpButtonAutoColor->SetText( SvxResId( RID_SVXSTR_DEFAULT ) );
            break;
        }
        default:
        {
            mpButtonAutoColor->Hide();
            mpAutomaticSeparator->Hide();
            break;
        }
    }

    mpColorSet->SetAccessibleName( GetText() );

    mpPaletteListBox->SetStyle( mpPaletteListBox->GetStyle() | WB_BORDER );
    mpPaletteListBox->SetSelectHdl( LINK( this, SvxColorWindow, SelectPaletteHdl ) );
    mpPaletteListBox->AdaptDropDownLineCountToMaximum();
    std::vector<OUString> aPaletteList = mxPaletteManager->GetPaletteList();
    for (const auto& rPalette : aPaletteList )
    {
        mpPaletteListBox->InsertEntry( rPalette );
    }
    OUString aPaletteName( officecfg::Office::Common::UserColors::PaletteName::get() );
    mpPaletteListBox->SelectEntry( aPaletteName );
    const sal_Int32 nSelectedEntry(mpPaletteListBox->GetSelectedEntryPos());
    if (nSelectedEntry != LISTBOX_ENTRY_NOTFOUND)
        mxPaletteManager->SetPalette(nSelectedEntry);

    mpButtonAutoColor->SetClickHdl( LINK( this, SvxColorWindow, AutoColorClickHdl ) );
    mpButtonNoneColor->SetClickHdl( LINK( this, SvxColorWindow, AutoColorClickHdl ) );
    mpButtonPicker->SetClickHdl( LINK( this, SvxColorWindow, OpenPickerClickHdl ) );

    mpColorSet->SetSelectHdl( LINK( this, SvxColorWindow, SelectHdl ) );
    mpRecentColorSet->SetSelectHdl( LINK( this, SvxColorWindow, SelectHdl ) );
    SetHelpId( HID_POPUP_COLOR );
    mpColorSet->SetHelpId( HID_POPUP_COLOR_CTRL );

    mxPaletteManager->ReloadColorSet(*mpColorSet);
    const sal_uInt32 nMaxItems(SvxColorValueSet::getMaxRowCount() * SvxColorValueSet::getColumnCount());
    Size aSize = mpColorSet->layoutAllVisible(nMaxItems);
    mpColorSet->set_height_request(aSize.Height());
    mpColorSet->set_width_request(aSize.Width());

    mxPaletteManager->ReloadRecentColorSet(*mpRecentColorSet);
    aSize = mpRecentColorSet->layoutAllVisible(mxPaletteManager->GetRecentColorCount());
    mpRecentColorSet->set_height_request(aSize.Height());
    mpRecentColorSet->set_width_request(aSize.Width());

    AddStatusListener( ".uno:ColorTableState" );
    AddStatusListener( maCommand );
    if ( maCommand == ".uno:FrameLineColor" )
    {
        AddStatusListener( ".uno:BorderTLBR" );
        AddStatusListener( ".uno:BorderBLTR" );
    }
}

ColorWindow::ColorWindow(std::shared_ptr<PaletteManager> const & rPaletteManager,
                         ColorStatus&               rColorStatus,
                         sal_uInt16                 nSlotId,
                         const Reference< XFrame >& rFrame,
                         weld::Window*              pParentWindow,
                         weld::MenuButton*          pMenuButton,
                         bool                       bInterimBuilder,
                         ColorSelectFunction const & aFunction)
    : ToolbarPopupBase(rFrame)
    , m_xBuilder(bInterimBuilder ? Application::CreateInterimBuilder(pMenuButton, "svx/ui/colorwindow.ui")
                                 : Application::CreateBuilder(pMenuButton, "svx/ui/colorwindow.ui"))
    , theSlotId(nSlotId)
    , mpParentWindow(pParentWindow)
    , mpMenuButton(pMenuButton)
    , mxPaletteManager(rPaletteManager)
    , mrColorStatus(rColorStatus)
    , maColorSelectFunction(aFunction)
    , mxColorSet(new ColorValueSet(m_xBuilder->weld_scrolled_window("colorsetwin")))
    , mxRecentColorSet(new ColorValueSet(nullptr))
    , mxTopLevel(m_xBuilder->weld_container("palette_popup_window"))
    , mxPaletteListBox(m_xBuilder->weld_combo_box("palette_listbox"))
    , mxButtonAutoColor(m_xBuilder->weld_button("auto_color_button"))
    , mxButtonNoneColor(m_xBuilder->weld_button("none_color_button"))
    , mxButtonPicker(m_xBuilder->weld_button("color_picker_button"))
    , mxAutomaticSeparator(m_xBuilder->weld_widget("separator4"))
    , mxColorSetWin(new weld::CustomWeld(*m_xBuilder, "colorset", *mxColorSet))
    , mxRecentColorSetWin(new weld::CustomWeld(*m_xBuilder, "recent_colorset", *mxRecentColorSet))
{
    mxColorSet->SetStyle( WinBits(WB_FLATVALUESET | WB_ITEMBORDER | WB_3DLOOK | WB_NO_DIRECTSELECT | WB_TABSTOP) );
    mxRecentColorSet->SetStyle( WinBits(WB_FLATVALUESET | WB_ITEMBORDER | WB_3DLOOK | WB_NO_DIRECTSELECT | WB_TABSTOP) );

    switch ( theSlotId )
    {
        case SID_ATTR_CHAR_COLOR_BACKGROUND:
        case SID_BACKGROUND_COLOR:
        case SID_ATTR_CHAR_BACK_COLOR:
        case SID_TABLE_CELL_BACKGROUND_COLOR:
        {
            mxButtonAutoColor->set_label( SvxResId( RID_SVXSTR_NOFILL ) );
            break;
        }
        case SID_AUTHOR_COLOR:
        {
            mxButtonAutoColor->set_label( SvxResId( RID_SVXSTR_BY_AUTHOR ) );
            break;
        }
        case SID_BMPMASK_COLOR:
        {
            mxButtonAutoColor->set_label( SvxResId( RID_SVXSTR_TRANSPARENT ) );
            break;
        }
        case SID_ATTR_CHAR_COLOR:
        case SID_ATTR_CHAR_COLOR2:
        case SID_EXTRUSION_3D_COLOR:
        {
            mxButtonAutoColor->set_label(EditResId(RID_SVXSTR_AUTOMATIC));
            break;
        }
        case SID_FM_CTL_PROPERTIES:
        {
            mxButtonAutoColor->set_label( SvxResId( RID_SVXSTR_DEFAULT ) );
            break;
        }
        default:
        {
            mxButtonAutoColor->hide();
            mxAutomaticSeparator->hide();
            break;
        }
    }

    mxPaletteListBox->connect_changed(LINK(this, ColorWindow, SelectPaletteHdl));
    std::vector<OUString> aPaletteList = mxPaletteManager->GetPaletteList();
    mxPaletteListBox->freeze();
    for (const auto& rPalette : aPaletteList)
        mxPaletteListBox->append_text(rPalette);
    mxPaletteListBox->thaw();
    OUString aPaletteName( officecfg::Office::Common::UserColors::PaletteName::get() );
    mxPaletteListBox->set_active_text(aPaletteName);
    const int nSelectedEntry(mxPaletteListBox->get_active());
    if (nSelectedEntry != -1)
        mxPaletteManager->SetPalette(nSelectedEntry);

    mxButtonAutoColor->connect_clicked(LINK(this, ColorWindow, AutoColorClickHdl));
    mxButtonNoneColor->connect_clicked(LINK(this, ColorWindow, AutoColorClickHdl));
    mxButtonPicker->connect_clicked(LINK(this, ColorWindow, OpenPickerClickHdl));

    mxColorSet->SetSelectHdl(LINK( this, ColorWindow, SelectHdl));
    mxRecentColorSet->SetSelectHdl(LINK( this, ColorWindow, SelectHdl));
    mxTopLevel->set_help_id(HID_POPUP_COLOR);
    mxTopLevel->connect_focus_in(LINK(this, ColorWindow, FocusHdl));
    mxColorSet->SetHelpId(HID_POPUP_COLOR_CTRL);

    mxPaletteManager->ReloadColorSet(*mxColorSet);
    const sal_uInt32 nMaxItems(SvxColorValueSet::getMaxRowCount() * SvxColorValueSet::getColumnCount());
    Size aSize = mxColorSet->layoutAllVisible(nMaxItems);
    mxColorSet->set_size_request(aSize.Width(), aSize.Height());

    mxPaletteManager->ReloadRecentColorSet(*mxRecentColorSet);
    aSize = mxRecentColorSet->layoutAllVisible(mxPaletteManager->GetRecentColorCount());
    mxRecentColorSet->set_size_request(aSize.Width(), aSize.Height());

    AddStatusListener( ".uno:ColorTableState" );
}

IMPL_LINK_NOARG(ColorWindow, FocusHdl, weld::Widget&, void)
{
    mxColorSet->GrabFocus();
}

void SvxColorWindow::ShowNoneButton()
{
    mpButtonNoneColor->Show();
}

void ColorWindow::ShowNoneButton()
{
    mxButtonNoneColor->show();
}

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

ColorWindow::~ColorWindow()
{
}

void SvxColorWindow::dispose()
{
    mpColorSet.clear();
    mpRecentColorSet.clear();
    mpPaletteListBox.clear();
    mpButtonAutoColor.clear();
    mpButtonNoneColor.clear();
    mpButtonPicker.clear();
    mpAutomaticSeparator.clear();
    mxParentWindow.clear();
    ToolbarPopup::dispose();
}

void SvxColorWindow::KeyInput( const KeyEvent& rKEvt )
{
    mpColorSet->GrabFocus();
    mpColorSet->KeyInput(rKEvt);
}

NamedColor SvxColorWindow::GetSelectEntryColor(ValueSet const * pColorSet)
{
    Color aColor = pColorSet->GetItemColor(pColorSet->GetSelectedItemId());
    OUString sColorName = pColorSet->GetItemText(pColorSet->GetSelectedItemId());
    return std::make_pair(aColor, sColorName);
}

NamedColor ColorWindow::GetSelectEntryColor(SvtValueSet const * pColorSet)
{
    Color aColor = pColorSet->GetItemColor(pColorSet->GetSelectedItemId());
    OUString sColorName = pColorSet->GetItemText(pColorSet->GetSelectedItemId());
    return std::make_pair(aColor, sColorName);
}

namespace
{
    NamedColor GetAutoColor(sal_uInt16 nSlotId)
    {
        Color aColor;
        OUString sColorName;
        switch (nSlotId)
        {
            case SID_ATTR_CHAR_COLOR_BACKGROUND:
            case SID_BACKGROUND_COLOR:
            case SID_ATTR_CHAR_BACK_COLOR:
            case SID_TABLE_CELL_BACKGROUND_COLOR:
                aColor = COL_TRANSPARENT;
                sColorName = SvxResId(RID_SVXSTR_NOFILL);
                break;
            case SID_AUTHOR_COLOR:
                aColor = COL_TRANSPARENT;
                sColorName = SvxResId(RID_SVXSTR_BY_AUTHOR);
                break;
            case SID_BMPMASK_COLOR:
                aColor = COL_TRANSPARENT;
                sColorName = SvxResId(RID_SVXSTR_TRANSPARENT);
                break;
            case SID_FM_CTL_PROPERTIES:
                aColor = COL_TRANSPARENT;
                sColorName = SvxResId(RID_SVXSTR_DEFAULT);
                break;
            case SID_ATTR_CHAR_COLOR:
            case SID_ATTR_CHAR_COLOR2:
            case SID_EXTRUSION_3D_COLOR:
            default:
                aColor = COL_AUTO;
                sColorName = EditResId(RID_SVXSTR_AUTOMATIC);
                break;
        }

        return std::make_pair(aColor, sColorName);
    }

    NamedColor GetNoneColor()
    {
        return std::make_pair(COL_NONE_COLOR, SvxResId(RID_SVXSTR_NONE));
    }
}

NamedColor SvxColorWindow::GetSelectEntryColor() const
{
    if (!mpColorSet->IsNoSelection())
        return GetSelectEntryColor(mpColorSet);
    if (!mpRecentColorSet->IsNoSelection())
        return GetSelectEntryColor(mpRecentColorSet);
    if (mpButtonNoneColor->GetStyle() & WB_DEFBUTTON)
        return GetNoneColor();
    return GetAutoColor();
}

NamedColor ColorWindow::GetSelectEntryColor() const
{
    if (!mxColorSet->IsNoSelection())
        return GetSelectEntryColor(mxColorSet.get());
    if (!mxRecentColorSet->IsNoSelection())
        return GetSelectEntryColor(mxRecentColorSet.get());
    if (mxButtonNoneColor->get_has_default())
        return GetNoneColor();
    return GetAutoColor();
}

IMPL_LINK(SvxColorWindow, SelectHdl, ValueSet*, pColorSet, void)
{
    VclPtr<SvxColorWindow> xThis(this);

    NamedColor aNamedColor = GetSelectEntryColor(pColorSet);

    if ( pColorSet != mpRecentColorSet )
    {
         mxPaletteManager->AddRecentColor(aNamedColor.first, aNamedColor.second);
         if ( !IsInPopupMode() )
            mxPaletteManager->ReloadRecentColorSet(*mpRecentColorSet);
    }

    if ( IsInPopupMode() )
        EndPopupMode();

    maSelectedLink.Call(aNamedColor);

    maColorSelectFunction(maCommand, aNamedColor);
}

IMPL_LINK(ColorWindow, SelectHdl, SvtValueSet*, pColorSet, void)
{
    NamedColor aNamedColor = GetSelectEntryColor(pColorSet);

    if (pColorSet != mxRecentColorSet.get())
    {
         mxPaletteManager->AddRecentColor(aNamedColor.first, aNamedColor.second);
         if (!mpMenuButton->get_active())
            mxPaletteManager->ReloadRecentColorSet(*mxRecentColorSet);
    }

    if (mpMenuButton->get_active())
        mpMenuButton->set_active(false);

    maColorSelectFunction(OUString(), aNamedColor);
}

IMPL_LINK_NOARG(SvxColorWindow, SelectPaletteHdl, ListBox&, void)
{
    sal_Int32 nPos = mpPaletteListBox->GetSelectedEntryPos();
    mxPaletteManager->SetPalette( nPos );
    mxPaletteManager->ReloadColorSet(*mpColorSet);
    mpColorSet->layoutToGivenHeight(mpColorSet->GetSizePixel().Height(), mxPaletteManager->GetColorCount());
}

IMPL_LINK_NOARG(ColorWindow, SelectPaletteHdl, weld::ComboBox&, void)
{
    int nPos = mxPaletteListBox->get_active();
    mxPaletteManager->SetPalette( nPos );
    mxPaletteManager->ReloadColorSet(*mxColorSet);
    mxColorSet->layoutToGivenHeight(mxColorSet->GetOutputSizePixel().Height(), mxPaletteManager->GetColorCount());
}

NamedColor SvxColorWindow::GetAutoColor() const
{
    return ::GetAutoColor(theSlotId);
}

NamedColor ColorWindow::GetAutoColor() const
{
    return ::GetAutoColor(theSlotId);
}

IMPL_LINK(SvxColorWindow, AutoColorClickHdl, Button*, pButton, void)
{
    VclPtr<SvxColorWindow> xThis(this);

    NamedColor aNamedColor = pButton == mpButtonAutoColor ? GetAutoColor() : GetNoneColor();

    mpRecentColorSet->SetNoSelection();

    if ( IsInPopupMode() )
        EndPopupMode();

    maSelectedLink.Call(aNamedColor);

    maColorSelectFunction(maCommand, aNamedColor);
}

IMPL_LINK(ColorWindow, AutoColorClickHdl, weld::Button&, rButton, void)
{
    NamedColor aNamedColor = &rButton == mxButtonAutoColor.get() ? GetAutoColor() : GetNoneColor();

    mxRecentColorSet->SetNoSelection();

    if (mpMenuButton->get_active())
        mpMenuButton->set_active(false);

    maColorSelectFunction(OUString(), aNamedColor);
}

IMPL_LINK_NOARG(SvxColorWindow, OpenPickerClickHdl, Button*, void)
{
    VclPtr<SvxColorWindow> xThis(this);

    if ( IsInPopupMode() )
        EndPopupMode();

    weld::Window* pParentFrame;
    if (mbReuseParentForPicker)
    {
        pParentFrame = mxParentWindow->GetFrameWeld();
    }
    else
    {
        const css::uno::Reference<css::awt::XWindow> xParent(mxFrame->getContainerWindow(), uno::UNO_QUERY);
        pParentFrame = Application::GetFrameWeld(xParent);
    }
    mxPaletteManager->PopupColorPicker(pParentFrame, maCommand, GetSelectEntryColor().first);
}

IMPL_LINK_NOARG(ColorWindow, OpenPickerClickHdl, weld::Button&, void)
{
    if (mpMenuButton->get_active())
        mpMenuButton->set_active(false);
    mxPaletteManager->PopupColorPicker(mpParentWindow, OUString(), GetSelectEntryColor().first);
}

void SvxColorWindow::StartSelection()
{
    mpColorSet->StartSelection();
    mpRecentColorSet->StartSelection();
}

void SvxColorWindow::SetNoSelection()
{
    mpColorSet->SetNoSelection();
    mpRecentColorSet->SetNoSelection();
    mpButtonAutoColor->set_property("has-default", "false");
    mpButtonNoneColor->set_property("has-default", "false");
}

void ColorWindow::SetNoSelection()
{
    mxColorSet->SetNoSelection();
    mxRecentColorSet->SetNoSelection();
    mxButtonAutoColor->set_has_default(false);
    mxButtonNoneColor->set_has_default(false);
}

bool SvxColorWindow::IsNoSelection() const
{
    if (!mpColorSet->IsNoSelection())
        return false;
    if (!mpRecentColorSet->IsNoSelection())
        return false;
    return !mpButtonAutoColor->IsVisible() && !mpButtonNoneColor->IsVisible();
}

bool ColorWindow::IsNoSelection() const
{
    if (!mxColorSet->IsNoSelection())
        return false;
    if (!mxRecentColorSet->IsNoSelection())
        return false;
    return !mxButtonAutoColor->get_visible() && !mxButtonNoneColor->get_visible();
}

void SvxColorWindow::statusChanged( const css::frame::FeatureStateEvent& rEvent )
{
    if (rEvent.FeatureURL.Complete == ".uno:ColorTableState")
    {
        if (rEvent.IsEnabled && mxPaletteManager->GetPalette() == 0)
        {
            mxPaletteManager->ReloadColorSet(*mpColorSet);
            mpColorSet->layoutToGivenHeight(mpColorSet->GetSizePixel().Height(), mxPaletteManager->GetColorCount());
        }
    }
    else
    {
        mrColorStatus.statusChanged(rEvent);
        SelectEntry(mrColorStatus.GetColor());
    }
}

void ColorWindow::statusChanged( const css::frame::FeatureStateEvent& rEvent )
{
    if (rEvent.FeatureURL.Complete == ".uno:ColorTableState")
    {
        if (rEvent.IsEnabled && mxPaletteManager->GetPalette() == 0)
        {
            mxPaletteManager->ReloadColorSet(*mxColorSet);
            mxColorSet->layoutToGivenHeight(mxColorSet->GetOutputSizePixel().Height(), mxPaletteManager->GetColorCount());
        }
    }
    else
    {
        mrColorStatus.statusChanged(rEvent);
        SelectEntry(mrColorStatus.GetColor());
    }
}

bool SvxColorWindow::SelectValueSetEntry(SvxColorValueSet* pColorSet, const Color& rColor)
{
    for (size_t i = 1; i <= pColorSet->GetItemCount(); ++i)
    {
        if (rColor == pColorSet->GetItemColor(i))
        {
            pColorSet->SelectItem(i);
            return true;
        }
    }
    return false;
}

bool ColorWindow::SelectValueSetEntry(ColorValueSet* pColorSet, const Color& rColor)
{
    for (size_t i = 1; i <= pColorSet->GetItemCount(); ++i)
    {
        if (rColor == pColorSet->GetItemColor(i))
        {
            pColorSet->SelectItem(i);
            return true;
        }
    }
    return false;
}

void SvxColorWindow::SelectEntry(const NamedColor& rNamedColor)
{
    SetNoSelection();

    const Color &rColor = rNamedColor.first;

    if (rColor == COL_TRANSPARENT || rColor == COL_AUTO)
    {
        mpButtonAutoColor->set_property("has-default", "true");
        return;
    }

    if (mpButtonNoneColor->IsVisible() && rColor == COL_NONE_COLOR)
    {
        mpButtonNoneColor->set_property("has-default", "true");
        return;
    }

    // try current palette
    bool bFoundColor = SelectValueSetEntry(mpColorSet, rColor);
    // try recently used
    if (!bFoundColor)
        bFoundColor = SelectValueSetEntry(mpRecentColorSet, rColor);
    // if it's not there, add it there now to the end of the recently used
    // so its available somewhere handy, but not without trashing the
    // whole recently used
    if (!bFoundColor)
    {
        const OUString& rColorName = rNamedColor.second;
        mxPaletteManager->AddRecentColor(rColor, rColorName, false);
        mxPaletteManager->ReloadRecentColorSet(*mpRecentColorSet);
        SelectValueSetEntry(mpRecentColorSet, rColor);
    }
}

void SvxColorWindow::SelectEntry(const Color& rColor)
{
    OUString sColorName = "#" + rColor.AsRGBHexString().toAsciiUpperCase();
    SvxColorWindow::SelectEntry(std::make_pair(rColor, sColorName));
}

void ColorWindow::SelectEntry(const NamedColor& rNamedColor)
{
    SetNoSelection();

    const Color &rColor = rNamedColor.first;

    if (mxButtonNoneColor->get_visible() && (rColor == COL_TRANSPARENT || rColor == COL_AUTO))
    {
        mxButtonAutoColor->set_has_default(true);
        return;
    }

    if (mxButtonNoneColor->get_visible() && rColor == COL_NONE_COLOR)
    {
        mxButtonNoneColor->set_has_default(true);
        return;
    }

    // try current palette
    bool bFoundColor = SelectValueSetEntry(mxColorSet.get(), rColor);
    // try recently used
    if (!bFoundColor)
        bFoundColor = SelectValueSetEntry(mxRecentColorSet.get(), rColor);
    // if it's not there, add it there now to the end of the recently used
    // so its available somewhere handy, but not without trashing the
    // whole recently used
    if (!bFoundColor)
    {
        const OUString& rColorName = rNamedColor.second;
        mxPaletteManager->AddRecentColor(rColor, rColorName, false);
        mxPaletteManager->ReloadRecentColorSet(*mxRecentColorSet);
        SelectValueSetEntry(mxRecentColorSet.get(), rColor);
    }
}

void ColorWindow::SelectEntry(const Color& rColor)
{
    OUString sColorName = "#" + rColor.AsRGBHexString().toAsciiUpperCase();
    ColorWindow::SelectEntry(std::make_pair(rColor, sColorName));
}

ColorStatus::ColorStatus() :
    maColor( COL_TRANSPARENT ),
    maTLBRColor( COL_TRANSPARENT ),
    maBLTRColor( COL_TRANSPARENT )
{
}

ColorStatus::~ColorStatus()
{
}

void ColorStatus::statusChanged( const css::frame::FeatureStateEvent& rEvent )
{
    Color aColor( COL_TRANSPARENT );
    css::table::BorderLine2 aTable;

    if ( rEvent.State >>= aTable )
    {
        SvxBorderLine aLine;
        SvxBoxItem::LineToSvxLine( aTable, aLine, false );
        if ( !aLine.isEmpty() )
            aColor = aLine.GetColor();
    }
    else
        rEvent.State >>= aColor;

    if ( rEvent.FeatureURL.Path == "BorderTLBR" )
        maTLBRColor = aColor;
    else if ( rEvent.FeatureURL.Path == "BorderBLTR" )
        maBLTRColor = aColor;
    else
        maColor = aColor;
}

Color ColorStatus::GetColor()
{
    Color aColor( maColor );

    if ( maTLBRColor != COL_TRANSPARENT )
    {
        if ( aColor != maTLBRColor && aColor != COL_TRANSPARENT )
            return COL_TRANSPARENT;
        aColor = maTLBRColor;
    }

    if ( maBLTRColor != COL_TRANSPARENT )
    {
        if ( aColor != maBLTRColor && aColor != COL_TRANSPARENT )
            return COL_TRANSPARENT;
        return maBLTRColor;
    }

    return aColor;
}


SvxFrameWindow_Impl::SvxFrameWindow_Impl ( svt::ToolboxController& rController, vcl::Window* pParentWindow ) :
    ToolbarPopup( rController.getFrameInterface(), pParentWindow, WB_STDPOPUP | WB_MOVEABLE | WB_CLOSEABLE ),
    aFrameSet   ( VclPtr<SvxFrmValueSet_Impl>::Create(this, WinBits( WB_ITEMBORDER | WB_DOUBLEBORDER | WB_3DLOOK | WB_NO_DIRECTSELECT )) ),
    mrController( rController ),
    bParagraphMode(false)
{
    AddStatusListener(".uno:BorderReducedMode");
    InitImageList();

    /*
     *  1       2        3         4
     *  -------------------------------------
     *  NONE    LEFT     RIGHT     LEFTRIGHT
     *  TOP     BOTTOM   TOPBOTTOM OUTER
     *  -------------------------------------
     *  HOR     HORINNER VERINNER   ALL         <- can be switched of via bParagraphMode
     */

    sal_uInt16 i = 0;

    for ( i=1; i<9; i++ )
        aFrameSet->InsertItem(i, Image(aImgVec[i-1]));

    //bParagraphMode should have been set in StateChanged
    if ( !bParagraphMode )
        for ( i = 9; i < 13; i++ )
            aFrameSet->InsertItem(i, Image(aImgVec[i-1]));

    aFrameSet->SetColCount( 4 );
    aFrameSet->SetSelectHdl( LINK( this, SvxFrameWindow_Impl, SelectHdl ) );
    CalcSizeValueSet();

    SetHelpId( HID_POPUP_FRAME );
    SetText( SvxResId(RID_SVXSTR_FRAME) );
    aFrameSet->SetAccessibleName( SvxResId(RID_SVXSTR_FRAME) );
    aFrameSet->Show();
}

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

void SvxFrameWindow_Impl::dispose()
{
    aFrameSet.disposeAndClear();
    ToolbarPopup::dispose();
}

void SvxFrameWindow_Impl::GetFocus()
{
    if (aFrameSet)
        aFrameSet->StartSelection();
}

void SvxFrameWindow_Impl::KeyInput( const KeyEvent& rKEvt )
{
    aFrameSet->GrabFocus();
    aFrameSet->KeyInput( rKEvt );
}

void SvxFrameWindow_Impl::DataChanged( const DataChangedEvent& rDCEvt )
{
    ToolbarPopup::DataChanged( rDCEvt );

    if ( ( rDCEvt.GetType() == DataChangedEventType::SETTINGS ) && ( rDCEvt.GetFlags() & AllSettingsFlags::STYLE ) )
    {
        InitImageList();

        sal_uInt16 nNumOfItems = aFrameSet->GetItemCount();
        for ( sal_uInt16 i = 1 ; i <= nNumOfItems ; ++i )
            aFrameSet->SetItemImage( i, Image(aImgVec[i-1]) );
    }
}

enum class FrmValidFlags {
    NONE      = 0x00,
    Left      = 0x01,
    Right     = 0x02,
    Top       = 0x04,
    Bottom    = 0x08,
    HInner    = 0x10,
    VInner    = 0x20,
    AllMask   = 0x3f,
};
namespace o3tl {
    template<> struct typed_flags<FrmValidFlags> : is_typed_flags<FrmValidFlags, 0x3f> {};
}

// By default unset lines remain unchanged.
// Via Shift unset lines are reset

IMPL_LINK_NOARG(SvxFrameWindow_Impl, SelectHdl, ValueSet*, void)
{
    VclPtr<SvxFrameWindow_Impl> xThis(this);

    SvxBoxItem          aBorderOuter( SID_ATTR_BORDER_OUTER );
    SvxBoxInfoItem      aBorderInner( SID_ATTR_BORDER_INNER );
    SvxBorderLine       theDefLine;
    SvxBorderLine       *pLeft = nullptr,
                        *pRight = nullptr,
                        *pTop = nullptr,
                        *pBottom = nullptr;
    sal_uInt16           nSel = aFrameSet->GetSelectedItemId();
    sal_uInt16           nModifier = aFrameSet->GetModifier();
    FrmValidFlags        nValidFlags = FrmValidFlags::NONE;

    theDefLine.GuessLinesWidths(theDefLine.GetBorderLineStyle(),
            DEF_LINE_WIDTH_0);
    switch ( nSel )
    {
        case 1: nValidFlags |= FrmValidFlags::AllMask;
        break;  // NONE
        case 2: pLeft = &theDefLine;
                nValidFlags |= FrmValidFlags::Left;
        break;  // LEFT
        case 3: pRight = &theDefLine;
                nValidFlags |= FrmValidFlags::Right;
        break;  // RIGHT
        case 4: pLeft = pRight = &theDefLine;
                nValidFlags |=  FrmValidFlags::Right|FrmValidFlags::Left;
        break;  // LEFTRIGHT
        case 5: pTop = &theDefLine;
                nValidFlags |= FrmValidFlags::Top;
        break;  // TOP
        case 6: pBottom = &theDefLine;
                nValidFlags |= FrmValidFlags::Bottom;
        break;  // BOTTOM
        case 7: pTop =  pBottom = &theDefLine;
                nValidFlags |= FrmValidFlags::Bottom|FrmValidFlags::Top;
        break;  // TOPBOTTOM
        case 8: pLeft = pRight = pTop = pBottom = &theDefLine;
                nValidFlags |= FrmValidFlags::Left | FrmValidFlags::Right | FrmValidFlags::Top | FrmValidFlags::Bottom;
        break;  // OUTER

        // Inner Table:
        case 9: // HOR
            pTop = pBottom = &theDefLine;
            aBorderInner.SetLine( &theDefLine, SvxBoxInfoItemLine::HORI );
            aBorderInner.SetLine( nullptr, SvxBoxInfoItemLine::VERT );
            nValidFlags |= FrmValidFlags::HInner|FrmValidFlags::Top|FrmValidFlags::Bottom;
            break;

        case 10: // HORINNER
            pLeft = pRight = pTop = pBottom = &theDefLine;
            aBorderInner.SetLine( &theDefLine, SvxBoxInfoItemLine::HORI );
            aBorderInner.SetLine( nullptr, SvxBoxInfoItemLine::VERT );
            nValidFlags |= FrmValidFlags::Right|FrmValidFlags::Left|FrmValidFlags::HInner|FrmValidFlags::Top|FrmValidFlags::Bottom;
            break;

        case 11: // VERINNER
            pLeft = pRight = pTop = pBottom = &theDefLine;
            aBorderInner.SetLine( nullptr, SvxBoxInfoItemLine::HORI );
            aBorderInner.SetLine( &theDefLine, SvxBoxInfoItemLine::VERT );
            nValidFlags |= FrmValidFlags::Right|FrmValidFlags::Left|FrmValidFlags::VInner|FrmValidFlags::Top|FrmValidFlags::Bottom;
        break;

        case 12: // ALL
            pLeft = pRight = pTop = pBottom = &theDefLine;
            aBorderInner.SetLine( &theDefLine, SvxBoxInfoItemLine::HORI );
            aBorderInner.SetLine( &theDefLine, SvxBoxInfoItemLine::VERT );
            nValidFlags |= FrmValidFlags::AllMask;
            break;

        default:
        break;
    }
    aBorderOuter.SetLine( pLeft, SvxBoxItemLine::LEFT );
    aBorderOuter.SetLine( pRight, SvxBoxItemLine::RIGHT );
    aBorderOuter.SetLine( pTop, SvxBoxItemLine::TOP );
    aBorderOuter.SetLine( pBottom, SvxBoxItemLine::BOTTOM );

    if(nModifier == KEY_SHIFT)
        nValidFlags |= FrmValidFlags::AllMask;
    aBorderInner.SetValid( SvxBoxInfoItemValidFlags::TOP,       bool(nValidFlags&FrmValidFlags::Top ));
    aBorderInner.SetValid( SvxBoxInfoItemValidFlags::BOTTOM,    bool(nValidFlags&FrmValidFlags::Bottom ));
    aBorderInner.SetValid( SvxBoxInfoItemValidFlags::LEFT,      bool(nValidFlags&FrmValidFlags::Left));
    aBorderInner.SetValid( SvxBoxInfoItemValidFlags::RIGHT,     bool(nValidFlags&FrmValidFlags::Right ));
    aBorderInner.SetValid( SvxBoxInfoItemValidFlags::HORI,      bool(nValidFlags&FrmValidFlags::HInner ));
    aBorderInner.SetValid( SvxBoxInfoItemValidFlags::VERT,      bool(nValidFlags&FrmValidFlags::VInner));
    aBorderInner.SetValid( SvxBoxInfoItemValidFlags::DISTANCE );
    aBorderInner.SetValid( SvxBoxInfoItemValidFlags::DISABLE,   false );

    if ( IsInPopupMode() )
        EndPopupMode();

    Any a;
    Sequence< PropertyValue > aArgs( 2 );
    aArgs[0].Name = "OuterBorder";
    aBorderOuter.QueryValue( a );
    aArgs[0].Value = a;
    aArgs[1].Name = "InnerBorder";
    aBorderInner.QueryValue( a );
    aArgs[1].Value = a;

    if (aFrameSet)
    {
        /* #i33380# Moved the following line above the Dispatch() call.
           This instance may be deleted in the meantime (i.e. when a dialog is opened
           while in Dispatch()), accessing members will crash in this case. */
        aFrameSet->SetNoSelection();
    }

    mrController.dispatchCommand( ".uno:SetBorderStyle", aArgs );
}

void SvxFrameWindow_Impl::statusChanged( const css::frame::FeatureStateEvent& rEvent )
{
    if ( rEvent.FeatureURL.Complete == ".uno:BorderReducedMode" )
    {
        bool bValue;
        if ( rEvent.State >>= bValue )
        {
            bParagraphMode = bValue;
            //initial calls mustn't insert or remove elements
            if(aFrameSet->GetItemCount())
            {
                bool bTableMode = ( aFrameSet->GetItemCount() == 12 );
                bool bResize    = false;

                if ( bTableMode && bParagraphMode )
                {
                    for ( sal_uInt16 i = 9; i < 13; i++ )
                        aFrameSet->RemoveItem(i);
                    bResize = true;
                }
                else if ( !bTableMode && !bParagraphMode )
                {
                    for ( sal_uInt16 i = 9; i < 13; i++ )
                        aFrameSet->InsertItem(i, Image(aImgVec[i-1]));
                    bResize = true;
                }

                if ( bResize )
                {
                    CalcSizeValueSet();
                }
            }
        }
    }
}

void SvxFrameWindow_Impl::CalcSizeValueSet()
{
    Size aItemSize( 20 * GetParent()->GetDPIScaleFactor(), 20 * GetParent()->GetDPIScaleFactor() );
    Size aSize = aFrameSet->CalcWindowSizePixel( aItemSize );
    aFrameSet->SetPosSizePixel( Point( 2, 2 ), aSize );
    aSize.AdjustWidth(4 );
    aSize.AdjustHeight(4 );
    SetOutputSizePixel( aSize );
}

void SvxFrameWindow_Impl::InitImageList()
{
    aImgVec.clear();
    aImgVec.emplace_back(RID_SVXBMP_FRAME1);
    aImgVec.emplace_back(RID_SVXBMP_FRAME2);
    aImgVec.emplace_back(RID_SVXBMP_FRAME3);
    aImgVec.emplace_back(RID_SVXBMP_FRAME4);
    aImgVec.emplace_back(RID_SVXBMP_FRAME5);
    aImgVec.emplace_back(RID_SVXBMP_FRAME6);
    aImgVec.emplace_back(RID_SVXBMP_FRAME7);
    aImgVec.emplace_back(RID_SVXBMP_FRAME8);
    aImgVec.emplace_back(RID_SVXBMP_FRAME9);
    aImgVec.emplace_back(RID_SVXBMP_FRAME10);
    aImgVec.emplace_back(RID_SVXBMP_FRAME11);
    aImgVec.emplace_back(RID_SVXBMP_FRAME12);
}

static Color lcl_mediumColor( Color aMain, Color /*aDefault*/ )
{
    return SvxBorderLine::threeDMediumColor( aMain );
}

SvxCurrencyList_Impl::SvxCurrencyList_Impl(
    SvxCurrencyToolBoxControl* pControl,
    vcl::Window* pParentWindow,
    OUString& rSelectedFormat,
    LanguageType& eSelectedLanguage ) :
    ToolbarPopup( pControl->getFrameInterface(), pParentWindow, WB_STDPOPUP | WB_MOVEABLE | WB_CLOSEABLE ),
    m_pCurrencyLb( VclPtr<ListBox>::Create(this) ),
    m_xControl( pControl ),
    m_rSelectedFormat( rSelectedFormat ),
    m_eSelectedLanguage( eSelectedLanguage )
{
    m_pCurrencyLb->setPosSizePixel( 2, 2, 300, 140 );
    SetOutputSizePixel( Size( 304, 144 ) );

    std::vector< OUString > aList;
    std::vector< sal_uInt16 > aCurrencyList;
    const NfCurrencyTable& rCurrencyTable = SvNumberFormatter::GetTheCurrencyTable();
    sal_uInt16 nLen = rCurrencyTable.size();

    SvNumberFormatter aFormatter( m_xControl->getContext(), LANGUAGE_SYSTEM );
    m_eFormatLanguage = aFormatter.GetLanguage();

    SvxCurrencyToolBoxControl::GetCurrencySymbols( aList, true, aCurrencyList );

    sal_uInt16 nPos = 0, nCount = 0;
    sal_Int32 nSelectedPos = -1;
    bool bIsSymbol;
    NfWSStringsDtor aStringsDtor;

    for( const auto& rItem : aList )
    {
        sal_uInt16& rCurrencyIndex = aCurrencyList[ nCount ];
        if ( rCurrencyIndex < nLen )
        {
            m_pCurrencyLb->InsertEntry( rItem );
            const NfCurrencyEntry& aCurrencyEntry = rCurrencyTable[ rCurrencyIndex ];

            bIsSymbol = nPos >= nLen;

            sal_uInt16 nDefaultFormat = aFormatter.GetCurrencyFormatStrings( aStringsDtor, aCurrencyEntry, bIsSymbol );
            const OUString& rFormatStr = aStringsDtor[ nDefaultFormat ];
            m_aFormatEntries.push_back( rFormatStr );
            if( rFormatStr == m_rSelectedFormat )
                nSelectedPos = nPos;
            ++nPos;
        }
        ++nCount;
    }
    m_pCurrencyLb->SetSelectHdl( LINK( this, SvxCurrencyList_Impl, SelectHdl ) );
    SetText( SvxResId( RID_SVXSTR_TBLAFMT_CURRENCY ) );
    if ( nSelectedPos >= 0 )
        m_pCurrencyLb->SelectEntryPos( nSelectedPos );
    m_pCurrencyLb->Show();
}

void SvxCurrencyList_Impl::dispose()
{
    m_xControl.clear();
    m_pCurrencyLb.disposeAndClear();
    ToolbarPopup::dispose();
}

SvxLineWindow_Impl::SvxLineWindow_Impl( svt::ToolboxController& rController, vcl::Window* pParentWindow ) :
    ToolbarPopup( rController.getFrameInterface(), pParentWindow, WB_STDPOPUP | WB_MOVEABLE | WB_CLOSEABLE ),
    m_aLineStyleLb( VclPtr<LineListBox>::Create(this) ),
    m_rController( rController )
{
    try
    {
        Reference< lang::XServiceInfo > xServices( rController.getFrameInterface()->getController()->getModel(), UNO_QUERY_THROW );
        m_bIsWriter = xServices->supportsService("com.sun.star.text.TextDocument");
    }
    catch(const uno::Exception& )
    {
    }

    m_aLineStyleLb->setPosSizePixel( 2, 2, 110, 140 );
    SetOutputSizePixel( Size( 114, 144 ) );

    m_aLineStyleLb->SetSourceUnit( FieldUnit::TWIP );
    m_aLineStyleLb->SetNone( SvxResId(RID_SVXSTR_NONE) );

    m_aLineStyleLb->InsertEntry( SvxBorderLine::getWidthImpl( SvxBorderLineStyle::SOLID ), SvxBorderLineStyle::SOLID );
    m_aLineStyleLb->InsertEntry( SvxBorderLine::getWidthImpl( SvxBorderLineStyle::DOTTED ), SvxBorderLineStyle::DOTTED );
    m_aLineStyleLb->InsertEntry( SvxBorderLine::getWidthImpl( SvxBorderLineStyle::DASHED ), SvxBorderLineStyle::DASHED );

    // Double lines
    m_aLineStyleLb->InsertEntry( SvxBorderLine::getWidthImpl( SvxBorderLineStyle::DOUBLE ), SvxBorderLineStyle::DOUBLE );
    m_aLineStyleLb->InsertEntry( SvxBorderLine::getWidthImpl( SvxBorderLineStyle::THINTHICK_SMALLGAP ), SvxBorderLineStyle::THINTHICK_SMALLGAP, 20 );
    m_aLineStyleLb->InsertEntry( SvxBorderLine::getWidthImpl( SvxBorderLineStyle::THINTHICK_MEDIUMGAP ), SvxBorderLineStyle::THINTHICK_MEDIUMGAP );
    m_aLineStyleLb->InsertEntry( SvxBorderLine::getWidthImpl( SvxBorderLineStyle::THINTHICK_LARGEGAP ), SvxBorderLineStyle::THINTHICK_LARGEGAP );
    m_aLineStyleLb->InsertEntry( SvxBorderLine::getWidthImpl( SvxBorderLineStyle::THICKTHIN_SMALLGAP ), SvxBorderLineStyle::THICKTHIN_SMALLGAP, 20 );
    m_aLineStyleLb->InsertEntry( SvxBorderLine::getWidthImpl( SvxBorderLineStyle::THICKTHIN_MEDIUMGAP ), SvxBorderLineStyle::THICKTHIN_MEDIUMGAP );
    m_aLineStyleLb->InsertEntry( SvxBorderLine::getWidthImpl( SvxBorderLineStyle::THICKTHIN_LARGEGAP ), SvxBorderLineStyle::THICKTHIN_LARGEGAP );

    // Engraved / Embossed
    m_aLineStyleLb->InsertEntry( SvxBorderLine::getWidthImpl( SvxBorderLineStyle::EMBOSSED ), SvxBorderLineStyle::EMBOSSED, 15,
            &SvxBorderLine::threeDLightColor, &SvxBorderLine::threeDDarkColor,
            &lcl_mediumColor );
    m_aLineStyleLb->InsertEntry( SvxBorderLine::getWidthImpl( SvxBorderLineStyle::ENGRAVED ), SvxBorderLineStyle::ENGRAVED, 15,
            &SvxBorderLine::threeDDarkColor, &SvxBorderLine::threeDLightColor,
            &lcl_mediumColor );

    // Inset / Outset
    m_aLineStyleLb->InsertEntry( SvxBorderLine::getWidthImpl( SvxBorderLineStyle::OUTSET ), SvxBorderLineStyle::OUTSET, 10,
           &SvxBorderLine::lightColor, &SvxBorderLine::darkColor );
    m_aLineStyleLb->InsertEntry( SvxBorderLine::getWidthImpl( SvxBorderLineStyle::INSET ), SvxBorderLineStyle::INSET, 10,
           &SvxBorderLine::darkColor, &SvxBorderLine::lightColor );
    m_aLineStyleLb->SetWidth( 20 ); // 1pt by default

    m_aLineStyleLb->SetSelectHdl( LINK( this, SvxLineWindow_Impl, SelectHdl ) );

    SetHelpId( HID_POPUP_LINE );
    SetText( SvxResId(RID_SVXSTR_FRAME_STYLE) );
    m_aLineStyleLb->Show();
}

IMPL_LINK_NOARG(SvxCurrencyList_Impl, SelectHdl, ListBox&, void)
{
    VclPtr<SvxCurrencyList_Impl> xThis(this);

    if ( IsInPopupMode() )
        EndPopupMode();

    if (!m_xControl.is())
        return;

    m_rSelectedFormat = m_aFormatEntries[ m_pCurrencyLb->GetSelectedEntryPos() ];
    m_eSelectedLanguage = m_eFormatLanguage;

    m_xControl->execute( m_pCurrencyLb->GetSelectedEntryPos() + 1 );
}

IMPL_LINK_NOARG(SvxLineWindow_Impl, SelectHdl, ListBox&, void)
{
    VclPtr<SvxLineWindow_Impl> xThis(this);

    SvxLineItem     aLineItem( SID_FRAME_LINESTYLE );
    SvxBorderLineStyle  nStyle = m_aLineStyleLb->GetSelectEntryStyle();

    if ( m_aLineStyleLb->GetSelectedEntryPos( ) > 0 )
    {
        SvxBorderLine aTmp;
        aTmp.SetBorderLineStyle( nStyle );
        aTmp.SetWidth( 20 ); // TODO Make it depend on a width field
        aLineItem.SetLine( &aTmp );
    }
    else
        aLineItem.SetLine( nullptr );

    if ( IsInPopupMode() )
        EndPopupMode();

    Any a;
    Sequence< PropertyValue > aArgs( 1 );
    aArgs[0].Name = "LineStyle";
    aLineItem.QueryValue( a, m_bIsWriter ? CONVERT_TWIPS : 0 );
    aArgs[0].Value = a;

    m_rController.dispatchCommand( ".uno:LineStyle", aArgs );
}

void SvxLineWindow_Impl::Resize()
{
    m_aLineStyleLb->Resize();
}

void SvxLineWindow_Impl::GetFocus()
{
    if ( m_aLineStyleLb )
        m_aLineStyleLb->GrabFocus();
}

SfxStyleControllerItem_Impl::SfxStyleControllerItem_Impl(
    const Reference< XDispatchProvider >& rDispatchProvider,
    sal_uInt16                                nSlotId,      // Family-ID
    const OUString&                  rCommand,     // .uno: command bound to this item
    SvxStyleToolBoxControl&               rTbxCtl )     // controller instance, which the item is assigned to.
    :   SfxStatusListener( rDispatchProvider, nSlotId, rCommand ),
        rControl( rTbxCtl )
{
}

void SfxStyleControllerItem_Impl::StateChanged(
    sal_uInt16, SfxItemState eState, const SfxPoolItem* pState )
{
    switch ( GetId() )
    {
        case SID_STYLE_FAMILY1:
        case SID_STYLE_FAMILY2:
        case SID_STYLE_FAMILY3:
        case SID_STYLE_FAMILY4:
        case SID_STYLE_FAMILY5:
        {
            const sal_uInt16 nIdx = GetId() - SID_STYLE_FAMILY_START;

            if ( SfxItemState::DEFAULT == eState )
            {
                const SfxTemplateItem* pStateItem =
                    dynamic_cast<const SfxTemplateItem*>( pState  );
                DBG_ASSERT( pStateItem != nullptr, "SfxTemplateItem expected" );
                rControl.SetFamilyState( nIdx, pStateItem );
            }
            else
                rControl.SetFamilyState( nIdx, nullptr );
            break;
        }
    }
}

struct SvxStyleToolBoxControl::Impl
{
    OUString                     aClearForm;
    OUString                     aMore;
    ::std::vector< OUString >    aDefaultStyles;
    bool                     bSpecModeWriter;
    bool                     bSpecModeCalc;

    Impl()
        :aClearForm         ( SvxResId( RID_SVXSTR_CLEARFORM ) )
        ,aMore              ( SvxResId( RID_SVXSTR_MORE_STYLES ) )
        ,bSpecModeWriter    ( false )
        ,bSpecModeCalc      ( false )
    {


    }
    void InitializeStyles(const Reference < frame::XModel >& xModel)
    {
        //now convert the default style names to the localized names
        try
        {
            Reference< style::XStyleFamiliesSupplier > xStylesSupplier( xModel, UNO_QUERY_THROW );
            Reference< lang::XServiceInfo > xServices( xModel, UNO_QUERY_THROW );
            bSpecModeWriter = xServices->supportsService("com.sun.star.text.TextDocument");
            if(bSpecModeWriter)
            {
                Reference<container::XNameAccess> xParaStyles;
                xStylesSupplier->getStyleFamilies()->getByName("ParagraphStyles") >>=
                    xParaStyles;
                static const std::vector<OUString> aWriterStyles =
                {
                    "Standard",
                    "Text body",
                    "Title",
                    "Subtitle",
                    "Heading 1",
                    "Heading 2",
                    "Heading 3",
                    "Quotations"
                };
                for( const OUString& aStyle: aWriterStyles )
                {
                    try
                    {
                        Reference< beans::XPropertySet > xStyle;
                        xParaStyles->getByName( aStyle ) >>= xStyle;
                        OUString sName;
                        xStyle->getPropertyValue("DisplayName") >>= sName;
                        if( !sName.isEmpty() )
                            aDefaultStyles.push_back(sName);
                    }
                    catch( const uno::Exception& )
                    {}
                }

            }
            else if( (
                bSpecModeCalc = xServices->supportsService(
                    "com.sun.star.sheet.SpreadsheetDocument")))
            {
                static const sal_Char* aCalcStyles[] =
                {
                    "Default",
                    "Heading1",
                    "Result",
                    "Result2"
                };
                Reference<container::XNameAccess> xCellStyles;
                xStylesSupplier->getStyleFamilies()->getByName("CellStyles") >>= xCellStyles;
                for(const char* pCalcStyle : aCalcStyles)
                {
                    try
                    {
                        const OUString sStyleName( OUString::createFromAscii( pCalcStyle ) );
                        if( xCellStyles->hasByName( sStyleName ) )
                        {
                            Reference< beans::XPropertySet > xStyle( xCellStyles->getByName( sStyleName), UNO_QUERY_THROW );
                            OUString sName;
                            xStyle->getPropertyValue("DisplayName") >>= sName;
                            if( !sName.isEmpty() )
                                aDefaultStyles.push_back(sName);
                        }
                    }
                    catch( const uno::Exception& )
                    {}
                }
            }
        }
        catch(const uno::Exception& )
        {
            OSL_FAIL("error while initializing style names");
        }
    }
};

// mapping table from bound items. BE CAREFUL this table must be in the
// same order as the uno commands bound to the slots SID_STYLE_FAMILY1..n
// MAX_FAMILIES must also be correctly set!
static const char* StyleSlotToStyleCommand[MAX_FAMILIES] =
{
    ".uno:CharStyle",
    ".uno:ParaStyle",
    ".uno:FrameStyle",
    ".uno:PageStyle",
    ".uno:TemplateFamily5"
};

SvxStyleToolBoxControl::SvxStyleToolBoxControl(
    sal_uInt16 nSlotId, sal_uInt16 nId, ToolBox& rTbx )
    :   SfxToolBoxControl   ( nSlotId, nId, rTbx ),
        pImpl               ( new Impl ),
        pStyleSheetPool     ( nullptr ),
        nActFamily          ( 0xffff )
{
    for ( sal_uInt16 i=0; i<MAX_FAMILIES; i++ )
    {
        pBoundItems[i] = nullptr;
        m_xBoundItems[i].clear();
        pFamilyState[i]  = nullptr;
    }
}

SvxStyleToolBoxControl::~SvxStyleToolBoxControl()
{
}

void SAL_CALL SvxStyleToolBoxControl::initialize( const Sequence< Any >& aArguments )
{
    SfxToolBoxControl::initialize( aArguments );

    // After initialize we should have a valid frame member where we can retrieve our
    // dispatch provider.
    if ( m_xFrame.is() )
    {
        pImpl->InitializeStyles(m_xFrame->getController()->getModel());
        Reference< XDispatchProvider > xDispatchProvider( m_xFrame->getController(), UNO_QUERY );
        for ( sal_uInt16 i=0; i<MAX_FAMILIES; i++ )
        {
            pBoundItems[i]   = new SfxStyleControllerItem_Impl( xDispatchProvider,
                                                                SID_STYLE_FAMILY_START + i,
                                                                OUString::createFromAscii( StyleSlotToStyleCommand[i] ),
                                                                *this );
            m_xBoundItems[i].set( static_cast< OWeakObject* >( pBoundItems[i] ), UNO_QUERY );
            pFamilyState[i]  = nullptr;
        }
    }
}

// XComponent
void SAL_CALL SvxStyleToolBoxControl::dispose()
{
    SfxToolBoxControl::dispose();

    for( sal_uInt16 i=0; i<MAX_FAMILIES; i++ )
    {
        if ( m_xBoundItems[i].is() )
        {
            try
            {
                m_xBoundItems[i]->dispose();
            }
            catch ( Exception& )
            {
            }

            m_xBoundItems[i].clear();
            pBoundItems[i] = nullptr;
        }
        pFamilyState[i].reset();
    }
    pStyleSheetPool = nullptr;
    pImpl.reset();
}

void SAL_CALL SvxStyleToolBoxControl::update()
{
    // Do nothing, we will start binding our listener when we are visible.
    // See link SvxStyleToolBoxControl::VisibilityNotification.
    SvxStyleBox_Impl* pBox = static_cast<SvxStyleBox_Impl*>(GetToolBox().GetItemWindow( GetId() ));
    if ( pBox->IsVisible() )
    {
        for (SfxStyleControllerItem_Impl* pBoundItem : pBoundItems)
            pBoundItem->ReBind();

        bindListener();
    }
}

SfxStyleFamily SvxStyleToolBoxControl::GetActFamily()
{
    switch ( nActFamily-1 + SID_STYLE_FAMILY_START )
    {
        case SID_STYLE_FAMILY1: return SfxStyleFamily::Char;
        case SID_STYLE_FAMILY2: return SfxStyleFamily::Para;
        case SID_STYLE_FAMILY3: return SfxStyleFamily::Frame;
        case SID_STYLE_FAMILY4: return SfxStyleFamily::Page;
        case SID_STYLE_FAMILY5: return SfxStyleFamily::Pseudo;
        default:
            OSL_FAIL( "unknown style family" );
            break;
    }
    return SfxStyleFamily::Para;
}

void SvxStyleToolBoxControl::FillStyleBox()
{
    SvxStyleBox_Impl* pBox = static_cast<SvxStyleBox_Impl*>(GetToolBox().GetItemWindow( GetId() ));

    DBG_ASSERT( pStyleSheetPool, "StyleSheetPool not found!" );
    DBG_ASSERT( pBox,            "Control not found!" );

    if ( pStyleSheetPool && pBox && nActFamily!=0xffff )
    {
        const SfxStyleFamily    eFamily     = GetActFamily();
        sal_uInt16              nCount      = pStyleSheetPool->Count();
        SfxStyleSheetBase*      pStyle      = nullptr;
        bool                    bDoFill     = false;

        pStyleSheetPool->SetSearchMask( eFamily, SfxStyleSearchBits::Used );

        // Check whether fill is necessary
        pStyle = pStyleSheetPool->First();
        //!!! TODO: This condition isn't right any longer, because we always show some default entries
        //!!! so the list doesn't show the count
        if ( nCount != pBox->GetEntryCount() )
        {
            bDoFill = true;
        }
        else
        {
            sal_uInt16 i= 0;
            while ( pStyle && !bDoFill )
            {
                bDoFill = ( pBox->GetEntry(i) != pStyle->GetName() );
                pStyle = pStyleSheetPool->Next();
                i++;
            }
        }

        if ( bDoFill )
        {
            pBox->SetUpdateMode( false );
            pBox->Clear();

            {
                pStyle = pStyleSheetPool->Next();

                if( pImpl->bSpecModeWriter || pImpl->bSpecModeCalc )
                {
                    while ( pStyle )
                    {
                        // sort out default styles
                        bool bInsert = true;
                        OUString aName( pStyle->GetName() );
                        for( auto const & _i: pImpl->aDefaultStyles )
                        {
                            if( _i == aName )
                            {
                                bInsert = false;
                                break;
                            }
                        }

                        if( bInsert )
                            pBox->InsertEntry( aName );
                        pStyle = pStyleSheetPool->Next();
                    }
                }
                else
                {
                    while ( pStyle )
                    {
                        pBox->InsertEntry( pStyle->GetName() );
                        pStyle = pStyleSheetPool->Next();
                    }
                }
            }

            if( pImpl->bSpecModeWriter || pImpl->bSpecModeCalc )
            {
                // disable sort to preserve special order
                WinBits nWinBits = pBox->GetStyle();
                nWinBits &= ~WB_SORT;
                pBox->SetStyle( nWinBits );

                // insert default styles
                sal_uInt16 nPos = 1;
                for( auto const & _i: pImpl->aDefaultStyles )
                {
                    pBox->InsertEntry( _i, nPos );
                    ++nPos;
                }

                pBox->InsertEntry( pImpl->aClearForm, 0 );
                pBox->SetSeparatorPos( 0 );

                pBox->InsertEntry( pImpl->aMore );

                // enable sort again
                nWinBits |= WB_SORT;
                pBox->SetStyle( nWinBits );
            }

            pBox->SetUpdateMode( true );
            pBox->SetFamily( eFamily );

            sal_uInt16 nLines = static_cast<sal_uInt16>(
                    std::min( pBox->GetEntryCount(), static_cast<sal_Int32>(MAX_STYLES_ENTRIES)));
            pBox->SetDropDownLineCount( nLines );
        }
    }
}

void SvxStyleToolBoxControl::SelectStyle( const OUString& rStyleName )
{
    SvxStyleBox_Impl* pBox = static_cast<SvxStyleBox_Impl*>(GetToolBox().GetItemWindow( GetId() ));
    DBG_ASSERT( pBox, "Control not found!" );

    if ( pBox )
    {
        OUString aStrSel( pBox->GetText() );

        if ( !rStyleName.isEmpty() )
        {
            if ( rStyleName != aStrSel )
                pBox->SetText( rStyleName );
        }
        else
            pBox->SetNoSelection();
        pBox->SaveValue();
    }
}

void SvxStyleToolBoxControl::Update()
{
    SfxStyleSheetBasePool*  pPool     = nullptr;
    SfxObjectShell*         pDocShell = SfxObjectShell::Current();

    if ( pDocShell )
        pPool = pDocShell->GetStyleSheetPool();

    sal_uInt16 i;
    for ( i=0; i<MAX_FAMILIES; i++ )
        if( pFamilyState[i] )
            break;

    if ( i==MAX_FAMILIES || !pPool )
    {
        pStyleSheetPool = pPool;
        return;
    }


    const SfxTemplateItem* pItem = nullptr;

    if ( nActFamily == 0xffff || nullptr == (pItem = pFamilyState[nActFamily-1].get()) )
    // Current range not within allowed ranges or default
    {
        pStyleSheetPool = pPool;
        nActFamily      = 2;

        pItem = pFamilyState[nActFamily-1].get();
        if ( !pItem )
        {
            nActFamily++;
            pItem = pFamilyState[nActFamily-1].get();
        }

        if ( !pItem )
        {
            SAL_INFO( "svx", "Unknown Family" ); // can happen
        }
    }
    else if ( pPool != pStyleSheetPool )
        pStyleSheetPool = pPool;

    FillStyleBox(); // Decides by itself whether Fill is needed

    if ( pItem )
        SelectStyle( pItem->GetStyleName() );
}

void SvxStyleToolBoxControl::SetFamilyState( sal_uInt16 nIdx,
                                             const SfxTemplateItem* pItem )
{
    pFamilyState[nIdx].reset( pItem == nullptr ? nullptr : new SfxTemplateItem( *pItem ) );
    Update();
}

IMPL_LINK_NOARG(SvxStyleToolBoxControl, VisibilityNotification, SvxStyleBox_Impl&, void)
{
    // Call ReBind() && UnBind() according to visibility
    SvxStyleBox_Impl* pBox = static_cast<SvxStyleBox_Impl*>( GetToolBox().GetItemWindow( GetId() ));

    if ( pBox && pBox->IsVisible() && !isBound() )
    {
        for (SfxStyleControllerItem_Impl* pBoundItem : pBoundItems)
            pBoundItem->ReBind();

        bindListener();
    }
    else if ( (!pBox || !pBox->IsVisible()) && isBound() )
    {
        for (SfxStyleControllerItem_Impl* pBoundItem : pBoundItems)
            pBoundItem->UnBind();
        unbindListener();
    }
}

void SvxStyleToolBoxControl::StateChanged(
    sal_uInt16 , SfxItemState eState, const SfxPoolItem* pState )
{
    sal_uInt16       nId    = GetId();
    ToolBox&     rTbx   = GetToolBox();
    SvxStyleBox_Impl* pBox   = static_cast<SvxStyleBox_Impl*>(rTbx.GetItemWindow( nId ));
    TriState     eTri   = TRISTATE_FALSE;

    DBG_ASSERT( pBox, "Control not found!" );

    if ( SfxItemState::DISABLED == eState )
        pBox->Disable();
    else
        pBox->Enable();

    rTbx.EnableItem( nId, SfxItemState::DISABLED != eState );

    switch ( eState )
    {
        case SfxItemState::DEFAULT:
            eTri = static_cast<const SfxTemplateItem*>(pState)->GetValue() != SfxStyleSearchBits::Auto
                        ? TRISTATE_TRUE
                        : TRISTATE_FALSE;
            break;

        case SfxItemState::DONTCARE:
            eTri = TRISTATE_INDET;
            break;

        default:
            break;
    }

    rTbx.SetItemState( nId, eTri );

    if ( SfxItemState::DISABLED != eState )
        Update();
}

VclPtr<vcl::Window> SvxStyleToolBoxControl::CreateItemWindow( vcl::Window *pParent )
{
    VclPtrInstance<SvxStyleBox_Impl> pBox( pParent,
                                           OUString( ".uno:StyleApply" ),
                                           SfxStyleFamily::Para,
                                           Reference< XDispatchProvider >( m_xFrame->getController(), UNO_QUERY ),
                                           m_xFrame,
                                           pImpl->aClearForm,
                                           pImpl->aMore,
                                           pImpl->bSpecModeWriter || pImpl->bSpecModeCalc );
    if( !pImpl->aDefaultStyles.empty())
        pBox->SetDefaultStyle( pImpl->aDefaultStyles[0] );
    // Set visibility listener to bind/unbind controller
    pBox->SetVisibilityListener( LINK( this, SvxStyleToolBoxControl, VisibilityNotification ));

    return pBox.get();
}

class SvxFontNameToolBoxControl : public cppu::ImplInheritanceHelper< svt::ToolboxController,
                                                                      css::lang::XServiceInfo >
{
public:
    SvxFontNameToolBoxControl();

    // XStatusListener
    virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& rEvent ) override;

    // XToolbarController
    virtual css::uno::Reference< css::awt::XWindow > SAL_CALL createItemWindow( const css::uno::Reference< css::awt::XWindow >& rParent ) override;

    // XComponent
    virtual void SAL_CALL dispose() override;

    // XServiceInfo
    virtual OUString SAL_CALL getImplementationName() override;
    virtual sal_Bool SAL_CALL supportsService( const OUString& rServiceName ) override;
    virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;

private:
    VclPtr<SvxFontNameBox_Impl> m_pBox;
};

SvxFontNameToolBoxControl::SvxFontNameToolBoxControl()
{
}

void SvxFontNameToolBoxControl::statusChanged( const css::frame::FeatureStateEvent& rEvent )
{
    SolarMutexGuard aGuard;
    ToolBox* pToolBox = nullptr;
    sal_uInt16 nId = 0;
    if ( !getToolboxId( nId, &pToolBox ) )
        return;

    if ( !rEvent.IsEnabled )
    {
        m_pBox->Disable();
        m_pBox->Update( nullptr );
    }
    else
    {
        m_pBox->Enable();

        css::awt::FontDescriptor aFontDesc;
        if ( rEvent.State >>= aFontDesc )
            m_pBox->Update( &aFontDesc );
        else
            m_pBox->SetText( "" );
        m_pBox->SaveValue();
    }

    pToolBox->EnableItem( nId, rEvent.IsEnabled );
}

css::uno::Reference< css::awt::XWindow > SvxFontNameToolBoxControl::createItemWindow( const css::uno::Reference< css::awt::XWindow >& rParent )
{
    SolarMutexGuard aGuard;
    m_pBox = VclPtr<SvxFontNameBox_Impl>::Create( VCLUnoHelper::GetWindow( rParent ),
                                                  Reference< XDispatchProvider >( m_xFrame->getController(), UNO_QUERY ),
                                                  m_xFrame, 0);
    return VCLUnoHelper::GetInterface( m_pBox );
}

void SvxFontNameToolBoxControl::dispose()
{
    m_pBox.disposeAndClear();
    ToolboxController::dispose();
}

OUString SvxFontNameToolBoxControl::getImplementationName()
{
    return OUString( "com.sun.star.comp.svx.FontNameToolBoxControl" );
}

sal_Bool SvxFontNameToolBoxControl::supportsService( const OUString& rServiceName )
{
    return cppu::supportsService( this, rServiceName );
}

css::uno::Sequence< OUString > SvxFontNameToolBoxControl::getSupportedServiceNames()
{
    return { "com.sun.star.frame.ToolbarController" };
}

extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
com_sun_star_comp_svx_FontNameToolBoxControl_get_implementation(
    css::uno::XComponentContext*,
    css::uno::Sequence<css::uno::Any> const & )
{
    return cppu::acquire( new SvxFontNameToolBoxControl() );
}

SvxColorToolBoxControl::SvxColorToolBoxControl( const css::uno::Reference<css::uno::XComponentContext>& rContext ) :
    ImplInheritanceHelper( rContext, nullptr, OUString() ),
    m_bSplitButton(true),
    m_nSlotId(0),
    m_aColorSelectFunction(PaletteManager::DispatchColorCommand)
{
}

namespace {

sal_uInt16 MapCommandToSlotId(const OUString& rCommand)
{
    if (rCommand == ".uno:Color")
        return SID_ATTR_CHAR_COLOR;
    else if (rCommand == ".uno:FontColor")
        return SID_ATTR_CHAR_COLOR2;
    else if (rCommand == ".uno:BackColor")
        return SID_ATTR_CHAR_COLOR_BACKGROUND;
    else if (rCommand == ".uno:CharBackColor")
        return SID_ATTR_CHAR_BACK_COLOR;
    else if (rCommand == ".uno:BackgroundColor")
        return SID_BACKGROUND_COLOR;
    else if (rCommand == ".uno:TableCellBackgroundColor")
        return SID_TABLE_CELL_BACKGROUND_COLOR;
    else if (rCommand == ".uno:Extrusion3DColor")
        return SID_EXTRUSION_3D_COLOR;
    else if (rCommand == ".uno:XLineColor")
        return SID_ATTR_LINE_COLOR;
    else if (rCommand == ".uno:FillColor")
        return SID_ATTR_FILL_COLOR;
    else if (rCommand == ".uno:FrameLineColor")
        return SID_FRAME_LINECOLOR;

    SAL_WARN("svx.tbxcrtls", "Unknown color command: " << rCommand);
    return 0;
}

}

void SvxColorToolBoxControl::initialize( const css::uno::Sequence<css::uno::Any>& rArguments )
{
    PopupWindowController::initialize( rArguments );

    ToolBox* pToolBox = nullptr;
    sal_uInt16 nId = 0;
    if ( !getToolboxId( nId, &pToolBox ) )
    {
        SAL_WARN("svx.tbxcrtls", "ToolBox not found!");
        return;
    }

    m_nSlotId = MapCommandToSlotId( m_aCommandURL );
    if ( m_nSlotId == SID_ATTR_LINE_COLOR || m_nSlotId == SID_ATTR_FILL_COLOR ||
         m_nSlotId == SID_FRAME_LINECOLOR || m_nSlotId == SID_BACKGROUND_COLOR )
        // Sidebar uses wide buttons for those.
        m_bSplitButton = typeid( *pToolBox ) != typeid( sfx2::sidebar::SidebarToolBox );

    OUString aCommandLabel = vcl::CommandInfoProvider::GetLabelForCommand( getCommandURL(), getModuleName() );

    m_xBtnUpdater.reset( new svx::ToolboxButtonColorUpdater( m_nSlotId, nId, pToolBox, !m_bSplitButton,  aCommandLabel ) );
    pToolBox->SetItemBits( nId, pToolBox->GetItemBits( nId ) | ( m_bSplitButton ? ToolBoxItemBits::DROPDOWN : ToolBoxItemBits::DROPDOWNONLY ) );
}

void SvxColorToolBoxControl::update()
{
    PopupWindowController::update();

    switch( m_nSlotId )
    {
        case SID_ATTR_CHAR_COLOR2:
            addStatusListener( ".uno:CharColorExt");
            break;

        case SID_ATTR_CHAR_COLOR_BACKGROUND:
            addStatusListener( ".uno:CharBackgroundExt");
            break;

        case SID_FRAME_LINECOLOR:
            addStatusListener( ".uno:BorderTLBR");
            addStatusListener( ".uno:BorderBLTR");
            break;
    }
}

void SvxColorToolBoxControl::EnsurePaletteManager()
{
    if (!m_xPaletteManager)
    {
        m_xPaletteManager.reset(new PaletteManager);
        m_xPaletteManager->SetBtnUpdater(m_xBtnUpdater.get());
    }
}

SvxColorToolBoxControl::~SvxColorToolBoxControl()
{
    if (m_xPaletteManager)
        m_xPaletteManager->SetBtnUpdater(nullptr);
}

void SvxColorToolBoxControl::setColorSelectFunction(const ColorSelectFunction& aColorSelectFunction)
{
    m_aColorSelectFunction = aColorSelectFunction;
    if (m_xPaletteManager)
        m_xPaletteManager->SetColorSelectFunction(aColorSelectFunction);
}

VclPtr<vcl::Window> SvxColorToolBoxControl::createPopupWindow( vcl::Window* pParent )
{
    EnsurePaletteManager();

    VclPtrInstance<SvxColorWindow> pColorWin(
                            m_aCommandURL,
                            m_xPaletteManager,
                            m_aColorStatus,
                            m_nSlotId,
                            m_xFrame,
                            pParent,
                            false,
                            m_aColorSelectFunction);

    OUString aWindowTitle = vcl::CommandInfoProvider::GetLabelForCommand( m_aCommandURL, m_sModuleName );
    pColorWin->SetText( aWindowTitle );
    pColorWin->StartSelection();
    if ( m_bSplitButton )
        pColorWin->SetSelectedHdl( LINK( this, SvxColorToolBoxControl, SelectedHdl ) );
    return pColorWin;
}

IMPL_LINK(SvxColorToolBoxControl, SelectedHdl, const NamedColor&, rColor, void)
{
    m_xBtnUpdater->Update(rColor);
}

void SvxColorToolBoxControl::statusChanged( const css::frame::FeatureStateEvent& rEvent )
{
    ToolBox* pToolBox = nullptr;
    sal_uInt16 nId = 0;
    if ( !getToolboxId( nId, &pToolBox ) )
        return;

    if ( rEvent.FeatureURL.Complete == m_aCommandURL )
        pToolBox->EnableItem( nId, rEvent.IsEnabled );

    bool bValue;
    if ( !m_bSplitButton )
    {
        m_aColorStatus.statusChanged( rEvent );
        m_xBtnUpdater->Update( m_aColorStatus.GetColor() );
    }
    else if ( rEvent.State >>= bValue )
        pToolBox->CheckItem( nId, bValue );
}

void SvxColorToolBoxControl::execute(sal_Int16 /*nSelectModifier*/)
{
    if ( !m_bSplitButton )
    {
        // Open the popup also when Enter key is pressed.
        createPopupWindow();
        return;
    }

    OUString aCommand = m_aCommandURL;
    Color aColor = m_xBtnUpdater->GetCurrentColor();

    switch( m_nSlotId )
    {
        case SID_ATTR_CHAR_COLOR2 :
            aCommand    = ".uno:CharColorExt";
            break;

        case SID_ATTR_CHAR_COLOR_BACKGROUND :
            aCommand    = ".uno:CharBackgroundExt";
            break;
    }

    auto aArgs( comphelper::InitPropertySequence( {
        { m_aCommandURL.copy(5), css::uno::makeAny(aColor) }
    } ) );
    dispatchCommand( aCommand, aArgs );

    EnsurePaletteManager();
    OUString sColorName = "#" + aColor.AsRGBHexString().toAsciiUpperCase();
    m_xPaletteManager->AddRecentColor(aColor, sColorName);
}

sal_Bool SvxColorToolBoxControl::opensSubToolbar()
{
    // For a split button, we mark this controller as a sub-toolbar controller,
    // so we get notified (through updateImage method) on button image changes,
    // and could redraw the last used color on top of it.
    return m_bSplitButton;
}

void SvxColorToolBoxControl::updateImage()
{
    ToolBox* pToolBox = nullptr;
    sal_uInt16 nId = 0;
    if ( !getToolboxId( nId, &pToolBox ) )
        return;

    Image aImage = vcl::CommandInfoProvider::GetImageForCommand(m_aCommandURL, m_xFrame, pToolBox->GetImageSize());
    if ( !!aImage )
    {
        pToolBox->SetItemImage( nId, aImage );
        m_xBtnUpdater->Update(m_xBtnUpdater->GetCurrentColor(), true);
    }
}

OUString SvxColorToolBoxControl::getSubToolbarName()
{
    return OUString();
}

void SvxColorToolBoxControl::functionSelected( const OUString& /*rCommand*/ )
{
}

OUString SvxColorToolBoxControl::getImplementationName()
{
    return OUString( "com.sun.star.comp.svx.ColorToolBoxControl" );
}

css::uno::Sequence<OUString> SvxColorToolBoxControl::getSupportedServiceNames()
{
    return { "com.sun.star.frame.ToolbarController" };
}

extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
com_sun_star_comp_svx_ColorToolBoxControl_get_implementation(
    css::uno::XComponentContext* rContext,
    css::uno::Sequence<css::uno::Any> const & )
{
    return cppu::acquire( new SvxColorToolBoxControl( rContext ) );
}

// class SvxFrameToolBoxControl --------------------------------------------

class SvxFrameToolBoxControl : public svt::PopupWindowController
{
public:
    explicit SvxFrameToolBoxControl( const css::uno::Reference< css::uno::XComponentContext >& rContext );

    // XInitialization
    virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& rArguments ) override;

    // XServiceInfo
    virtual OUString SAL_CALL getImplementationName() override;
    virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;

private:
    virtual VclPtr<vcl::Window> createPopupWindow( vcl::Window* pParent ) override;
    using svt::ToolboxController::createPopupWindow;
};

SvxFrameToolBoxControl::SvxFrameToolBoxControl( const css::uno::Reference< css::uno::XComponentContext >& rContext )
    : svt::PopupWindowController( rContext, nullptr, OUString() )
{
}

void SvxFrameToolBoxControl::initialize( const css::uno::Sequence< css::uno::Any >& rArguments )
{
    svt::PopupWindowController::initialize( rArguments );
    ToolBox* pToolBox = nullptr;
    sal_uInt16 nId = 0;
    if ( getToolboxId( nId, &pToolBox ) )
        pToolBox->SetItemBits( nId, pToolBox->GetItemBits( nId ) | ToolBoxItemBits::DROPDOWNONLY );
}

VclPtr<vcl::Window> SvxFrameToolBoxControl::createPopupWindow( vcl::Window* pParent )
{
    if ( m_aCommandURL == ".uno:LineStyle" )
        return VclPtr<SvxLineWindow_Impl>::Create( *this, pParent );

    return VclPtr<SvxFrameWindow_Impl>::Create( *this, pParent );
}

OUString SvxFrameToolBoxControl::getImplementationName()
{
    return OUString( "com.sun.star.comp.svx.FrameToolBoxControl" );
}

css::uno::Sequence< OUString > SvxFrameToolBoxControl::getSupportedServiceNames()
{
    return { "com.sun.star.frame.ToolbarController" };
}

extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
com_sun_star_comp_svx_FrameToolBoxControl_get_implementation(
    css::uno::XComponentContext* rContext,
    css::uno::Sequence<css::uno::Any> const & )
{
    return cppu::acquire( new SvxFrameToolBoxControl( rContext ) );
}

SvxSimpleUndoRedoController::SvxSimpleUndoRedoController( sal_uInt16 nSlotId, sal_uInt16 nId, ToolBox& rTbx  )
    :SfxToolBoxControl( nSlotId, nId, rTbx )
{
    aDefaultText = rTbx.GetItemText( nId );
}

SvxSimpleUndoRedoController::~SvxSimpleUndoRedoController()
{
}

void SvxSimpleUndoRedoController::StateChanged( sal_uInt16, SfxItemState eState, const SfxPoolItem* pState )
{
    const SfxStringItem* pItem = dynamic_cast<const SfxStringItem*>( pState  );
    ToolBox& rBox = GetToolBox();
    if ( pItem && eState != SfxItemState::DISABLED )
    {
        OUString aNewText( MnemonicGenerator::EraseAllMnemonicChars( pItem->GetValue() ) );
        rBox.SetQuickHelpText( GetId(), aNewText );
    }
    if ( eState == SfxItemState::DISABLED )
        rBox.SetQuickHelpText( GetId(), aDefaultText );
    rBox.EnableItem( GetId(), eState != SfxItemState::DISABLED );
}

SvxCurrencyToolBoxControl::SvxCurrencyToolBoxControl( const css::uno::Reference<css::uno::XComponentContext>& rContext ) :
    PopupWindowController( rContext, nullptr, OUString() ),
    m_eLanguage( Application::GetSettings().GetLanguageTag().getLanguageType() ),
    m_nFormatKey( NUMBERFORMAT_ENTRY_NOT_FOUND )
{
}

SvxCurrencyToolBoxControl::~SvxCurrencyToolBoxControl() {}

void SvxCurrencyToolBoxControl::initialize( const css::uno::Sequence< css::uno::Any >& rArguments )
{
    PopupWindowController::initialize(rArguments);

    ToolBox* pToolBox = nullptr;
    sal_uInt16 nId = 0;
    if (getToolboxId(nId, &pToolBox) && pToolBox->GetItemCommand(nId) == m_aCommandURL)
        pToolBox->SetItemBits(nId, ToolBoxItemBits::DROPDOWN | pToolBox->GetItemBits(nId));
}

VclPtr<vcl::Window> SvxCurrencyToolBoxControl::createPopupWindow( vcl::Window* pParent )
{
    return VclPtr<SvxCurrencyList_Impl>::Create(this, pParent, m_aFormatString, m_eLanguage);
}

void SvxCurrencyToolBoxControl::execute( sal_Int16 nSelectModifier )
{
    sal_uInt32 nFormatKey;
    if (m_aFormatString.isEmpty())
        nFormatKey = NUMBERFORMAT_ENTRY_NOT_FOUND;
    else
    {
        if ( nSelectModifier > 0 )
        {
            try
            {
                uno::Reference< util::XNumberFormatsSupplier > xRef( m_xFrame->getController()->getModel(), uno::UNO_QUERY );
                uno::Reference< util::XNumberFormats > rxNumberFormats( xRef->getNumberFormats(), uno::UNO_QUERY_THROW );
                css::lang::Locale aLocale = LanguageTag::convertToLocale( m_eLanguage );
                nFormatKey = rxNumberFormats->queryKey( m_aFormatString, aLocale, false );
                if ( nFormatKey == NUMBERFORMAT_ENTRY_NOT_FOUND )
                    nFormatKey = rxNumberFormats->addNew( m_aFormatString, aLocale );
                }
                catch( const uno::Exception& )
                {
                    nFormatKey = m_nFormatKey;
                }
        }
        else
            nFormatKey = m_nFormatKey;
    }

    if( nFormatKey != NUMBERFORMAT_ENTRY_NOT_FOUND )
    {
        Sequence< PropertyValue > aArgs( 1 );
        aArgs[0].Name = "NumberFormatCurrency";
        aArgs[0].Value <<= nFormatKey;
        dispatchCommand( m_aCommandURL, aArgs );
        m_nFormatKey = nFormatKey;
    }
    else
        PopupWindowController::execute( nSelectModifier );
}

OUString SvxCurrencyToolBoxControl::getImplementationName()
{
    return OUString( "com.sun.star.comp.svx.CurrencyToolBoxControl" );
}

css::uno::Sequence<OUString> SvxCurrencyToolBoxControl::getSupportedServiceNames()
{
    return { "com.sun.star.frame.ToolbarController" };
}

extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
com_sun_star_comp_svx_CurrencyToolBoxControl_get_implementation(
    css::uno::XComponentContext* rContext,
    css::uno::Sequence<css::uno::Any> const & )
{
    return cppu::acquire( new SvxCurrencyToolBoxControl( rContext ) );
}

Reference< css::accessibility::XAccessible > SvxFontNameBox_Impl::CreateAccessible()
{
    FillList();
    return FontNameBox::CreateAccessible();
}

//static
void SvxCurrencyToolBoxControl::GetCurrencySymbols( std::vector<OUString>& rList, bool bFlag,
                                                    std::vector<sal_uInt16>& rCurrencyList )
{
    rCurrencyList.clear();

    const NfCurrencyTable& rCurrencyTable = SvNumberFormatter::GetTheCurrencyTable();
    sal_uInt16 nCount = rCurrencyTable.size();

    sal_uInt16 nStart = 1;

    OUString aString( ApplyLreOrRleEmbedding( rCurrencyTable[0].GetSymbol() ) );
    aString += " ";
    aString += ApplyLreOrRleEmbedding( SvtLanguageTable::GetLanguageString(
                                       rCurrencyTable[0].GetLanguage() ) );

    rList.push_back( aString );
    rCurrencyList.push_back( sal_uInt16(-1) ); // nAuto

    if( bFlag )
    {
        rList.push_back( aString );
        rCurrencyList.push_back( 0 );
        ++nStart;
    }

    CollatorWrapper aCollator( ::comphelper::getProcessComponentContext() );
    aCollator.loadDefaultCollator( Application::GetSettings().GetLanguageTag().getLocale(), 0 );

    const OUString aTwoSpace("  ");

    for( sal_uInt16 i = 1; i < nCount; ++i )
    {
        OUString aStr( ApplyLreOrRleEmbedding( rCurrencyTable[i].GetBankSymbol() ) );
        aStr += aTwoSpace;
        aStr += ApplyLreOrRleEmbedding( rCurrencyTable[i].GetSymbol() );
        aStr += aTwoSpace;
        aStr += ApplyLreOrRleEmbedding( SvtLanguageTable::GetLanguageString(
                                        rCurrencyTable[i].GetLanguage() ) );

        std::vector<OUString>::size_type j = nStart;
        for( ; j < rList.size(); ++j )
            if ( aCollator.compareString( aStr, rList[j] ) < 0 )
                break;  // insert before first greater than

        rList.insert( rList.begin() + j, aStr );
        rCurrencyList.insert( rCurrencyList.begin() + j, i );
    }

    // Append ISO codes to symbol list.
    // XXX If this is to be changed, various other places would had to be
    // adapted that assume this order!
    std::vector<OUString>::size_type nCont = rList.size();

    for ( sal_uInt16 i = 1; i < nCount; ++i )
    {
        bool bInsert = true;
        OUString aStr( ApplyLreOrRleEmbedding( rCurrencyTable[i].GetBankSymbol() ) );

        std::vector<OUString>::size_type j = nCont;
        for ( ; j < rList.size() && bInsert; ++j )
        {
            if( rList[j] == aStr )
                bInsert = false;
            else if ( aCollator.compareString( aStr, rList[j] ) < 0 )
                break;  // insert before first greater than
        }
        if ( bInsert )
        {
            rList.insert( rList.begin() + j, aStr );
            rCurrencyList.insert( rCurrencyList.begin() + j, i );
        }
    }
}

SvxListBoxColorWrapper::SvxListBoxColorWrapper(SvxColorListBox* pControl)
    : mxControl(pControl)
{
}

void SvxListBoxColorWrapper::operator()(const OUString& /*rCommand*/, const NamedColor& rColor)
{
    if (!mxControl)
        return;
    mxControl->Selected(rColor);
}

void SvxListBoxColorWrapper::dispose()
{
    mxControl.clear();
}

ListBoxColorWrapper::ListBoxColorWrapper(ColorListBox* pControl)
    : mpControl(pControl)
{
}

void ListBoxColorWrapper::operator()(const OUString& /*rCommand*/, const NamedColor& rColor)
{
    mpControl->Selected(rColor);
}

SvxColorListBox::SvxColorListBox(vcl::Window* pParent, WinBits nStyle)
    : MenuButton(pParent, nStyle)
    , m_aColorWrapper(this)
    , m_aAutoDisplayColor(Application::GetSettings().GetStyleSettings().GetDialogColor())
    , m_nSlotId(0)
    , m_bShowNoneButton(false)
{
    m_aSelectedColor = GetAutoColor(m_nSlotId);
    LockWidthRequest();
    ShowPreview(m_aSelectedColor);
    SetActivateHdl(LINK(this, SvxColorListBox, MenuActivateHdl));
}

void SvxColorListBox::EnsurePaletteManager()
{
    if (!m_xPaletteManager)
    {
        m_xPaletteManager.reset(new PaletteManager);
        m_xPaletteManager->SetColorSelectFunction(std::ref(m_aColorWrapper));
    }
}

void ColorListBox::EnsurePaletteManager()
{
    if (!m_xPaletteManager)
    {
        m_xPaletteManager.reset(new PaletteManager);
        m_xPaletteManager->SetColorSelectFunction(std::ref(m_aColorWrapper));
    }
}

void SvxColorListBox::SetSlotId(sal_uInt16 nSlotId, bool bShowNoneButton)
{
    m_nSlotId = nSlotId;
    m_bShowNoneButton = bShowNoneButton;
    m_xColorWindow.disposeAndClear();
    m_aSelectedColor = bShowNoneButton ? GetNoneColor() : GetAutoColor(m_nSlotId);
    ShowPreview(m_aSelectedColor);
    createColorWindow();
}

void ColorListBox::SetSlotId(sal_uInt16 nSlotId, bool bShowNoneButton)
{
    m_nSlotId = nSlotId;
    m_bShowNoneButton = bShowNoneButton;
    m_xButton->set_popover(nullptr);
    m_xColorWindow.reset();
    m_aSelectedColor = bShowNoneButton ? GetNoneColor() : GetAutoColor(m_nSlotId);
    ShowPreview(m_aSelectedColor);
    createColorWindow();
}

//to avoid the box resizing every time the color is changed to
//the optimal size of the individual color, get the longest
//standard color and stick with that as the size for all
void SvxColorListBox::LockWidthRequest()
{
    if (get_width_request() != -1)
        return;
    NamedColor aLongestColor;
    long nMaxStandardColorTextWidth = 0;
    XColorListRef const xColorTable = XColorList::CreateStdColorList();
    for (long i = 0; i != xColorTable->Count(); ++i)
    {
        XColorEntry& rEntry = *xColorTable->GetColor(i);
        long nColorTextWidth = GetTextWidth(rEntry.GetName());
        if (nColorTextWidth > nMaxStandardColorTextWidth)
        {
            nMaxStandardColorTextWidth = nColorTextWidth;
            aLongestColor.second = rEntry.GetName();
        }
    }
    ShowPreview(aLongestColor);
    set_width_request(get_preferred_size().Width());
}

void SvxColorListBox::ShowPreview(const NamedColor &rColor)
{
    const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
    Size aImageSize(rStyleSettings.GetListBoxPreviewDefaultPixelSize());

    ScopedVclPtrInstance<VirtualDevice> xDevice;
    xDevice->SetOutputSize(aImageSize);
    const tools::Rectangle aRect(Point(0, 0), aImageSize);
    if (m_bShowNoneButton && rColor.first == COL_NONE_COLOR)
    {
        const Color aW(COL_WHITE);
        const Color aG(0xef, 0xef, 0xef);
        xDevice->DrawCheckered(aRect.TopLeft(), aRect.GetSize(), 8, aW, aG);
        xDevice->SetFillColor();
    }
    else
    {
        if (rColor.first == COL_AUTO)
            xDevice->SetFillColor(m_aAutoDisplayColor);
        else
            xDevice->SetFillColor(rColor.first);
    }

    xDevice->SetLineColor(rStyleSettings.GetDisableColor());
    xDevice->DrawRect(aRect);

    BitmapEx aBitmap(xDevice->GetBitmapEx(Point(0, 0), xDevice->GetOutputSize()));
    SetImageAlign(ImageAlign::Left);
    SetModeImage(Image(aBitmap));
    SetText(rColor.second);
}

IMPL_LINK(SvxColorListBox, WindowEventListener, VclWindowEvent&, rWindowEvent, void)
{
    if (rWindowEvent.GetId() == VclEventId::WindowEndPopupMode)
    {
        m_xColorWindow.disposeAndClear();
        SetPopover(nullptr);
    }
}

IMPL_LINK_NOARG(SvxColorListBox, MenuActivateHdl, MenuButton *, void)
{
    if (!m_xColorWindow || m_xColorWindow->isDisposed())
        createColorWindow();
}

void SvxColorListBox::createColorWindow()
{
    const SfxViewFrame* pViewFrame = SfxViewFrame::Current();
    const SfxFrame* pFrame = pViewFrame ? &pViewFrame->GetFrame() : nullptr;
    css::uno::Reference<css::frame::XFrame> xFrame(pFrame ? pFrame->GetFrameInterface() : uno::Reference<css::frame::XFrame>());

    EnsurePaletteManager();

    m_xColorWindow = VclPtr<SvxColorWindow>::Create(
                            OUString() /*m_aCommandURL*/,
                            m_xPaletteManager,
                            m_aColorStatus,
                            m_nSlotId,
                            xFrame,
                            this,
                            true,
                            m_aColorWrapper);

    m_xColorWindow->AddEventListener(LINK(this, SvxColorListBox, WindowEventListener));

    SetNoSelection();
    if (m_bShowNoneButton)
        m_xColorWindow->ShowNoneButton();
    m_xColorWindow->SelectEntry(m_aSelectedColor);
    SetPopover(m_xColorWindow);
}

void SvxColorListBox::Selected(const NamedColor& rColor)
{
    ShowPreview(rColor);
    m_aSelectedColor = rColor;
    if (m_aSelectedLink.IsSet())
        m_aSelectedLink.Call(*this);
}

VCL_BUILDER_FACTORY(SvxColorListBox)

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

void SvxColorListBox::dispose()
{
    m_xColorWindow.disposeAndClear();
    m_aColorWrapper.dispose();
    MenuButton::dispose();
}

VclPtr<SvxColorWindow> const & SvxColorListBox::getColorWindow() const
{
    if (!m_xColorWindow || m_xColorWindow->isDisposed())
        const_cast<SvxColorListBox*>(this)->createColorWindow();
    return m_xColorWindow;
}

void SvxColorListBox::SelectEntry(const NamedColor& rColor)
{
    if (rColor.second.trim().isEmpty())
    {
        SelectEntry(rColor.first);
        return;
    }
    VclPtr<SvxColorWindow> xColorWindow = getColorWindow();
    xColorWindow->SelectEntry(rColor);
    m_aSelectedColor = xColorWindow->GetSelectEntryColor();
    ShowPreview(m_aSelectedColor);
}

void SvxColorListBox::SelectEntry(const Color& rColor)
{
    VclPtr<SvxColorWindow> xColorWindow = getColorWindow();
    xColorWindow->SelectEntry(rColor);
    m_aSelectedColor = xColorWindow->GetSelectEntryColor();
    ShowPreview(m_aSelectedColor);
}

ColorListBox::ColorListBox(std::unique_ptr<weld::MenuButton> pControl, weld::Window* pTopLevel)
    : m_xButton(std::move(pControl))
    , m_pTopLevel(pTopLevel)
    , m_aColorWrapper(this)
    , m_aAutoDisplayColor(Application::GetSettings().GetStyleSettings().GetDialogColor())
    , m_nSlotId(0)
    , m_bShowNoneButton(false)
{
    m_aSelectedColor = GetAutoColor(m_nSlotId);
    LockWidthRequest();
    ShowPreview(m_aSelectedColor);
}

ColorListBox::~ColorListBox()
{
}

ColorWindow* ColorListBox::getColorWindow() const
{
    if (!m_xColorWindow)
        const_cast<ColorListBox*>(this)->createColorWindow();
    return m_xColorWindow.get();
}

void ColorListBox::createColorWindow()
{
    const SfxViewFrame* pViewFrame = SfxViewFrame::Current();
    const SfxFrame* pFrame = pViewFrame ? &pViewFrame->GetFrame() : nullptr;
    css::uno::Reference<css::frame::XFrame> xFrame(pFrame ? pFrame->GetFrameInterface() : uno::Reference<css::frame::XFrame>());

    EnsurePaletteManager();

    m_xColorWindow.reset(new ColorWindow(
                            m_xPaletteManager,
                            m_aColorStatus,
                            m_nSlotId,
                            xFrame,
                            m_pTopLevel,
                            m_xButton.get(),
                            /*bInterimBuilder*/false,
                            m_aColorWrapper));

    SetNoSelection();
    m_xButton->set_popover(m_xColorWindow->GetWidget());
    if (m_bShowNoneButton)
        m_xColorWindow->ShowNoneButton();
    m_xColorWindow->SelectEntry(m_aSelectedColor);
}

void ColorListBox::SelectEntry(const Color& rColor)
{
    ColorWindow* pColorWindow = getColorWindow();
    pColorWindow->SelectEntry(rColor);
    m_aSelectedColor = pColorWindow->GetSelectEntryColor();
    ShowPreview(m_aSelectedColor);
}

void ColorListBox::Selected(const NamedColor& rColor)
{
    ShowPreview(rColor);
    m_aSelectedColor = rColor;
    if (m_aSelectedLink.IsSet())
        m_aSelectedLink.Call(*this);
}

//to avoid the box resizing every time the color is changed to
//the optimal size of the individual color, get the longest
//standard color and stick with that as the size for all
void ColorListBox::LockWidthRequest()
{
    NamedColor aLongestColor;
    long nMaxStandardColorTextWidth = 0;
    XColorListRef const xColorTable = XColorList::CreateStdColorList();
    for (long i = 0; i != xColorTable->Count(); ++i)
    {
        XColorEntry& rEntry = *xColorTable->GetColor(i);
        auto nColorTextWidth = m_xButton->get_pixel_size(rEntry.GetName()).Width();
        if (nColorTextWidth > nMaxStandardColorTextWidth)
        {
            nMaxStandardColorTextWidth = nColorTextWidth;
            aLongestColor.second = rEntry.GetName();
        }
    }
    ShowPreview(aLongestColor);
    m_xButton->set_size_request(m_xButton->get_preferred_size().Width(), -1);
}

void ColorListBox::ShowPreview(const NamedColor &rColor)
{
    const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
    Size aImageSize(rStyleSettings.GetListBoxPreviewDefaultPixelSize());

    ScopedVclPtrInstance<VirtualDevice> xDevice;
    xDevice->SetOutputSize(aImageSize);
    const tools::Rectangle aRect(Point(0, 0), aImageSize);
    if (m_bShowNoneButton && rColor.first == COL_NONE_COLOR)
    {
        const Color aW(COL_WHITE);
        const Color aG(0xef, 0xef, 0xef);
        xDevice->DrawCheckered(aRect.TopLeft(), aRect.GetSize(), 8, aW, aG);
        xDevice->SetFillColor();
    }
    else
    {
        if (rColor.first == COL_AUTO)
            xDevice->SetFillColor(m_aAutoDisplayColor);
        else
            xDevice->SetFillColor(rColor.first);
    }

    xDevice->SetLineColor(rStyleSettings.GetDisableColor());
    xDevice->DrawRect(aRect);

    m_xButton->set_image(xDevice.get());
    m_xButton->set_label(rColor.second);
}

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