diff --git a/slideshow/CppunitTest_slideshow_engine.mk b/slideshow/CppunitTest_slideshow_engine.mk new file mode 100644 index 000000000000..a88531df49f7 --- /dev/null +++ b/slideshow/CppunitTest_slideshow_engine.mk @@ -0,0 +1,50 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +#************************************************************************* +# +# 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/. +# +#************************************************************************* + +$(eval $(call gb_CppunitTest_CppunitTest,slideshow_engine)) + +$(eval $(call gb_CppunitTest_use_externals,slideshow_engine,\ + boost_headers \ +)) + +$(eval $(call gb_CppunitTest_add_exception_objects,slideshow_engine, \ + slideshow/qa/engine/engine \ +)) + +$(eval $(call gb_CppunitTest_use_libraries,slideshow_engine, \ + comphelper \ + cppu \ + slideshow \ + sal \ + test \ + unotest \ +)) + +$(eval $(call gb_CppunitTest_use_sdk_api,slideshow_engine)) + +$(eval $(call gb_CppunitTest_set_include,slideshow_engine,\ + -I$(SRCDIR)/slideshow/source/inc \ + -I$(SRCDIR)/slideshow/source/engine/animationnodes \ + $$(INCLUDE) \ +)) + +$(eval $(call gb_CppunitTest_use_ure,slideshow_engine)) +$(eval $(call gb_CppunitTest_use_vcl,slideshow_engine)) + +$(eval $(call gb_CppunitTest_use_rdb,slideshow_engine,services)) + +$(eval $(call gb_CppunitTest_use_custom_headers,slideshow_engine,\ + officecfg/registry \ +)) + +$(eval $(call gb_CppunitTest_use_configuration,slideshow_engine)) + +# vim: set noet sw=4 ts=4: diff --git a/slideshow/Library_slideshow.mk b/slideshow/Library_slideshow.mk index a57717990124..580a7ae60df3 100644 --- a/slideshow/Library_slideshow.mk +++ b/slideshow/Library_slideshow.mk @@ -28,6 +28,10 @@ $(eval $(call gb_Library_use_externals,slideshow,\ epoxy \ )) +$(eval $(call gb_Library_add_defs,slideshow,\ + -DSLIDESHOW_DLLIMPLEMENTATION \ +)) + $(eval $(call gb_Library_use_sdk_api,slideshow)) $(eval $(call gb_Library_use_libraries,slideshow,\ diff --git a/slideshow/Module_slideshow.mk b/slideshow/Module_slideshow.mk index 51b832cfa4f1..9cbaff1eb7e7 100644 --- a/slideshow/Module_slideshow.mk +++ b/slideshow/Module_slideshow.mk @@ -21,6 +21,7 @@ endif $(eval $(call gb_Module_add_check_targets,slideshow,\ CppunitTest_slideshow \ + CppunitTest_slideshow_engine \ )) # vim: set noet sw=4 ts=4: diff --git a/slideshow/qa/engine/data/video-loop.pptx b/slideshow/qa/engine/data/video-loop.pptx new file mode 100644 index 000000000000..4cb7e20b7428 Binary files /dev/null and b/slideshow/qa/engine/data/video-loop.pptx differ diff --git a/slideshow/qa/engine/engine.cxx b/slideshow/qa/engine/engine.cxx new file mode 100644 index 000000000000..09e56f73d3f8 --- /dev/null +++ b/slideshow/qa/engine/engine.cxx @@ -0,0 +1,102 @@ +/* -*- 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/. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +using namespace ::com::sun::star; + +namespace +{ +/// Covers slideshow/source/engine/ fixes. +class Test : public test::BootstrapFixture, public unotest::MacrosTest +{ +private: + uno::Reference mxComponent; + +public: + void setUp() override; + void tearDown() override; + uno::Reference& getComponent() { return mxComponent; } +}; + +void Test::setUp() +{ + test::BootstrapFixture::setUp(); + + mxDesktop.set(frame::Desktop::create(mxComponentContext)); +} + +void Test::tearDown() +{ + if (mxComponent.is()) + mxComponent->dispose(); + + test::BootstrapFixture::tearDown(); +} + +/// Get the first command node in the animation tree of the page, assuming that it's the first child +/// (recursively). +uno::Reference +GetFirstCommandNodeOfPage(const uno::Reference& xPage) +{ + uno::Reference xAnimationNodeSupplier(xPage, + uno::UNO_QUERY); + uno::Reference xNode = xAnimationNodeSupplier->getAnimationNode(); + while (true) + { + if (xNode->getType() == animations::AnimationNodeType::COMMAND) + { + break; + } + uno::Reference xEnumAccess(xNode, uno::UNO_QUERY); + uno::Reference xNodes = xEnumAccess->createEnumeration(); + xNode.set(xNodes->nextElement(), uno::UNO_QUERY); + } + uno::Reference xRet(xNode, uno::UNO_QUERY); + return xRet; +} +} + +CPPUNIT_TEST_FIXTURE(Test, testLoopingFromAnimation) +{ + // Given a document with a looping video, the looping is defined as part of its auto-play + // animation (and not on the media shape): + OUString aURL = m_directories.getURLFromSrc(u"slideshow/qa/engine/data/video-loop.pptx"); + getComponent().set(loadFromDesktop(aURL)); + uno::Reference xDoc(getComponent(), uno::UNO_QUERY); + uno::Reference xPage(xDoc->getDrawPages()->getByIndex(0), uno::UNO_QUERY); + uno::Reference xCommandNode = GetFirstCommandNodeOfPage(xPage); + uno::Reference xShape(xPage->getByIndex(0), uno::UNO_QUERY); + + // When determining if the video should be looping or not: + bool bLooping + = slideshow::internal::AnimationCommandNode::GetLoopingFromAnimation(xCommandNode, xShape); + + // Then make sure that we detect the looping is wanted: + CPPUNIT_ASSERT(bLooping); +} + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/slideshow/source/engine/animationnodes/animationcommandnode.cxx b/slideshow/source/engine/animationnodes/animationcommandnode.cxx index 2e8507e3a5f5..df70cb1ab37c 100644 --- a/slideshow/source/engine/animationnodes/animationcommandnode.cxx +++ b/slideshow/source/engine/animationnodes/animationcommandnode.cxx @@ -19,8 +19,14 @@ #include +#include +#include +#include +#include #include +#include + #include "animationcommandnode.hxx" #include #include @@ -28,6 +34,52 @@ using namespace com::sun::star; +namespace +{ +/// Determines if this is the root of the timing node tree. +bool IsTimingRootNode(const uno::Reference& xNode) +{ + uno::Sequence aUserData = xNode->getUserData(); + comphelper::SequenceAsHashMap aMap(aUserData); + auto it = aMap.find("node-type"); + if (it == aMap.end()) + { + return false; + } + + sal_Int16 nNodeType{}; + if (!(it->second >>= nNodeType)) + { + return false; + } + + return nNodeType == css::presentation::EffectNodeType::TIMING_ROOT; +} + +/// Walks the parent chain of xNode and stops at the timing root. +uno::Reference +GetTimingRoot(const uno::Reference& xNode) +{ + uno::Reference xParent(xNode->getParent(), uno::UNO_QUERY); + while (true) + { + if (!xParent.is()) + { + break; + } + + if (IsTimingRootNode(xParent)) + { + return xParent; + } + + xParent.set(xParent->getParent(), uno::UNO_QUERY); + } + + return {}; +} +} + namespace slideshow::internal { namespace EffectCommands = css::presentation::EffectCommands; @@ -43,6 +95,7 @@ AnimationCommandNode::AnimationCommandNode( uno::ReferencelookupShape( xShape ) ); mpShape = ::std::dynamic_pointer_cast< IExternalMediaShapeBase >( pShape ); + mxShape = xShape; } void AnimationCommandNode::dispose() @@ -52,6 +105,42 @@ void AnimationCommandNode::dispose() BaseNode::dispose(); } +bool AnimationCommandNode::GetLoopingFromAnimation( + const uno::Reference& xCommandNode, + const uno::Reference& xShape) +{ + uno::Reference xTimingRoot = GetTimingRoot(xCommandNode); + uno::Reference xEnumAccess(xTimingRoot, uno::UNO_QUERY); + if (!xEnumAccess.is()) + { + return false; + } + + uno::Reference xNodes = xEnumAccess->createEnumeration(); + while (xNodes->hasMoreElements()) + { + uno::Reference xNode(xNodes->nextElement(), uno::UNO_QUERY); + if (xNode->getType() != animations::AnimationNodeType::AUDIO) + { + continue; + } + + uno::Reference xAudio(xNode, uno::UNO_QUERY); + uno::Reference xSource(xAudio->getSource(), uno::UNO_QUERY); + if (xSource != xShape) + { + continue; + } + + animations::Timing eTiming{}; + if ((xAudio->getRepeatCount() >>= eTiming) && eTiming == animations::Timing_INDEFINITE) + { + return true; + } + } + return false; +} + void AnimationCommandNode::activate_st() { switch( mxCommandNode->getCommand() ) { @@ -71,6 +160,14 @@ void AnimationCommandNode::activate_st() if( mpShape ) { mpShape->setMediaTime(fMediaTime/1000.0); + + if (AnimationCommandNode::GetLoopingFromAnimation(mxCommandNode, mxShape)) + { + // If looping is requested from the animation, then that has priority over the + // looping from the shape itself. + mpShape->setLooping(true); + } + mpShape->play(); } break; diff --git a/slideshow/source/engine/animationnodes/animationcommandnode.hxx b/slideshow/source/engine/animationnodes/animationcommandnode.hxx index bcb61f1ddd3f..96ca886f8f2c 100644 --- a/slideshow/source/engine/animationnodes/animationcommandnode.hxx +++ b/slideshow/source/engine/animationnodes/animationcommandnode.hxx @@ -20,6 +20,7 @@ #ifndef INCLUDED_SLIDESHOW_SOURCE_ENGINE_ANIMATIONNODES_ANIMATIONCOMMANDNODE_HXX #define INCLUDED_SLIDESHOW_SOURCE_ENGINE_ANIMATIONNODES_ANIMATIONCOMMANDNODE_HXX +#include #include #include #include @@ -31,7 +32,7 @@ namespace slideshow::internal { This animation node encapsulates a command. Not yet implemented: verb & custom. */ -class AnimationCommandNode : public BaseNode +class SLIDESHOW_DLLPUBLIC AnimationCommandNode : public BaseNode { public: AnimationCommandNode( @@ -39,6 +40,12 @@ public: ::std::shared_ptr const& pParent, NodeContext const& rContext ); + /// Assuming that xCommandNode is a play command, determines if an audio node wants looping when + /// xShape plays. + static bool + GetLoopingFromAnimation(const css::uno::Reference& xCommandNode, + const css::uno::Reference& xShape); + protected: virtual void dispose() override; @@ -49,6 +56,7 @@ private: private: IExternalMediaShapeBaseSharedPtr mpShape; css::uno::Reference mxCommandNode; + css::uno::Reference mxShape; }; } // namespace slideshow::internal diff --git a/slideshow/source/engine/shapes/externalshapebase.cxx b/slideshow/source/engine/shapes/externalshapebase.cxx index 22205186fa7e..8a879db0dae2 100644 --- a/slideshow/source/engine/shapes/externalshapebase.cxx +++ b/slideshow/source/engine/shapes/externalshapebase.cxx @@ -143,6 +143,7 @@ namespace slideshow::internal implSetIntrinsicAnimationTime(fTime); } + void ExternalShapeBase::setLooping(bool bLooping) { implSetLooping(bLooping); } bool ExternalShapeBase::update() const { diff --git a/slideshow/source/engine/shapes/externalshapebase.hxx b/slideshow/source/engine/shapes/externalshapebase.hxx index 260d8630e170..d8c208db2a99 100644 --- a/slideshow/source/engine/shapes/externalshapebase.hxx +++ b/slideshow/source/engine/shapes/externalshapebase.hxx @@ -65,6 +65,7 @@ namespace slideshow::internal virtual void pause() override; virtual bool isPlaying() const override; virtual void setMediaTime(double) override; + void setLooping(bool bLooping) override; // render methods @@ -108,6 +109,7 @@ namespace slideshow::internal virtual bool implIsIntrinsicAnimationPlaying() const = 0; /// override in derived class to set media time virtual void implSetIntrinsicAnimationTime(double) = 0; + virtual void implSetLooping(bool /*bLooping*/) {} /// The associated XShape diff --git a/slideshow/source/engine/shapes/mediashape.cxx b/slideshow/source/engine/shapes/mediashape.cxx index 28d3cee86821..a5cbb926f4f5 100644 --- a/slideshow/source/engine/shapes/mediashape.cxx +++ b/slideshow/source/engine/shapes/mediashape.cxx @@ -82,6 +82,7 @@ namespace slideshow::internal virtual void implPauseIntrinsicAnimation() override; virtual bool implIsIntrinsicAnimationPlaying() const override; virtual void implSetIntrinsicAnimationTime(double) override; + void implSetLooping(bool bLooping) override; /// the list of active view shapes (one for each registered view layer) typedef ::std::vector< ViewMediaShapeSharedPtr > ViewMediaShapeVector; @@ -235,6 +236,13 @@ namespace slideshow::internal pViewMediaShape->setMediaTime( fTime ); } + void MediaShape::implSetLooping(bool bLooping) + { + for (const auto& pViewMediaShape : maViewMediaShapes) + { + pViewMediaShape->setLooping(bLooping); + } + } ShapeSharedPtr createMediaShape( const uno::Reference< drawing::XShape >& xShape, diff --git a/slideshow/source/engine/shapes/viewmediashape.cxx b/slideshow/source/engine/shapes/viewmediashape.cxx index 4c5b9f51f8f6..f91972f8fe40 100644 --- a/slideshow/source/engine/shapes/viewmediashape.cxx +++ b/slideshow/source/engine/shapes/viewmediashape.cxx @@ -144,6 +144,14 @@ namespace slideshow::internal mxPlayer->setMediaTime(fTime); } + void ViewMediaShape::setLooping(bool bLooping) + { + if (mxPlayer.is()) + { + mxPlayer->setPlaybackLoop(bLooping); + } + } + bool ViewMediaShape::render( const ::basegfx::B2DRectangle& rBounds ) const { #if !HAVE_FEATURE_AVMEDIA diff --git a/slideshow/source/engine/shapes/viewmediashape.hxx b/slideshow/source/engine/shapes/viewmediashape.hxx index 32515a47d864..fc37a3add8ce 100644 --- a/slideshow/source/engine/shapes/viewmediashape.hxx +++ b/slideshow/source/engine/shapes/viewmediashape.hxx @@ -112,6 +112,8 @@ namespace slideshow::internal */ void setMediaTime(double fTime); + void setLooping(bool bLooping); + // render methods diff --git a/slideshow/source/inc/iexternalmediashapebase.hxx b/slideshow/source/inc/iexternalmediashapebase.hxx index 156b0b2dd7ce..1a70ae093652 100644 --- a/slideshow/source/inc/iexternalmediashapebase.hxx +++ b/slideshow/source/inc/iexternalmediashapebase.hxx @@ -72,6 +72,8 @@ namespace slideshow::internal presented */ virtual void setMediaTime(double fTime) = 0; + + virtual void setLooping(bool /*bLooping*/){}; }; typedef ::std::shared_ptr< IExternalMediaShapeBase > IExternalMediaShapeBaseSharedPtr; diff --git a/slideshow/source/inc/slideshowdllapi.h b/slideshow/source/inc/slideshowdllapi.h new file mode 100644 index 000000000000..048a83dd271a --- /dev/null +++ b/slideshow/source/inc/slideshowdllapi.h @@ -0,0 +1,20 @@ +/* -*- 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/. + */ + +#pragma once + +#include + +#if defined(SLIDESHOW_DLLIMPLEMENTATION) +#define SLIDESHOW_DLLPUBLIC SAL_DLLPUBLIC_EXPORT +#else +#define SLIDESHOW_DLLPUBLIC SAL_DLLPUBLIC_IMPORT +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */