    /* -*- 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 <com/sun/star/presentation/EffectNodeType.hpp>
    #include <com/sun/star/animations/Timing.hpp>
    #include <com/sun/star/animations/Event.hpp>
    #include <com/sun/star/animations/EventTrigger.hpp>
    #include <com/sun/star/animations/AnimationFill.hpp>
    #include <com/sun/star/presentation/TextAnimationType.hpp>
    #include <com/sun/star/animations/ValuePair.hpp>
    #include <com/sun/star/awt/FontSlant.hpp>
    #include <com/sun/star/awt/FontWeight.hpp>
    #include <com/sun/star/awt/FontUnderline.hpp>
    #include <com/sun/star/drawing/XDrawPage.hpp>
    #include <com/sun/star/beans/XPropertySet.hpp>
    #include <com/sun/star/media/XManager.hpp>
    #include <com/sun/star/media/XPlayer.hpp>

    #include <memory>

    #include <i18nutil/unicode.hxx>
    #include <unotools/pathoptions.hxx>
    #include <vcl/tabctrl.hxx>
    #include <vcl/tabpage.hxx>
    #include <vcl/menubtn.hxx>
    #include <vcl/svapp.hxx>
    #include <vcl/fixed.hxx>
    #include <vcl/field.hxx>
    #include <vcl/lstbox.hxx>
    #include <vcl/layout.hxx>
    #include <vcl/weld.hxx>
    #include <vcl/decoview.hxx>
    #include <vcl/combobox.hxx>
    #include <vcl/menu.hxx>
    #include <vcl/settings.hxx>
    #include <vcl/builderfactory.hxx>

    #include <svtools/ctrlbox.hxx>
    #include <svtools/ctrltool.hxx>
    #include <sfx2/objsh.hxx>

    #include <svx/svxids.hrc>
    #include <editeng/flstitem.hxx>
    #include <svx/drawitem.hxx>

    #include <svx/colorbox.hxx>
    #include <svx/xtable.hxx>
    #include <svx/gallery.hxx>

    #include <svx/dialogs.hrc>
    #include <sdresid.hxx>

    #include "CustomAnimationDialog.hxx"
    #include "CustomAnimationPane.hxx"
    #include "STLPropertySet.hxx"

    #include <avmedia/mediawindow.hxx>

    #include <filedlg.hxx>
    #include <strings.hrc>
    #include <helpids.h>

    using namespace ::com::sun::star;
    using namespace ::com::sun::star::animations;
    using namespace ::com::sun::star::presentation;

    using ::com::sun::star::uno::UNO_QUERY;
    using ::com::sun::star::uno::UNO_QUERY_THROW;
    using ::com::sun::star::uno::Any;
    using ::com::sun::star::uno::makeAny;
    using ::com::sun::star::uno::Sequence;
    using ::com::sun::star::uno::Reference;
    using ::com::sun::star::uno::Exception;
    using ::com::sun::star::drawing::XShape;
    using ::com::sun::star::drawing::XDrawPage;
    using ::com::sun::star::beans::XPropertySet;

    namespace sd {

    class PresetPropertyBox  : public PropertySubControl
    {
    public:
        PresetPropertyBox( sal_Int32 nControlType, vcl::Window* pParent, const Any& rValue, const OUString& aPresetId, const Link<LinkParamNone*,void>& rModifyHdl );
        virtual ~PresetPropertyBox() override;

        virtual Any getValue() override;
        virtual void setValue( const Any& rValue, const OUString& rPresetId ) override;
        virtual Control* getControl() override;

    private:
        std::map< sal_uInt16, OUString > maPropertyValues;
        VclPtr<ListBox> mpControl;
        DECL_LINK(OnSelect, ListBox&, void);
        Link<LinkParamNone*,void> maModifyLink;
    };

    PresetPropertyBox::PresetPropertyBox( sal_Int32 nControlType, vcl::Window* pParent, const Any& rValue, const OUString& aPresetId, const Link<LinkParamNone*,void>& rModifyHdl )
    : PropertySubControl( nControlType ), maModifyLink(rModifyHdl)
    {
        mpControl = VclPtr<ListBox>::Create( pParent, WB_BORDER|WB_TABSTOP|WB_DROPDOWN );
        mpControl->set_hexpand(true);
        mpControl->SetDropDownLineCount( 10 );
        mpControl->SetSelectHdl( LINK(this, PresetPropertyBox, OnSelect) );
        mpControl->SetHelpId( HID_SD_CUSTOMANIMATIONPANE_PRESETPROPERTYBOX );

        setValue( rValue, aPresetId );
    }

    IMPL_LINK_NOARG(PresetPropertyBox, OnSelect, ListBox&, void)
    {
        maModifyLink.Call(nullptr);
    }

    void PresetPropertyBox::setValue( const Any& rValue, const OUString& rPresetId )
    {
        if( mpControl )
        {
            mpControl->Clear();

            const CustomAnimationPresets& rPresets = CustomAnimationPresets::getCustomAnimationPresets();
            CustomAnimationPresetPtr pDescriptor = rPresets.getEffectDescriptor( rPresetId );
            if( pDescriptor.get() )
            {

                OUString aPropertyValue;
                rValue >>= aPropertyValue;

                std::vector<OUString> aSubTypes( pDescriptor->getSubTypes() );

                mpControl->Enable( !aSubTypes.empty() );

                for( auto& aSubType : aSubTypes )
                {
                    sal_Int32 nPos = mpControl->InsertEntry( rPresets.getUINameForProperty( aSubType ) );
                    if( aSubType == aPropertyValue )
                        mpControl->SelectEntryPos( nPos );
                    maPropertyValues[nPos] = aSubType;
                }
            }
            else
            {
                mpControl->Enable( false );
            }
        }
    }

    PresetPropertyBox::~PresetPropertyBox()
    {
        mpControl.disposeAndClear();
    }

    Any PresetPropertyBox::getValue()
    {
        return makeAny( maPropertyValues[mpControl->GetSelectedEntryPos()] );
    }

    Control* PresetPropertyBox::getControl()
    {
        return mpControl;
    }

    class ColorPropertyBox  : public PropertySubControl
    {
    public:
        ColorPropertyBox( sal_Int32 nControlType, vcl::Window* pParent, const Any& rValue, const Link<LinkParamNone*,void>& rModifyHdl );
        virtual ~ColorPropertyBox() override;

        virtual Any getValue() override;
        virtual void setValue( const Any& rValue, const OUString& rPresetId  ) override;
        virtual Control* getControl() override;

    private:
        VclPtr<SvxColorListBox> mpControl;
        DECL_LINK(OnSelect, SvxColorListBox&, void);
        Link<LinkParamNone*,void> maModifyLink;
    };

    ColorPropertyBox::ColorPropertyBox( sal_Int32 nControlType, vcl::Window* pParent, const Any& rValue, const Link<LinkParamNone*,void>& rModifyHdl )
    : PropertySubControl( nControlType ), maModifyLink(rModifyHdl)
    {
        mpControl = VclPtr<SvxColorListBox>::Create(pParent);
        mpControl->set_hexpand(true);
        mpControl->SetSelectHdl( LINK(this, ColorPropertyBox, OnSelect) );
        mpControl->SetHelpId( HID_SD_CUSTOMANIMATIONPANE_COLORPROPERTYBOX );

        sal_Int32 nColor = 0;
        rValue >>= nColor;
        mpControl->SelectEntry(Color(nColor));
    }

    IMPL_LINK_NOARG(ColorPropertyBox, OnSelect, SvxColorListBox&, void)
    {
        maModifyLink.Call(nullptr);
    }

    ColorPropertyBox::~ColorPropertyBox()
    {
        mpControl.disposeAndClear();
    }

    void ColorPropertyBox::setValue( const Any& rValue, const OUString& )
    {
        if( mpControl )
        {
            sal_Int32 nColor = 0;
            rValue >>= nColor;

            mpControl->SetNoSelection();
            mpControl->SelectEntry(Color(nColor));
        }
    }

    Any ColorPropertyBox::getValue()
    {
        return makeAny( sal_Int32(mpControl->GetSelectEntryColor().GetRGBColor()) );
    }

    Control* ColorPropertyBox::getControl()
    {
        return mpControl;
    }

    class FontPropertyBox : public PropertySubControl
    {
    public:
        FontPropertyBox( sal_Int32 nControlType, vcl::Window* pParent, const Any& rValue, const Link<LinkParamNone*,void>& rModifyHdl );
        virtual ~FontPropertyBox() override;

        virtual Any getValue() override;
        virtual void setValue( const Any& rValue, const OUString& rPresetId  ) override;

        virtual Control* getControl() override;

    private:
        VclPtr<FontNameBox>         mpControl;
        Link<LinkParamNone*,void>   maModifyHdl;
        DECL_LINK(ControlSelectHdl, ComboBox&, void);
    };

    FontPropertyBox::FontPropertyBox( sal_Int32 nControlType, vcl::Window* pParent, const Any& rValue, const Link<LinkParamNone*,void>& rModifyHdl )
    : PropertySubControl( nControlType ), maModifyHdl(rModifyHdl)
    {
        mpControl = VclPtr<FontNameBox>::Create( pParent, WB_BORDER|WB_TABSTOP|WB_DROPDOWN );
        mpControl->set_hexpand(true);
        mpControl->SetDropDownLineCount( 10 );
        mpControl->SetSelectHdl( LINK(this, FontPropertyBox, ControlSelectHdl) );
        mpControl->SetHelpId( HID_SD_CUSTOMANIMATIONPANE_FONTPROPERTYBOX );

        SfxObjectShell* pDocSh = SfxObjectShell::Current();
        const SfxPoolItem* pItem;

        const FontList* pFontList = nullptr;
        bool bMustDelete = false;

        if ( pDocSh && ( (pItem = pDocSh->GetItem( SID_ATTR_CHAR_FONTLIST ) ) != nullptr) )
            pFontList = static_cast<const SvxFontListItem*>(pItem)->GetFontList();

        if(!pFontList)
        {
            pFontList = new FontList(Application::GetDefaultDevice(), nullptr);
            bMustDelete = true;
        }

        mpControl->Fill( pFontList );

        if( bMustDelete )
            delete pFontList;

        setValue( rValue, OUString() );
    }

    IMPL_LINK_NOARG(FontPropertyBox, ControlSelectHdl, ComboBox&, void)
    {
        maModifyHdl.Call(nullptr);
    }

    void FontPropertyBox::setValue( const Any& rValue, const OUString& )
    {
        if( mpControl )
        {
            OUString aFontName;
            rValue >>= aFontName;
            mpControl->SetText( aFontName );
        }
    }

    FontPropertyBox::~FontPropertyBox()
    {
        mpControl.disposeAndClear();
    }

    Any FontPropertyBox::getValue()
    {
        OUString aFontName( mpControl->GetText() );
        return makeAny( aFontName );
    }

    Control* FontPropertyBox::getControl()
    {
        return mpControl;
    }

    class DropdownMenuBox : public Edit
    {
    public:
        DropdownMenuBox( vcl::Window* pParent, Edit* pSubControl, PopupMenu* pMenu );
        virtual ~DropdownMenuBox() override;
        virtual void dispose() override;

        void Resize() override;
        bool PreNotify( NotifyEvent& rNEvt ) override;

        void SetMenuSelectHdl( const Link<MenuButton *, void>& rLink ) { mpDropdownButton->SetSelectHdl( rLink ); }

    private:
        VclPtr<Edit>        mpSubControl;
        VclPtr<MenuButton>  mpDropdownButton;
        VclPtr<PopupMenu>   mpMenu;
    };

    DropdownMenuBox::DropdownMenuBox( vcl::Window* pParent, Edit* pSubControl, PopupMenu* pMenu )
    :   Edit( pParent, WB_BORDER|WB_TABSTOP| WB_DIALOGCONTROL ),
        mpSubControl(pSubControl),mpDropdownButton(nullptr),mpMenu(pMenu)
    {
        mpDropdownButton = VclPtr<MenuButton>::Create( this, WB_NOLIGHTBORDER | WB_RECTSTYLE | WB_NOTABSTOP);
        mpDropdownButton->SetSymbol(SymbolType::SPIN_DOWN);
        mpDropdownButton->Show();
        mpDropdownButton->SetPopupMenu( pMenu );

        SetSubEdit( mpSubControl );
        set_hexpand(true);
        mpSubControl->SetParent( this );
        mpSubControl->Show();
    }

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

    void DropdownMenuBox::dispose()
    {
        SetSubEdit(nullptr);
        mpDropdownButton.disposeAndClear();
        mpMenu.disposeAndClear();
        mpSubControl.disposeAndClear();
        Edit::dispose();
    }

    void DropdownMenuBox::Resize()
    {
        Size aOutSz = GetOutputSizePixel();

        long nSBWidth = GetSettings().GetStyleSettings().GetScrollBarSize();
        nSBWidth = CalcZoom( nSBWidth );
        mpSubControl->setPosSizePixel( 0, 1, aOutSz.Width() - nSBWidth, aOutSz.Height()-2 );
        mpDropdownButton->setPosSizePixel( aOutSz.Width() - nSBWidth, 0, nSBWidth, aOutSz.Height() );
    }

    bool DropdownMenuBox::PreNotify( NotifyEvent& rNEvt )
    {
        bool bResult = true;

        MouseNotifyEvent nSwitch=rNEvt.GetType();
        if (nSwitch==MouseNotifyEvent::KEYINPUT)
        {
            const vcl::KeyCode& aKeyCode=rNEvt.GetKeyEvent()->GetKeyCode();
            sal_uInt16 nKey=aKeyCode.GetCode();

            if (nKey==KEY_DOWN && aKeyCode.IsMod2())
            {
                mpDropdownButton->KeyInput( *rNEvt.GetKeyEvent() );
            }
            else
            {
                bResult=Edit::PreNotify(rNEvt);
            }
        }
        else
            bResult=Edit::PreNotify(rNEvt);

        return bResult;
    }

    class CharHeightPropertyBox : public PropertySubControl
    {
    public:
        CharHeightPropertyBox( sal_Int32 nControlType, vcl::Window* pParent, const Any& rValue, const Link<LinkParamNone*,void>& rModifyHdl );
        virtual ~CharHeightPropertyBox() override;

        virtual Any getValue() override;
        virtual void setValue( const Any& rValue, const OUString& ) override;

        virtual Control* getControl() override;

        DECL_LINK( implMenuSelectHdl, MenuButton*, void );

    private:
        DECL_LINK( EditModifyHdl, Edit&, void );
        VclBuilder maBuilder;
        VclPtr<DropdownMenuBox> mpControl;
        VclPtr<PopupMenu> mpMenu;
        VclPtr<MetricField> mpMetric;
        Link<LinkParamNone*,void> maModifyHdl;
    };

    CharHeightPropertyBox::CharHeightPropertyBox(sal_Int32 nControlType, vcl::Window* pParent, const Any& rValue, const Link<LinkParamNone*,void>& rModifyHdl)
        : PropertySubControl(nControlType)
        , maBuilder(nullptr, VclBuilderContainer::getUIRootDir(), "modules/simpress/ui/fontsizemenu.ui", "")
        , maModifyHdl(rModifyHdl)
    {
        mpMetric.set( VclPtr<MetricField>::Create( pParent, WB_TABSTOP|WB_IGNORETAB| WB_NOBORDER) );
        mpMetric->SetUnit( FUNIT_PERCENT );
        mpMetric->SetMin( 0 );
        mpMetric->SetMax( 1000 );

        mpMenu = maBuilder.get_menu("menu");
        mpControl = VclPtr<DropdownMenuBox>::Create( pParent, mpMetric, mpMenu );
        mpControl->SetMenuSelectHdl( LINK( this, CharHeightPropertyBox, implMenuSelectHdl ));
        mpControl->SetModifyHdl( LINK( this, CharHeightPropertyBox, EditModifyHdl ) );
        mpControl->SetHelpId( HID_SD_CUSTOMANIMATIONPANE_CHARHEIGHTPROPERTYBOX );

        setValue( rValue, OUString() );
    }

    CharHeightPropertyBox::~CharHeightPropertyBox()
    {
        maBuilder.disposeBuilder();
        mpControl.disposeAndClear();
    }

    IMPL_LINK_NOARG( CharHeightPropertyBox, EditModifyHdl, Edit&, void )
    {
        maModifyHdl.Call(nullptr);
    }

    IMPL_LINK( CharHeightPropertyBox, implMenuSelectHdl, MenuButton*, pPb, void )
    {
        sal_Int32 nValue = pPb->GetCurItemIdent().toInt32();
        mpMetric->SetValue(nValue);
        mpMetric->Modify();
    }

    void CharHeightPropertyBox::setValue( const Any& rValue, const OUString& )
    {
        if( mpMetric.get() )
        {
            double fValue = 0.0;
            rValue >>= fValue;
            mpMetric->SetValue( static_cast<long>(fValue * 100.0) );
        }
    }

    Any CharHeightPropertyBox::getValue()
    {
        return makeAny( static_cast<double>(mpMetric->GetValue()) / 100.0 );
    }

    Control* CharHeightPropertyBox::getControl()
    {
        return mpControl;
    }

    class TransparencyPropertyBox : public PropertySubControl
    {
    public:
        TransparencyPropertyBox( sal_Int32 nControlType, vcl::Window* pParent, const Any& rValue, const Link<LinkParamNone*,void>& rModifyHdl );
        virtual ~TransparencyPropertyBox() override;

        virtual Any getValue() override;
        virtual void setValue( const Any& rValue, const OUString& rPresetId  ) override;

        virtual Control* getControl() override;

        DECL_LINK( implMenuSelectHdl, MenuButton*, void );
        DECL_LINK( implModifyHdl, Edit&, void );

        void updateMenu();

    private:
        VclPtr<DropdownMenuBox>   mpControl;
        VclPtr<PopupMenu>         mpMenu;
        VclPtr<MetricField>       mpMetric;
        Link<LinkParamNone*,void> maModifyHdl;
    };

    TransparencyPropertyBox::TransparencyPropertyBox( sal_Int32 nControlType, vcl::Window* pParent, const Any& rValue, const Link<LinkParamNone*,void>& rModifyHdl )
    : PropertySubControl( nControlType )
    , maModifyHdl( rModifyHdl )
    {
        mpMetric.set( VclPtr<MetricField>::Create( pParent ,WB_TABSTOP|WB_IGNORETAB| WB_NOBORDER) );
        mpMetric->SetUnit( FUNIT_PERCENT );
        mpMetric->SetMin( 0 );
        mpMetric->SetMax( 100 );

        mpMenu = VclPtr<PopupMenu>::Create();
        for( sal_Int32 i = 25; i < 101; i += 25 )
        {
            OUString aStr(unicode::formatPercent(i,
                Application::GetSettings().GetUILanguageTag()));
            mpMenu->InsertItem( i, aStr );
        }

        mpControl = VclPtr<DropdownMenuBox>::Create( pParent, mpMetric, mpMenu );
        mpControl->SetMenuSelectHdl( LINK( this, TransparencyPropertyBox, implMenuSelectHdl ));
        mpControl->SetHelpId( HID_SD_CUSTOMANIMATIONPANE_TRANSPARENCYPROPERTYBOX );

        Link<Edit&,void> aLink( LINK( this, TransparencyPropertyBox, implModifyHdl ) );
        mpControl->SetModifyHdl( aLink );

        setValue( rValue, OUString()  );
    }

    TransparencyPropertyBox::~TransparencyPropertyBox()
    {
        mpControl.disposeAndClear();
    }

    void TransparencyPropertyBox::updateMenu()
    {
        sal_Int64 nValue = mpMetric->GetValue();
        for( sal_uInt16 i = 25; i < 101; i += 25 )
            mpMenu->CheckItem( i, nValue == i );
    }

    IMPL_LINK_NOARG(TransparencyPropertyBox, implModifyHdl, Edit&, void)
    {
        updateMenu();
        maModifyHdl.Call(nullptr);
    }

    IMPL_LINK( TransparencyPropertyBox, implMenuSelectHdl, MenuButton*, pPb, void )
    {
        if( pPb->GetCurItemId() != mpMetric->GetValue() )
        {
            mpMetric->SetValue( pPb->GetCurItemId() );
            mpMetric->Modify();
        }
    }

    void TransparencyPropertyBox::setValue( const Any& rValue, const OUString& )
    {
        if( mpMetric.get() )
        {
            double fValue = 0.0;
            rValue >>= fValue;
            long nValue = static_cast<long>(fValue * 100);
            mpMetric->SetValue( nValue );
            updateMenu();
        }
    }

    Any TransparencyPropertyBox::getValue()
    {
        return makeAny( static_cast<double>(mpMetric->GetValue()) / 100.0 );
    }

    Control* TransparencyPropertyBox::getControl()
    {
        return mpControl;
    }

    class RotationPropertyBox : public PropertySubControl
    {
    public:
        RotationPropertyBox( sal_Int32 nControlType, vcl::Window* pParent, const Any& rValue, const Link<LinkParamNone*,void>& rModifyHdl );
        virtual ~RotationPropertyBox() override;

        virtual Any getValue() override;
        virtual void setValue( const Any& rValue, const OUString& ) override;

        virtual Control* getControl() override;

        DECL_LINK( implMenuSelectHdl, MenuButton*, void );
        DECL_LINK( implModifyHdl, Edit&, void );

        void updateMenu();

    private:
        VclBuilder                maBuilder;
        VclPtr<DropdownMenuBox>   mpControl;
        VclPtr<PopupMenu>         mpMenu;
        VclPtr<MetricField>       mpMetric;
        Link<LinkParamNone*,void> maModifyHdl;
    };

    RotationPropertyBox::RotationPropertyBox( sal_Int32 nControlType, vcl::Window* pParent, const Any& rValue, const Link<LinkParamNone*,void>& rModifyHdl )
        : PropertySubControl(nControlType)
        , maBuilder(nullptr, VclBuilderContainer::getUIRootDir(), "modules/simpress/ui/rotatemenu.ui", "")
        , maModifyHdl(rModifyHdl)
    {
        mpMetric.set( VclPtr<MetricField>::Create( pParent ,WB_TABSTOP|WB_IGNORETAB| WB_NOBORDER) );
        mpMetric->SetUnit( FUNIT_CUSTOM );
        mpMetric->SetCustomUnitText( OUString( u'\x00b0') ); // degree sign
        mpMetric->SetMin( -10000 );
        mpMetric->SetMax( 10000 );

        mpMenu = maBuilder.get_menu("menu");
        mpControl = VclPtr<DropdownMenuBox>::Create( pParent, mpMetric, mpMenu );
        mpControl->SetMenuSelectHdl( LINK( this, RotationPropertyBox, implMenuSelectHdl ));
        mpControl->SetHelpId( HID_SD_CUSTOMANIMATIONPANE_ROTATIONPROPERTYBOX );

        Link<Edit&,void> aLink( LINK( this, RotationPropertyBox, implModifyHdl ) );
        mpControl->SetModifyHdl( aLink );

        setValue( rValue, OUString() );
    }

    RotationPropertyBox::~RotationPropertyBox()
    {
        maBuilder.disposeBuilder();
        mpControl.disposeAndClear();
    }

    void RotationPropertyBox::updateMenu()
    {
        sal_Int64 nValue = mpMetric->GetValue();
        bool bDirection = nValue >= 0;
        nValue = (nValue < 0 ? -nValue : nValue);

        mpMenu->CheckItem("90", nValue == 90);
        mpMenu->CheckItem("180", nValue == 180);
        mpMenu->CheckItem("360", nValue == 360);
        mpMenu->CheckItem("720", nValue == 720);

        mpMenu->CheckItem("closewise", bDirection);
        mpMenu->CheckItem("counterclock", !bDirection);
    }

    IMPL_LINK_NOARG(RotationPropertyBox, implModifyHdl, Edit&, void)
    {
        updateMenu();
        maModifyHdl.Call(nullptr);
    }

    IMPL_LINK( RotationPropertyBox, implMenuSelectHdl, MenuButton*, pPb, void )
    {
        sal_Int64 nValue = mpMetric->GetValue();
        bool bDirection = nValue >= 0;
        nValue = (nValue < 0 ? -nValue : nValue);

        OString sIdent = pPb->GetCurItemIdent();
        if (sIdent == "clockwise")
            bDirection = true;
        else if (sIdent == "counterclock")
            bDirection = false;
        else
            nValue = sIdent.toInt32();

        if( !bDirection )
            nValue = -nValue;

        if( nValue != mpMetric->GetValue() )
        {
            mpMetric->SetValue( nValue );
            mpMetric->Modify();
        }
    }

    void RotationPropertyBox::setValue( const Any& rValue, const OUString& )
    {
        if( mpMetric.get() )
        {
            double fValue = 0.0;
            rValue >>= fValue;
            long nValue = static_cast<long>(fValue);
            mpMetric->SetValue( nValue );
            updateMenu();
        }
    }

    Any RotationPropertyBox::getValue()
    {
        return makeAny( static_cast<double>(mpMetric->GetValue()) );
    }

    Control* RotationPropertyBox::getControl()
    {
        return mpControl;
    }

    class ScalePropertyBox : public PropertySubControl
    {
    public:
        ScalePropertyBox( sal_Int32 nControlType, vcl::Window* pParent, const Any& rValue, const Link<LinkParamNone*,void>& rModifyHdl );
        virtual ~ScalePropertyBox() override;

        virtual Any getValue() override;
        virtual void setValue( const Any& rValue, const OUString& ) override;

        virtual Control* getControl() override;

        DECL_LINK( implMenuSelectHdl, MenuButton*, void );
        DECL_LINK( implModifyHdl, Edit&, void );

        void updateMenu();

    private:
        VclBuilder maBuilder;
        VclPtr<DropdownMenuBox>   mpControl;
        VclPtr<PopupMenu>         mpMenu;
        VclPtr<MetricField>       mpMetric;
        Link<LinkParamNone*,void> maModifyHdl;
        int                       mnDirection;
    };

    ScalePropertyBox::ScalePropertyBox(sal_Int32 nControlType, vcl::Window* pParent, const Any& rValue, const Link<LinkParamNone*,void>& rModifyHdl)
        : PropertySubControl( nControlType )
        , maBuilder(nullptr, VclBuilderContainer::getUIRootDir(), "modules/simpress/ui/scalemenu.ui", "")
        , maModifyHdl( rModifyHdl )
    {
        mpMetric.set( VclPtr<MetricField>::Create( pParent ,WB_TABSTOP|WB_IGNORETAB| WB_NOBORDER) );
        mpMetric->SetUnit( FUNIT_PERCENT );
        mpMetric->SetMin( 0 );
        mpMetric->SetMax( 10000 );

        mpMenu = maBuilder.get_menu("menu");
        mpControl = VclPtr<DropdownMenuBox>::Create( pParent, mpMetric, mpMenu );
        mpControl->SetMenuSelectHdl( LINK( this, ScalePropertyBox, implMenuSelectHdl ));
        mpControl->SetHelpId( HID_SD_CUSTOMANIMATIONPANE_SCALEPROPERTYBOX );

        Link<Edit&,void> aLink( LINK( this, ScalePropertyBox, implModifyHdl ) );
        mpControl->SetModifyHdl( aLink );

        setValue( rValue, OUString() );
    }

    ScalePropertyBox::~ScalePropertyBox()
    {
        maBuilder.disposeBuilder();
        mpControl.disposeAndClear();
    }

    void ScalePropertyBox::updateMenu()
    {
        sal_Int64 nValue = mpMetric->GetValue();

        mpMenu->CheckItem("25", nValue == 25);
        mpMenu->CheckItem("50", nValue == 50);
        mpMenu->CheckItem("150", nValue == 150);
        mpMenu->CheckItem("400", nValue == 400);

        mpMenu->CheckItem("hori", mnDirection == 1);
        mpMenu->CheckItem("vert", mnDirection == 2);
        mpMenu->CheckItem("both", mnDirection == 3);
    }

    IMPL_LINK_NOARG(ScalePropertyBox, implModifyHdl, Edit&, void)
    {
        updateMenu();
        maModifyHdl.Call(nullptr);
    }

    IMPL_LINK( ScalePropertyBox, implMenuSelectHdl, MenuButton*, pPb, void )
    {
        sal_Int64 nValue = mpMetric->GetValue();

        int nDirection = mnDirection;

        OString sIdent(pPb->GetCurItemIdent());
        if (sIdent == "hori")
            nDirection = 1;
        else if (sIdent == "veri")
            nDirection = 2;
        else if (sIdent == "both")
            nDirection = 3;
        else
            nValue = sIdent.toInt32();

        bool bModified = false;

        if( nDirection != mnDirection )
        {
            mnDirection = nDirection;
            bModified = true;
        }

        if( nValue != mpMetric->GetValue() )
        {
            mpMetric->SetValue( nValue );
            bModified = true;
        }

        if( bModified )
        {
            mpMetric->Modify();
            updateMenu();
        }
    }

    void ScalePropertyBox::setValue( const Any& rValue, const OUString& )
    {
        if( mpMetric.get() )
        {
            ValuePair aValues;
            rValue >>= aValues;

            double fValue1 = 0.0;
            double fValue2 = 0.0;

            aValues.First >>= fValue1;
            aValues.Second >>= fValue2;

            if( fValue2 == 0.0 )
                mnDirection = 1;
            else if( fValue1 == 0.0 )
                mnDirection = 2;
            else
                mnDirection = 3;

            // Shrink animation is represented by negative value
            // Shrink factor is calculated as (1 + $fValue)
            // e.g 1 + (-0.75) = 0.25 => shrink to 25% of the size
            // 0.25 = -0.75 + 1
            if ( fValue1 < 0.0 )
                fValue1 += 1;
            if ( fValue2 < 0.0 )
                fValue2 += 1;

            long nValue;
            if( fValue1 )
                nValue = static_cast<long>(fValue1 * 100.0);
            else
                nValue = static_cast<long>(fValue2 * 100.0);
            mpMetric->SetValue( nValue );
            updateMenu();
        }
    }

    Any ScalePropertyBox::getValue()
    {
        double fValue1 = static_cast<double>(mpMetric->GetValue()) / 100.0;

        // Shrink animation is represented by value < 1 (< 100%)
        // Shrink factor is calculated as (1 + $fValue)
        // e.g shrink to 25% of the size: 0.25 = 1 + $fValue =>
        // $fValue = -0.75; -0.75 = 0.25 -1
        if ( fValue1 < 1.0 )
            fValue1 -= 1;

        double fValue2 = fValue1;

        if( mnDirection == 1 )
            fValue2 = 0.0;
        else if( mnDirection == 2 )
            fValue1 = 0.0;

        ValuePair aValues;
        aValues.First <<= fValue1;
        aValues.Second <<= fValue2;

        return makeAny( aValues );
    }

    Control* ScalePropertyBox::getControl()
    {
        return mpControl;
    }

    class FontStylePropertyBox : public PropertySubControl
    {
    public:
        FontStylePropertyBox( sal_Int32 nControlType, vcl::Window* pParent, const Any& rValue, const Link<LinkParamNone*,void>& rModifyHdl );
        virtual ~FontStylePropertyBox() override;

        virtual Any getValue() override;
        virtual void setValue( const Any& rValue, const OUString& ) override;

        virtual Control* getControl() override;

        DECL_LINK( implMenuSelectHdl, MenuButton*, void );

        void update();

    private:
        VclBuilder                maBuilder;
        VclPtr<DropdownMenuBox>   mpControl;
        VclPtr<PopupMenu>         mpMenu;
        VclPtr<Edit>              mpEdit;
        Link<LinkParamNone*,void> maModifyHdl;

        float mfFontWeight;
        awt::FontSlant meFontSlant;
        sal_Int16 mnFontUnderline;
    };

    FontStylePropertyBox::FontStylePropertyBox( sal_Int32 nControlType, vcl::Window* pParent, const Any& rValue, const Link<LinkParamNone*,void>& rModifyHdl )
        : PropertySubControl(nControlType)
        , maBuilder(nullptr, VclBuilderContainer::getUIRootDir(), "modules/simpress/ui/fontstylemenu.ui", "")
        , maModifyHdl( rModifyHdl )
    {
        mpEdit.set( VclPtr<Edit>::Create( pParent, WB_TABSTOP|WB_IGNORETAB|WB_NOBORDER|WB_READONLY) );
        mpEdit->SetText( SdResId(STR_CUSTOMANIMATION_SAMPLE) );

        mpMenu = maBuilder.get_menu("menu");
        mpControl = VclPtr<DropdownMenuBox>::Create( pParent, mpEdit, mpMenu );
        mpControl->SetMenuSelectHdl( LINK( this, FontStylePropertyBox, implMenuSelectHdl ));
        mpControl->SetHelpId( HID_SD_CUSTOMANIMATIONPANE_FONTSTYLEPROPERTYBOX );

        setValue( rValue, OUString() );
    }

    FontStylePropertyBox::~FontStylePropertyBox()
    {
        maBuilder.disposeBuilder();
        mpControl.disposeAndClear();
    }

    void FontStylePropertyBox::update()
    {
        // update menu
        mpMenu->CheckItem("bold", mfFontWeight == awt::FontWeight::BOLD);
        mpMenu->CheckItem("italic", meFontSlant == awt::FontSlant_ITALIC);
        mpMenu->CheckItem("underline", mnFontUnderline != awt::FontUnderline::NONE );

        // update sample edit
        vcl::Font aFont( mpEdit->GetFont() );
        aFont.SetWeight( mfFontWeight == awt::FontWeight::BOLD ? WEIGHT_BOLD : WEIGHT_NORMAL );
        aFont.SetItalic( meFontSlant == awt::FontSlant_ITALIC ? ITALIC_NORMAL : ITALIC_NONE  );
        aFont.SetUnderline( mnFontUnderline == awt::FontUnderline::NONE ? LINESTYLE_NONE : LINESTYLE_SINGLE );
        mpEdit->SetFont( aFont );
        mpEdit->Invalidate();
    }

    IMPL_LINK( FontStylePropertyBox, implMenuSelectHdl, MenuButton*, pPb, void )
    {
        OString sIdent = pPb->GetCurItemIdent();
        if (sIdent == "bold")
        {
            if( mfFontWeight == awt::FontWeight::BOLD )
                mfFontWeight = awt::FontWeight::NORMAL;
            else
                mfFontWeight = awt::FontWeight::BOLD;
        }
        else if (sIdent == "italic")
        {
            if( meFontSlant == awt::FontSlant_ITALIC )
                meFontSlant = awt::FontSlant_NONE;
            else
                meFontSlant = awt::FontSlant_ITALIC;
        }
        else if (sIdent == "underline")
        {
            if( mnFontUnderline == awt::FontUnderline::SINGLE )
                mnFontUnderline = awt::FontUnderline::NONE;
            else
                mnFontUnderline = awt::FontUnderline::SINGLE;
        }

        update();
        maModifyHdl.Call(nullptr);
    }

    void FontStylePropertyBox::setValue( const Any& rValue, const OUString& )
    {
        Sequence<Any> aValues;
        rValue >>= aValues;

        aValues[0] >>= mfFontWeight;
        aValues[1] >>= meFontSlant;
        aValues[2] >>= mnFontUnderline;

        update();
    }

    Any FontStylePropertyBox::getValue()
    {
        Sequence<Any> aValues(3);
        aValues[0] <<= mfFontWeight;
        aValues[1] <<= meFontSlant;
        aValues[2] <<= mnFontUnderline;
        return makeAny( aValues );
    }

    Control* FontStylePropertyBox::getControl()
    {
        return mpControl;
    }

    class CustomAnimationEffectTabPage : public TabPage
    {
    public:
        CustomAnimationEffectTabPage( vcl::Window* pParent, const STLPropertySet* pSet );
        virtual ~CustomAnimationEffectTabPage() override;
        virtual void dispose() override;

        void update( STLPropertySet* pSet );
        DECL_LINK( implSelectHdl, ListBox&, void );
        DECL_LINK( implClickHdl, Button*, void );
        void implHdl(Control const *);

    private:
        void updateControlStates();
        void fillSoundListBox();
        void clearSoundListBox();
        sal_Int32 getSoundObject( const OUString& rStr );
        void openSoundFileDialog();
        void onSoundPreview();

    private:
        ::std::vector< OUString > maSoundList;
        bool mbHasText;
        const STLPropertySet* mpSet;

        VclPtr<VclFrame>       mpSettings;
        VclPtr<FixedText>      mpFTProperty1;
        VclPtr<PropertyControl> mpLBProperty1;
        VclPtr<VclHBox>        mpPlaceholderBox;
        VclPtr<CheckBox>       mpCBSmoothStart;
        VclPtr<CheckBox>       mpCBSmoothEnd;

        VclPtr<FixedText>      mpFTSound;
        VclPtr<ListBox>        mpLBSound;
        VclPtr<PushButton>     mpPBSoundPreview;
        VclPtr<FixedText>      mpFTAfterEffect;
        VclPtr<ListBox>        mpLBAfterEffect;
        VclPtr<FixedText>      mpFTDimColor;
        VclPtr<SvxColorListBox> mpCLBDimColor;
        VclPtr<FixedText>      mpFTTextAnim;
        VclPtr<ListBox>        mpLBTextAnim;
        VclPtr<MetricField>    mpMFTextDelay;
        VclPtr<FixedText>      mpFTTextDelay;

        css::uno::Reference< css::media::XPlayer > mxPlayer;
    };

    CustomAnimationEffectTabPage::CustomAnimationEffectTabPage( vcl::Window* pParent, const STLPropertySet* pSet )
    : TabPage( pParent, "EffectTab", "modules/simpress/ui/customanimationeffecttab.ui" ), mbHasText( false ), mpSet(pSet )
    {
        get(mpSettings, "settings" );
        get(mpFTProperty1, "prop_label1" );
        get(mpLBProperty1, "prop_list1" );
        get(mpPlaceholderBox, "placeholder" );
        get(mpCBSmoothStart, "smooth_start" );
        get(mpCBSmoothEnd, "smooth_end" );
        get(mpFTSound, "sound_label");
        get(mpLBSound, "sound_list" );
        get(mpPBSoundPreview, "sound_preview" );
        get(mpFTAfterEffect, "aeffect_label" );
        get(mpLBAfterEffect, "aeffect_list" );
        get(mpFTDimColor, "dim_color_label" );
        get(mpCLBDimColor, "dim_color_list" );
        mpCLBDimColor->SelectEntry(COL_BLACK);
        get(mpFTTextAnim, "text_animation_label" );
        get(mpLBTextAnim, "text_animation_list" );
        get(mpMFTextDelay,"text_delay" );
        get(mpFTTextDelay,"text_delay_label" );

        // fill the soundbox
        fillSoundListBox();

        mpLBSound->SetSelectHdl( LINK( this, CustomAnimationEffectTabPage, implSelectHdl ) );

        mpPBSoundPreview->SetClickHdl( LINK( this, CustomAnimationEffectTabPage, implClickHdl ) );

        // only show settings if all selected effects have the same preset-id
        if( pSet->getPropertyState( nHandlePresetId ) != STLPropertyState::Ambiguous )
        {
            OUString aPresetId;
            pSet->getPropertyValue( nHandlePresetId ) >>= aPresetId;

            // property 1

            if( pSet->getPropertyState( nHandleProperty1Type ) != STLPropertyState::Ambiguous )
            {
                sal_Int32 nType = 0;
                pSet->getPropertyValue( nHandleProperty1Type ) >>= nType;

                if( nType != nPropertyTypeNone )
                {
                    // set ui name for property at fixed text
                    OUString aPropertyName( getPropertyName( nType ) );

                    if( !aPropertyName.isEmpty() )
                    {
                        mpSettings->Show();
                        mpFTProperty1->SetText( aPropertyName );
                    }

                    // get property value
                    const Any aValue( pSet->getPropertyValue( nHandleProperty1Value ) );

                    // create property sub control
                    mpLBProperty1->setSubControl( PropertySubControl::create( nType, mpPlaceholderBox, aValue, aPresetId, Link<LinkParamNone*,void>() ));
                }
            }

            mpFTProperty1->Enable( mpLBProperty1->IsEnabled() );

            // accelerate & decelerate

            if( pSet->getPropertyState( nHandleAccelerate ) == STLPropertyState::Direct )
            {
                mpCBSmoothStart->Show();
                mpCBSmoothEnd->Show();

                double fTemp = 0.0;
                pSet->getPropertyValue( nHandleAccelerate ) >>= fTemp;
                mpCBSmoothStart->Check( fTemp > 0.0 );

                pSet->getPropertyValue( nHandleDecelerate ) >>= fTemp;
                mpCBSmoothEnd->Check( fTemp > 0.0 );
            }
        }

        // init after effect controls

        mpLBAfterEffect->SetSelectHdl( LINK( this, CustomAnimationEffectTabPage, implSelectHdl ) );
        mpLBTextAnim->SetSelectHdl( LINK( this, CustomAnimationEffectTabPage, implSelectHdl ) );

        if( (pSet->getPropertyState( nHandleHasAfterEffect ) != STLPropertyState::Ambiguous) &&
            (pSet->getPropertyState( nHandleAfterEffectOnNextEffect ) != STLPropertyState::Ambiguous) &&
            (pSet->getPropertyState( nHandleDimColor ) != STLPropertyState::Ambiguous))
        {
            bool bHasAfterEffect = false;
            pSet->getPropertyValue( nHandleHasAfterEffect ) >>= bHasAfterEffect;

            sal_Int32 nPos = 0;
            if( bHasAfterEffect )
            {
                nPos++;

                bool bAfterEffectOnNextClick = false;
                pSet->getPropertyValue( nHandleAfterEffectOnNextEffect ) >>= bAfterEffectOnNextClick;
                Any aDimColor( pSet->getPropertyValue( nHandleDimColor ) );

                if( aDimColor.hasValue() )
                {
                    sal_Int32 nColor = 0;
                    aDimColor >>= nColor;
                    Color aColor = Color(nColor);
                    mpCLBDimColor->SelectEntry(aColor);
                }
                else
                {
                    nPos++;
                    if( bAfterEffectOnNextClick )
                        nPos++;
                }
            }

            mpLBAfterEffect->SelectEntryPos( nPos );
        }

        if( pSet->getPropertyState( nHandleHasText ) != STLPropertyState::Ambiguous )
            pSet->getPropertyValue( nHandleHasText ) >>= mbHasText;

        if( mbHasText )
        {
            if( pSet->getPropertyState( nHandleIterateType ) != STLPropertyState::Ambiguous)
            {
                sal_Int32 nPos = LISTBOX_ENTRY_NOTFOUND;

                sal_Int32 nIterateType = 0;
                pSet->getPropertyValue( nHandleIterateType ) >>= nIterateType;
                switch( nIterateType )
                {
                case TextAnimationType::BY_PARAGRAPH:   nPos = 0; break;
                case TextAnimationType::BY_WORD:        nPos = 1; break;
                case TextAnimationType::BY_LETTER:      nPos = 2; break;
                }

                mpLBTextAnim->SelectEntryPos( nPos );
            }

            if( pSet->getPropertyState( nHandleIterateInterval ) != STLPropertyState::Default )
            {
                double fIterateInterval = 0.0;
                pSet->getPropertyValue( nHandleIterateInterval ) >>= fIterateInterval;
                mpMFTextDelay->SetValue( static_cast<long>(fIterateInterval*10) );
            }
        }
        else
        {
            mpFTTextAnim->Enable( false );
            mpLBTextAnim->Enable( false );
            mpMFTextDelay->Enable( false );
            mpFTTextDelay->Enable( false );

        }

        if( pSet->getPropertyState( nHandleSoundURL ) != STLPropertyState::Ambiguous )
        {
            sal_Int32 nPos = 0;

            const Any aValue( pSet->getPropertyValue( nHandleSoundURL ) );

            if( aValue.getValueType() == ::cppu::UnoType<sal_Bool>::get() )
            {
                nPos = 1;
            }
            else
            {
                OUString aSoundURL;
                aValue >>= aSoundURL;

                if( !aSoundURL.isEmpty() )
                {
                    sal_uLong i;
                    for( i = 0; i < maSoundList.size(); i++ )
                    {
                        OUString aString = maSoundList[ i ];
                        if( aString == aSoundURL )
                        {
                            nPos = static_cast<sal_Int32>(i)+2;
                            break;
                        }
                    }

                    if( nPos == 0 )
                    {
                        nPos = static_cast<sal_Int32>(maSoundList.size())+2;
                        maSoundList.push_back( aSoundURL );
                        INetURLObject aURL( aSoundURL );
                        nPos = mpLBSound->InsertEntry( aURL.GetBase(), nPos );
                    }
                }
            }

            if( nPos != LISTBOX_ENTRY_NOTFOUND )
                mpLBSound->SelectEntryPos( nPos );
        }

        updateControlStates();

    }

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

    void CustomAnimationEffectTabPage::dispose()
    {
        clearSoundListBox();
        mpSettings.clear();
        mpFTProperty1.clear();
        mpLBProperty1.clear();
        mpPlaceholderBox.clear();
        mpCBSmoothStart.clear();
        mpCBSmoothEnd.clear();
        mpFTSound.clear();
        mpLBSound.clear();
        mpPBSoundPreview.clear();
        mpFTAfterEffect.clear();
        mpLBAfterEffect.clear();
        mpFTDimColor.clear();
        mpCLBDimColor.clear();
        mpFTTextAnim.clear();
        mpLBTextAnim.clear();
        mpMFTextDelay.clear();
        mpFTTextDelay.clear();
        TabPage::dispose();
    }

    void CustomAnimationEffectTabPage::updateControlStates()
    {
        sal_Int32 nPos = mpLBAfterEffect->GetSelectedEntryPos();
        mpCLBDimColor->Enable( nPos == 1 );
        mpFTDimColor->Enable( nPos == 1 );

        if( mbHasText )
        {
            nPos = mpLBTextAnim->GetSelectedEntryPos();
            mpMFTextDelay->Enable( nPos != 0 );
            mpFTTextDelay->Enable( nPos != 0 );
        }

        nPos = mpLBSound->GetSelectedEntryPos();
        mpPBSoundPreview->Enable( nPos >= 2 );
    }

    IMPL_LINK( CustomAnimationEffectTabPage, implClickHdl, Button*, pBtn, void )
    {
        implHdl(pBtn);
    }
    IMPL_LINK( CustomAnimationEffectTabPage, implSelectHdl, ListBox&, rListBox, void )
    {
        implHdl(&rListBox);
    }

    void CustomAnimationEffectTabPage::implHdl(Control const * pControl )
    {
        if( pControl == mpLBTextAnim )
        {
            if( mpMFTextDelay->GetValue() == 0 )
                mpMFTextDelay->SetValue( 100 );
        }
        else if( pControl == mpLBSound )
        {
            sal_Int32 nPos = mpLBSound->GetSelectedEntryPos();
            if( nPos == (mpLBSound->GetEntryCount() - 1) )
            {
                openSoundFileDialog();
            }
        }
        else if( pControl == mpPBSoundPreview )
        {
            onSoundPreview();
        }

        updateControlStates();
    }

    void CustomAnimationEffectTabPage::update( STLPropertySet* pSet )
    {
        if( mpLBProperty1->getSubControl() )
        {
            Any aNewValue( mpLBProperty1->getSubControl()->getValue() );
            Any aOldValue;
            if( mpSet->getPropertyState( nHandleProperty1Value ) != STLPropertyState::Ambiguous)
                aOldValue = mpSet->getPropertyValue( nHandleProperty1Value );

            if( aOldValue != aNewValue )
                pSet->setPropertyValue( nHandleProperty1Value, aNewValue );
        }

        if( mpCBSmoothStart->IsVisible() )
        {
            // set selected value for accelerate if different than in original set

            double fTemp = mpCBSmoothStart->IsChecked() ? 0.5 : 0.0;

            double fOldTemp = 0.0;
            if(mpSet->getPropertyState( nHandleAccelerate ) != STLPropertyState::Ambiguous)
                mpSet->getPropertyValue( nHandleAccelerate ) >>= fOldTemp;
            else
                fOldTemp = -2.0;

            if( fOldTemp != fTemp )
                pSet->setPropertyValue( nHandleAccelerate, makeAny( fTemp ) );

            // set selected value for decelerate if different than in original set
            fTemp = mpCBSmoothEnd->IsChecked() ? 0.5 : 0.0;

            if(mpSet->getPropertyState( nHandleDecelerate ) != STLPropertyState::Ambiguous)
                mpSet->getPropertyValue( nHandleDecelerate ) >>= fOldTemp;
            else
                fOldTemp = -2.0;

            if( fOldTemp != fTemp )
                pSet->setPropertyValue( nHandleDecelerate, makeAny( fTemp ) );
        }

        sal_Int32 nPos = mpLBAfterEffect->GetSelectedEntryPos();
        if( nPos != LISTBOX_ENTRY_NOTFOUND )
        {
            bool bAfterEffect = nPos != 0;

            bool bOldAfterEffect = false;

            if(mpSet->getPropertyState( nHandleHasAfterEffect ) != STLPropertyState::Ambiguous)
                mpSet->getPropertyValue( nHandleHasAfterEffect ) >>= bOldAfterEffect;
            else
                bOldAfterEffect = !bAfterEffect;

            if( bOldAfterEffect != bAfterEffect )
                pSet->setPropertyValue( nHandleHasAfterEffect, makeAny( bAfterEffect ) );

            Any aDimColor;
            if( nPos == 1 )
            {
                Color aSelectedColor = mpCLBDimColor->GetSelectEntryColor();
                aDimColor <<= aSelectedColor.GetRGBColor();
            }

            if( (mpSet->getPropertyState( nHandleDimColor ) == STLPropertyState::Ambiguous) ||
                (mpSet->getPropertyValue( nHandleDimColor ) != aDimColor) )
                pSet->setPropertyValue( nHandleDimColor, aDimColor );

            bool bAfterEffectOnNextEffect = nPos != 2;
            bool bOldAfterEffectOnNextEffect = !bAfterEffectOnNextEffect;

            if( mpSet->getPropertyState( nHandleAfterEffectOnNextEffect ) != STLPropertyState::Ambiguous)
                mpSet->getPropertyValue( nHandleAfterEffectOnNextEffect ) >>= bOldAfterEffectOnNextEffect;

            if( bAfterEffectOnNextEffect != bOldAfterEffectOnNextEffect )
                pSet->setPropertyValue( nHandleAfterEffectOnNextEffect, makeAny( bAfterEffectOnNextEffect ) );
        }

        nPos = mpLBTextAnim->GetSelectedEntryPos();
        if( nPos != LISTBOX_ENTRY_NOTFOUND )
        {
            sal_Int16 nIterateType;

            switch( nPos )
            {
            case 1: nIterateType = TextAnimationType::BY_WORD; break;
            case 2: nIterateType = TextAnimationType::BY_LETTER; break;
            default:
                nIterateType = TextAnimationType::BY_PARAGRAPH;
            }

            sal_Int16 nOldIterateType = nIterateType-1;

            if(mpSet->getPropertyState( nHandleIterateType ) != STLPropertyState::Ambiguous)
                mpSet->getPropertyValue( nHandleIterateType ) >>= nOldIterateType;

            if( nIterateType != nOldIterateType )
                pSet->setPropertyValue( nHandleIterateType, makeAny( nIterateType ) );
        }

        {
            double fIterateInterval = static_cast< double >( mpMFTextDelay->GetValue() ) / 10;
            double fOldIterateInterval = -1.0;

            if( mpSet->getPropertyState( nHandleIterateInterval ) != STLPropertyState::Ambiguous )
                mpSet->getPropertyValue( nHandleIterateInterval ) >>= fOldIterateInterval;

            if( fIterateInterval != fOldIterateInterval )
                pSet->setPropertyValue( nHandleIterateInterval, makeAny( fIterateInterval ) );
        }

        nPos = mpLBSound->GetSelectedEntryPos();
        if( nPos != LISTBOX_ENTRY_NOTFOUND )
        {
            Any aNewSoundURL, aOldSoundURL( makeAny( sal_Int32(0) ) );

            if( nPos == 0 )
            {
                // 0 means no sound, so leave any empty
            }
            else if( nPos == 1 )
            {
                // this means stop sound
                aNewSoundURL <<= true;
            }
            else
            {
                OUString aSoundURL( maSoundList[ nPos-2 ] );
                aNewSoundURL <<= aSoundURL;
            }

            if( mpSet->getPropertyState( nHandleSoundURL ) != STLPropertyState::Ambiguous )
                aOldSoundURL = mpSet->getPropertyValue( nHandleSoundURL  );

            if( aNewSoundURL != aOldSoundURL )
                pSet->setPropertyValue( nHandleSoundURL, aNewSoundURL );
        }
    }

    void CustomAnimationEffectTabPage::fillSoundListBox()
    {
        GalleryExplorer::FillObjList( GALLERY_THEME_SOUNDS, maSoundList );
        GalleryExplorer::FillObjList( GALLERY_THEME_USERSOUNDS, maSoundList );

        mpLBSound->InsertEntry( SdResId(STR_CUSTOMANIMATION_NO_SOUND) );
        mpLBSound->InsertEntry( SdResId(STR_CUSTOMANIMATION_STOP_PREVIOUS_SOUND) );
        for(const OUString & rString : maSoundList)
        {
            INetURLObject aURL( rString );
            mpLBSound->InsertEntry( aURL.GetBase() );
        }
        mpLBSound->InsertEntry( SdResId(STR_CUSTOMANIMATION_BROWSE_SOUND) );
    }

    void CustomAnimationEffectTabPage::clearSoundListBox()
    {
        maSoundList.clear();
        mpLBSound->Clear();
    }

    sal_Int32 CustomAnimationEffectTabPage::getSoundObject( const OUString& rStr )
    {
        size_t i;
        const size_t nCount = maSoundList.size();
        for( i = 0; i < nCount; i++ )
        {
            if( maSoundList[ i ].equalsIgnoreAsciiCase(rStr) )
                return i+2;
        }

        return -1;
    }

    void CustomAnimationEffectTabPage::openSoundFileDialog()
    {
        SdOpenSoundFileDialog aFileDialog(GetFrameWeld());

        bool bValidSoundFile = false;
        bool bQuitLoop = false;
        long nPos = 0;

        while( !bQuitLoop && (aFileDialog.Execute() == ERRCODE_NONE) )
        {
            OUString aFile = aFileDialog.GetPath();
            nPos = getSoundObject( aFile );

            if( nPos < 0 ) // not in Soundliste
            {
                // try to insert in Gallery
                if( GalleryExplorer::InsertURL( GALLERY_THEME_USERSOUNDS, aFile ) )
                {
                    clearSoundListBox();
                    fillSoundListBox();

                    nPos = getSoundObject( aFile );
                    DBG_ASSERT( nPos >= 0, "sd::CustomAnimationEffectTabPage::openSoundFileDialog(), Recently inserted sound not in list!" );

                    bValidSoundFile=true;
                    bQuitLoop=true;
                }
                else
                {
                    OUString aStrWarning(SdResId(STR_WARNING_NOSOUNDFILE));
                    aStrWarning = aStrWarning.replaceFirst("%", aFile);
                    std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(nullptr,
                                                               VclMessageType::Warning, VclButtonsType::NONE,
                                                               aStrWarning));
                    xWarn->add_button(Button::GetStandardText(StandardButtonType::Retry), RET_RETRY);
                    xWarn->add_button(Button::GetStandardText(StandardButtonType::Cancel), RET_CANCEL);
                    bQuitLoop = xWarn->run() != RET_RETRY;

                    bValidSoundFile=false;
                }
            }
            else
            {
                bValidSoundFile=true;
                bQuitLoop=true;
            }
        }

        if( !bValidSoundFile )
            nPos = 0;

        mpLBSound->SelectEntryPos( nPos );
    }

    void CustomAnimationEffectTabPage::onSoundPreview()
    {
        const sal_Int32 nPos = mpLBSound->GetSelectedEntryPos();

        if( nPos >= 2 ) try
        {
            const OUString aSoundURL( maSoundList[ nPos-2 ] );
            mxPlayer.set( avmedia::MediaWindow::createPlayer( aSoundURL, "" ), uno::UNO_QUERY_THROW );
            mxPlayer->start();
        }
        catch( uno::Exception& )
        {
            OSL_FAIL("CustomAnimationEffectTabPage::onSoundPreview(), exception caught!" );
        }
    }

    class CustomAnimationDurationTabPage : public TabPage
    {
    public:
        CustomAnimationDurationTabPage( vcl::Window* pParent, const STLPropertySet* pSet );
        virtual ~CustomAnimationDurationTabPage() override;
        virtual void dispose() override;

        void update( STLPropertySet* pSet );

        DECL_LINK( implControlHdl, ListBox&, void );
        DECL_LINK( implClickHdl, Button*, void );
        DECL_LINK( DurationModifiedHdl, Edit&, void );
        void implHdl(Control const *);

    private:
        const STLPropertySet* mpSet;

        VclPtr<FixedText> mpFTStart;
        VclPtr<ListBox> mpLBStart;
        VclPtr<FixedText> mpFTStartDelay;
        VclPtr<MetricField> mpMFStartDelay;
        VclPtr<FixedText> mpFTDuration;
        VclPtr<MetricBox> mpCBXDuration;
        VclPtr<FixedText> mpFTRepeat;
        VclPtr<ListBox> mpCBRepeat;
        VclPtr<CheckBox> mpCBXRewind;
        VclPtr<RadioButton> mpRBClickSequence;
        VclPtr<RadioButton> mpRBInteractive;
        VclPtr<ListBox> mpLBTrigger;
    };

    CustomAnimationDurationTabPage::CustomAnimationDurationTabPage(vcl::Window* pParent, const STLPropertySet* pSet)
    : TabPage( pParent, "TimingTab", "modules/simpress/ui/customanimationtimingtab.ui" ), mpSet( pSet )
    {
        get(mpFTStart,"start_label" );
        get(mpLBStart, "start_list" );
        get(mpFTStartDelay, "delay_label" );
        get(mpMFStartDelay, "delay_value" );
        get(mpFTDuration, "duration_label" );
        get(mpCBXDuration, "anim_duration" );
        get(mpFTRepeat, "repeat_label" );
        get(mpCBRepeat, "repeat_list" );
        get(mpCBXRewind, "rewind" );
        get(mpRBClickSequence, "rb_click_sequence" );
        get(mpRBInteractive, "rb_interactive" );
        get(mpLBTrigger, "trigger_list");
        mpLBTrigger->set_width_request(approximate_char_width() * 40);

        fillRepeatComboBox( mpCBRepeat );

        //fillDurationMetricComboBox
        mpCBXDuration->InsertValue(50, FUNIT_CUSTOM);
        mpCBXDuration->InsertValue(100, FUNIT_CUSTOM);
        mpCBXDuration->InsertValue(200, FUNIT_CUSTOM);
        mpCBXDuration->InsertValue(300, FUNIT_CUSTOM);
        mpCBXDuration->InsertValue(500, FUNIT_CUSTOM);
        mpCBXDuration->AdaptDropDownLineCountToMaximum();

        mpRBClickSequence->SetClickHdl( LINK( this, CustomAnimationDurationTabPage, implClickHdl ) );
        mpLBTrigger->SetSelectHdl( LINK( this, CustomAnimationDurationTabPage, implControlHdl ) );
        mpCBXDuration->SetModifyHdl(LINK( this, CustomAnimationDurationTabPage, DurationModifiedHdl));

        if( pSet->getPropertyState( nHandleStart ) != STLPropertyState::Ambiguous )
        {
            sal_Int16 nStart = 0;
            pSet->getPropertyValue( nHandleStart ) >>= nStart;
            sal_Int32 nPos = 0;
            switch( nStart )
            {
                case EffectNodeType::WITH_PREVIOUS:     nPos = 1; break;
                case EffectNodeType::AFTER_PREVIOUS:    nPos = 2; break;
            }
            mpLBStart->SelectEntryPos( nPos );
        }

        if( pSet->getPropertyState( nHandleBegin ) != STLPropertyState::Ambiguous )
        {
            double fBegin = 0.0;
            pSet->getPropertyValue( nHandleBegin ) >>= fBegin;
            mpMFStartDelay->SetValue( static_cast<long>(fBegin*10) );
        }

        if( pSet->getPropertyState( nHandleDuration ) != STLPropertyState::Ambiguous )
        {
            double fDuration = 0.0;
            pSet->getPropertyValue( nHandleDuration ) >>= fDuration;

            if( fDuration == 0.001 )
            {
                mpFTDuration->Disable();
                mpCBXDuration->Disable();
                mpFTRepeat->Disable();
                mpCBRepeat->Disable();
                mpCBXRewind->Disable();
            }
            else
            {
                mpCBXDuration->SetValue( fDuration*100.0 );
            }
        }

        if( pSet->getPropertyState( nHandleRepeat ) != STLPropertyState::Ambiguous )
        {
            Any aRepeatCount( pSet->getPropertyValue( nHandleRepeat ) );
            if( (aRepeatCount.getValueType() == ::cppu::UnoType<double>::get()) || !aRepeatCount.hasValue() )
            {
                double fRepeat = 0.0;
                if( aRepeatCount.hasValue() )
                    aRepeatCount >>= fRepeat;

                sal_Int32 nPos = LISTBOX_ENTRY_NOTFOUND;

                if( fRepeat == 0 )
                    nPos = 0;
                else if( fRepeat == 2.0 )
                    nPos = 1;
                else if( fRepeat == 3.0 )
                    nPos = 2;
                else if( fRepeat == 4.0 )
                    nPos = 3;
                else if( fRepeat == 5.0 )
                    nPos = 4;
                else if( fRepeat == 10.0 )
                    nPos = 5;

                if( nPos != LISTBOX_ENTRY_NOTFOUND )
                    mpCBRepeat->SelectEntryPos( nPos );
                else
                    mpCBRepeat->SetText(OUString::number(fRepeat));
            }
            else if( aRepeatCount.getValueType() == ::cppu::UnoType<Timing>::get() )
            {
                Any aEnd;
                if( pSet->getPropertyState( nHandleEnd ) != STLPropertyState::Ambiguous )
                    aEnd = pSet->getPropertyValue( nHandleEnd );

                mpCBRepeat->SelectEntryPos( aEnd.hasValue() ? 6 : 7 );
            }
        }

        if( pSet->getPropertyState( nHandleRewind ) != STLPropertyState::Ambiguous )
        {
            sal_Int16 nFill = 0;
            if( pSet->getPropertyValue( nHandleRewind ) >>= nFill )
            {
                mpCBXRewind->Check( nFill == AnimationFill::REMOVE );
            }
            else
            {
                mpCBXRewind->SetState( TRISTATE_INDET );
            }
        }

        Reference< XShape > xTrigger;

        if( pSet->getPropertyState( nHandleTrigger ) != STLPropertyState::Ambiguous )
        {
            pSet->getPropertyValue( nHandleTrigger ) >>= xTrigger;

            mpRBInteractive->Check( xTrigger.is() );
            mpRBClickSequence->Check( !xTrigger.is() );
        }

        Reference< XDrawPage > xCurrentPage;
        pSet->getPropertyValue( nHandleCurrentPage ) >>= xCurrentPage;
        if( xCurrentPage.is() )
        {
            const OUString aStrIsEmptyPresObj( "IsEmptyPresentationObject" );

            sal_Int32 nShape, nCount = xCurrentPage->getCount();
            for( nShape = 0; nShape < nCount; nShape++ )
            {
                Reference< XShape > xShape( xCurrentPage->getByIndex( nShape ), UNO_QUERY );

                if( !xShape.is() )
                    continue;

                Reference< XPropertySet > xSet( xShape, UNO_QUERY );
                if( xSet.is() && xSet->getPropertySetInfo()->hasPropertyByName( aStrIsEmptyPresObj ) )
                {
                    bool bIsEmpty = false;
                    xSet->getPropertyValue( aStrIsEmptyPresObj ) >>= bIsEmpty;
                    if( bIsEmpty )
                        continue;
                }

                OUString aDescription( getShapeDescription( xShape, true ) );
                sal_Int32 nPos = mpLBTrigger->InsertEntry( aDescription );

                mpLBTrigger->SetEntryData( nPos, reinterpret_cast<void*>(static_cast<sal_IntPtr>(nShape)) );
                if( xShape == xTrigger )
                    mpLBTrigger->SelectEntryPos( nPos );
            }
        }
    }

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

    void CustomAnimationDurationTabPage::dispose()
    {
        mpFTStart.clear();
        mpLBStart.clear();
        mpFTStartDelay.clear();
        mpMFStartDelay.clear();
        mpFTDuration.clear();
        mpCBXDuration.clear();
        mpFTRepeat.clear();
        mpCBRepeat.clear();
        mpCBXRewind.clear();
        mpRBClickSequence.clear();
        mpRBInteractive.clear();
        mpLBTrigger.clear();
        TabPage::dispose();
    }

    IMPL_LINK( CustomAnimationDurationTabPage, implClickHdl, Button*, pBtn, void )
    {
        implHdl(pBtn);
    }

    IMPL_LINK( CustomAnimationDurationTabPage, implControlHdl, ListBox&, rListBox, void )
    {
        implHdl(&rListBox);
    }

    IMPL_LINK_NOARG(CustomAnimationDurationTabPage, DurationModifiedHdl, Edit&, void)
    {
        if(!(mpCBXDuration->GetText()).isEmpty() )
        {
            double duration_value = static_cast<double>(mpCBXDuration->GetValue());
            if(duration_value <= 0.0)
                mpCBXDuration->SetValue(1);
            else
                mpCBXDuration->SetValue(duration_value);
        }
    }

    void CustomAnimationDurationTabPage::implHdl( Control const * pControl )
    {
        if( pControl == mpLBTrigger )
        {
            mpRBClickSequence->Check( false );
            mpRBInteractive->Check();
        }
    }

    void CustomAnimationDurationTabPage::update( STLPropertySet* pSet )
    {
        sal_Int32 nPos = mpLBStart->GetSelectedEntryPos();
        if( nPos != LISTBOX_ENTRY_NOTFOUND )
        {
            sal_Int16 nStart;
            sal_Int16 nOldStart = -1;

            switch( nPos )
            {
            case 1: nStart = EffectNodeType::WITH_PREVIOUS; break;
            case 2: nStart = EffectNodeType::AFTER_PREVIOUS; break;
            default:
                nStart = EffectNodeType::ON_CLICK; break;
            }

            if(mpSet->getPropertyState( nHandleStart ) != STLPropertyState::Ambiguous)
                mpSet->getPropertyValue( nHandleStart ) >>= nOldStart;

            if( nStart != nOldStart )
                pSet->setPropertyValue( nHandleStart, makeAny( nStart ) );
        }

        {
            double fBegin = static_cast<double>( mpMFStartDelay->GetValue()) / 10.0;
            double fOldBegin = -1.0;

            if( mpSet->getPropertyState( nHandleBegin ) != STLPropertyState::Ambiguous )
                mpSet->getPropertyValue( nHandleBegin ) >>= fOldBegin;

            if( fBegin != fOldBegin )
                pSet->setPropertyValue( nHandleBegin, makeAny( fBegin ) );
        }

        nPos = mpCBRepeat->GetSelectedEntryPos();
        if( (nPos != LISTBOX_ENTRY_NOTFOUND) || (!mpCBRepeat->GetText().isEmpty()) )
        {
            Any aRepeatCount;
            Any aEnd;

            switch( nPos )
            {
            case 0:
                break;

            case 6:
                {
                    Event aEvent;
                    aEvent.Trigger = EventTrigger::ON_NEXT;
                    aEvent.Repeat = 0;
                    aEnd <<= aEvent;
                }
                SAL_FALLTHROUGH;
            case 7:
                aRepeatCount <<= Timing_INDEFINITE;
                break;
            default:
                {
                    OUString aText(mpCBRepeat->GetEntry(nPos));
                    if( !aText.isEmpty() )
                        aRepeatCount <<= aText.toDouble();
                }
            }

            Any aOldRepeatCount( aRepeatCount );
            if( mpSet->getPropertyState( nHandleRepeat ) != STLPropertyState::Ambiguous )
                aOldRepeatCount = mpSet->getPropertyValue( nHandleRepeat );

            if( aRepeatCount != aOldRepeatCount )
                pSet->setPropertyValue( nHandleRepeat, aRepeatCount );

            Any aOldEnd( aEnd );
            if( mpSet->getPropertyState( nHandleEnd ) != STLPropertyState::Ambiguous )
                aOldEnd = mpSet->getPropertyValue( nHandleEnd );

            if( aEnd != aOldEnd )
                pSet->setPropertyValue( nHandleEnd, aEnd );
        }

        double fDuration = -1.0;

        if(!(mpCBXDuration->GetText()).isEmpty() )
        {
            double duration_value = static_cast<double>(mpCBXDuration->GetValue());

            if(duration_value > 0)
                fDuration = duration_value/100.0;
        }

        if( fDuration != -1.0 )
        {
            double fOldDuration = -1;

            if( mpSet->getPropertyState( nHandleDuration ) != STLPropertyState::Ambiguous )
                mpSet->getPropertyValue( nHandleDuration ) >>= fOldDuration;

            if( fDuration != fOldDuration )
                pSet->setPropertyValue( nHandleDuration, makeAny( fDuration ) );
        }

        if( mpCBXRewind->GetState() != TRISTATE_INDET )
        {
            sal_Int16 nFill = mpCBXRewind->IsChecked() ? AnimationFill::REMOVE : AnimationFill::HOLD;

            bool bSet = true;

            if( mpSet->getPropertyState( nHandleRewind ) != STLPropertyState::Ambiguous )
            {
                sal_Int16 nOldFill = 0;
                mpSet->getPropertyValue( nHandleRewind ) >>= nOldFill;
                bSet = nFill != nOldFill;
            }

            if( bSet )
                pSet->setPropertyValue( nHandleRewind, makeAny( nFill ) );
        }

        Reference< XShape > xTrigger;

        if( mpRBInteractive->IsChecked() )
        {
            nPos = mpLBTrigger->GetSelectedEntryPos();
            if( nPos != LISTBOX_ENTRY_NOTFOUND )
            {
                sal_Int32 nShape = static_cast<sal_Int32>(reinterpret_cast<sal_IntPtr>(mpLBTrigger->GetEntryData( nPos )));

                Reference< XDrawPage > xCurrentPage;
                mpSet->getPropertyValue( nHandleCurrentPage ) >>= xCurrentPage;

                if( xCurrentPage.is() && (nShape >= 0) && (nShape < xCurrentPage->getCount()) )
                    xCurrentPage->getByIndex( nShape ) >>= xTrigger;
            }
        }

        if( xTrigger.is() || mpRBClickSequence->IsChecked() )
        {
            Any aNewValue( makeAny( xTrigger ) );
            Any aOldValue;

            if( mpSet->getPropertyState( nHandleTrigger ) != STLPropertyState::Ambiguous )
                aOldValue = mpSet->getPropertyValue( nHandleTrigger );

            if( aNewValue != aOldValue )
                pSet->setPropertyValue( nHandleTrigger, aNewValue );
        }
    }

    class CustomAnimationTextAnimTabPage : public TabPage
    {
    public:
        CustomAnimationTextAnimTabPage( vcl::Window* pParent, const STLPropertySet* pSet );
        virtual ~CustomAnimationTextAnimTabPage() override;
        virtual void dispose() override;

        void update( STLPropertySet* pSet );

        void updateControlStates();
        DECL_LINK(implSelectHdl, ListBox&, void);

    private:
        VclPtr<FixedText>   maFTGroupText;
        VclPtr<ListBox>     maLBGroupText;
        VclPtr<CheckBox>    maCBXGroupAuto;
        VclPtr<MetricField> maMFGroupAuto;
        VclPtr<CheckBox>    maCBXAnimateForm;
        VclPtr<CheckBox>    maCBXReverse;

        const STLPropertySet* mpSet;

        bool mbHasVisibleShapes;
    };

    CustomAnimationTextAnimTabPage::CustomAnimationTextAnimTabPage(vcl::Window* pParent, const STLPropertySet* pSet)
    :   TabPage( pParent, "TextAnimationTab", "modules/simpress/ui/customanimationtexttab.ui" ),
        mpSet( pSet ),
        mbHasVisibleShapes(true)
    {
        get( maFTGroupText, "group_text_label" );
        get( maLBGroupText, "group_text_list" );
        get( maCBXGroupAuto, "auto_after" );
        get( maMFGroupAuto, "auto_after_value" );
        get( maCBXAnimateForm, "animate_shape" );
        get( maCBXReverse, "reverse_order" );

        maLBGroupText->SetSelectHdl( LINK( this, CustomAnimationTextAnimTabPage, implSelectHdl ) );

        if( pSet->getPropertyState( nHandleTextGrouping ) != STLPropertyState::Ambiguous )
        {
            sal_Int32 nTextGrouping = 0;
            if( pSet->getPropertyValue( nHandleTextGrouping ) >>= nTextGrouping )
                maLBGroupText->SelectEntryPos( nTextGrouping + 1 );
        }

        if( pSet->getPropertyState( nHandleHasVisibleShape ) != STLPropertyState::Ambiguous )
            pSet->getPropertyValue( nHandleHasVisibleShape ) >>= mbHasVisibleShapes;

        if( pSet->getPropertyState( nHandleTextGroupingAuto ) != STLPropertyState::Ambiguous )
        {
            double fTextGroupingAuto = 0.0;
            if( pSet->getPropertyValue( nHandleTextGroupingAuto ) >>= fTextGroupingAuto )
            {
                maCBXGroupAuto->Check( fTextGroupingAuto >= 0.0 );
                if( fTextGroupingAuto >= 0.0 )
                    maMFGroupAuto->SetValue( static_cast<long>(fTextGroupingAuto*10) );
            }
        }
        else
        {
            maCBXGroupAuto->SetState( TRISTATE_INDET );
        }

        maCBXAnimateForm->SetState( TRISTATE_INDET );
        if( pSet->getPropertyState( nHandleAnimateForm ) != STLPropertyState::Ambiguous )
        {
            bool bAnimateForm = false;
            if( pSet->getPropertyValue( nHandleAnimateForm ) >>= bAnimateForm )
            {
                maCBXAnimateForm->Check( bAnimateForm );
            }
        }
        else
        {
            maCBXAnimateForm->Enable( false );
        }

        maCBXReverse->SetState( TRISTATE_INDET );
        if( pSet->getPropertyState( nHandleTextReverse ) != STLPropertyState::Ambiguous )
        {
            bool bTextReverse = false;
            if( pSet->getPropertyValue( nHandleTextReverse ) >>= bTextReverse )
            {
                maCBXReverse->Check( bTextReverse );
            }
        }

        if( pSet->getPropertyState( nHandleMaxParaDepth ) == STLPropertyState::Direct )
        {
            sal_Int32 nMaxParaDepth = 0;
            pSet->getPropertyValue( nHandleMaxParaDepth ) >>= nMaxParaDepth;
            nMaxParaDepth += 1;

            sal_Int32 nPos = 6;
            while( (nPos > 2) && (nPos > nMaxParaDepth) )
            {
                maLBGroupText->RemoveEntry( nPos );
                nPos--;
            }
        }

        updateControlStates();
    }

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

    void CustomAnimationTextAnimTabPage::dispose()
    {
        maFTGroupText.clear();
        maLBGroupText.clear();
        maCBXGroupAuto.clear();
        maMFGroupAuto.clear();
        maCBXAnimateForm.clear();
        maCBXReverse.clear();
        TabPage::dispose();
    }

    void CustomAnimationTextAnimTabPage::update( STLPropertySet* pSet )
    {
        sal_Int32 nPos = maLBGroupText->GetSelectedEntryPos();
        if( nPos != LISTBOX_ENTRY_NOTFOUND )
        {
            sal_Int32 nTextGrouping = nPos - 1;
            sal_Int32 nOldGrouping = -2;

            if(mpSet->getPropertyState( nHandleTextGrouping ) != STLPropertyState::Ambiguous)
                mpSet->getPropertyValue( nHandleTextGrouping ) >>= nOldGrouping;

            if( nTextGrouping != nOldGrouping )
                pSet->setPropertyValue( nHandleTextGrouping, makeAny( nTextGrouping ) );
        }

        if( nPos > 0 )
        {
            bool bTextReverse = maCBXReverse->IsChecked();
            bool bOldTextReverse = !bTextReverse;

            if(mpSet->getPropertyState( nHandleTextReverse ) != STLPropertyState::Ambiguous)
                mpSet->getPropertyValue( nHandleTextReverse ) >>= bOldTextReverse;

            if( bTextReverse != bOldTextReverse )
                pSet->setPropertyValue( nHandleTextReverse, makeAny( bTextReverse ) );

            if( nPos > 1 )
            {
                double fTextGroupingAuto = maCBXGroupAuto->IsChecked() ? maMFGroupAuto->GetValue() / 10.0 : -1.0;
                double fOldTextGroupingAuto = -2.0;

                if(mpSet->getPropertyState( nHandleTextGroupingAuto ) != STLPropertyState::Ambiguous)
                    mpSet->getPropertyValue( nHandleTextGroupingAuto ) >>= fOldTextGroupingAuto;

                if( fTextGroupingAuto != fOldTextGroupingAuto )
                    pSet->setPropertyValue( nHandleTextGroupingAuto, makeAny( fTextGroupingAuto ) );
            }
        }
        //#i120049# impress crashes when modifying the "Random effects" animation
        //effect's trigger condition to "Start effect on click of".
        //If this control is disabled, we should ignore its value
        if (maCBXAnimateForm->IsEnabled())
        {
            bool bAnimateForm = maCBXAnimateForm->IsChecked();
            bool bOldAnimateForm = !bAnimateForm;

            if(mpSet->getPropertyState( nHandleAnimateForm ) != STLPropertyState::Ambiguous)
                mpSet->getPropertyValue( nHandleAnimateForm ) >>= bOldAnimateForm;

            if( bAnimateForm != bOldAnimateForm )
                pSet->setPropertyValue( nHandleAnimateForm, makeAny( bAnimateForm ) );
        }
    }

    void CustomAnimationTextAnimTabPage::updateControlStates()
    {
        sal_Int32 nPos = maLBGroupText->GetSelectedEntryPos();

        maCBXGroupAuto->Enable( nPos > 1 );
        maMFGroupAuto->Enable( nPos > 1 );
        maCBXReverse->Enable( nPos > 0 );

        if( !mbHasVisibleShapes && nPos > 0 )
        {
            maCBXAnimateForm->Check(false);
            maCBXAnimateForm->Enable(false);
        }
        else
        {
            maCBXAnimateForm->Enable();
        }
    }

    IMPL_LINK_NOARG(CustomAnimationTextAnimTabPage, implSelectHdl, ListBox&, void)
    {
        updateControlStates();
    }

    CustomAnimationDialog::CustomAnimationDialog(vcl::Window* pParent, std::unique_ptr<STLPropertySet> pSet, const OString& sPage)
    : TabDialog( pParent, "CustomAnimationProperties", "modules/simpress/ui/customanimationproperties.ui")
    , mpSet( std::move(pSet) )
    {
        get(mpTabControl, "tabs");

        sal_uInt16 nEffectId = mpTabControl->GetPageId("effect");
        sal_uInt16 nTimingId = mpTabControl->GetPageId("timing");
        sal_uInt16 nTextAnimId = mpTabControl->GetPageId("textanim");

        mpEffectTabPage = VclPtr<CustomAnimationEffectTabPage>::Create( mpTabControl, mpSet.get() );
        mpTabControl->SetTabPage( nEffectId, mpEffectTabPage );
        mpDurationTabPage = VclPtr<CustomAnimationDurationTabPage>::Create( mpTabControl, mpSet.get() );
        mpTabControl->SetTabPage( nTimingId, mpDurationTabPage );

        bool bHasText = false;
        if( mpSet->getPropertyState( nHandleHasText ) != STLPropertyState::Ambiguous )
            mpSet->getPropertyValue( nHandleHasText ) >>= bHasText;

        if( bHasText )
        {
            mpTextAnimTabPage = VclPtr<CustomAnimationTextAnimTabPage>::Create( mpTabControl, mpSet.get() );
            mpTabControl->SetTabPage( nTextAnimId, mpTextAnimTabPage );
        }
        else
        {
            mpTextAnimTabPage = nullptr;
            mpTabControl->RemovePage( nTextAnimId );
        }

        if (!sPage.isEmpty())
            mpTabControl->SelectTabPage(mpTabControl->GetPageId(sPage));
    }

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

    void CustomAnimationDialog::dispose()
    {
        mpEffectTabPage.disposeAndClear();
        mpDurationTabPage.disposeAndClear();
        mpTextAnimTabPage.disposeAndClear();

        mpSet.reset();
        mpResultSet.reset();

        mpTabControl.clear();
        TabDialog::dispose();
    }

    STLPropertySet* CustomAnimationDialog::getResultSet()
    {
        mpResultSet = createDefaultSet();

        mpEffectTabPage->update( mpResultSet.get() );
        mpDurationTabPage->update( mpResultSet.get() );
        if( mpTextAnimTabPage )
            mpTextAnimTabPage->update( mpResultSet.get() );

        return mpResultSet.get();
    }

    std::unique_ptr<STLPropertySet> CustomAnimationDialog::createDefaultSet()
    {
        Any aEmpty;

        std::unique_ptr<STLPropertySet> pSet(new STLPropertySet());
        pSet->setPropertyDefaultValue( nHandleMaxParaDepth, makeAny( sal_Int32(-1) ) );

        pSet->setPropertyDefaultValue( nHandleHasAfterEffect, makeAny( false ) );
        pSet->setPropertyDefaultValue( nHandleAfterEffectOnNextEffect, makeAny( false ) );
        pSet->setPropertyDefaultValue( nHandleDimColor, aEmpty );
        pSet->setPropertyDefaultValue( nHandleIterateType, makeAny( sal_Int16(0) ) );
        pSet->setPropertyDefaultValue( nHandleIterateInterval, makeAny( 0.0 ) );

        pSet->setPropertyDefaultValue( nHandleStart, makeAny( sal_Int16(EffectNodeType::ON_CLICK) ) );
        pSet->setPropertyDefaultValue( nHandleBegin, makeAny( 0.0 ) );
        pSet->setPropertyDefaultValue( nHandleDuration, makeAny( 2.0 ) );
        pSet->setPropertyDefaultValue( nHandleRepeat, aEmpty );
        pSet->setPropertyDefaultValue( nHandleRewind, makeAny( AnimationFill::HOLD ) );

        pSet->setPropertyDefaultValue( nHandleEnd, aEmpty );

        pSet->setPropertyDefaultValue( nHandlePresetId, aEmpty );
        pSet->setPropertyDefaultValue( nHandleProperty1Type, makeAny( nPropertyTypeNone ) );
        pSet->setPropertyDefaultValue( nHandleProperty1Value, aEmpty );
        pSet->setPropertyDefaultValue( nHandleProperty2Type, makeAny( nPropertyTypeNone ) );
        pSet->setPropertyDefaultValue( nHandleProperty2Value, aEmpty );
        pSet->setPropertyDefaultValue( nHandleAccelerate, aEmpty );
        pSet->setPropertyDefaultValue( nHandleDecelerate, aEmpty );
        pSet->setPropertyDefaultValue( nHandleAutoReverse, aEmpty );
        pSet->setPropertyDefaultValue( nHandleTrigger, aEmpty );

        pSet->setPropertyDefaultValue( nHandleHasText, makeAny( false ) );
        pSet->setPropertyDefaultValue( nHandleHasVisibleShape, makeAny( false ) );
        pSet->setPropertyDefaultValue( nHandleTextGrouping, makeAny( sal_Int32(-1) ) );
        pSet->setPropertyDefaultValue( nHandleAnimateForm, makeAny( true ) );
        pSet->setPropertyDefaultValue( nHandleTextGroupingAuto, makeAny( -1.0 ) );
        pSet->setPropertyDefaultValue( nHandleTextReverse, makeAny( false ) );

        pSet->setPropertyDefaultValue( nHandleCurrentPage, aEmpty );

        pSet->setPropertyDefaultValue( nHandleSoundURL, aEmpty );
        pSet->setPropertyDefaultValue( nHandleSoundVolumne, makeAny( 1.0) );
        pSet->setPropertyDefaultValue( nHandleSoundEndAfterSlide, makeAny( sal_Int32(0) ) );

        pSet->setPropertyDefaultValue( nHandleCommand, makeAny( sal_Int16(0) ) );
        return pSet;
    }

    PropertyControl::PropertyControl( vcl::Window* pParent )
    : ListBox( pParent, WB_TABSTOP | WB_BORDER | WB_DROPDOWN )
    {
    }

    VCL_BUILDER_FACTORY(PropertyControl)

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

    void PropertyControl::dispose()
    {
        mpSubControl.reset();
        ListBox::dispose();
    }

    void PropertyControl::setSubControl( std::unique_ptr<PropertySubControl> pSubControl )
    {
        mpSubControl = std::move(pSubControl);

        Control* pControl = mpSubControl ? mpSubControl->getControl() : nullptr;

        if( pControl )
        {
            pControl->SetPosSizePixel( GetPosPixel(), GetSizePixel() );
            pControl->SetZOrder( this, ZOrderFlags::Before );
            pControl->Show();
            Hide();
        }
        else
        {
            Show();
        }
    }

    void PropertyControl::Resize()
    {
        Control* pControl = mpSubControl ? mpSubControl->getControl() : nullptr;
        if( pControl )
            pControl->SetPosSizePixel( GetPosPixel(), GetSizePixel() );
        ListBox::Resize();
    }

    PropertySubControl::~PropertySubControl()
    {
    }

    std::unique_ptr<PropertySubControl> PropertySubControl::create( sal_Int32 nType, vcl::Window* pParent, const Any& rValue, const OUString& rPresetId, const Link<LinkParamNone*,void>& rModifyHdl )
    {
        std::unique_ptr<PropertySubControl> pSubControl;
        switch( nType )
        {
        case nPropertyTypeDirection:
        case nPropertyTypeSpokes:
        case nPropertyTypeZoom:
            pSubControl.reset( new PresetPropertyBox( nType, pParent, rValue, rPresetId, rModifyHdl ) );
            break;

        case nPropertyTypeColor:
        case nPropertyTypeFillColor:
        case nPropertyTypeFirstColor:
        case nPropertyTypeCharColor:
        case nPropertyTypeLineColor:
            pSubControl.reset( new ColorPropertyBox( nType, pParent, rValue, rModifyHdl ) );
            break;

        case nPropertyTypeFont:
            pSubControl.reset( new FontPropertyBox( nType, pParent, rValue, rModifyHdl ) );
            break;

        case nPropertyTypeCharHeight:
            pSubControl.reset( new CharHeightPropertyBox( nType, pParent, rValue, rModifyHdl ) );
            break;

        case nPropertyTypeRotate:
            pSubControl.reset( new RotationPropertyBox( nType, pParent, rValue, rModifyHdl ) );
            break;

        case nPropertyTypeTransparency:
            pSubControl.reset( new TransparencyPropertyBox( nType, pParent, rValue, rModifyHdl ) );
            break;

        case nPropertyTypeScale:
            pSubControl.reset( new ScalePropertyBox( nType, pParent, rValue, rModifyHdl ) );
            break;

        case nPropertyTypeCharDecoration:
            pSubControl.reset( new FontStylePropertyBox( nType, pParent, rValue, rModifyHdl ) );
            break;
        }

        return pSubControl;
    }

    }

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

↑ V547 Expression 'bHasText' is always false.