/* -*- 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 <sfx2/dispatch.hxx>
#include <sfx2/objsh.hxx>
#include <sfx2/viewsh.hxx>
#include <rtl/ustring.hxx>
#include <vcl/event.hxx>
#include <vcl/settings.hxx>
#include <vcl/toolbox.hxx>
#include <vcl/virdev.hxx>
#include <svl/itemset.hxx>
#include <svx/svxids.hrc>
#include <tools/json_writer.hxx>

constexpr OUString TMP_STR_BEGIN = u"["_ustr;
constexpr OUString TMP_STR_END = u"]"_ustr;

#include <svx/drawitem.hxx>
#include <svx/xfillit0.hxx>
#include <svx/xtable.hxx>
#include <svx/fillctrl.hxx>
#include <svx/itemwin.hxx>
#include <svx/xflclit.hxx>
#include <svx/xflgrit.hxx>
#include <svx/xflhtit.hxx>
#include <svx/xbtmpit.hxx>
#include <memory>

using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::util;

namespace {

enum eFillStyle
{
    NONE,
    SOLID,
    GRADIENT,
    HATCH,
    BITMAP,
    PATTERN
};

drawing::FillStyle toCssFillStyle( eFillStyle eXFS )
{
    if (eXFS == PATTERN)
    {
        return drawing::FillStyle_BITMAP;
    }

    return static_cast<drawing::FillStyle>(eXFS);
}

}

SFX_IMPL_TOOLBOX_CONTROL( SvxFillToolBoxControl, XFillStyleItem );

SvxFillToolBoxControl::SvxFillToolBoxControl(
    sal_uInt16 nSlotId,
    ToolBoxItemId nId,
    ToolBox& rTbx )
    : SfxToolBoxControl( nSlotId, nId, rTbx )
    , mxFillControl(nullptr)
    , mpLbFillType(nullptr)
    , mpToolBoxColor(nullptr)
    , mpLbFillAttr(nullptr)
    , mnLastXFS(-1)
    , mnLastPosGradient(0)
    , mnLastPosHatch(0)
    , mnLastPosBitmap(0)
    , mnLastPosPattern(0)
{
    addStatusListener( u".uno:FillColor"_ustr);
    addStatusListener( u".uno:FillGradient"_ustr);
    addStatusListener( u".uno:FillHatch"_ustr);
    addStatusListener( u".uno:FillBitmap"_ustr);
    addStatusListener( u".uno:ColorTableState"_ustr);
    addStatusListener( u".uno:GradientListState"_ustr);
    addStatusListener( u".uno:HatchListState"_ustr);
    addStatusListener( u".uno:BitmapListState"_ustr);
}

SvxFillToolBoxControl::~SvxFillToolBoxControl()
{
}

void SvxFillToolBoxControl::StateChangedAtToolBoxControl(
    sal_uInt16 nSID,
    SfxItemState eState,
    const SfxPoolItem* pState)
{
    const bool bDisabled(SfxItemState::DISABLED == eState);

    switch(nSID)
    {
        case SID_ATTR_FILL_STYLE:
        {
            if(bDisabled)
            {
                mpLbFillType->set_sensitive(false);
                mpLbFillType->set_active(-1);
                mpLbFillAttr->show();
                mpLbFillAttr->set_sensitive(false);
                mpLbFillAttr->set_active(-1);
                mpToolBoxColor->hide();
                mnLastXFS = -1;
                mpStyleItem.reset();
            }

            if(eState >= SfxItemState::DEFAULT)
            {
                const XFillStyleItem* pItem = dynamic_cast< const XFillStyleItem* >(pState);

                if(pItem)
                {
                    mpStyleItem.reset(pItem->Clone());
                    mpLbFillType->set_sensitive(true);
                    drawing::FillStyle eXFS = mpStyleItem->GetValue();
                    mnLastXFS = sal::static_int_cast< sal_Int32 >(eXFS);
                    mpLbFillType->set_active(mnLastXFS);

                    if(drawing::FillStyle_NONE == eXFS)
                    {
                        mpLbFillAttr->set_active(-1);
                        mpLbFillAttr->set_sensitive(false);
                    }

                    Update();
                    break;
                }
            }

            mpLbFillType->set_active(-1);
            mpLbFillAttr->show();
            mpLbFillAttr->set_sensitive(false);
            mpLbFillAttr->set_active(-1);
            mpToolBoxColor->hide();
            mnLastXFS = -1;
            mpStyleItem.reset();
            mxFillControl->Resize();
            break;
        }
        case SID_ATTR_FILL_COLOR:
        {
            if(SfxItemState::DEFAULT == eState)
            {
                mpColorItem.reset(pState ? static_cast<XFillColorItem*>(pState->Clone()) : nullptr);
            }

            if(mpStyleItem && drawing::FillStyle_SOLID == mpStyleItem->GetValue())
            {
                mpLbFillAttr->hide();
                mpToolBoxColor->show();
                mxFillControl->Resize();

                Update();
            }
            break;
        }
        case SID_ATTR_FILL_GRADIENT:
        {
            if(SfxItemState::DEFAULT == eState)
            {
                mpFillGradientItem.reset(pState ? static_cast<XFillGradientItem*>(pState->Clone()) : nullptr);
            }

            if(mpStyleItem && drawing::FillStyle_GRADIENT == mpStyleItem->GetValue())
            {
                mpLbFillAttr->show();
                mpToolBoxColor->hide();
                mxFillControl->Resize();

                if(SfxItemState::DEFAULT == eState)
                {
                    mpLbFillAttr->set_sensitive(true);
                    Update();
                }
                else if(SfxItemState::DISABLED == eState )
                {
                    mpLbFillAttr->set_sensitive(false);
                    mpLbFillAttr->set_active(-1);
                }
                else
                {
                    mpLbFillAttr->set_active(-1);
                }
            }
            break;
        }
        case SID_ATTR_FILL_HATCH:
        {
            if(SfxItemState::DEFAULT == eState)
            {
                mpHatchItem.reset(pState ? static_cast<XFillHatchItem*>(pState->Clone()) : nullptr);
            }

            if(mpStyleItem && drawing::FillStyle_HATCH == mpStyleItem->GetValue())
            {
                mpLbFillAttr->show();
                mpToolBoxColor->hide();
                mxFillControl->Resize();

                if(SfxItemState::DEFAULT == eState)
                {
                    mpLbFillAttr->set_sensitive(true);
                    Update();
                }
                else if(SfxItemState::DISABLED == eState )
                {
                    mpLbFillAttr->set_sensitive(false);
                    mpLbFillAttr->set_active(-1);
                }
                else
                {
                    mpLbFillAttr->set_active(-1);
                }
            }
            break;
        }
        case SID_ATTR_FILL_BITMAP:
        {
            if(SfxItemState::DEFAULT == eState)
            {
                mpBitmapItem.reset(pState ? static_cast<XFillBitmapItem*>(pState->Clone()) : nullptr);
            }

            if(mpStyleItem && drawing::FillStyle_BITMAP == mpStyleItem->GetValue())
            {
                mpLbFillAttr->show();
                mpToolBoxColor->hide();
                mxFillControl->Resize();

                if(SfxItemState::DEFAULT == eState)
                {
                    mpLbFillAttr->set_sensitive(true);
                    Update();
                }
                else if(SfxItemState::DISABLED == eState )
                {
                    mpLbFillAttr->set_sensitive(false);
                    mpLbFillAttr->set_active(-1);
                }
                else
                {
                    mpLbFillAttr->set_active(-1);
                }
            }
            break;
        }
        case SID_GRADIENT_LIST:
        {
            if(SfxItemState::DEFAULT == eState)
            {
                if(mpStyleItem && drawing::FillStyle_GRADIENT == mpStyleItem->GetValue())
                {
                    if(mpFillGradientItem)
                    {
                        const OUString aString( mpFillGradientItem->GetName() );
                        mpLbFillAttr->clear();
                        if (const SfxObjectShell* pSh = SfxObjectShell::Current())
                        {
                            mpLbFillAttr->set_sensitive(true);
                            SvxFillAttrBox::Fill(*mpLbFillAttr, pSh->GetItem(SID_GRADIENT_LIST)->GetGradientList());
                        }
                        mpLbFillAttr->set_active_text(aString);
                    }
                    else
                    {
                        mpLbFillAttr->set_active(-1);
                    }
                }
            }
            break;
        }
        case SID_HATCH_LIST:
        {
            if(SfxItemState::DEFAULT == eState)
            {
                if(mpStyleItem && drawing::FillStyle_HATCH == mpStyleItem->GetValue())
                {
                    if(mpHatchItem)
                    {
                        const OUString aString( mpHatchItem->GetName() );
                        mpLbFillAttr->clear();
                        if (const SfxObjectShell* pSh = SfxObjectShell::Current())
                        {
                            mpLbFillAttr->set_sensitive(true);
                            SvxFillAttrBox::Fill(*mpLbFillAttr, pSh->GetItem(SID_HATCH_LIST)->GetHatchList());
                        }
                        mpLbFillAttr->set_active_text(aString);
                    }
                    else
                    {
                        mpLbFillAttr->set_active(-1);
                    }
                }
            }
            break;
        }
        case SID_BITMAP_LIST:
        {
            if(SfxItemState::DEFAULT == eState)
            {
                if(mpStyleItem && drawing::FillStyle_BITMAP == mpStyleItem->GetValue())
                {
                    if(mpBitmapItem)
                    {
                        const OUString aString( mpBitmapItem->GetName() );
                        mpLbFillAttr->clear();
                        if (const SfxObjectShell* pSh = SfxObjectShell::Current())
                        {
                            mpLbFillAttr->set_sensitive(true);
                            SvxFillAttrBox::Fill(*mpLbFillAttr, pSh->GetItem(SID_BITMAP_LIST)->GetBitmapList());
                        }
                        mpLbFillAttr->set_active_text(aString);
                    }
                    else
                    {
                        mpLbFillAttr->set_active(-1);
                    }
                }
            }
            break;
        }
    }
}

void SvxFillToolBoxControl::Update()
{
    if(!mpStyleItem)
        return;

    const drawing::FillStyle eXFS = mpStyleItem->GetValue();
    SfxObjectShell* pSh = SfxObjectShell::Current();

    switch( eXFS )
    {
        case drawing::FillStyle_NONE:
        {
            mpLbFillAttr->show();
            mpToolBoxColor->hide();
            mxFillControl->Resize();
            break;
        }
        case drawing::FillStyle_SOLID:
        {
            if(mpColorItem)
            {
                mpLbFillAttr->hide();
                mpToolBoxColor->show();
                mxFillControl->Resize();
            }
            break;
        }
        case drawing::FillStyle_GRADIENT:
        {
            mpLbFillAttr->show();
            mpToolBoxColor->hide();
            mxFillControl->Resize();

            if(pSh && pSh->GetItem(SID_GRADIENT_LIST))
            {
                mpLbFillAttr->set_sensitive(true);
                mpLbFillAttr->clear();
                SvxFillAttrBox::Fill(*mpLbFillAttr, pSh->GetItem(SID_GRADIENT_LIST)->GetGradientList());

                if(mpFillGradientItem)
                {
                    const OUString aString(mpFillGradientItem->GetName());

                    mpLbFillAttr->set_active_text(aString);

                    // Check if the entry is not in the list
                    if (mpLbFillAttr->get_active_text() != aString)
                    {
                        sal_Int32 nCount = mpLbFillAttr->get_count();
                        OUString aTmpStr;
                        if( nCount > 0 )
                        {
                            // Last entry gets tested against temporary entry
                            aTmpStr = mpLbFillAttr->get_text( nCount - 1 );
                            if( aTmpStr.startsWith(TMP_STR_BEGIN) &&
                                aTmpStr.endsWith(TMP_STR_END) )
                            {
                                mpLbFillAttr->remove(nCount - 1);
                            }
                        }
                        aTmpStr = TMP_STR_BEGIN + aString + TMP_STR_END;

                        rtl::Reference<XGradientList> xGradientList = new XGradientList( u""_ustr, u""_ustr/*TODO?*/ );
                        xGradientList->Insert(std::make_unique<XGradientEntry>(mpFillGradientItem->GetGradientValue(), aTmpStr));
                        xGradientList->SetDirty( false );
                        const Bitmap aBmp = xGradientList->GetUiBitmap( 0 );

                        if (!aBmp.IsEmpty())
                        {
                            ScopedVclPtrInstance< VirtualDevice > pVD;
                            const Size aBmpSize(aBmp.GetSizePixel());
                            pVD->SetOutputSizePixel(aBmpSize, false);
                            pVD->DrawBitmap(Point(), aBmp);
                            mpLbFillAttr->append(u""_ustr, xGradientList->Get(0)->GetName(), *pVD);
                            mpLbFillAttr->set_active(mpLbFillAttr->get_count() - 1);
                        }
                    }

                }
                else
                {
                    mpLbFillAttr->set_active(-1);
                }
            }
            else
            {
                mpLbFillAttr->set_active(-1);
            }
            break;
        }
        case drawing::FillStyle_HATCH:
        {
            mpLbFillAttr->show();
            mpToolBoxColor->hide();
            mxFillControl->Resize();

            if(pSh && pSh->GetItem(SID_HATCH_LIST))
            {
                mpLbFillAttr->set_sensitive(true);
                mpLbFillAttr->clear();
                SvxFillAttrBox::Fill(*mpLbFillAttr, pSh->GetItem(SID_HATCH_LIST)->GetHatchList());

                if(mpHatchItem)
                {
                    const OUString aString(mpHatchItem->GetName());

                    mpLbFillAttr->set_active_text( aString );

                    // Check if the entry is not in the list
                    if( mpLbFillAttr->get_active_text() != aString )
                    {
                        const sal_Int32 nCount = mpLbFillAttr->get_count();
                        OUString aTmpStr;
                        if( nCount > 0 )
                        {
                            // Last entry gets tested against temporary entry
                            aTmpStr = mpLbFillAttr->get_text( nCount - 1 );
                            if(  aTmpStr.startsWith(TMP_STR_BEGIN) &&
                                 aTmpStr.endsWith(TMP_STR_END) )
                            {
                                mpLbFillAttr->remove( nCount - 1 );
                            }
                        }
                        aTmpStr = TMP_STR_BEGIN + aString + TMP_STR_END;

                        rtl::Reference<XHatchList> xHatchList = new XHatchList( u""_ustr, u""_ustr/*TODO?*/ );
                        xHatchList->Insert(std::make_unique<XHatchEntry>(mpHatchItem->GetHatchValue(), aTmpStr));
                        xHatchList->SetDirty( false );
                        const Bitmap aBmp = xHatchList->GetUiBitmap( 0 );

                        if( !aBmp.IsEmpty() )
                        {
                            ScopedVclPtrInstance< VirtualDevice > pVD;
                            const Size aBmpSize(aBmp.GetSizePixel());
                            pVD->SetOutputSizePixel(aBmpSize, false);
                            pVD->DrawBitmap(Point(), aBmp);
                            mpLbFillAttr->append(u""_ustr, xHatchList->GetHatch(0)->GetName(), *pVD);
                            mpLbFillAttr->set_active(mpLbFillAttr->get_count() - 1);
                        }
                    }
                }
                else
                {
                    mpLbFillAttr->set_active(-1);
                }
            }
            else
            {
                mpLbFillAttr->set_active(-1);
            }
            break;
        }
        case drawing::FillStyle_BITMAP:
        {
            mpLbFillAttr->show();
            mpToolBoxColor->hide();
            mxFillControl->Resize();

            if(pSh)
            {
                mpLbFillAttr->set_sensitive(true);
                mpLbFillAttr->clear();

                if(mpBitmapItem && !mpBitmapItem->isPattern() && pSh->GetItem(SID_BITMAP_LIST))
                {
                    SvxFillAttrBox::Fill(*mpLbFillAttr, pSh->GetItem(SID_BITMAP_LIST)->GetBitmapList());

                    const OUString aString(mpBitmapItem->GetName());

                    mpLbFillAttr->set_active_text(aString);

                    // Check if the entry is not in the list
                    if (mpLbFillAttr->get_active_text() != aString)
                    {
                        sal_Int32 nCount = mpLbFillAttr->get_count();
                        OUString aTmpStr;
                        if( nCount > 0 )
                        {
                            // Last entry gets tested against temporary entry
                            aTmpStr = mpLbFillAttr->get_text(nCount - 1);
                            if( aTmpStr.startsWith(TMP_STR_BEGIN) &&
                                aTmpStr.endsWith(TMP_STR_END) )
                            {
                                mpLbFillAttr->remove(nCount - 1);
                            }
                        }
                        aTmpStr = TMP_STR_BEGIN + aString + TMP_STR_END;

                        XBitmapListRef xBitmapList =
                            XPropertyList::AsBitmapList(
                                XPropertyList::CreatePropertyList(
                                    XPropertyListType::Bitmap, u"TmpList"_ustr, u""_ustr/*TODO?*/));
                        xBitmapList->Insert(std::make_unique<XBitmapEntry>(mpBitmapItem->GetGraphicObject(), aTmpStr));
                        xBitmapList->SetDirty( false );
                        SvxFillAttrBox::Fill(*mpLbFillAttr, xBitmapList);
                        mpLbFillAttr->set_active(mpLbFillAttr->get_count() - 1);
                    }

                }
                else if (mpBitmapItem && mpBitmapItem->isPattern() && pSh->GetItem(SID_PATTERN_LIST))
                {
                    mnLastXFS = sal::static_int_cast<sal_Int32>(PATTERN);
                    mpLbFillType->set_active(mnLastXFS);

                    SvxFillAttrBox::Fill(*mpLbFillAttr, pSh->GetItem(SID_PATTERN_LIST)->GetPatternList());
                    const OUString aString(mpBitmapItem->GetName());

                    mpLbFillAttr->set_active_text(aString);
                }
                else
                {
                    mpLbFillAttr->set_active(-1);
                }
            }
            else
            {
                mpLbFillAttr->set_active(-1);
            }
            break;
        }
        default:
            OSL_ENSURE(false, "Non supported FillType (!)");
        break;
    }

}

VclPtr<InterimItemWindow> SvxFillToolBoxControl::CreateItemWindow(vcl::Window *pParent)
{
    if(GetSlotId() == SID_ATTR_FILL_STYLE)
    {
        mxFillControl.reset(VclPtr<FillControl>::Create(pParent, m_xFrame));

        mpLbFillType = mxFillControl->mxLbFillType.get();
        mpLbFillAttr = mxFillControl->mxLbFillAttr.get();
        mpToolBoxColor = mxFillControl->mxToolBoxColor.get();

        mpLbFillType->connect_changed(LINK(this,SvxFillToolBoxControl,SelectFillTypeHdl));
        mpLbFillAttr->connect_changed(LINK(this,SvxFillToolBoxControl,SelectFillAttrHdl));


        return mxFillControl;
    }
    return VclPtr<InterimItemWindow>();
}

FillControl::FillControl(vcl::Window* pParent, const css::uno::Reference<css::frame::XFrame>& rFrame)
    : InterimItemWindow(pParent, u"svx/ui/fillctrlbox.ui"_ustr, u"FillCtrlBox"_ustr)
    , mxLbFillType(m_xBuilder->weld_combo_box(u"type"_ustr))
    , mxToolBoxColor(m_xBuilder->weld_toolbar(u"color"_ustr))
    , mxColorDispatch(new ToolbarUnoDispatcher(*mxToolBoxColor, *m_xBuilder, rFrame))
    , mxLbFillAttr(m_xBuilder->weld_combo_box(u"attr"_ustr))
    , mnTypeCurPos(0)
    , mnAttrCurPos(0)
{
    InitControlBase(mxLbFillType.get());

    mxLbFillAttr->connect_key_press(LINK(this, FillControl, AttrKeyInputHdl));
    mxLbFillType->connect_key_press(LINK(this, FillControl, TypeKeyInputHdl));
    mxToolBoxColor->connect_key_press(LINK(this, FillControl, ColorKeyInputHdl));

    mxLbFillType->connect_get_property_tree(LINK(this, FillControl, DumpAsPropertyTreeHdl));

    mxLbFillType->connect_focus_in(LINK(this, FillControl, TypeFocusHdl));
    mxLbFillAttr->connect_focus_in(LINK(this, FillControl, AttrFocusHdl));

    SvxFillTypeBox::Fill(*mxLbFillType);

    SetOptimalSize();
}

IMPL_STATIC_LINK(FillControl, DumpAsPropertyTreeHdl, tools::JsonWriter&, rJsonWriter, void)
{
    rJsonWriter.put("command", ".uno:FillStyle");
}

void FillControl::ReleaseFocus_Impl()
{
    SfxViewShell* pCurSh = SfxViewShell::Current();
    if (pCurSh)
    {
        vcl::Window* pShellWnd = pCurSh->GetWindow();
        if (pShellWnd)
            pShellWnd->GrabFocus();
    }
}

IMPL_LINK(FillControl, TypeKeyInputHdl, const KeyEvent&, rKEvt, bool)
{
    const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
    sal_uInt16 nCode = rKeyCode.GetCode();

    if (nCode == KEY_ESCAPE)
    {
        mxLbFillType->set_active(mnTypeCurPos);
        ReleaseFocus_Impl();
        return true;
    }

    if (nCode != KEY_TAB)
        return false;
    if (rKeyCode.IsShift())
        return ChildKeyInput(rKEvt);
    if (mxLbFillAttr->get_visible() && !mxLbFillAttr->get_sensitive())
        return ChildKeyInput(rKEvt);
    return false;
}

IMPL_LINK_NOARG(FillControl, TypeFocusHdl, weld::Widget&, void)
{
    mnTypeCurPos = mxLbFillType->get_active();
}

IMPL_LINK_NOARG(FillControl, AttrFocusHdl, weld::Widget&, void)
{
    mnAttrCurPos = mxLbFillAttr->get_active();
}

IMPL_LINK(FillControl, AttrKeyInputHdl, const KeyEvent&, rKEvt, bool)
{
    const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
    sal_uInt16 nCode = rKeyCode.GetCode();

    if (nCode == KEY_ESCAPE)
    {
        mxLbFillAttr->set_active(mnAttrCurPos);
        ReleaseFocus_Impl();
        return true;
    }

    return ChildKeyInput(rKEvt);
}

IMPL_LINK(FillControl, ColorKeyInputHdl, const KeyEvent&, rKEvt, bool)
{
    return ChildKeyInput(rKEvt);
}

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

void FillControl::dispose()
{
    mxLbFillAttr.reset();
    mxColorDispatch.reset();
    mxToolBoxColor.reset();
    mxLbFillType.reset();
    InterimItemWindow::dispose();
}

IMPL_LINK_NOARG(SvxFillToolBoxControl, SelectFillTypeHdl, weld::ComboBox&, void)
{
    sal_Int32 nXFS = mpLbFillType->get_active();

    if(mnLastXFS == nXFS)
        return;

    eFillStyle eXFS = static_cast<eFillStyle>(nXFS);
    mpLbFillAttr->clear();
    SfxObjectShell* pSh = SfxObjectShell::Current();
    const XFillStyleItem aXFillStyleItem(toCssFillStyle(eXFS));

    // #i122676# Do no longer trigger two Execute calls, one for SID_ATTR_FILL_STYLE
    // and one for setting the fill attribute itself, but add two SfxPoolItems to the
    // call to get just one action at the SdrObject and to create only one Undo action, too.
    // Checked that this works in all apps.
    switch( eXFS )
    {
        default:
        case NONE:
        {
            mpLbFillAttr->show();
            mpToolBoxColor->hide();
            mpLbFillAttr->set_sensitive(false);
            if (pSh)
            {
                // #i122676# need to call a single SID_ATTR_FILL_STYLE change
                pSh->GetDispatcher()->ExecuteList(
                    SID_ATTR_FILL_STYLE, SfxCallMode::RECORD,
                    { &aXFillStyleItem });
            }
            break;
        }
        case SOLID:
        {
            mpLbFillAttr->hide();
            mpToolBoxColor->show();
            if (pSh)
            {
                const ::Color aColor = mpColorItem ? mpColorItem->GetColorValue() : COL_AUTO;
                const XFillColorItem aXFillColorItem( u""_ustr, aColor );

                // #i122676# change FillStyle and Color in one call
                pSh->GetDispatcher()->ExecuteList(
                    SID_ATTR_FILL_COLOR, SfxCallMode::RECORD,
                    { &aXFillColorItem, &aXFillStyleItem });
            }
            break;
        }
        case GRADIENT:
        {
            mpLbFillAttr->show();
            mpToolBoxColor->hide();

            if(pSh && pSh->GetItem(SID_GRADIENT_LIST))
            {
                if(!mpLbFillAttr->get_count())
                {
                    mpLbFillAttr->set_sensitive(true);
                    mpLbFillAttr->clear();
                    SvxFillAttrBox::Fill(*mpLbFillAttr, pSh->GetItem(SID_GRADIENT_LIST)->GetGradientList());
                }

                if (mnLastPosGradient != -1)
                {
                    const SvxGradientListItem * pItem = pSh->GetItem(SID_GRADIENT_LIST);

                    if(mnLastPosGradient < pItem->GetGradientList()->Count())
                    {
                        const basegfx::BGradient aGradient = pItem->GetGradientList()->GetGradient(mnLastPosGradient)->GetGradient();
                        const XFillGradientItem aXFillGradientItem(mpLbFillAttr->get_text(mnLastPosGradient), aGradient);

                        // #i122676# change FillStyle and Gradient in one call
                        pSh->GetDispatcher()->ExecuteList(
                            SID_ATTR_FILL_GRADIENT, SfxCallMode::RECORD,
                            { &aXFillGradientItem, &aXFillStyleItem });
                        mpLbFillAttr->set_active(mnLastPosGradient);
                    }
                }
            }
            else
            {
                mpLbFillAttr->set_sensitive(false);
            }
            break;
        }
        case HATCH:
        {
            mpLbFillAttr->show();
            mpToolBoxColor->hide();

            if(pSh && pSh->GetItem(SID_HATCH_LIST))
            {
                if(!mpLbFillAttr->get_count())
                {
                    mpLbFillAttr->set_sensitive(true);
                    mpLbFillAttr->clear();
                    SvxFillAttrBox::Fill(*mpLbFillAttr, pSh->GetItem(SID_HATCH_LIST)->GetHatchList());
                }

                if (mnLastPosHatch != -1)
                {
                    const SvxHatchListItem * pItem = pSh->GetItem(SID_HATCH_LIST);

                    if(mnLastPosHatch < pItem->GetHatchList()->Count())
                    {
                        const XHatch aHatch = pItem->GetHatchList()->GetHatch(mnLastPosHatch)->GetHatch();
                        const XFillHatchItem aXFillHatchItem(mpLbFillAttr->get_active_text(), aHatch);

                        // #i122676# change FillStyle and Hatch in one call
                        pSh->GetDispatcher()->ExecuteList(
                            SID_ATTR_FILL_HATCH, SfxCallMode::RECORD,
                            { &aXFillHatchItem, &aXFillStyleItem });
                        mpLbFillAttr->set_active(mnLastPosHatch);
                    }
                }
            }
            else
            {
                mpLbFillAttr->set_sensitive(false);
            }
            break;
        }
        case BITMAP:
        {
            mpLbFillAttr->show();
            mpToolBoxColor->hide();

            if(pSh && pSh->GetItem(SID_BITMAP_LIST))
            {
                if(!mpLbFillAttr->get_count())
                {
                    mpLbFillAttr->set_sensitive(true);
                    mpLbFillAttr->clear();
                    SvxFillAttrBox::Fill(*mpLbFillAttr, pSh->GetItem(SID_BITMAP_LIST)->GetBitmapList());
                }

                if (mnLastPosBitmap != -1)
                {
                    const SvxBitmapListItem * pItem = pSh->GetItem(SID_BITMAP_LIST);

                    if(mnLastPosBitmap < pItem->GetBitmapList()->Count())
                    {
                        const XBitmapEntry* pXBitmapEntry = pItem->GetBitmapList()->GetBitmap(mnLastPosBitmap);
                        const XFillBitmapItem aXFillBitmapItem(mpLbFillAttr->get_active_text(), pXBitmapEntry->GetGraphicObject());

                        // #i122676# change FillStyle and Bitmap in one call
                        pSh->GetDispatcher()->ExecuteList(
                            SID_ATTR_FILL_BITMAP, SfxCallMode::RECORD,
                            { &aXFillBitmapItem, &aXFillStyleItem });
                        mpLbFillAttr->set_active(mnLastPosBitmap);
                    }
                }
            }
            else
            {
                mpLbFillAttr->set_sensitive(false);
            }
            break;
        }
        case PATTERN:
        {
            mpLbFillAttr->show();
            mpToolBoxColor->hide();

            if(pSh && pSh->GetItem(SID_PATTERN_LIST))
            {
                if(!mpLbFillAttr->get_count())
                {
                    mpLbFillAttr->set_sensitive(true);
                    mpLbFillAttr->clear();
                    SvxFillAttrBox::Fill(*mpLbFillAttr, pSh->GetItem(SID_PATTERN_LIST)->GetPatternList());
                }

                if (mnLastPosPattern != -1)
                {
                    const SvxPatternListItem * pItem = pSh->GetItem(SID_PATTERN_LIST);

                    if(mnLastPosPattern < pItem->GetPatternList()->Count())
                    {
                        const XBitmapEntry* pXBitmapEntry = pItem->GetPatternList()->GetBitmap(mnLastPosPattern);
                        const XFillBitmapItem aXFillBitmapItem(mpLbFillAttr->get_active_text(), pXBitmapEntry->GetGraphicObject());

                        // #i122676# change FillStyle and Bitmap in one call
                        pSh->GetDispatcher()->ExecuteList(
                            SID_ATTR_FILL_BITMAP, SfxCallMode::RECORD,
                            { &aXFillBitmapItem, &aXFillStyleItem });
                        mpLbFillAttr->set_active(mnLastPosPattern);
                    }
                }
            }
            else
            {
                mpLbFillAttr->set_sensitive(false);
            }
            break;
        }

    }

    mnLastXFS = nXFS;

    mxFillControl->Resize();
}

IMPL_LINK_NOARG(SvxFillToolBoxControl, SelectFillAttrHdl, weld::ComboBox&, void)
{
    sal_Int32 nXFS = mpLbFillType->get_active();
    eFillStyle eXFS = static_cast<eFillStyle>(nXFS);

    const XFillStyleItem aXFillStyleItem(toCssFillStyle(eXFS));
    SfxObjectShell* pSh = SfxObjectShell::Current();

    // #i122676# dependent from bFillStyleChange, do execute a single or two
    // changes in one Execute call
    const bool bFillStyleChange(mnLastXFS != nXFS);

    switch (eXFS)
    {
        case SOLID:
        {
            if (bFillStyleChange && pSh)
            {
                // #i122676# Single FillStyle change call needed here
                pSh->GetDispatcher()->ExecuteList(
                    SID_ATTR_FILL_STYLE, SfxCallMode::RECORD,
                    { &aXFillStyleItem });
            }
            break;
        }
        case GRADIENT:
        {
            sal_Int32 nPos = mpLbFillAttr->get_active();

            if (nPos == -1)
            {
                nPos = mnLastPosGradient;
            }

            if (nPos != -1 && pSh && pSh->GetItem(SID_GRADIENT_LIST))
            {
                const SvxGradientListItem * pItem = pSh->GetItem(SID_GRADIENT_LIST);

                if(nPos < pItem->GetGradientList()->Count())
                {
                    const basegfx::BGradient aGradient = pItem->GetGradientList()->GetGradient(nPos)->GetGradient();
                    const XFillGradientItem aXFillGradientItem(mpLbFillAttr->get_active_text(), aGradient);

                    // #i122676# Change FillStyle and Gradient in one call
                    pSh->GetDispatcher()->ExecuteList(
                        SID_ATTR_FILL_GRADIENT, SfxCallMode::RECORD,
                        bFillStyleChange
                            ? std::initializer_list<SfxPoolItem const*>{ &aXFillGradientItem, &aXFillStyleItem }
                            : std::initializer_list<SfxPoolItem const*>{ &aXFillGradientItem });
                }
            }

            if (nPos != -1)
            {
                mnLastPosGradient = nPos;
            }
            break;
        }
        case HATCH:
        {
            sal_Int32 nPos = mpLbFillAttr->get_active();

            if (nPos == -1)
            {
                nPos = mnLastPosHatch;
            }

            if (nPos != -1 && pSh && pSh->GetItem(SID_HATCH_LIST))
            {
                const SvxHatchListItem * pItem = pSh->GetItem(SID_HATCH_LIST);

                if(nPos < pItem->GetHatchList()->Count())
                {
                    const XHatch aHatch = pItem->GetHatchList()->GetHatch(nPos)->GetHatch();
                    const XFillHatchItem aXFillHatchItem( mpLbFillAttr->get_active_text(), aHatch);

                    // #i122676# Change FillStyle and Hatch in one call
                    pSh->GetDispatcher()->ExecuteList(
                        SID_ATTR_FILL_HATCH, SfxCallMode::RECORD,
                        bFillStyleChange
                            ? std::initializer_list<SfxPoolItem const*>{ &aXFillHatchItem, &aXFillStyleItem }
                            : std::initializer_list<SfxPoolItem const*>{ &aXFillHatchItem });
                }
            }

            if (nPos != -1)
            {
                mnLastPosHatch = nPos;
            }
            break;
        }
        case BITMAP:
        {
            sal_Int32 nPos = mpLbFillAttr->get_active();

            if (nPos == -1)
            {
                nPos = mnLastPosBitmap;
            }

            if (nPos != -1 && pSh && pSh->GetItem(SID_BITMAP_LIST))
            {
                const SvxBitmapListItem * pItem = pSh->GetItem(SID_BITMAP_LIST);

                if(nPos < pItem->GetBitmapList()->Count())
                {
                    const XBitmapEntry* pXBitmapEntry = pItem->GetBitmapList()->GetBitmap(nPos);
                    const XFillBitmapItem aXFillBitmapItem(mpLbFillAttr->get_active_text(), pXBitmapEntry->GetGraphicObject());

                    // #i122676# Change FillStyle and Bitmap in one call
                    pSh->GetDispatcher()->ExecuteList(
                        SID_ATTR_FILL_BITMAP, SfxCallMode::RECORD,
                        bFillStyleChange
                            ? std::initializer_list<SfxPoolItem const*>{ &aXFillBitmapItem, &aXFillStyleItem }
                            : std::initializer_list<SfxPoolItem const*>{ &aXFillBitmapItem });
                }
            }

            if (nPos != -1)
            {
                mnLastPosBitmap = nPos;
            }
            break;
        }
        case PATTERN:
        {
            sal_Int32 nPos = mpLbFillAttr->get_active();

            if (nPos == -1)
            {
                nPos = mnLastPosPattern;
            }

            if (nPos != -1 && pSh && pSh->GetItem(SID_PATTERN_LIST))
            {
                const SvxPatternListItem * pItem = pSh->GetItem(SID_PATTERN_LIST);

                if(nPos < pItem->GetPatternList()->Count())
                {
                    const XBitmapEntry* pXBitmapEntry = pItem->GetPatternList()->GetBitmap(nPos);
                    const XFillBitmapItem aXFillBitmapItem(mpLbFillAttr->get_active_text(), pXBitmapEntry->GetGraphicObject());

                    // #i122676# Change FillStyle and Bitmap in one call
                    pSh->GetDispatcher()->ExecuteList(
                        SID_ATTR_FILL_BITMAP, SfxCallMode::RECORD,
                        bFillStyleChange
                            ? std::initializer_list<SfxPoolItem const*>{ &aXFillBitmapItem, &aXFillStyleItem }
                            : std::initializer_list<SfxPoolItem const*>{ &aXFillBitmapItem });
                }
            }

            if (nPos != -1)
            {
                mnLastPosPattern = nPos;
            }
            break;
        }

        default: break;
    }
}

void FillControl::SetOptimalSize()
{
    Size aSize(mxLbFillType->get_preferred_size());
    Size aFirstSize(mxToolBoxColor->get_preferred_size());
    auto nWidth = std::max(aFirstSize.Width(), LogicToPixel(Size(55, 0), MapMode(MapUnit::MapAppFont)).Width());
    auto nHeight = std::max(aSize.Height(), aFirstSize.Height());
    mxToolBoxColor->set_size_request(nWidth, -1);
    mxLbFillAttr->set_size_request(42, -1); //something narrow so the toolbar sets the overall size of this column
    SetSizePixel(Size(m_xContainer->get_preferred_size().Width(), nHeight));
}

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

void FillControl::GetFocus()
{
    // tdf#148047 if the dropdown is active then leave the focus
    // there and don't grab back to a different widget
    if (mxToolBoxColor->get_menu_item_active(u".uno:FillColor"_ustr))
        return;
    InterimItemWindow::GetFocus();
}

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