/* -*- 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/animations/XAnimationNodeSupplier.hpp>
#include <com/sun/star/animations/AnimationFill.hpp>
#include <com/sun/star/animations/AnimationRestart.hpp>
#include <com/sun/star/animations/Timing.hpp>
#include <com/sun/star/animations/Event.hpp>
#include <com/sun/star/animations/AnimationEndSync.hpp>
#include <com/sun/star/animations/Command.hpp>
#include <com/sun/star/animations/EventTrigger.hpp>
#include <com/sun/star/animations/AnimationNodeType.hpp>
#include <com/sun/star/animations/AnimationTransformType.hpp>
#include <com/sun/star/animations/AnimationCalcMode.hpp>
#include <com/sun/star/animations/AnimationValueType.hpp>
#include <com/sun/star/animations/AnimationAdditiveMode.hpp>
#include <com/sun/star/animations/XIterateContainer.hpp>
#include <com/sun/star/animations/XAnimateSet.hpp>
#include <com/sun/star/animations/XAnimationNode.hpp>
#include <com/sun/star/animations/XAudio.hpp>
#include <com/sun/star/animations/XCommand.hpp>
#include <com/sun/star/animations/XTransitionFilter.hpp>
#include <com/sun/star/animations/XAnimateColor.hpp>
#include <com/sun/star/animations/XAnimateMotion.hpp>
#include <com/sun/star/animations/XAnimateTransform.hpp>
#include <com/sun/star/animations/ValuePair.hpp>
#include <com/sun/star/animations/AnimationColorSpace.hpp>
#include <com/sun/star/presentation/EffectNodeType.hpp>
#include <com/sun/star/presentation/EffectPresetClass.hpp>
#include <com/sun/star/presentation/ShapeAnimationSubType.hpp>
#include <com/sun/star/presentation/EffectCommands.hpp>
#include <com/sun/star/beans/NamedValue.hpp>
#include <com/sun/star/container/XEnumerationAccess.hpp>
#include <com/sun/star/drawing/XDrawPage.hpp>
#include <com/sun/star/io/WrongFormatException.hpp>
#include <com/sun/star/presentation/ParagraphTarget.hpp>
#include <com/sun/star/presentation/TextAnimationType.hpp>
#include <comphelper/processfactory.hxx>
#include <oox/helper/addtosequence.hxx>
#include <oox/ppt/pptfilterhelpers.hxx>
#include <rtl/ustrbuf.hxx>
#include <rtl/math.hxx>
#include <sal/log.hxx>
#include <tools/debug.hxx>
#include <osl/diagnose.h>
#include <o3tl/string_view.hxx>
 
#include <svx/svdotext.hxx>
#include <editeng/outlobj.hxx>
#include <editeng/editobj.hxx>
#include <animations.hxx>
#include "pptanimations.hxx"
#include "pptinanimations.hxx"
#include "pptatom.hxx"
#include "pptin.hxx"
#include <randomnode.hxx>
 
#include <algorithm>
#include <memory>
 
using ::com::sun::star::beans::NamedValue;
using ::com::sun::star::container::XEnumerationAccess;
using ::com::sun::star::container::XEnumeration;
 
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::drawing;
using namespace ::com::sun::star::animations;
using namespace ::com::sun::star::presentation;
 
namespace ppt
{
 
static SvStream& operator>>(SvStream& rIn, AnimationNode& rNode )
{
    rIn.ReadInt32( rNode.mnU1 );
    rIn.ReadInt32( rNode.mnRestart );
    rIn.ReadInt32( rNode.mnGroupType );
    rIn.ReadInt32( rNode.mnFill );
    rIn.ReadInt32( rNode.mnU3 );
    rIn.ReadInt32( rNode.mnU4 );
    rIn.ReadInt32( rNode.mnDuration );
    rIn.ReadInt32( rNode.mnNodeType );
 
    return rIn;
}
 
bool PropertySet::hasProperty( sal_Int32 nProperty ) const
{
    return maProperties.find( nProperty ) != maProperties.end();
}
 
Any PropertySet::getProperty( sal_Int32 nProperty ) const
{
    PropertySetMap_t::const_iterator aIter( maProperties.find( nProperty ) );
    if( aIter != maProperties.end() )
        return (*aIter).second;
    else
        return Any();
}
 
AnimationImporter::AnimationImporter( ImplSdPPTImport* pPPTImport, SvStream& rStCtrl )
: mpPPTImport( pPPTImport ), mrStCtrl( rStCtrl )
{
}
 
int AnimationImporter::import( const Reference< XDrawPage >& xPage, const DffRecordHeader& rProgTagContentHd )
{
    int nNodes = 0;
 
#ifdef DBG_ANIM_LOG
    static int ppt_anim_debug_stream_number = 1;
    OUString ppt_anim_debug_filename("ppt-animation-import-debug-output-");
    ppt_anim_debug_filename += OUString::number(ppt_anim_debug_stream_number++);
    ppt_anim_debug_filename += ".xml";
    mpFile = fopen( OUStringToOString( ppt_anim_debug_filename, RTL_TEXTENCODING_UTF8).getStr() , "w+" );
#endif
    dump("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
 
    Reference< XAnimationNodeSupplier > xNodeSupplier( xPage, UNO_QUERY );
    if( xNodeSupplier.is() )
    {
        mxRootNode = xNodeSupplier->getAnimationNode();
        if( mxRootNode.is() )
        {
            Reference< XAnimationNode > xParent;
 
            std::unique_ptr<Atom> pAtom(Atom::import( rProgTagContentHd, mrStCtrl ));
            if( pAtom )
            {
                nNodes = importAnimationContainer( pAtom.get(), xParent );
            }
 
            std::for_each( maAfterEffectNodes.begin(), maAfterEffectNodes.end(),
                           sd::stl_process_after_effect_node_func );
        }
    }
 
#ifdef DBG_ANIM_LOG
    fclose( mpFile );
#endif
 
    return nNodes;
}
 
Reference< XAnimationNode > AnimationImporter::createNode( const Atom* pAtom, const AnimationNode& rNode )
{
    const char* pServiceName = nullptr;
 
    switch( rNode.mnGroupType )
    {
    case mso_Anim_GroupType_PAR:
        if( pAtom->hasChildAtom( DFF_msofbtAnimIteration ) )
            pServiceName = "com.sun.star.animations.IterateContainer";
        else
            pServiceName = "com.sun.star.animations.ParallelTimeContainer";
        break;
    case mso_Anim_GroupType_SEQ:
        pServiceName = "com.sun.star.animations.SequenceTimeContainer";
        break;
    case mso_Anim_GroupType_NODE:
    {
        switch( rNode.mnNodeType )
        {
        case mso_Anim_Behaviour_FILTER:
        case mso_Anim_Behaviour_ANIMATION:
            if( pAtom->hasChildAtom( DFF_msofbtAnimateSet ) )
                pServiceName = "com.sun.star.animations.AnimateSet";
            else if( pAtom->hasChildAtom( DFF_msofbtAnimateColor ) )
                pServiceName = "com.sun.star.animations.AnimateColor";
            else if( pAtom->hasChildAtom( DFF_msofbtAnimateScale ) )
                pServiceName = "com.sun.star.animations.AnimateTransform";
            else if( pAtom->hasChildAtom( DFF_msofbtAnimateRotation ) )
                pServiceName = "com.sun.star.animations.AnimateTransform";
            else if( pAtom->hasChildAtom( DFF_msofbtAnimateMotion ) )
                pServiceName = "com.sun.star.animations.AnimateMotion";
            else if( pAtom->hasChildAtom( DFF_msofbtAnimateFilter ) )
                pServiceName = "com.sun.star.animations.TransitionFilter";
            else if( pAtom->hasChildAtom( DFF_msofbtAnimCommand ) )
                pServiceName = "com.sun.star.animations.Command";
            else
                pServiceName = "com.sun.star.animations.Animate";
            break;
        }
        break;
    }
    case mso_Anim_GroupType_MEDIA:
        pServiceName = "com.sun.star.animations.Audio";
        break;
 
    default:
        pServiceName = "com.sun.star.animations.Animate";
        break;
    }
 
    Reference< XAnimationNode > xNode;
    if( pServiceName )
    {
        const Reference< XComponentContext >& xContext = ::comphelper::getProcessComponentContext();
        const OUString aServiceName( OUString::createFromAscii(pServiceName) );
        Reference< XInterface > xFac( xContext->getServiceManager()->createInstanceWithContext(aServiceName, xContext) );
        xNode.set(xFac , UNO_QUERY );
    }
 
    DBG_ASSERT( xNode.is(), "sd::AnimationImporter::createNode(), node creation failed!" );
    return xNode;
}
 
static bool is_random( const AnimationNode& rNode, const PropertySet& rSet, sal_Int32& rPresetClass )
{
    if( rNode.mnGroupType != mso_Anim_GroupType_PAR )
        return false;
 
    if( !rSet.hasProperty( DFF_ANIM_PRESET_ID ) || !rSet.hasProperty( DFF_ANIM_PRESET_CLASS ) )
        return false;
 
    sal_Int32 nPresetId = 0;
    if( !(rSet.getProperty( DFF_ANIM_PRESET_ID ) >>= nPresetId) || (nPresetId != 24) )
        return false;
 
    sal_Int32 nPresetClass = 0;
    if( !(rSet.getProperty( DFF_ANIM_PRESET_CLASS ) >>= nPresetClass) )
        return false;
 
    switch( nPresetClass )
    {
    case DFF_ANIM_PRESS_CLASS_ENTRANCE: rPresetClass = EffectPresetClass::ENTRANCE; return true;
    case DFF_ANIM_PRESS_CLASS_EXIT: rPresetClass = EffectPresetClass::EXIT; return true;
    }
    return false;
}
 
int AnimationImporter::importAnimationContainer( const Atom* pAtom, const Reference< XAnimationNode >& xParent )
{
    int nNodes = 0;
    if( pAtom->seekToContent() )
    {
        AnimationNode aNode;
        const Atom* pAnimationNodeAtom = pAtom->findFirstChildAtom( DFF_msofbtAnimNode );
        if( pAnimationNodeAtom && pAnimationNodeAtom->seekToContent() )
            mrStCtrl >> aNode;
 
        PropertySet aSet;
        const Atom* pAnimationPropertySetAtom = pAtom->findFirstChildAtom( DFF_msofbtAnimPropertySet );
        if( pAnimationPropertySetAtom )
            importPropertySetContainer( pAnimationPropertySetAtom, aSet );
 
        Reference< XAnimationNode > xNode;
 
        if( xParent.is() )
        {
            sal_Int32 nPresetClass;
            if( is_random( aNode, aSet, nPresetClass ) )
            {
                // create a random animation node with the given preset class
                xNode.set( sd::RandomAnimationNode_createInstance( static_cast<sal_Int16>(nPresetClass) ), UNO_QUERY );
            }
 
            if( !xNode.is() )
            {
                // create a node for the given atom
                xNode = createNode( pAtom, aNode );
            }
        }
        else
        {
            // if we have no parent we fill the root node
            xNode = mxRootNode;
        }
 
        // import if we have a node and it's not random
        if( xNode.is() )
        {
            fillNode( xNode, aNode, aSet );
 
            switch( aNode.mnGroupType )
            {
            case mso_Anim_GroupType_PAR:
            {
                dump( "<par" );
                dump( aNode );
                dump( aSet );
                nNodes += importTimeContainer( pAtom, xNode );
                dump( "</par>\n" );
 
                // for iteration containers, map target from children to iteration
                Reference< XIterateContainer > xIter( xNode, UNO_QUERY );
                if( xIter.is() )
                {
                    double fDuration = 0.0;
                    Any aTarget, aEmpty;
                    Reference< XEnumerationAccess > xEnumerationAccess( xNode, UNO_QUERY );
                    if( xEnumerationAccess.is() )
                    {
                        Reference< XEnumeration > xEnumeration = xEnumerationAccess->createEnumeration();
                        if( xEnumeration.is() )
                        {
                            while( xEnumeration->hasMoreElements() )
                            {
                                Reference< XAnimate > xChildNode( xEnumeration->nextElement(), UNO_QUERY );
                                if( xChildNode.is() )
                                {
                                    double fChildBegin = 0.0;
                                    double fChildDuration = 0.0;
                                    xChildNode->getBegin() >>= fChildBegin;
                                    xChildNode->getDuration() >>= fChildDuration;
 
                                    fChildDuration += fChildBegin;
                                    if( fChildDuration > fDuration )
                                        fDuration = fChildDuration;
 
                                    if( !aTarget.hasValue() )
                                        aTarget = xChildNode->getTarget();
 
                                    xChildNode->setTarget( aEmpty );
                                }
                            }
                        }
                    }
 
                    xIter->setTarget( aTarget );
 
                    double fIterateInterval = xIter->getIterateInterval() * fDuration / 100;
                    xIter->setIterateInterval( fIterateInterval );
                }
            }
            break;
 
            case mso_Anim_GroupType_SEQ:
            {
                dump( "<seq" );
                dump( aNode );
                dump( aSet );
                nNodes += importTimeContainer( pAtom, xNode );
                dump( "</seq>\n" );
 
                if( aSet.hasProperty( DFF_ANIM_NODE_TYPE ) )
                {
                    sal_Int32 nPPTNodeType = 0;
                    if( aSet.getProperty( DFF_ANIM_NODE_TYPE ) >>= nPPTNodeType )
                    {
                        switch(nPPTNodeType)
                        {
                        case DFF_ANIM_NODE_TYPE_MAIN_SEQUENCE:
                            oox::ppt::fixMainSequenceTiming( xNode );
                            break;
                        case DFF_ANIM_NODE_TYPE_INTERACTIVE_SEQ:
                            oox::ppt::fixInteractiveSequenceTiming( xNode );
                            break;
                        }
                    }
                }
            }
            break;
 
            case mso_Anim_GroupType_NODE:
            {
#ifdef DBG_ANIM_LOG
                if( pAtom->hasChildAtom( DFF_msofbtAnimateSet ) )
                {
                    dump( "<set" );
                }
                else if( pAtom->hasChildAtom( DFF_msofbtAnimateColor ) )
                {
                    dump( "<animateColor" );
                }
                else if( pAtom->hasChildAtom( DFF_msofbtAnimateScale ) )
                {
                    dump( "<animateScale" );
                }
                else if( pAtom->hasChildAtom( DFF_msofbtAnimateRotation ) )
                {
                    dump( "<animateRotation" );
                }
                else if( pAtom->hasChildAtom( DFF_msofbtAnimateMotion ) )
                {
                    dump( "<animateMotion" );
                }
                else if( pAtom->hasChildAtom( DFF_msofbtAnimate ) )
                {
                    dump( "<animate" );
                }
                else if( pAtom->hasChildAtom( DFF_msofbtAnimateFilter ) )
                {
                    dump( "<animateFilter" );
                }
                else if( pAtom->hasChildAtom( DFF_msofbtAnimCommand ) )
                {
                    dump( "<command" );
                }
                else
                {
                    OSL_FAIL( "unknown node atom!" );
                    dump_atom_header( pAtom, true, false );
                    dump_atom( pAtom );
                    dump_atom_header( pAtom, false, false );
                    break;
                }
                dump( aNode );
                dump( aSet );
#endif
                int nANCNodes = importAnimationNodeContainer( pAtom, xNode );
                if( !convertAnimationNode( xNode, xParent ) )
                    xNode = nullptr;
                else
                    nNodes += nANCNodes;
                dump( "/>\n");
 
            }
            break;
 
            case mso_Anim_GroupType_MEDIA:
            {
                dump( "<audio" );
                dump( aNode );
                dump( aSet );
                nNodes += importAudioContainer( pAtom, xNode );
                dump( "</audio>\n" );
            }
            break;
 
            default:
                OSL_FAIL( "unknown group atom!" );
 
                dump_atom_header( pAtom, true, false );
                dump_atom( pAtom );
                dump_atom_header( pAtom, false, false );
                break;
 
            }
        }
 
        if( xParent.is() && xNode.is() )
        {
            Reference< XTimeContainer > xParentContainer( xParent, UNO_QUERY );
            DBG_ASSERT( xParentContainer.is(), "parent is no container, then why do I have a child here?" );
            if( xParentContainer.is() )
            {
                xParentContainer->appendChild( xNode );
            }
        }
    }
 
    return nNodes;
}
 
bool AnimationImporter::convertAnimationNode( const Reference< XAnimationNode >& xNode, const Reference< XAnimationNode >& xParent )
{
    Reference< XAnimate > xAnimate( xNode, UNO_QUERY );
    if( !xAnimate.is() )
        return true;
 
    if( !xAnimate->getTarget().hasValue() )
        return false;
 
    const sal_Int16 nNodeType = xNode->getType();
 
    if( nNodeType == AnimationNodeType::TRANSITIONFILTER )
        return true;
 
    OUString aAttributeName( xAnimate->getAttributeName() );
 
    if( (nNodeType == AnimationNodeType::SET) && aAttributeName == "fill.on" )
        return false;
 
    const oox::ppt::ImplAttributeNameConversion* p = oox::ppt::getAttributeConversionList();
 
    oox::ppt::AnimationAttributeEnum eAttribute = oox::ppt::AnimationAttributeEnum::UNKNOWN;
 
    if( (nNodeType == AnimationNodeType::ANIMATEMOTION) ||
        (nNodeType == AnimationNodeType::ANIMATETRANSFORM) )
    {
        aAttributeName.clear();
    }
    else
    {
        while( p->mpMSName )
        {
            if( aAttributeName.equalsAscii( p->mpMSName ) )
                break;
 
            p++;
        }
 
        DBG_ASSERT( p->mpMSName || aAttributeName.isEmpty(), "sd::AnimationImporter::convertAnimationNode(), unknown attribute!" );
#ifdef DBG_ANIM_LOG
        if( p->mpMSName == 0 ) dump( "<error text=\"sd::AnimationImporter::convertAnimationNode(), unknown attribute!\"/>\n" );
#endif
 
        eAttribute = p->meAttribute;
 
        if( p->mpAPIName )
            aAttributeName = OUString::createFromAscii( p->mpAPIName );
    }
 
    xAnimate->setAttributeName( aAttributeName );
 
    if(eAttribute != oox::ppt::AnimationAttributeEnum::UNKNOWN)
    {
        Any aAny( xAnimate->getFrom() );
        if( aAny.hasValue() )
        {
            if(oox::ppt::convertAnimationValue(eAttribute, aAny))
                xAnimate->setFrom( aAny );
        }
 
        aAny = xAnimate->getBy();
        if( aAny.hasValue() )
        {
            if(oox::ppt::convertAnimationValue(eAttribute, aAny))
                xAnimate->setBy( aAny );
        }
 
        aAny = xAnimate->getTo();
        if( aAny.hasValue() )
        {
            if(oox::ppt::convertAnimationValue(eAttribute, aAny))
                xAnimate->setTo( aAny );
        }
 
        Sequence< Any > aValues( xAnimate->getValues() );
        if( aValues.hasElements() )
        {
            for( Any& rValue : asNonConstRange(aValues) )
                oox::ppt::convertAnimationValue(eAttribute, rValue);
 
            xAnimate->setValues( aValues );
        }
 
        OUString aFormula( xAnimate->getFormula() );
        if( !aFormula.isEmpty() )
        {
            if(oox::ppt::convertMeasure(aFormula))
                xAnimate->setFormula( aFormula );
        }
    }
 
    // check for after-effect
    Sequence< NamedValue > aUserData( xNode->getUserData() );
    NamedValue* pLastValue = aUserData.getArray();
    sal_Int32 nRemoved = 0;
 
    bool bAfterEffect = false;
    sal_Int32 nMasterRel = 0;
    for (const NamedValue& rValue : aUserData)
    {
        if ( rValue.Name == "after-effect" )
        {
            rValue.Value >>= bAfterEffect;
            nRemoved++;
        }
        else if ( rValue.Name == "master-rel" )
        {
            rValue.Value >>= nMasterRel;
            nRemoved++;
        }
        else
        {
            if( nRemoved )
                *pLastValue = rValue;
            pLastValue++;
        }
    }
 
    if( nRemoved )
    {
        aUserData.realloc( aUserData.getLength() - nRemoved );
        xNode->setUserData( aUserData );
    }
 
    // if it's an after effect node, add it to the list for
    // later processing
    // after effect nodes are not inserted at their import
    // position, so return false in this case
    if( bAfterEffect )
    {
        if( nMasterRel != 2 )
        {
            Event aEvent;
 
            aEvent.Source <<= xParent;
            aEvent.Trigger = EventTrigger::END_EVENT;
            aEvent.Repeat = 0;
 
            xNode->setBegin( Any( aEvent ) );
        }
 
        // add to after effect nodes for later processing
        sd::AfterEffectNode aNode( xNode, xParent, nMasterRel == 2 );
        maAfterEffectNodes.push_back( aNode );
        return false;
    }
 
    return true;
}
 
void AnimationImporter::fillNode( Reference< XAnimationNode > const & xNode, const AnimationNode& rNode, const PropertySet& rSet )
{
    bool bAfterEffect = false;
 
    // attribute Restart
    if( rNode.mnRestart )
    {
        sal_Int16 nRestart = AnimationRestart::DEFAULT;
        switch( rNode.mnRestart )
        {
        case 1: nRestart = AnimationRestart::ALWAYS; break;
        case 2: nRestart = AnimationRestart::WHEN_NOT_ACTIVE; break;
        case 3: nRestart = AnimationRestart::NEVER; break;
        }
        xNode->setRestart( nRestart );
    }
 
    // attribute Fill
    if( rNode.mnFill )
    {
        sal_Int16 nFill = AnimationFill::DEFAULT;
        switch( rNode.mnFill )
        {
        case 1: nFill = AnimationFill::REMOVE; break;
        case 2: nFill = AnimationFill::FREEZE; break;
        case 3: nFill = AnimationFill::HOLD; break;
        case 4: nFill = AnimationFill::TRANSITION; break;
        }
        xNode->setFill( nFill );
    }
 
    // attribute Duration
    if( rNode.mnDuration )
    {
        Any aDuration;
        if( rNode.mnDuration > 0 )
        {
            aDuration <<= rNode.mnDuration / 1000.0;
        }
        else if( rNode.mnDuration < 0 )
        {
            aDuration <<= Timing_INDEFINITE;
        }
        xNode->setDuration( aDuration );
    }
 
    // TODO: DFF_ANIM_PATH_EDIT_MODE
 
    // set user data
    Sequence< NamedValue > aUserData;
 
    // attribute Type
    if( rSet.hasProperty( DFF_ANIM_NODE_TYPE ) )
    {
        sal_Int32 nPPTNodeType = 0;
        if( rSet.getProperty( DFF_ANIM_NODE_TYPE ) >>= nPPTNodeType )
        {
            sal_Int16 nNodeType = css::presentation::EffectNodeType::DEFAULT;
            switch( nPPTNodeType )
            {
                case DFF_ANIM_NODE_TYPE_CLICK_PARALLEL: [[fallthrough]];
                case DFF_ANIM_NODE_TYPE_ON_CLICK:       nNodeType = css::presentation::EffectNodeType::ON_CLICK;   break;
                case DFF_ANIM_NODE_TYPE_WITH_GROUP:     [[fallthrough]];
                case DFF_ANIM_NODE_TYPE_WITH_PREVIOUS:  nNodeType = css::presentation::EffectNodeType::WITH_PREVIOUS; break;
                case DFF_ANIM_NODE_TYPE_AFTER_GROUP:    [[fallthrough]];
                case DFF_ANIM_NODE_TYPE_AFTER_PREVIOUS: nNodeType = css::presentation::EffectNodeType::AFTER_PREVIOUS; break;
                case DFF_ANIM_NODE_TYPE_MAIN_SEQUENCE:  nNodeType = css::presentation::EffectNodeType::MAIN_SEQUENCE; break;
                case DFF_ANIM_NODE_TYPE_TIMING_ROOT:    nNodeType = css::presentation::EffectNodeType::TIMING_ROOT; break;
                case DFF_ANIM_NODE_TYPE_INTERACTIVE_SEQ:nNodeType = css::presentation::EffectNodeType::INTERACTIVE_SEQUENCE; break;
            }
 
            sal_Int32 nSize = aUserData.getLength();
            aUserData.realloc(nSize+1);
            auto pUserData = aUserData.getArray();
            pUserData[nSize].Name = "node-type";
            pUserData[nSize].Value <<= nNodeType;
        }
    }
 
    if( rSet.hasProperty( DFF_ANIM_GROUP_ID ) )
    {
        sal_Int32 nGroupId;
        if( rSet.getProperty( DFF_ANIM_GROUP_ID ) >>= nGroupId )
        {
            sal_Int32 nSize = aUserData.getLength();
            aUserData.realloc(nSize+1);
            auto pUserData = aUserData.getArray();
            pUserData[nSize].Name = "group-id";
            pUserData[nSize].Value <<= nGroupId;
        }
    }
 
    sal_Int16 nEffectPresetClass = EffectPresetClass::CUSTOM;
    sal_Int32 nPresetId = 0;
 
    if( rSet.hasProperty( DFF_ANIM_PRESET_CLASS ) )
    {
        sal_Int32 nPresetClass = 0;
        if ( rSet.getProperty( DFF_ANIM_PRESET_CLASS ) >>= nPresetClass )
        {
            switch( nPresetClass )
            {
            case DFF_ANIM_PRESS_CLASS_ENTRANCE:     nEffectPresetClass = EffectPresetClass::ENTRANCE; break;
            case DFF_ANIM_PRESS_CLASS_EXIT:         nEffectPresetClass = EffectPresetClass::EXIT; break;
            case DFF_ANIM_PRESS_CLASS_EMPHASIS:     nEffectPresetClass = EffectPresetClass::EMPHASIS; break;
            case DFF_ANIM_PRESS_CLASS_MOTIONPATH:   nEffectPresetClass = EffectPresetClass::MOTIONPATH; break;
            case DFF_ANIM_PRESS_CLASS_OLE_ACTION:   nEffectPresetClass = EffectPresetClass::OLEACTION; break;
            case DFF_ANIM_PRESS_CLASS_MEDIACALL:    nEffectPresetClass = EffectPresetClass::MEDIACALL; break;
            }
            sal_Int32 nSize = aUserData.getLength();
            aUserData.realloc(nSize+1);
            auto pUserData = aUserData.getArray();
            pUserData[nSize].Name = "preset-class";
            pUserData[nSize].Value <<= nEffectPresetClass;
        }
    }
 
    if( rSet.hasProperty( DFF_ANIM_PRESET_ID ) )
    {
        if( rSet.getProperty( DFF_ANIM_PRESET_ID ) >>= nPresetId )
        {
            sal_Int32 nSize = aUserData.getLength();
            aUserData.realloc(nSize+1);
            auto pUserData = aUserData.getArray();
            pUserData[nSize].Name = "preset-id";
 
            const oox::ppt::preset_mapping* p = oox::ppt::preset_mapping::getList();
            while( p->mpStrPresetId && ((p->mnPresetClass != nEffectPresetClass) || (p->mnPresetId != nPresetId )) )
                p++;
 
            if( p->mpStrPresetId )
            {
                pUserData[nSize].Value <<= OUString::createFromAscii( p->mpStrPresetId );
            }
            else
            {
                OUStringBuffer sBuffer( "ppt_" );
                switch( nEffectPresetClass )
                {
                case EffectPresetClass::ENTRANCE: sBuffer.append( "entrance_" ); break;
                case EffectPresetClass::EXIT: sBuffer.append( "exit_" ); break;
                case EffectPresetClass::EMPHASIS: sBuffer.append( "emphasis_" ); break;
                case EffectPresetClass::MOTIONPATH: sBuffer.append( "motionpath_" ); break;
                case EffectPresetClass::OLEACTION: sBuffer.append( "oleaction_" ); break;
                case EffectPresetClass::MEDIACALL: sBuffer.append( "mediacall_" ); break;
                }
                sBuffer.append( nPresetId );
 
                pUserData[nSize].Value <<= sBuffer.makeStringAndClear();
            }
        }
    }
 
    if( rSet.hasProperty( DFF_ANIM_PRESET_SUB_TYPE ) )
    {
        sal_Int32 nPresetSubType = 0;
        if( rSet.getProperty( DFF_ANIM_PRESET_SUB_TYPE ) >>= nPresetSubType )
        {
            if( nPresetSubType )
            {
                sal_Int32 nSize = aUserData.getLength();
                aUserData.realloc(nSize+1);
                auto pUserData = aUserData.getArray();
                pUserData[nSize].Name = "preset-sub-type";
                pUserData[nSize].Value <<= oox::ppt::getConvertedSubType( nEffectPresetClass, nPresetId, nPresetSubType );
            }
        }
    }
 
    if( rSet.hasProperty( DFF_ANIM_AFTEREFFECT ) )
    {
        if( rSet.getProperty( DFF_ANIM_AFTEREFFECT ) >>= bAfterEffect )
        {
            sal_Int32 nSize = aUserData.getLength();
            aUserData.realloc(nSize+1);
            auto pUserData = aUserData.getArray();
            pUserData[nSize].Name = "after-effect";
            pUserData[nSize].Value <<= bAfterEffect;
        }
    }
 
    if( bAfterEffect && rSet.hasProperty( DFF_ANIM_MASTERREL ) )
    {
        sal_Int32 nMasterRel = 2;
        if( rSet.getProperty( DFF_ANIM_MASTERREL ) >>= nMasterRel )
        {
            sal_Int32 nSize = aUserData.getLength();
            aUserData.realloc(nSize+1);
            auto pUserData = aUserData.getArray();
            pUserData[nSize].Name = "master-rel";
            pUserData[nSize].Value <<= nMasterRel;
        }
    }
 
    xNode->setUserData( aUserData );
 
    // TODO: DFF_ANIM_ID
    if( rSet.hasProperty( DFF_ANIM_ID ) )
    {
        OUString aString;
        rSet.getProperty( DFF_ANIM_ID ) >>= aString;
        //if( !aString.isEmpty() )
        //{
        //}
    }
 
    // TODO: DFF_ANIM_EVENT_FILTER
    if( rSet.hasProperty( DFF_ANIM_EVENT_FILTER ) )
    {
        OUString aString;
        rSet.getProperty( DFF_ANIM_EVENT_FILTER ) >>= aString;
        //if( !aString.isEmpty() )
        //{
        //}
    }
 
    // DFF_ANIM_TIMEFILTER
    if( rSet.hasProperty( DFF_ANIM_TIMEFILTER ) )
    {
        Reference< XAnimate > xAnim( xNode, UNO_QUERY );
        if( xAnim.is() )
        {
            OUString aString;
            rSet.getProperty( DFF_ANIM_TIMEFILTER ) >>= aString;
            if( !aString.isEmpty() )
            {
                sal_Int32 nElements = 1; // a non empty string has at least one value
 
                sal_Int32 fromIndex = 0;
                while(true)
                {
                    fromIndex = aString.indexOf( ';', fromIndex );
                    if( fromIndex == -1 )
                        break;
 
                    fromIndex++;
                    nElements++;
                }
 
                Sequence< TimeFilterPair > aTimeFilter( nElements );
 
                TimeFilterPair* pValues = aTimeFilter.getArray();
                sal_Int32 nIndex = 0;
                while( (nElements--) && (nIndex >= 0) )
                {
                    const std::u16string_view aToken( o3tl::getToken(aString, 0, ';', nIndex ) );
 
                    size_t nPos = aToken.find( ',' );
                    if( nPos != std::u16string_view::npos )
                    {
                        pValues->Time = o3tl::toDouble(aToken.substr( 0, nPos ));
                        pValues->Progress = o3tl::toDouble(aToken.substr( nPos+1 ));
                    }
                    pValues++;
                }
 
                xAnim->setTimeFilter( aTimeFilter );
            }
        }
    }
 
// TODO: DFF_ANIM_ENDAFTERSLIDE / DFF_ANIM_VOLUME handling. git history has sample code
    Reference< XAnimateColor > xColor( xNode, UNO_QUERY );
    if( !xColor.is() )
        return;
 
    if( rSet.hasProperty( DFF_ANIM_DIRECTION ) )
    {
        bool bDirection = false;
        if( rSet.getProperty( DFF_ANIM_DIRECTION ) >>= bDirection )
            xColor->setDirection( !bDirection );
    }
 
    if( rSet.hasProperty( DFF_ANIM_COLORSPACE ) )
    {
        sal_Int32 nColorSpace = 0;
        rSet.getProperty( DFF_ANIM_COLORSPACE ) >>= nColorSpace;
        xColor->setColorInterpolation( (nColorSpace == 0) ? AnimationColorSpace::RGB : AnimationColorSpace::HSL );
    }
}
 
int AnimationImporter::importTimeContainer( const Atom* pAtom, const Reference< XAnimationNode >& xNode )
{
    int nNodes = 0;
 
    DBG_ASSERT( pAtom && xNode.is(), "invalid call to ppt::AnimationImporter::importTimeContainer()!");
    if( pAtom && xNode.is() )
    {
        importAnimationEvents( pAtom, xNode );
        importAnimationValues( pAtom, xNode );
        importAnimationActions( pAtom, xNode );
 
        dump(">\n");
 
        // import sub containers
        const Atom* pChildAtom = pAtom->findFirstChildAtom();
 
        while( pChildAtom )
        {
            switch( pChildAtom->getType() )
            {
                case DFF_msofbtAnimNode:
                case DFF_msofbtAnimEvent:
                case DFF_msofbtAnimValue:
                case DFF_msofbtAnimAction:
                case DFF_msofbtAnimPropertySet:
                    break;
 
                case DFF_msofbtAnimSubGoup :
                {
                    if( pChildAtom->hasChildAtom( DFF_msofbtAnimCommand ) )
                    {
                        const Reference< XComponentContext >& xContext = ::comphelper::getProcessComponentContext();
                        Reference< XAnimationNode > xChildNode( Command::create(xContext), UNO_QUERY_THROW );
                        nNodes += importAnimationNodeContainer( pChildAtom, xChildNode );
                        Reference< XTimeContainer > xParentContainer( xNode, UNO_QUERY );
                        if( xParentContainer.is() && xChildNode.is() )
                            xParentContainer->appendChild( xChildNode );
                    }
                    else
                    {
                        nNodes += importAnimationContainer( pChildAtom, xNode );
                    }
                }
                break;
                case DFF_msofbtAnimGroup :
                {
                    nNodes += importAnimationContainer( pChildAtom, xNode );
                }
                break;
                case DFF_msofbtAnimIteration:
                {
                    if( pChildAtom->seekToContent() )
                    {
                        float fInterval(0.0);
                        sal_Int32 nTextUnitEffect(0), nU1(0), nU2(0), nU3(0);
 
                        mrStCtrl.ReadFloat( fInterval ).ReadInt32( nTextUnitEffect ).ReadInt32( nU1 ).ReadInt32( nU2 ).ReadInt32( nU3 );
 
                        Reference< XIterateContainer > xIter( xNode, UNO_QUERY );
                        if( xIter.is() )
                        {
                            sal_Int16 nIterateType = TextAnimationType::BY_PARAGRAPH;
                            switch( nTextUnitEffect )
                            {
                            case 1: nIterateType = TextAnimationType::BY_WORD; break;
                            case 2: nIterateType = TextAnimationType::BY_LETTER; break;
                            }
                            xIter->setIterateType( nIterateType );
                            xIter->setIterateInterval( static_cast<double>(fInterval) );
                        }
 
                        nNodes++;
 
                        dump( "<iterate" );
                        dump( " iterateType=\"%s\"", (nTextUnitEffect == 0) ? "byElement" : (nTextUnitEffect == 1) ? "byWord" : "byLetter" );
                        dump( " iterateInterval=\"%g\"", fInterval );
                        dump( " u1=\"%ld\"", nU1 );
                        dump( " u2=\"%ld\"", nU2 );
                        dump( " u3=\"%ld\"/>\n", nU3 );
                    }
                }
                break;
 
                case 0xf136:
                {
#ifdef DBG_ANIM_LOG
                    sal_uInt32 nU1, nU2;
                    mrStCtrl.ReadUInt32(nU1).ReadUInt32(nU2);
 
                    fprintf( mpFile, "<unknown_0xf136 nU1=\"%" SAL_PRIdINT32 "\" nU2=\"%" SAL_PRIdINT32 "\"/>\n", nU1, nU2 );
#endif
                }
                break;
 
                default:
                {
                    dump_atom_header( pChildAtom, true, false );
                    dump_atom( pChildAtom );
                    dump_atom_header( pChildAtom, false, false );
                }
                break;
            }
 
            pChildAtom = Atom::findNextChildAtom( pChildAtom );
        }
    }
 
    return nNodes;
}
 
int AnimationImporter::importAnimationNodeContainer( const Atom* pAtom, const Reference< XAnimationNode >& xNode )
{
    int nNodes = 0;
 
    DBG_ASSERT( pAtom && xNode.is(), "invalid call to ppt::AnimationImporter::importAnimationNodeContainer()!");
    if( pAtom && xNode.is() )
    {
        importAnimationEvents( pAtom, xNode );
        importAnimationValues( pAtom, xNode );
        importAnimationActions( pAtom, xNode );
 
        const Atom* pChildAtom = pAtom->findFirstChildAtom();
 
        while( pChildAtom )
        {
            nNodes ++;
            switch( pChildAtom->getType() )
            {
                case DFF_msofbtAnimNode:
                case DFF_msofbtAnimEvent:
                case DFF_msofbtAnimValue:
                case DFF_msofbtAnimAction:
                case DFF_msofbtAnimPropertySet:
                    break;
 
                case DFF_msofbtAnimateFilter:
                    importAnimateFilterContainer( pChildAtom, xNode );
                    break;
 
                case DFF_msofbtAnimateSet:
                    importAnimateSetContainer( pChildAtom, xNode );
                    break;
 
                case DFF_msofbtAnimate:
                    importAnimateContainer( pChildAtom, xNode );
                    break;
 
                case DFF_msofbtAnimateScale:
                    importAnimateScaleContainer( pChildAtom, xNode );
                    break;
 
                case DFF_msofbtAnimateColor:
                    importAnimateColorContainer( pChildAtom, xNode );
                    break;
 
                case DFF_msofbtAnimateRotation:
                    importAnimateRotationContainer( pChildAtom, xNode );
                    break;
 
                case DFF_msofbtAnimateMotion:
                    importAnimateMotionContainer( pChildAtom, xNode );
                    break;
 
                case DFF_msofbtAnimCommand:
                    importCommandContainer( pChildAtom, xNode );
                    break;
 
                default:
                {
                    nNodes --;
                    dump_atom_header( pChildAtom, true, false );
                    dump_atom( pChildAtom );
                    dump_atom_header( pChildAtom, false, false );
                }
                break;
            }
 
            pChildAtom = Atom::findNextChildAtom( pChildAtom );
        }
    }
 
    return nNodes;
}
 
void AnimationImporter::importAnimateFilterContainer( const Atom* pAtom, const Reference< XAnimationNode >& xNode )
{
    Reference< XTransitionFilter > xFilter( xNode, UNO_QUERY );
 
    DBG_ASSERT( pAtom && pAtom->getType() == DFF_msofbtAnimateFilter && xFilter.is(), "invalid call to ppt::AnimationImporter::importAnimateFilterContainer()!");
    if( !(pAtom && xFilter.is()) )
        return;
 
    sal_uInt32 nBits = 0;
 
    const Atom* pChildAtom = pAtom->findFirstChildAtom();
 
    while( pChildAtom )
    {
        if( !pChildAtom->isContainer() )
        {
            if( !pChildAtom->seekToContent() )
                break;
        }
 
        switch( pChildAtom->getType() )
        {
        case DFF_msofbtAnimateFilterData:
        {
            sal_uInt32 transition(0);
            mrStCtrl.ReadUInt32( nBits );
            mrStCtrl.ReadUInt32( transition );
 
            if( nBits & 1 )
                xFilter->setMode( transition == 0 );
 
            dump( " transition=\"%s\"", (transition == 0) ? "in" : "out" );
        }
        break;
 
        case DFF_msofbtAnimAttributeValue:
        {
            if( (nBits & 2 ) && ( pChildAtom->getInstance() == 1 )  )
            {
                Any aAny;
                if ( importAttributeValue( pChildAtom, aAny ) )
                {
                    OUString filter;
                    aAny >>= filter;
 
                    dump( " filter=\"%s\"", filter );
 
                    const oox::ppt::transition* pTransition = oox::ppt::transition::find( filter );
                    if( pTransition )
                    {
                        xFilter->setTransition( pTransition->mnType );
                        xFilter->setSubtype( pTransition->mnSubType );
                        xFilter->setDirection( pTransition->mbDirection );
                    }
                    else
                    {
                        OSL_FAIL( "unknown transition!" );
                    }
                }
            }
        }
        break;
 
        case DFF_msofbtAnimateTarget:
            importAnimateAttributeTargetContainer( pChildAtom, xNode );
            break;
 
        default:
            dump( " unknown_atom=\"%ld\"", static_cast<sal_Int32>(pChildAtom->getType()) );
            break;
 
        }
 
        pChildAtom = Atom::findNextChildAtom( pChildAtom );
    }
}
 
void AnimationImporter::importAnimateAttributeTargetContainer( const Atom* pAtom, const Reference< XAnimationNode >& xNode )
{
    DBG_ASSERT( pAtom && pAtom->getType() == DFF_msofbtAnimateTarget, "invalid call to ppt::AnimationImporter::importAnimateAttributeTargetContainer()!");
 
    Any aTarget;
 
    Reference< XAnimate > xAnimate( xNode, UNO_QUERY );
 
    bool bWrongContext = false;
 
    if( pAtom )
    {
        const Atom* pChildAtom = pAtom->findFirstChildAtom();
 
        while( pChildAtom )
        {
            if( !pChildAtom->isContainer() )
            {
                if( !pChildAtom->seekToContent() )
                    break;
            }
 
            switch( pChildAtom->getType() )
            {
            case DFF_msofbtAnimPropertySet:
            {
                PropertySet aSet;
                importPropertySetContainer( pChildAtom, aSet );
                if( aSet.hasProperty( DFF_ANIM_RUNTIMECONTEXT ) )
                {
                    OUString aContext;
                    if( aSet.getProperty( DFF_ANIM_RUNTIMECONTEXT ) >>= aContext )
                    {
                        if( aContext != "PPT" )
                            bWrongContext = true;
                    }
                }
 
                dump( aSet );
            }
            break;
 
            case DFF_msofbtAnimateTargetSettings:
            {
                if( xAnimate.is() )
                {
                    sal_uInt32 nBits(0);
                    sal_uInt32 nAdditive(0);
                    sal_uInt32 nAccumulate(0);
                    sal_uInt32 nTransformType(0);
 
                    mrStCtrl.ReadUInt32( nBits ).ReadUInt32( nAdditive ).ReadUInt32( nAccumulate ).ReadUInt32( nTransformType );
 
                    // nBits %0001: additive, %0010: accumulate, %0100: attributeName, %1000: transformtype
                    // nAdditive 0 = base, 1 = sum, 2 = replace, 3 = multiply, 4 = none
                    // nAccumulate 0 = none, 1 = always
                    // nTransformType 0: "property" else "image"
 
                    if( nBits & 3 && xAnimate.is() )
                    {
                        if( nBits & 1 )
                        {
                            sal_Int16 nTemp = AnimationAdditiveMode::BASE;
                            switch( nAdditive )
                            {
                            case 1: nTemp = AnimationAdditiveMode::SUM; break;
                            case 2: nTemp = AnimationAdditiveMode::REPLACE; break;
                            case 3: nTemp = AnimationAdditiveMode::MULTIPLY; break;
                            case 4: nTemp = AnimationAdditiveMode::NONE; break;
                            }
                            xAnimate->setAdditive( nTemp );
                        }
 
                        if( nBits & 2 )
                        {
                            xAnimate->setAccumulate( nAccumulate == 0 );
                        }
                    }
#ifdef DBG_ANIM_LOG
                    if( nBits & 1 )
                        fprintf( mpFile, " additive=\"%s\"", (nAdditive == 0) ? "base" : (nAdditive == 2) ? "replace" : (nAdditive == 1) ? "sum" : (nAdditive == 3 ) ? "multiply" : (nAdditive == 4) ? "none" : "unknown" );
 
                    if( nBits & 2 )
                        fprintf( mpFile, " accumulate=\"%s\"", (nAccumulate == 0) ? "none" : "always" );
 
                    if( nBits & 8 )
                        fprintf( mpFile, " transformType=\"%s\"", (nTransformType == 0) ? "property" : "image" );
#endif
                }
            }
            break;
 
            case DFF_msofbtAnimateAttributeNames:
            {
                if( xAnimate.is() )
                {
                    OUString aAttributeName;
                    importAttributeNamesContainer( pChildAtom, aAttributeName );
                    if( xAnimate.is() )
                        xAnimate->setAttributeName( aAttributeName );
                    dump( " attributeName=\"%s\"", aAttributeName );
                }
            }
            break;
 
            case DFF_msofbtAnimateTargetElement:
            {
                sal_Int16 nSubType;
                importTargetElementContainer( pChildAtom, aTarget, nSubType );
                if( xAnimate.is() )
                    xAnimate->setSubItem( nSubType );
 
                dump( " target=\"" );
                dump_target( aTarget );
                dump( "\"" );
            }
            break;
 
            default:
                dump( " unknown_atom=\"%ld\"", static_cast<sal_Int32>(pChildAtom->getType()) );
                break;
            }
 
            pChildAtom = Atom::findNextChildAtom( pChildAtom );
        }
    }
 
    if( bWrongContext )
        aTarget.clear();
 
    if( xAnimate.is() )
        xAnimate->setTarget( aTarget );
    else
    {
        Reference< XCommand > xCommand( xNode, UNO_QUERY );
        if( xCommand.is() )
            xCommand->setTarget( aTarget );
    }
}
 
sal_Int16 AnimationImporter::implGetColorSpace( sal_Int32 nMode, sal_Int32 /*nA*/, sal_Int32 /*nB*/, sal_Int32 /*nC*/ )
{
    switch( nMode )
    {
    case 2: // index
    default:
    case 0: // rgb
        return AnimationColorSpace::RGB;
 
    case 1: // hsl
        return AnimationColorSpace::HSL;
    }
}
 
Any AnimationImporter::implGetColorAny( sal_Int32 nMode, sal_Int32  nA, sal_Int32 nB, sal_Int32 nC )
{
    switch( nMode )
    {
    case 0: // rgb
        {
            dump( "rgb(%ld", nA );
            dump( ",%ld", nB );
            dump( ",%ld)", nC );
            Color aColor( static_cast<sal_uInt8>(nA), static_cast<sal_uInt8>(nB), static_cast<sal_uInt8>(nC) );
            return Any( static_cast<sal_Int32>(aColor.GetRGBColor()) );
        }
    case 1: // hsl
        {
            dump( "hsl(%ld", nA );
            dump( ",%ld", nB );
            dump( ",%ld)", nC );
            Sequence< double > aHSL{ nA * 360.0/255.0,
                                     nB / 255.0,
                                     nC / 255.0 };
            return Any( aHSL );
        }
 
    case 2: // index
        {
            Color aColor;
            mpPPTImport->GetColorFromPalette(static_cast<sal_uInt16>(nA), aColor );
            dump( "index(%ld", nA );
            dump( " [%ld", static_cast<sal_Int32>(aColor.GetRed()) );
            dump( ",%ld", static_cast<sal_Int32>(aColor.GetGreen()) );
            dump( ",%ld])", static_cast<sal_Int32>(aColor.GetBlue()) );
            return Any( static_cast<sal_Int32>(aColor.GetRGBColor()) );
        }
 
    default:
        {
            dump( "unknown_%ld(", nMode );
            dump( "%ld", nA );
            dump( ",%ld", nB );
            dump( ",%ld)", nC );
            OSL_FAIL( "ppt::implGetColorAny(), unhandled color type" );
 
            Any aAny;
            return aAny;
        }
    }
}
 
void AnimationImporter::importAnimateColorContainer( const Atom* pAtom, const Reference< XAnimationNode >& xNode )
{
    Reference< XAnimateColor > xColor( xNode, UNO_QUERY );
 
    DBG_ASSERT( pAtom && pAtom->getType() == DFF_msofbtAnimateColor && xColor.is(), "invalid call to ppt::AnimationImporter::importAnimateColorContainer()!");
    if( !(pAtom && xColor.is()) )
        return;
 
    const Atom* pChildAtom = pAtom->findFirstChildAtom();
 
    while( pChildAtom )
    {
        if( !pChildAtom->isContainer() )
        {
            if( !pChildAtom->seekToContent() )
                break;
        }
 
        switch( pChildAtom->getType() )
        {
        case DFF_msofbtAnimateColorData:
        {
            sal_uInt32 nBits;
            sal_Int32 nByMode, nByA, nByB, nByC;
            sal_Int32 nFromMode, nFromA, nFromB, nFromC;
            sal_Int32 nToMode, nToA, nToB, nToC;
            mrStCtrl.ReadUInt32( nBits );
            mrStCtrl.ReadInt32( nByMode ).ReadInt32( nByA ).ReadInt32( nByB ).ReadInt32( nByC );
            mrStCtrl.ReadInt32( nFromMode ).ReadInt32( nFromA ).ReadInt32( nFromB ).ReadInt32( nFromC );
            mrStCtrl.ReadInt32( nToMode ).ReadInt32( nToA ).ReadInt32( nToB ).ReadInt32( nToC );
 
            if (!mrStCtrl.good())
            {
                SAL_WARN("filter.ms", "DFF_msofbtAnimateColorData: short read");
                break;
            }
 
            if( nBits & 1 )
            {
                dump( " by=\"" );
                xColor->setBy( implGetColorAny( nByMode, nByA, nByB, nByC ) );
                xColor->setColorInterpolation( implGetColorSpace( nByMode, nByA, nByB, nByC ) );
                dump( "\"");
            }
 
            if( nBits & 2 )
            {
                dump( " from=\"" );
                xColor->setFrom( implGetColorAny( nFromMode, nFromA, nFromB, nFromC ) );
                xColor->setColorInterpolation( implGetColorSpace( nFromMode, nFromA, nFromB, nFromC ) );
                dump( "\"");
            }
 
            if( nBits & 4 )
            {
                dump( " to=\"" );
                xColor->setTo( implGetColorAny( nToMode, nToA, nToB, nToC ) );
                xColor->setColorInterpolation( implGetColorSpace( nToMode, nToA, nToB, nToC ) );
                dump( "\"");
            }
        }
        break;
 
        case DFF_msofbtAnimateTarget:
            importAnimateAttributeTargetContainer( pChildAtom, xNode );
            break;
 
        default:
            dump( " unknown_atom=\"%ld\"", static_cast<sal_Int32>(pChildAtom->getType()) );
            break;
        }
 
        pChildAtom = Atom::findNextChildAtom( pChildAtom );
    }
}
 
void AnimationImporter::importAnimateSetContainer( const Atom* pAtom, const Reference< XAnimationNode >& xNode )
{
    Reference< XAnimateSet > xSet( xNode, UNO_QUERY );
 
    DBG_ASSERT( pAtom && pAtom->getType() == DFF_msofbtAnimateSet && xSet.is(), "invalid call to ppt::AnimationImporter::importAnimateSetContainer()!");
    if( !(pAtom && xSet.is()) )
        return;
 
    const Atom* pChildAtom = pAtom->findFirstChildAtom();
 
    while( pChildAtom )
    {
        if( !pChildAtom->isContainer() )
        {
            if( !pChildAtom->seekToContent() )
                break;
        }
 
        switch( pChildAtom->getType() )
        {
        case DFF_msofbtAnimateSetData:
        {
            sal_Int32 nU1, nU2;
            mrStCtrl.ReadInt32( nU1 ).ReadInt32( nU2 );
 
            dump( " set_1=\"%ld\"", nU1 );
            dump( " set_2=\"%ld\"", nU2 );
        }
        break;
 
        case DFF_msofbtAnimAttributeValue:
        {
            Any aTo;
            if ( importAttributeValue( pChildAtom, aTo ) )
            {
                xSet->setTo( aTo );
 
                dump( " value=\"" );
                dump( aTo );
                dump( "\"" );
            }
        }
        break;
 
        case DFF_msofbtAnimateTarget:
            importAnimateAttributeTargetContainer( pChildAtom, xNode );
            break;
 
        default:
            dump( " unknown_atom=\"%ld\"", static_cast<sal_Int32>(pChildAtom->getType()) );
            break;
        }
 
        pChildAtom = Atom::findNextChildAtom( pChildAtom );
    }
}
 
void AnimationImporter::importAnimateContainer( const Atom* pAtom, const Reference< XAnimationNode >& xNode )
{
    Reference< XAnimate > xAnim( xNode, UNO_QUERY );
 
    DBG_ASSERT( pAtom && pAtom->getType() == DFF_msofbtAnimate && xAnim.is(), "invalid call to ppt::AnimationImporter::importAnimateContainer()!");
    if( !(pAtom && xAnim.is()) )
        return;
 
    const Atom* pChildAtom = pAtom->findFirstChildAtom();
 
    while( pChildAtom )
    {
        if( !pChildAtom->isContainer() )
        {
            if( !pChildAtom->seekToContent() )
                break;
        }
 
        switch( pChildAtom->getType() )
        {
        case DFF_msofbtAnimateData:
        {
            sal_uInt32 nCalcmode(0), nBits(0), nValueType(0);
            mrStCtrl.ReadUInt32( nCalcmode ).ReadUInt32( nBits ).ReadUInt32( nValueType );
 
            if( nBits & 0x08 )
            {
                sal_Int16 n = (nCalcmode == 1) ? AnimationCalcMode::LINEAR : /* (nCalcmode == 2) ? AnimationCalcMode::FORMULA : */ AnimationCalcMode::DISCRETE;
                xAnim->setCalcMode( n );
                dump( " calcmode=\"%s\"", (nCalcmode == 0) ? "discrete" : (nCalcmode == 1) ? "linear" : (nCalcmode == 2) ? "formula" : "unknown" );
            }
 
            if( nBits & 0x30 )
            {
                sal_Int16 n = (nValueType == 1) ? AnimationValueType::NUMBER : (nValueType == 2 ) ? AnimationValueType::COLOR : AnimationValueType::STRING;
                xAnim->setValueType( n );
                dump( " valueType=\"%s\"", (nValueType == 0) ? "string" : (nValueType == 1) ? "number" : (nValueType == 2) ? "color" : "unknown" );
            }
        }
        break;
 
        case DFF_msofbtAnimateTarget:
            importAnimateAttributeTargetContainer( pChildAtom, xNode );
            break;
 
        case DFF_msofbtAnimKeyPoints:
            importAnimateKeyPoints( pChildAtom, xNode );
            break;
 
        case DFF_msofbtAnimAttributeValue:
            {
                Any a;
                if ( importAttributeValue( pChildAtom, a ) )
                {
                    switch( pChildAtom->getInstance() )
                    {
                    case 1: xAnim->setBy( a ); dump( " by=\"" ); break;
                    case 2: xAnim->setFrom( a ); dump( " from=\"" ); break;
                    case 3: xAnim->setTo( a ); dump( " to=\"" ); break;
                    default:
                        dump( " unknown_value=\"" );
                    }
 
                    dump( a );
                    dump( "\"" );
                }
            }
            break;
        default:
            dump( " unknown_atom=\"%ld\"", static_cast<sal_Int32>(pChildAtom->getType()) );
            break;
        }
 
        pChildAtom = Atom::findNextChildAtom( pChildAtom );
    }
}
 
void AnimationImporter::importAnimateMotionContainer( const Atom* pAtom, const Reference< XAnimationNode >& xNode )
{
    Reference< XAnimateMotion > xMotion( xNode, UNO_QUERY );
 
    DBG_ASSERT( pAtom && pAtom->getType() == DFF_msofbtAnimateMotion && xMotion.is(), "invalid call to ppt::AnimationImporter::importAnimateMotionContainer()!");
    if( !(pAtom && xMotion.is()) )
        return;
 
    const Atom* pChildAtom = pAtom->findFirstChildAtom();
 
    while( pChildAtom )
    {
        if( !pChildAtom->isContainer() )
        {
            if( !pChildAtom->seekToContent() )
                break;
        }
 
        switch( pChildAtom->getType() )
        {
        case DFF_msofbtAnimateMotionData:
        {
            sal_uInt32 nBits, nOrigin;
            float fByX, fByY, fFromX, fFromY, fToX, fToY;
 
            mrStCtrl.ReadUInt32( nBits ).ReadFloat( fByX ).ReadFloat( fByY ).ReadFloat( fFromX ).ReadFloat( fFromY ).ReadFloat( fToX ).ReadFloat( fToY ).ReadUInt32( nOrigin );
 
#ifdef DBG_ANIM_LOG
            if( nBits & 1 )
                fprintf( mpFile, " by=\"%g,%g\"", (double)fByX, (double)fByY );
 
            if( nBits & 2 )
                fprintf( mpFile, " from=\"%g,%g\"", (double)fFromX, (double)fFromY );
 
            if( nBits & 4 )
                fprintf( mpFile, " to=\"%g,%g\"", (double)fToX, (double)fToY );
 
            if( nBits & 8 )
                fprintf( mpFile, " origin=\"%s\"", (nOrigin == 1) ? "parent" : (nOrigin == 2) ? "layout" : "unknown" );
 
#endif
        }
        break;
 
        case DFF_msofbtAnimAttributeValue:
        {
            Any aPath;
            if ( importAttributeValue( pChildAtom, aPath ) )
            {
                OUString aStr;
                if ( aPath >>= aStr )
                {
                    // E can appear inside a number, so we only check for its presence at the end
                    aStr = aStr.trim();
                    if (aStr.endsWith("E"))
                        aStr = aStr.copy(0, aStr.getLength() - 1);
                    aStr = aStr.trim();
                    aPath <<= aStr;
                    xMotion->setPath( aPath );
                    dump( " path=\"" );
                    dump( aPath );
                    dump( "\"" );
                }
            }
        }
        break;
 
        case DFF_msofbtAnimateTarget:
            importAnimateAttributeTargetContainer( pChildAtom, xNode );
            break;
 
        default:
            dump( " unknown_atom=\"%ld\"", static_cast<sal_Int32>(pChildAtom->getType()) );
            break;
        }
 
        pChildAtom = Atom::findNextChildAtom( pChildAtom );
    }
}
 
void AnimationImporter::importCommandContainer( const Atom* pAtom, const Reference< XAnimationNode >& xNode )
{
    Reference< XCommand > xCommand( xNode, UNO_QUERY );
    DBG_ASSERT( pAtom && pAtom->getType() == DFF_msofbtAnimCommand && xCommand.is(), "invalid call to ppt::AnimationImporter::importCommandContainer()!");
    if( !(pAtom && xCommand.is()) )
        return;
 
    sal_Int32 nBits = 0;
    Any aValue;
 
    const Atom* pChildAtom = pAtom->findFirstChildAtom();
 
    while( pChildAtom )
    {
        if( !pChildAtom->isContainer() )
        {
            if( !pChildAtom->seekToContent() )
                break;
        }
 
        switch( pChildAtom->getType() )
        {
        case DFF_msofbtCommandData:
        {
            sal_Int32 nCommandType;
            // looks like U1 is a bitset, bit 1 enables the type and bit 2 enables
            // a propertyvalue that follows
            mrStCtrl.ReadInt32( nBits );
            mrStCtrl.ReadInt32( nCommandType );
 
            if( nBits & 1 )
            {
                dump( " type=\"%s\"", (nCommandType == 0) ? "event" : ( nCommandType == 1) ? "call" : "verb" );
            }
        }
        break;
 
        case DFF_msofbtAnimAttributeValue:
        {
            if ( importAttributeValue( pChildAtom, aValue ) )
            {
                if( nBits & 2 )
                {
                    dump( " cmd=\"" );
                    dump( aValue );
                    dump( "\"" );
                }
            }
        }
        break;
 
        case DFF_msofbtAnimateTarget:
            importAnimateAttributeTargetContainer( pChildAtom, xNode );
            break;
 
        default:
            dump( " unknown_atom=\"%ld\"", static_cast<sal_Int32>(pChildAtom->getType()) );
            break;
        }
 
        pChildAtom = Atom::findNextChildAtom( pChildAtom );
    }
 
    if( !(nBits & 3) )
        return;
 
    OUString aParam;
    aValue >>= aParam;
 
    sal_Int16 nCommand = EffectCommands::CUSTOM;
 
    NamedValue aParamValue;
 
    if ( aParam == "onstopaudio" )
    {
        nCommand = EffectCommands::STOPAUDIO;
    }
    else if ( aParam == "play" )
    {
        nCommand = EffectCommands::PLAY;
    }
    else if( aParam.startsWith( "playFrom" ) )
    {
        const std::u16string_view aMediaTime( aParam.subView( 9, aParam.getLength() - 10 ) );
        rtl_math_ConversionStatus eStatus;
        double fMediaTime = ::rtl::math::stringToDouble( aMediaTime, u'.', u',', &eStatus );
        if( eStatus == rtl_math_ConversionStatus_Ok )
        {
            aParamValue.Name = "MediaTime";
            aParamValue.Value <<= fMediaTime;
        }
        nCommand = EffectCommands::PLAY;
    }
    else if ( aParam == "togglePause" )
    {
        nCommand = EffectCommands::TOGGLEPAUSE;
    }
    else if ( aParam == "stop" )
    {
        nCommand = EffectCommands::STOP;
    }
 
    xCommand->setCommand( nCommand );
    if( nCommand == EffectCommands::CUSTOM )
    {
        OSL_FAIL("sd::AnimationImporter::importCommandContainer(), unknown command!");
        aParamValue.Name = "UserDefined";
        aParamValue.Value <<= aParam;
    }
 
    if( aParamValue.Value.hasValue() )
    {
        Sequence< NamedValue > aParamSeq( &aParamValue, 1 );
        xCommand->setParameter( Any( aParamSeq ) );
    }
}
 
int AnimationImporter::importAudioContainer( const Atom* pAtom, const Reference< XAnimationNode >& xNode )
{
    int nNodes = 0;
 
    Reference< XAudio > xAudio( xNode, UNO_QUERY );
    DBG_ASSERT( pAtom && xAudio.is() &&
                 ( (pAtom->getType() == DFF_msofbtAnimGroup) ||
                   (pAtom->getType() == DFF_msofbtAnimSubGoup) ), "invalid call to ppt::AnimationImporter::importAudioContainer()!");
    if( pAtom && xAudio.is() )
    {
        importAnimationEvents( pAtom, xNode );
        importAnimationValues( pAtom, xNode );
        importAnimationActions( pAtom, xNode );
 
        dump(">\n");
 
        const Atom* pChildAtom = pAtom->findFirstChildAtom();
 
        while( pChildAtom )
        {
            if( !pChildAtom->isContainer() )
            {
                if( !pChildAtom->seekToContent() )
                    break;
            }
 
            switch( pChildAtom->getType() )
            {
            case DFF_msofbtAnimNode:
            case DFF_msofbtAnimEvent:
            case DFF_msofbtAnimValue:
            case DFF_msofbtAnimAction:
            case DFF_msofbtAnimPropertySet:
                break;
 
            case DFF_msofbtAnimAttributeValue:
            {
                Any aValue;
                if ( importAttributeValue( pChildAtom, aValue ) )
                {
                    nNodes ++;
                    dump( " value=\"" );
                    dump( aValue );
                    dump( "\"" );
                }
            }
            break;
 
            case DFF_msofbtAnimateTargetElement:
            {
                sal_Int16 nSubType;
                Any aSource;
                importTargetElementContainer( pChildAtom, aSource, nSubType );
                if( xAudio.is() ) {
                    xAudio->setSource( aSource );
                    nNodes ++;
                }
            }
            break;
 
            default:
                dump( " unknown_atom=\"%ld\"", static_cast<sal_Int32>(pChildAtom->getType()) );
                break;
            }
 
            pChildAtom = Atom::findNextChildAtom( pChildAtom );
        }
 
        // TODO: What to do with them?
        Any aEmpty;
        xAudio->setBegin( aEmpty );
        xAudio->setEnd( aEmpty );
    }
 
    return nNodes;
}
 
void AnimationImporter::importAnimateScaleContainer( const Atom* pAtom, const Reference< XAnimationNode >& xNode )
{
    Reference< XAnimateTransform > xTransform( xNode, UNO_QUERY );
 
    DBG_ASSERT( pAtom && pAtom->getType() == DFF_msofbtAnimateScale && xTransform.is(), "invalid call to ppt::AnimationImporter::importAnimateScaleContainer()!");
    if( !(pAtom && xTransform.is()) )
        return;
 
    xTransform->setTransformType( AnimationTransformType::SCALE );
 
    const Atom* pChildAtom = pAtom->findFirstChildAtom();
 
    while( pChildAtom )
    {
        if( !pChildAtom->isContainer() )
        {
            if( !pChildAtom->seekToContent() )
                break;
        }
 
        switch( pChildAtom->getType() )
        {
        case DFF_msofbtAnimateScaleData:
        {
            sal_uInt32 nBits(0), nZoomContents(0);
            float fByX(0.0), fByY(0.0), fFromX(0.0), fFromY(0.0), fToX(0.0), fToY(0.0);
 
            // nBits %001: by, %010: from, %100: to, %1000: zoomContents(bool)
            mrStCtrl.ReadUInt32( nBits ).ReadFloat( fByX ).ReadFloat( fByY ).ReadFloat( fFromX ).ReadFloat( fFromY ).ReadFloat( fToX ).ReadFloat( fToY ).ReadUInt32( nZoomContents );
 
            ValuePair aPair;
            // 'from' value
            if( nBits & 2 )
            {
                aPair.First <<= static_cast<double>(fFromX) / 100.0;
                aPair.Second <<= static_cast<double>(fFromY) / 100.0;
                xTransform->setFrom( Any( aPair ) );
            }
 
            // 'to' value
            if( nBits & 4 )
            {
                aPair.First <<= static_cast<double>(fToX) / 100.0;
                aPair.Second <<= static_cast<double>(fToY) / 100.0;
                xTransform->setTo( Any( aPair ) );
            }
 
            // 'by' value
            if( nBits & 1 )
            {
                aPair.First <<= static_cast<double>(fByX) / 100.0;
                aPair.Second <<= static_cast<double>(fByY) / 100.0;
 
                if( nBits & 2 )
                {
                    // 'from' value given, import normally
                    xTransform->setBy( Any( aPair ) );
                }
                else
                {
                    // mapping 'by' to 'to', if no 'from' is
                    // given. This is due to a non-conformity in
                    // PPT, which exports animateScale effects
                    // with a sole 'by' value, but with the
                    // semantics of a sole 'to' animation
                    xTransform->setTo( Any( aPair ) );
                }
            }
 
#ifdef DBG_ANIM_LOG
            if( nBits & 1 )
                fprintf( mpFile, " by=\"%g,%g\"", (double)fByX, (double)fByY );
 
            if( nBits & 2 )
                fprintf( mpFile, " from=\"%g,%g\"", (double)fFromX, (double)fFromY );
 
            if( nBits & 4 )
                fprintf( mpFile, " to=\"%g,%g\"", (double)fToX, (double)fToY );
 
            if( nBits & 8 )
                fprintf( mpFile, " zoomContents=\"%s\"", nZoomContents ? "true" : "false" );
#endif
        }
        break;
 
        case DFF_msofbtAnimateTarget:
            importAnimateAttributeTargetContainer( pChildAtom, xNode );
            break;
 
        default:
            dump( " unknown_atom=\"%ld\"", static_cast<sal_Int32>(pChildAtom->getType()) );
            break;
        }
 
        pChildAtom = Atom::findNextChildAtom( pChildAtom );
    }
}
 
void AnimationImporter::importAnimateRotationContainer( const Atom* pAtom, const Reference< XAnimationNode >& xNode )
{
    Reference< XAnimateTransform > xTransform( xNode, UNO_QUERY );
 
    DBG_ASSERT( pAtom && pAtom->getType() == DFF_msofbtAnimateRotation && xTransform.is(), "invalid call to ppt::AnimationImporter::importAnimateRotationContainer()!");
    if( !(pAtom && xTransform.is()) )
        return;
 
    xTransform->setTransformType( AnimationTransformType::ROTATE );
 
    const Atom* pChildAtom = pAtom->findFirstChildAtom();
 
    while( pChildAtom )
    {
        if( !pChildAtom->isContainer() )
        {
            if( !pChildAtom->seekToContent() )
                break;
        }
 
        switch( pChildAtom->getType() )
        {
        case DFF_msofbtAnimateRotationData:
        {
            sal_uInt32 nBits(0), nU1(0);
            float fBy(0.0), fFrom(0.0), fTo(0.0);
 
            // nBits %001: by, %010: from, %100: to, %1000: zoomContents(bool)
            mrStCtrl.ReadUInt32( nBits ).ReadFloat( fBy ).ReadFloat( fFrom ).ReadFloat( fTo ).ReadUInt32( nU1 );
 
            if( nBits & 1 )
                xTransform->setBy( Any( static_cast<double>(fBy) ) );
 
            if( nBits & 2 )
                xTransform->setFrom( Any( static_cast<double>(fFrom) ) );
 
            if( nBits & 4 )
                xTransform->setTo( Any( static_cast<double>(fTo) ) );
 
#ifdef DBG_ANIM_LOG
            if( nBits & 1 )
                fprintf( mpFile, " by=\"%g\"", (double)fBy );
 
            if( nBits & 2 )
                fprintf( mpFile, " from=\"%g\"", (double)fFrom );
 
            if( nBits & 4 )
                fprintf( mpFile, " to=\"%g\"", (double)fTo );
 
            if( nU1 )
                fprintf( mpFile, " rotation_1=\"%" SAL_PRIdINT32 "\"", nU1 );
#endif
        }
        break;
 
        case DFF_msofbtAnimateTarget:
            importAnimateAttributeTargetContainer( pChildAtom, xNode );
            break;
 
        default:
            dump( " unknown_atom=\"%ld\"", static_cast<sal_Int32>(pChildAtom->getType()) );
            break;
        }
 
        pChildAtom = Atom::findNextChildAtom( pChildAtom );
    }
}
 
void AnimationImporter::importAttributeNamesContainer( const Atom* pAtom, OUString& rAttributeNames )
{
    OUStringBuffer aNames;
 
    DBG_ASSERT( pAtom && (pAtom->getType() == DFF_msofbtAnimateAttributeNames), "invalid call to ppt::AnimationImporter::importAttributeName()!" );
    if( pAtom )
    {
        const Atom* pAttributeValueAtom = pAtom->findFirstChildAtom( DFF_msofbtAnimAttributeValue );
 
        while( pAttributeValueAtom )
        {
            Any aAny;
            if ( importAttributeValue( pAttributeValueAtom, aAny ) )
            {
                OUString aName;
                if( aAny >>= aName )
                {
                    if( !aNames.isEmpty() )
                        aNames.append( ';' );
 
                    aNames.append( aName );
                }
            }
            else
            {
                OSL_FAIL( "error during ppt::AnimationImporter::importAttributeName()!" );
            }
 
            pAttributeValueAtom = pAtom->findNextChildAtom( DFF_msofbtAnimAttributeValue, pAttributeValueAtom );
        }
    }
 
    rAttributeNames = aNames.makeStringAndClear();
}
 
void AnimationImporter::importAnimationValues( const Atom* pAtom, const Reference< XAnimationNode >& xNode )
{
    DBG_ASSERT( pAtom, "invalid call to ppt::AnimationImporter::importAnimationValues()!" );
 
    if( !pAtom )
        return;
 
    const Atom* pValueAtom = pAtom->findFirstChildAtom( DFF_msofbtAnimValue );
 
    while( pValueAtom && pValueAtom->seekToContent() )
    {
        sal_uInt32 nType(0);
        mrStCtrl.ReadUInt32( nType );
        switch( nType )
        {
        case 0:
        {
            float fRepeat(0.0);
            mrStCtrl.ReadFloat( fRepeat );
            xNode->setRepeatCount( (fRepeat < (float(3.40282346638528860e+38))) ? Any( static_cast<double>(fRepeat) ) : Any( Timing_INDEFINITE ) );
 
#ifdef DBG_ANIM_LOG
            if( (fRepeat < ((float)3.40282346638528860e+38)) )
            {
                dump( " repeat=\"%g\"", (double)fRepeat );
            }
            else
            {
                dump( " repeat=\"indefinite\"" );
            }
#endif
        }
        break;
 
        case 3:
        {
            float faccelerate(0.0);
            mrStCtrl.ReadFloat( faccelerate );
            xNode->setAcceleration( faccelerate );
            dump( " accelerate=\"%g\"", static_cast<double>(faccelerate) );
        }
        break;
 
        case 4:
        {
            float fdecelerate(0.0);
            mrStCtrl.ReadFloat( fdecelerate );
            xNode->setDecelerate( fdecelerate );
            dump( " decelerate=\"%g\"", static_cast<double>(fdecelerate) );
        }
        break;
 
        case 5:
        {
            sal_Int32 nAutoreverse(0);
            mrStCtrl.ReadInt32( nAutoreverse );
            xNode->setAutoReverse( nAutoreverse != 0 );
            dump( " autoreverse=\"%#lx\"", nAutoreverse );
        }
        break;
 
        default:
        {
            sal_uInt32 nUnknown;
            mrStCtrl.ReadUInt32( nUnknown );
#ifdef DBG_ANIM_LOG
            fprintf(mpFile, " attribute_%d=\"%#lx\"", nType, nUnknown );
#endif
        }
        break;
        }
 
        pValueAtom = pAtom->findNextChildAtom( DFF_msofbtAnimValue, pValueAtom );
    }
}
 
void AnimationImporter::importAnimateKeyPoints( const Atom* pAtom, const Reference< XAnimationNode >& xNode )
{
    Reference< XAnimate > xAnim( xNode, UNO_QUERY );
 
    DBG_ASSERT( pAtom && pAtom->getType() == DFF_msofbtAnimKeyPoints && xAnim.is(), "invalid call to ppt::AnimationImporter::importAnimateKeyPoints()!" );
 
    if( !(pAtom && xAnim.is()) )
        return;
 
    // first count keytimes
    const Atom* pIter = nullptr;
    int nKeyTimes = 0;
 
    while( (pIter = pAtom->findNextChildAtom( DFF_msofbtAnimKeyTime,  pIter )) != nullptr )
        nKeyTimes++;
 
    Sequence< double > aKeyTimes( nKeyTimes );
    auto aKeyTimesRange = asNonConstRange(aKeyTimes);
    Sequence< Any > aValues( nKeyTimes );
    auto aValuesRange = asNonConstRange(aValues);
    OUString aFormula;
 
    pIter = pAtom->findFirstChildAtom(DFF_msofbtAnimKeyTime);
    bool bToNormalize = false;
    for( int nKeyTime = 0; (nKeyTime < nKeyTimes) && pIter; nKeyTime++ )
    {
        if( pIter->seekToContent() )
        {
            sal_Int32 nTemp(0);
            mrStCtrl.ReadInt32(nTemp);
            double fTemp = static_cast<double>(nTemp) / 1000.0;
            aKeyTimesRange[nKeyTime] = fTemp;
            if( fTemp == -1 )
                bToNormalize = true;
 
            const Atom* pValue = Atom::findNextChildAtom(pIter);
            if( pValue && pValue->getType() == DFF_msofbtAnimAttributeValue )
            {
                Any aValue1, aValue2;
                if( importAttributeValue( pValue, aValue1 ) )
                {
                    pValue = Atom::findNextChildAtom(pValue);
                    if( pValue && pValue->getType() == DFF_msofbtAnimAttributeValue )
                    {
                        // Any occurrence of the formula becomes the formula of the whole list.
                        if (importAttributeValue(pValue, aValue2) && aFormula.isEmpty())
                            aValue2 >>= aFormula;
                    }
                    aValuesRange[nKeyTime] = aValue1;
                }
            }
        }
        pIter = pAtom->findNextChildAtom(DFF_msofbtAnimKeyTime, pIter);
    }
 
#ifdef DBG_ANIM_LOG
    dump( " keyTimes=\"" );
    for( int i=0; i<nKeyTimes; ++i )
        dump( "%f;", aKeyTimes[i] );
 
    if( !aFormula.isEmpty() )
    {
        dump( "formula=\"%s", aFormula );
    }
 
    dump( "\" values=\"" );
    double nVal;
    OUString aStr;
    for( int i=0; i<nKeyTimes; ++i )
    {
        if( i != 0 )
            dump( ";" );
 
        if( aValues[i] >>= aStr )
            dump( "%s",
                  OUStringToOString( aStr,
                                            RTL_TEXTENCODING_ASCII_US ).getStr() );
        else if( aValues[i] >>= nVal )
            dump( "%f", nVal );
        else
        {
            ValuePair aValuePair;
 
            if( aValues[i] >>= aValuePair )
            {
                if( aValuePair.First >>= aStr )
                    dump( "%s",
                          OUStringToOString( aStr,
                                                    RTL_TEXTENCODING_ASCII_US ).getStr() );
                else if( aValuePair.First >>= nVal )
                    dump( "%f", nVal );
                else
                    dump( "%X", (sal_Int64)&aValuePair.First );
 
                if( aValuePair.Second >>= aStr )
                    dump( ",%s",
                          OUStringToOString( aStr,
                                                    RTL_TEXTENCODING_ASCII_US ).getStr() );
                else if( aValuePair.Second >>= nVal )
                    dump( ",%f", nVal );
                else
                    dump( ",%X", (sal_Int64)&aValuePair.Second );
            }
        }
    }
    dump( "\"" );
#endif
    if( bToNormalize && nKeyTimes >= 2 )
    {
        // if TimeAnimationValueList contains time -1000, key points must be evenly distributed between 0 and 1 ([MS-PPT] 2.8.31)
        for( int nKeyTime = 0; nKeyTime < nKeyTimes; ++nKeyTime )
        {
            aKeyTimesRange[nKeyTime] = static_cast<double>(nKeyTime) / static_cast<double>(nKeyTimes - 1);
        }
    }
 
    if (aValues.getLength() != aKeyTimes.getLength())
        throw css::io::WrongFormatException();
 
    xAnim->setKeyTimes( aKeyTimes );
    xAnim->setValues( aValues );
    xAnim->setFormula( aFormula );
}
 
bool AnimationImporter::importAttributeValue( const Atom* pAtom, Any& rAny )
{
    DBG_ASSERT( pAtom && pAtom->getType() == DFF_msofbtAnimAttributeValue, "invalid call to ppt::AnimationImporter::importAttributeValue()!" );
 
    bool bOk = false;
 
    if( pAtom && pAtom->seekToContent() )
    {
        sal_uInt32 nRecLen = pAtom->getLength();
        if ( nRecLen >= 1 )
        {
            sal_Int8 nType(0);
            mrStCtrl.ReadSChar( nType );
            switch( nType )
            {
                case DFF_ANIM_PROP_TYPE_BYTE :
                {
                    if ( nRecLen == 2 )
                    {
                        sal_uInt8 nByte(0);
                        mrStCtrl.ReadUChar( nByte );
                        rAny <<= nByte;
 
                        bOk = true;
                    }
                }
                break;
 
                case DFF_ANIM_PROP_TYPE_INT32 :
                {
                    if ( nRecLen == 5 )
                    {
                        sal_uInt32 nInt32(0);
                        mrStCtrl.ReadUInt32( nInt32 );
                        rAny <<= nInt32;
 
                        bOk = true;
                    }
                }
                break;
 
                case DFF_ANIM_PROP_TYPE_FLOAT:
                {
                    if( nRecLen == 5 )
                    {
                        float fFloat(0.0);
                        mrStCtrl.ReadFloat( fFloat );
                        rAny <<= static_cast<double>(fFloat);
 
                        bOk = true;
                    }
                }
                break;
 
                case DFF_ANIM_PROP_TYPE_UNISTRING :
                {
                    if ( ( nRecLen & 1 ) && ( nRecLen > 1 ) )
                    {
                        OUString aOUString = SvxMSDffManager::MSDFFReadZString( mrStCtrl, nRecLen - 1, true );
                        rAny <<= aOUString;
 
                        bOk = true;
                    }
                }
                break;
            }
        }
    }
 
    DBG_ASSERT( bOk, "invalid value inside ppt::AnimationImporter::importAttributeValue()!" );
    return bOk;
}
 
void AnimationImporter::importAnimationEvents( const Atom* pAtom, const Reference< XAnimationNode >& xNode )
{
    DBG_ASSERT( xNode.is() && pAtom, "invalid call to ppt::AnimationImporter::importAnimationEvents()!" );
 
    Any aBegin, aEnd, aNext, aPrev;
 
    const Atom* pEventAtom = pAtom->findFirstChildAtom( DFF_msofbtAnimEvent );
    while( pEventAtom )
    {
        Any* pEvents = nullptr;
 
        switch( pEventAtom->getInstance() )
        {
        case 1: pEvents = &aBegin; break;
        case 2: pEvents = &aEnd; break;
        case 3: pEvents = &aNext; break;
        case 4: pEvents = &aPrev; break;
        }
 
        if( pEvents )
        {
            Event aEvent;
            aEvent.Trigger = EventTrigger::NONE;
            aEvent.Repeat = 0;
 
            const Atom* pChildAtom = pEventAtom->findFirstChildAtom();
 
            while( pChildAtom && pChildAtom->seekToContent() )
            {
                switch( pChildAtom->getType() )
                {
                case DFF_msofbtAnimTrigger:
                {
                    sal_Int32 nU1(0), nTrigger(0), nU3(0), nBegin(0);
                    mrStCtrl.ReadInt32( nU1 );
                    mrStCtrl.ReadInt32( nTrigger );
                    mrStCtrl.ReadInt32( nU3 );
                    mrStCtrl.ReadInt32( nBegin );
 
                    switch( nTrigger )
                    {
                    case 0: aEvent.Trigger = EventTrigger::NONE; break;
                    case 1: aEvent.Trigger = EventTrigger::ON_BEGIN; break;
                    case 2: aEvent.Trigger = EventTrigger::ON_END; break;
                    case 3: aEvent.Trigger = EventTrigger::BEGIN_EVENT; break;
                    case 4: aEvent.Trigger = EventTrigger::END_EVENT; break;
                    case 5: aEvent.Trigger = EventTrigger::ON_CLICK; break;
                    case 6: aEvent.Trigger = EventTrigger::ON_DBL_CLICK; break;
                    case 7: aEvent.Trigger = EventTrigger::ON_MOUSE_ENTER; break;
                    case 8: aEvent.Trigger = EventTrigger::ON_MOUSE_LEAVE; break;
                    case 9: aEvent.Trigger = EventTrigger::ON_NEXT; break;
                    case 10: aEvent.Trigger = EventTrigger::ON_PREV; break;
                    case 11: aEvent.Trigger = EventTrigger::ON_STOP_AUDIO; break;
                    }
 
                    if( (nBegin != 0) || (aEvent.Trigger == EventTrigger::NONE) )
                        aEvent.Offset = (nBegin == -1) ? Any( Timing_INDEFINITE ) : Any( nBegin / 1000.0 );
                }
                break;
                case DFF_msofbtAnimateTargetElement:
                {
                    sal_Int16 nSubType;
                    importTargetElementContainer( pChildAtom, aEvent.Source, nSubType );
                }
                break;
                default:
                {
                    OSL_FAIL("unknown atom inside ppt::AnimationImporter::importAnimationEvents()!");
                }
                }
 
                pChildAtom = Atom::findNextChildAtom( pChildAtom );
            }
 
            *pEvents = oox::addToSequence( *pEvents, (aEvent.Trigger == EventTrigger::NONE) ? aEvent.Offset : Any( aEvent ) );
        }
 
        pEventAtom = pAtom->findNextChildAtom( DFF_msofbtAnimEvent, pEventAtom );
    }
 
    xNode->setBegin( aBegin );
    xNode->setEnd( aEnd );
    // TODO: xNode->setNext( aNext );
    // TODO: xNode->setPrev( aNext );
 
#ifdef DBG_ANIM_LOG
    if( aBegin.hasValue() )
    {
        dump( " begin=\"" );
        dump( aBegin );
        dump( "\"" );
    }
 
    if( aEnd.hasValue() )
    {
        dump( " end=\"" );
        dump( aEnd );
        dump( "\"" );
    }
 
    if( aNext.hasValue() )
    {
        dump( " next=\"" );
        dump( aNext );
        dump( "\"" );
    }
 
    if( aPrev.hasValue() )
    {
        dump( " prev=\"" );
        dump( aPrev );
        dump( "\"" );
    }
#endif
}
 
void AnimationImporter::importAnimationActions( const Atom* pAtom, const Reference< XAnimationNode >& xNode )
{
    DBG_ASSERT( pAtom && xNode.is(), "invalid call to ppt::AnimationImporter::importAnimationActions()!");
 
    if( !pAtom )
        return;
 
    const Atom* pActionAtom = pAtom->findFirstChildAtom( DFF_msofbtAnimAction );
 
    if( !(pActionAtom && pActionAtom->seekToContent()) )
        return;
 
    sal_Int32 nConcurrent(0), nNextAction(0), nEndSync(0), nU4(0), nU5(0);
    mrStCtrl.ReadInt32( nConcurrent );
    mrStCtrl.ReadInt32( nNextAction );
    mrStCtrl.ReadInt32( nEndSync );
    mrStCtrl.ReadInt32( nU4 );
    mrStCtrl.ReadInt32( nU5 );
 
    if( nEndSync == 1 )
        xNode->setEndSync( Any( AnimationEndSync::ALL ) );
 
#ifdef DBG_ANIM_LOG
    dump( " concurrent=\"%s\"", nConcurrent == 0 ? "disabled" : (nConcurrent == 1 ? "enabled" : "unknown") );
 
    dump( " nextAction=\"%s\"", nNextAction == 0 ? "none" : (nNextAction == 1 ? "seek" : "unknown") );
 
    if( nEndSync != 0 )
    {
        dump( " endSync=\"%s\"", nEndSync == 1 ? "all" : "unknown" );
    }
 
    dump( " action_4=\"%#lx\"", nU4 );
    dump( " action_5=\"%#lx\"", nU5 );
#endif
}
 
void AnimationImporter::importTargetElementContainer( const Atom* pAtom, Any& rTarget, sal_Int16& rSubType )
{
    rSubType = ShapeAnimationSubType::AS_WHOLE;
    sal_Int32 nRefMode = -1;
 
    DBG_ASSERT( pAtom && (pAtom->getType() == DFF_msofbtAnimateTargetElement), "invalid call to ppt::AnimationImporter::importTargetElementContainer()!" );
    if( !pAtom )
        return;
 
    const Atom* pChildAtom = pAtom->findFirstChildAtom();
    while( pChildAtom && pChildAtom->seekToContent() )
    {
        switch( pChildAtom->getType() )
        {
        case DFF_msofbtAnimReference:
        {
            sal_Int32 nRefType(0), nRefId(0);
            sal_Int32 begin(0), end(0);
            mrStCtrl.ReadInt32( nRefMode );
            mrStCtrl.ReadInt32( nRefType );
            mrStCtrl.ReadInt32( nRefId );
            mrStCtrl.ReadInt32( begin );
            mrStCtrl.ReadInt32( end );
 
            switch( nRefType )
            {
            case 1: // shape
            {
                SdrObject* pSdrObject = mpPPTImport->getShapeForId( nRefId );
                if( pSdrObject == nullptr )
                    break;
 
                rTarget <<= pSdrObject->getUnoShape();
 
                switch( nRefMode )
                {
                case 6: rSubType = ShapeAnimationSubType::ONLY_BACKGROUND; break;
                case 8: rSubType = ShapeAnimationSubType::ONLY_TEXT; break;
                case 2: // one paragraph
                {
                    if((begin == -1) && (end == -1))
                        break;
 
                    SdrTextObj* pTextObj = DynCastSdrTextObj( pSdrObject );
                    if(!pTextObj)
                        break;
 
                    const OutlinerParaObject* pOPO = pTextObj->GetOutlinerParaObject();
                    if( pOPO == nullptr )
                        break;
 
                    const EditTextObject& rEditTextObject = pOPO->GetTextObject();
 
                    const sal_Int32 nParaCount = rEditTextObject.GetParagraphCount();
 
                    sal_Int32 nPara = 0;
 
                    while( (nPara < nParaCount) && (begin > 0) )
                    {
                        sal_Int32 nParaLength = rEditTextObject.GetTextLen( nPara ) + 1;
                        begin -= nParaLength;
                        end -= nParaLength;
                        nPara++;
                    }
 
                    if( nPara < nParaCount )
                    {
                        ParagraphTarget aParaTarget;
                        rTarget >>= aParaTarget.Shape;
                        /* FIXME: Paragraph should be sal_Int32 as well */
                        aParaTarget.Paragraph = static_cast<sal_Int16>(nPara);
                        rTarget <<= aParaTarget;
 
                        rSubType = ShapeAnimationSubType::ONLY_TEXT;
                        dump( " paragraph %d,", nPara);
                        dump( " %d characters", end );
                    }
                }
                }
            }
            break;
 
            case 2: // sound
                {
                    OUString aSoundURL( mpPPTImport->ReadSound( nRefId ) );
                    rTarget <<= aSoundURL;
                    dump( " srcRef=\"%s\"", aSoundURL );
                }
                break;
            case 3: // audio object
            case 4: // video object
                {
                    SdrObject* pSdrObject = mpPPTImport->getShapeForId( nRefId );
                    if( pSdrObject == nullptr )
                        break;
 
                    rTarget <<= pSdrObject->getUnoShape();
                }
                break;
            default:
                OSL_FAIL("unknown reference type");
            }
 
        }
        break;
        case 0x2b01:
        {
            sal_Int32 nU1;
            mrStCtrl.ReadInt32( nU1 );
        }
        break;
        default:
            OSL_FAIL("unknown atom inside ppt::AnimationImporter::importTargetElementContainer()!");
            break;
        }
 
        pChildAtom = Atom::findNextChildAtom( pChildAtom );
 
    }
}
 
void AnimationImporter::importPropertySetContainer( const Atom* pAtom, PropertySet& rSet )
{
    DBG_ASSERT( pAtom && (pAtom->getType() == DFF_msofbtAnimPropertySet), "invalid call to ppt::AnimationImporter::importPropertySetContainer()!" );
 
    if( !pAtom )
        return;
 
    const Atom* pChildAtom = pAtom->findFirstChildAtom();
    while( pChildAtom )
    {
        if( pChildAtom->getType() == DFF_msofbtAnimAttributeValue )
        {
            Any aAny;
            (void)importAttributeValue( pChildAtom, aAny );
            rSet.maProperties[ pChildAtom->getInstance() ] = aAny;
        }
        else
        {
            OSL_FAIL("unknown atom inside ppt::AnimationImporter::importPropertySetContainer()!");
        }
 
        pChildAtom = Atom::findNextChildAtom( pChildAtom );
    }
}
 
#ifdef DBG_ANIM_LOG
void AnimationImporter::dump_atom_header( const Atom* pAtom, bool bOpen, bool bAppend )
{
    if( pAtom )
    {
        const char* pTitle;
 
        switch( pAtom->getType() )
        {
        case DFF_msofbtAnimEvent: pTitle = "AnimEvent"; break;
        case DFF_msofbtAnimTrigger: pTitle = "AnimTrigger"; break;
        case DFF_msofbtAnimateMotion:   pTitle = "AnimateMotion"; break;
        case DFF_msofbtAnimPropertySet: pTitle = "AnimPropertySet"; break;
        case DFF_msofbtAnimateAttributeNames: pTitle = "AnimAttributeName"; break;
        case DFF_msofbtAnimAttributeValue: pTitle = "AnimAttributeValue"; break;
        case DFF_msofbtAnimGroup: pTitle = "AnimGroup"; break;
        case DFF_msofbtAnimNode: pTitle = "AnimNode"; break;
        case DFF_msofbtAnimValue: pTitle = "AnimValue"; break;
        case DFF_msofbtAnimateFilter: pTitle = "animateFilter"; break;
        case DFF_msofbtAnimate: pTitle = "animate"; break;
        case DFF_msofbtAnimateSet: pTitle = "set"; break;
        case DFF_msofbtAnimKeyTime: pTitle = "AnimKeyTime"; break;
        case DFF_msofbtAnimKeyPoints: pTitle = "AnimKeyPoints"; break;
        case DFF_msofbtAnimReference: pTitle = "AnimReference"; break;
        case DFF_msofbtAnimateTargetElement: pTitle = "AnimTargetElementContainer"; break;
        case DFF_msofbtAnimAction: pTitle = "AnimAction"; break;
        case DFF_msofbtAnimCommand: pTitle = "AnimCommand"; break;
        case DFF_msofbtAnimateTarget: pTitle = "TransformationTarget"; break;
        case DFF_msofbtAnimateTargetSettings: pTitle = "TransformationTargetSettings"; break;
        case DFF_msofbtAnimIteration: pTitle = "iterate"; break;
        case DFF_msofbtAnimateColorData: pTitle = "colorData"; break;
        case DFF_msofbtAnimateScaleData: pTitle = "scaleData"; break;
        case DFF_msofbtAnimateSetData: pTitle = "setData"; break;
 
        default:
            {
                static char buffer[128];
                sprintf( buffer, "unknown_%#x", pAtom->getType() );
                pTitle = buffer;
            }
        }
 
        if( bOpen )
        {
            fprintf(mpFile, "<%s", pTitle );
 
            fprintf(mpFile, " instance=\"%hu\"%s",
                        pAtom->getInstance(),
                        bAppend ? "" : ">\n");
        }
        else
        {
            if( bAppend )
                fprintf(mpFile,"/>\n");
            else
                fprintf(mpFile, "</%s>\n", pTitle );
        }
    }
}
 
void AnimationImporter::dump( sal_uInt32 nLen, bool bNewLine )
{
    char * faul = "0123456789abcdef";
 
    sal_uInt32 i = 0;
    int b = 0;
    char nData;
 
    for( i = 0; i < nLen; i++ )
    {
        mrStCtrl.ReadChar(nData);
 
        fprintf( mpFile, "%c%c ", faul[ (nData >> 4) & 0x0f ], faul[ nData & 0x0f ] );
 
        b++;
        if( bNewLine && (b == 32) )
        {
            fprintf(mpFile,"\n");
            b = 0;
        }
    }
    if( (b != 0) && bNewLine )
        fprintf(mpFile,"\n");
}
 
void AnimationImporter::dump_atom( const Atom* pAtom, bool bNewLine )
{
    if( pAtom )
    {
        if( pAtom->isContainer() )
        {
            const Atom* pChildAtom = pAtom->findFirstChildAtom();
            while( pChildAtom )
            {
                if( pChildAtom->getType() == DFF_msofbtAnimAttributeValue )
                {
                    fprintf(mpFile, "<attributeValue instance=\"%hu\"", pChildAtom->getInstance() );
 
                    Any aValue;
                    if( importAttributeValue( pChildAtom, aValue ) )
                    {
                        sal_Int32 nInt;
                        OUString aString;
                        double fDouble;
 
                        if( aValue >>= nInt )
                        {
                            fprintf(mpFile, " value=\"%" SAL_PRIdINT32 "\"", nInt );
                        }
                        else if( aValue >>= aString )
                        {
                            fprintf(mpFile, " value=\"%s\"",
                                OUStringToOString(aString,
                                    RTL_TEXTENCODING_UTF8).getStr());
                        }
                        else if( aValue >>= fDouble )
                        {
                            fprintf(mpFile, " value=\"%g\"", fDouble );
                        }
                    }
                    else
                    {
                        if( pChildAtom->seekToContent() )
                        {
                            fprintf(mpFile, " value=\""  );
                            dump_atom( pChildAtom, false );
                            fprintf(mpFile, "\"");
                        }
                    }
 
                    fprintf(mpFile, "/>\n" );
                }
                else
                {
                    dump_atom_header( pChildAtom, true, pChildAtom->getType() == DFF_msofbtAnimAttributeValue );
                    dump_atom( pChildAtom );
                    dump_atom_header( pChildAtom, false, pChildAtom->getType() == DFF_msofbtAnimAttributeValue );
                }
 
                pChildAtom = Atom::findNextChildAtom(pChildAtom);
            }
        }
        else if( pAtom->seekToContent() )
        {
            dump( pAtom->getLength(), bNewLine );
        }
    }
}
 
void AnimationImporter::dump_anim_group( const Atom* pAtom, const AnimationNode& rNode, const PropertySet& rSet, bool bOpen )
{
    fprintf( mpFile, bOpen ? "<" : "</" );
 
    switch( rNode.mnGroupType )
    {
    case mso_Anim_GroupType_PAR:
        fprintf( mpFile, "par" );
        break;
    case mso_Anim_GroupType_SEQ:
        fprintf( mpFile, "seq" );
        break;
    case mso_Anim_GroupType_NODE:
        switch( rNode.mnNodeType )
        {
        case mso_Anim_Behaviour_FILTER:
            fprintf( mpFile, "animateFilter" );
            break;
        case mso_Anim_Behaviour_ANIMATION:
            if( pAtom->hasChildAtom( DFF_msofbtAnimateSet ) )
                fprintf( mpFile, "set" );
            else if( pAtom->hasChildAtom( DFF_msofbtAnimateColor ) )
                fprintf( mpFile, "animateColor" );
            else if( pAtom->hasChildAtom( DFF_msofbtAnimateScale ) )
                fprintf( mpFile, "animateScale" );
            else if( pAtom->hasChildAtom( DFF_msofbtAnimateRotation ) )
                fprintf( mpFile, "animateRotation" );
            else if( pAtom->hasChildAtom( DFF_msofbtAnimateMotion ) )
                fprintf( mpFile, "animateMotion" );
            else if( pAtom->hasChildAtom( DFF_msofbtAnimCommand ) )
                fprintf( mpFile, "command" );
            else
                fprintf( mpFile, "animation" );
            break;
        default:
            {
                fprintf( mpFile, "unknown_node_%#lx", rNode.mnNodeType );
            }
            break;
        }
        break;
    case mso_Anim_GroupType_MEDIA:
        fprintf( mpFile, "media" );
        break;
    default:
        fprintf( mpFile, "unknown_group_%#lx", rNode.mnGroupType );
        break;
    }
 
    if( bOpen )
    {
        dump( rNode );
        dump( rSet );
    }
 
    fprintf(mpFile,">\n");
}
 
void AnimationImporter::dump( const AnimationNode& rNode )
{
    // dump animation node
    if( rNode.mnRestart != 0 )
    {
        fprintf(mpFile," restart=\"%s\"",
            rNode.mnRestart == 1 ? "always" : (rNode.mnRestart == 2 ? "whenOff" : (rNode.mnRestart == 3 ? "never" : "unknown")) );
    }
 
    if( rNode.mnFill )
    {
        fprintf(mpFile," fill=\"%s\"",
            rNode.mnFill == 1 ? "remove" : (rNode.mnFill == 3 ? "hold" : (rNode.mnFill == 2 ? "freeze" : "unknown")) );
    }
 
    if( rNode.mnDuration > 0 )
    {
        double fSeconds = rNode.mnDuration;
        fSeconds /= 1000.0;
        fprintf(mpFile, " dur=\"%g\"", fSeconds);
    }
    else if( rNode.mnDuration < 0 )
    {
        fprintf(mpFile, " dur=\"indefinite\"" );
    }
 
    if( rNode.mnU1 ) fprintf(mpFile," u1=\"%#lx\"", rNode.mnU1);
    if( rNode.mnU3 ) fprintf(mpFile," u3=\"%#lx\"", rNode.mnU3);
    if( rNode.mnU4 ) fprintf(mpFile," u4=\"%#lx\"", rNode.mnU4);
}
 
void AnimationImporter::dump( Any& rAny )
{
    Sequence< Any > aSeq;
    sal_Int32 nInt;
    double fDouble;
    OUString aString;
    sal_Bool bBool;
    Event aEvent;
    Timing aTiming;
 
    if( rAny >>= aSeq )
    {
        const sal_Int32 nSize = aSeq.getLength();
        sal_Int32 nIndex = 0;
        while( nIndex < nSize )
        {
            dump( aSeq[nIndex++] );
            if(nIndex < nSize)
                fprintf( mpFile, "," );
        }
    }
    else if( rAny >>= aString )
    {
        fprintf( mpFile, "%s", OUStringToOString(aString,
            RTL_TEXTENCODING_UTF8).getStr() );
    }
    else if( rAny >>= nInt )
    {
        fprintf( mpFile, "%" SAL_PRIdINT32, nInt );
    }
    else if( rAny >>= bBool )
    {
        fprintf( mpFile, "%s", bBool ? "true" : "false" );
    }
    else if( rAny >>= fDouble )
    {
        fprintf( mpFile, "%g", fDouble );
    }
    else if( rAny >>= aTiming )
    {
        fprintf( mpFile, "%s", aTiming == (Timing_INDEFINITE) ? "indefinite" : "media" );
    }
    else if( rAny >>= aEvent )
    {
        if( aEvent.Trigger != EventTrigger::NONE )
        {
            static const char* triggers[] =
            {
                "none","onbegin","onend","begin",
                "end","onclick","ondoubleclick","onmouseenter",
                "onmouseleave","onpptnext","onpptprev","onstopaudio"
            };
 
            if( aEvent.Source.hasValue() )
            {
                dump_target( aEvent.Source );
                dump( "." );
            }
 
            dump( triggers[ aEvent.Trigger ] );
        }
 
        if( aEvent.Offset.hasValue() )
        {
            double fOffset;
            if( aEvent.Offset >>= fOffset )
                fprintf( mpFile, "%g", fOffset );
            else
                dump( "indefinite" );
        }
    }
}
 
void AnimationImporter::dump( const PropertySet& rSet )
{
    // dump property set
 
    for( const auto& rProp : rSet.maProperties )
    {
        bool bKnown = false;
 
        const sal_Int32 nInstance = rProp.first;
        Any aAny( rProp.second );
 
        switch ( nInstance )
        {
        case DFF_ANIM_COLORSPACE:
        {
            sal_Int32 nColorSpace;
            if( aAny >>= nColorSpace )
            {
                fprintf( mpFile, " colorSpace=\"%s\"", (nColorSpace == 0) ? "rgb" : (nColorSpace == 1) ? "hsl" : "unknown" );
                bKnown = true;
            }
        }
        break;
 
        case DFF_ANIM_DIRECTION:
        {
            sal_Bool bDirection;
            if( aAny >>= bDirection )
            {
                fprintf( mpFile, " direction=\"%s\"", bDirection ? "cclockwise" : "clockwise"  );
                bKnown = true;
            }
            else
            {
                sal_Int32 nMasterRel;
                if( aAny >>= nMasterRel )
                {
                    fprintf( mpFile, " direction=\"%s\"", nMasterRel == 0 ? "sameClick" : ( nMasterRel == 2 ? "nextClick" : "lastClick" )  );
                    bKnown = true;
                }
            }
        }
        break;
 
        case DFF_ANIM_OVERRIDE:     // TODO
        {
            sal_Int32 nOverride;
            if( aAny >>= nOverride )
            {
                fprintf( mpFile, " override=\"%s\"", (nOverride == 1) ? "childStyle" : (nOverride == 0) ? "normal" : "unknown" );
                bKnown = true;
            }
        }
        break;
 
        case DFF_ANIM_PATH_EDIT_MODE:
        {
            sal_Bool bPathEditMode;
            if( aAny >>= bPathEditMode )
            {
                fprintf( mpFile, " pptPathEditMode=\"%s\"", bPathEditMode ? "relative" : "fixed" );
                bKnown = true;
            }
        }
        break;
 
        case DFF_ANIM_PRESET_ID :
        {
            sal_Int32 nPresetId ;
            if( aAny >>= nPresetId )
            {
                fprintf(mpFile, " presetid=\"%" SAL_PRIdINT32 "\"", nPresetId );
                bKnown = true;
            }
        }
        break;
 
        case DFF_ANIM_PRESET_SUB_TYPE :
        {
            sal_Int32 nPointsType ;
            if( aAny >>= nPointsType )
            {
                fprintf(mpFile, " presetSubType=\"%" SAL_PRIdINT32 "\"", nPointsType );
                bKnown = true;
            }
        }
        break;
 
        case DFF_ANIM_PRESET_CLASS :
        {
            sal_Int32 nPresetClass;
            if ( aAny >>= nPresetClass )
            {
                const char* pMode;
                switch( nPresetClass )
                {
                case DFF_ANIM_PRESS_CLASS_USER_DEFINED:     pMode = "userdefined"; break;
                case DFF_ANIM_PRESS_CLASS_ENTRANCE:         pMode = "entrance"; break;
                case DFF_ANIM_PRESS_CLASS_EXIT:             pMode = "exit"; break;
                case DFF_ANIM_PRESS_CLASS_EMPHASIS:         pMode = "emphasis"; break;
                case DFF_ANIM_PRESS_CLASS_MOTIONPATH:       pMode = "motionpath"; break;
                case DFF_ANIM_PRESS_CLASS_OLE_ACTION:       pMode = "oleaction"; break;
                case DFF_ANIM_PRESS_CLASS_MEDIACALL:        pMode = "mediacall"; break;
                default:
                    pMode = nullptr;
                break;
                }
 
                if (pMode)
                    fprintf(mpFile, " class=\"%s\"", pMode);
                else
                    fprintf(mpFile, " class =\"%" SAL_PRIdINT32 "\"", nPresetClass);
                bKnown = true;
            }
        }
        break;
 
        case DFF_ANIM_NODE_TYPE :
        {
            sal_Int32 nNodeType;
            if ( aAny >>= nNodeType )
            {
                const char* pNode;
                switch( nNodeType )
                {
                    case DFF_ANIM_NODE_TYPE_ON_CLICK:       pNode = "onclick";  break;
                    case DFF_ANIM_NODE_TYPE_WITH_PREVIOUS:  pNode = "withprevious"; break;
                    case DFF_ANIM_NODE_TYPE_AFTER_PREVIOUS: pNode = "afterprevious"; break;
                    case DFF_ANIM_NODE_TYPE_MAIN_SEQUENCE:  pNode = "mainsequence"; break;
                    case DFF_ANIM_NODE_TYPE_TIMING_ROOT:    pNode = "timingroot"; break;
                    case DFF_ANIM_NODE_TYPE_INTERACTIVE_SEQ:pNode = "interactivesequence"; break;
                    default :
                    {
                        static char buffer[128];
                        sprintf( buffer, "%" SAL_PRIdINT32, nNodeType );
                        pNode = buffer;
                    }
                    break;
                }
 
                fprintf(mpFile, " nodeType=\"%s\"", pNode);
                bKnown = true;
            }
        }
        break;
 
        case DFF_ANIM_GROUP_ID:
        {
            sal_Int32 nGroupId;
            if ( aAny >>= nGroupId )
            {
                fprintf( mpFile, " groupId=\"%" SAL_PRIdINT32 "\"", nGroupId );
                bKnown = true;
            }
        }
        break;
 
        case DFF_ANIM_ID:
        {
            OUString aString;
            if( aAny >>= aString )
            {
                fprintf( mpFile, " id=\"%s\"",
                    OUStringToOString(aString,
                        RTL_TEXTENCODING_UTF8).getStr() );
                bKnown = true;
            }
        }
        break;
 
        case DFF_ANIM_EVENT_FILTER:
        {
            OUString aString;
            if( aAny >>= aString )
            {
                fprintf( mpFile, " eventFilter=\"%s\"",
                    OUStringToOString(aString,
                        RTL_TEXTENCODING_UTF8).getStr() );
                bKnown = true;
            }
        }
        break;
 
        case DFF_ANIM_ENDAFTERSLIDE:
        {
            sal_Int32 nEndAfterSlide;
            if( aAny >>= nEndAfterSlide )
            {
                fprintf(mpFile, " endAfterSlide=\"%" SAL_PRIdINT32 "\"", nEndAfterSlide );
            bKnown = true;
            }
        }
 
        case DFF_ANIM_TIMEFILTER:
        {
            OUString aString;
            if( aAny >>= aString )
            {
                fprintf( mpFile, " timeFilter=\"%s\"",
                    OUStringToOString(aString,
                        RTL_TEXTENCODING_UTF8).getStr() );
                bKnown = true;
            }
        }
        break;
 
        case DFF_ANIM_RUNTIMECONTEXT:
        {
            OUString aString;
            if( aAny >>= aString )
            {
                fprintf( mpFile, " runtimeContext=\"%s\"",
                    OUStringToOString(aString,
                        RTL_TEXTENCODING_UTF8).getStr() );
                bKnown = true;
            }
        }
        break;
 
        case DFF_ANIM_VOLUME:
        {
            double fVolume(0.0);
            if( aAny >>= fVolume )
            {
                fprintf( mpFile, " volume=\"%g%%\"", (double)(fVolume * 100.0) );
                bKnown = true;
            }
        }
        break;
 
        case DFF_ANIM_AFTEREFFECT:
        {
            sal_Bool bAfterEffect;
            if( aAny >>= bAfterEffect )
            {
                fprintf( mpFile, "afterEffect=\"%s\"", bAfterEffect ? "true" : "false" );
                bKnown = true;
            }
        }
        break;
 
        }
 
        if( !bKnown )
        {
            fprintf( mpFile, " unknown_%" SAL_PRIdINT32 "=\"", nInstance );
            dump( aAny );
            fprintf( mpFile, "\"" );
        }
    }
}
 
void AnimationImporter::dump_target( Any& rAny )
{
    Any aSource, aSourceData;
    Sequence< Any > aSeq;
    if( rAny >>= aSeq )
    {
        if( aSeq.getLength() >= 1 ) aSource = aSeq[0];
        if( aSeq.getLength() >= 2 ) aSourceData = aSeq[1];
    }
    else
    {
        aSource = rAny;
    }
 
    Reference< XShape > xShape;
    aSource >>= xShape;
    if( xShape.is() )
    {
        OUString aStr( xShape->getShapeType() );
        dump( aStr );
 
        if( aSourceData.hasValue() )
        {
            dump( "(" );
            dump( aSourceData );
            dump( ")" );
        }
    }
}
 
void AnimationImporter::dump( const char * pText )
{
    fprintf( mpFile, "%s", pText );
}
 
void AnimationImporter::dump( const OUString& rString )
{
    fprintf( mpFile, OUStringToOString(rString,
        RTL_TEXTENCODING_UTF8).getStr() );
}
 
void AnimationImporter::dump( const char * pText, sal_Int64 nInt )
{
    fprintf( mpFile, pText, nInt );
}
 
void AnimationImporter::dump( const char * pText, sal_Int32 nInt )
{
    fprintf( mpFile, pText, nInt );
}
 
void AnimationImporter::dump( const char * pText, double fDouble )
{
    fprintf( mpFile, pText, fDouble );
}
 
void AnimationImporter::dump( const char * pText, const char * pText2 )
{
    fprintf( mpFile, pText, pText2 );
}
 
void AnimationImporter::dump( const char * pText, const OUString& rString )
{
    fprintf( mpFile, pText, OUStringToOString(rString,
        RTL_TEXTENCODING_UTF8).getStr() );
}
 
#else
 
void AnimationImporter::dump_atom_header( const Atom* , bool , bool  )
{
}
 
void AnimationImporter::dump_atom( const Atom* , bool  )
{
}
 
void AnimationImporter::dump_target( css::uno::Any&  )
{
}
 
void AnimationImporter::dump( css::uno::Any&  )
{
}
 
void AnimationImporter::dump( const PropertySet&  )
{
}
 
void AnimationImporter::dump( const AnimationNode&  )
{
}
 
void AnimationImporter::dump( const char *  )
{
}
 
void AnimationImporter::dump( const char * , sal_Int32  )
{
}
 
void AnimationImporter::dump( const char * , double  )
{
}
 
void AnimationImporter::dump( const char * , const char *  )
{
}
 
void AnimationImporter::dump( const char * , std::u16string_view  )
{
}
 
#endif
 
} // namespace ppt;
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V547 Expression 'nColorSpace == 0' is always true.

V547 Expression 'bAfterEffect' is always false.

V547 Expression 'nPresetSubType' is always false.

V547 Expression '!bDirection' is always true.

V560 A part of conditional expression is always true: (nPresetId != 24).

V601 The 'nBegin / 1000.0' value is implicitly cast to the bool type. Inspect the first argument.

V785 Constant expression in switch statement.

V785 Constant expression in switch statement.

V785 Constant expression in switch statement.

V785 Constant expression in switch statement.