diff options
author | thb <thb@openoffice.org> | 2010-01-07 13:07:30 +0100 |
---|---|---|
committer | thb <thb@openoffice.org> | 2010-01-07 13:07:30 +0100 |
commit | 609f39ca92eb983ca552178f154da1f2b9b4467b (patch) | |
tree | c52d9384114042da79b09e9cee8d54e20a2f3afa /slideshow/source/engine | |
parent | e91988cf05e3b5e2fb7be7e34d78bce12a352a4a (diff) | |
parent | 17ffefd4513392729e377beedb2d075e9da64106 (diff) |
eraser01: merge with DEV300 m68
Diffstat (limited to 'slideshow/source/engine')
29 files changed, 1770 insertions, 211 deletions
diff --git a/slideshow/source/engine/OGLTrans/makefile.mk b/slideshow/source/engine/OGLTrans/makefile.mk index 2062b8c7d..6353b2da5 100644 --- a/slideshow/source/engine/OGLTrans/makefile.mk +++ b/slideshow/source/engine/OGLTrans/makefile.mk @@ -61,7 +61,6 @@ SHL1TARGET=$(TARGET).uno .IF "$(GUI)"=="UNX" .IF "$(GUIBASE)"=="aqua" SHL1STDLIBS= $(SALLIB) $(VCLLIB) $(CPPULIB) $(CPPUHELPERLIB) $(COMPHELPERLIB) $(CANVASTOOLSLIB) - OBJCXXFLAGS=-x objective-c++ -fobjc-exceptions CFLAGSCXX+=$(OBJCXXFLAGS) .ELSE SHL1STDLIBS= $(SALLIB) $(VCLLIB) $(CPPULIB) $(CPPUHELPERLIB) $(COMPHELPERLIB) $(CANVASTOOLSLIB) -lGL -lGLU -lX11 diff --git a/slideshow/source/engine/activities/activitiesfactory.cxx b/slideshow/source/engine/activities/activitiesfactory.cxx index 21a98940d..97ff422d1 100644 --- a/slideshow/source/engine/activities/activitiesfactory.cxx +++ b/slideshow/source/engine/activities/activitiesfactory.cxx @@ -259,7 +259,7 @@ public: { if (this->isDisposed() || !mpAnim) return; - (*mpAnim)( + (*mpAnim)( getPresentationValue( accumulate( maEndValue, mbCumulative * nRepeatCount, // means: mbCumulative ? nRepeatCount : 0, @@ -289,11 +289,18 @@ public: BaseType::getNumberOfKeyTimes() ) ) ) ); } + using BaseType::isAutoReverse; + virtual void performEnd() { // xxx todo: good guess if (mpAnim) - (*mpAnim)( getPresentationValue( maEndValue ) ); + { + if (isAutoReverse()) + (*mpAnim)( getPresentationValue( maStartValue ) ); + else + (*mpAnim)( getPresentationValue( maEndValue ) ); + } } /// Disposable: diff --git a/slideshow/source/engine/animationnodes/animationaudionode.cxx b/slideshow/source/engine/animationnodes/animationaudionode.cxx index c1df04a4b..112df2228 100644 --- a/slideshow/source/engine/animationnodes/animationaudionode.cxx +++ b/slideshow/source/engine/animationnodes/animationaudionode.cxx @@ -94,14 +94,16 @@ void AnimationAudioNode::activate_st() // no node duration. Take inherent media time, then scheduleDeactivationEvent( makeDelay( boost::bind( &AnimationNode::deactivate, getSelf() ), - mpPlayer->getDuration() ) ); + mpPlayer->getDuration(), + "AnimationAudioNode::deactivate with delay") ); } } else { // deactivate ASAP: scheduleDeactivationEvent( - makeEvent( boost::bind( &AnimationNode::deactivate, getSelf() ) ) ); + makeEvent( boost::bind( &AnimationNode::deactivate, getSelf() ), + "AnimationAudioNode::deactivate without delay") ); } } @@ -127,7 +129,8 @@ void AnimationAudioNode::deactivate_st( NodeState /*eDestState*/ ) getContext().mrEventQueue.addEvent( makeEvent( boost::bind( &EventMultiplexer::notifyAudioStopped, boost::ref(getContext().mrEventMultiplexer), - getSelf() ) ) ); + getSelf() ), + "AnimationAudioNode::notifyAudioStopped") ); } bool AnimationAudioNode::hasPendingAnimation() const diff --git a/slideshow/source/engine/animationnodes/animationbasenode.cxx b/slideshow/source/engine/animationnodes/animationbasenode.cxx index e788aeb52..fffe5a7c6 100644 --- a/slideshow/source/engine/animationnodes/animationbasenode.cxx +++ b/slideshow/source/engine/animationnodes/animationbasenode.cxx @@ -456,7 +456,8 @@ AnimationBaseNode::fillCommonParameters() const EventSharedPtr pEndEvent; if (pSelf) { pEndEvent = makeEvent( - boost::bind( &AnimationNode::deactivate, pSelf ) ); + boost::bind( &AnimationNode::deactivate, pSelf ), + "AnimationBaseNode::deactivate"); } // Calculate the minimum frame count that depends on the duration and diff --git a/slideshow/source/engine/animationnodes/animationcommandnode.cxx b/slideshow/source/engine/animationnodes/animationcommandnode.cxx index 042fd529c..91d5e22d3 100644 --- a/slideshow/source/engine/animationnodes/animationcommandnode.cxx +++ b/slideshow/source/engine/animationnodes/animationcommandnode.cxx @@ -124,7 +124,8 @@ void AnimationCommandNode::activate_st() // deactivate ASAP: scheduleDeactivationEvent( - makeEvent( boost::bind( &AnimationNode::deactivate, getSelf() ) ) ); + makeEvent( boost::bind( &AnimationNode::deactivate, getSelf() ), + "AnimationCommandNode::deactivate" ) ); } bool AnimationCommandNode::hasPendingAnimation() const diff --git a/slideshow/source/engine/animationnodes/animationsetnode.cxx b/slideshow/source/engine/animationnodes/animationsetnode.cxx index 9800074ef..7078ce68a 100644 --- a/slideshow/source/engine/animationnodes/animationsetnode.cxx +++ b/slideshow/source/engine/animationnodes/animationsetnode.cxx @@ -80,7 +80,8 @@ AnimationActivitySharedPtr AnimationSetNode::createActivity() const pSelf, "cannot cast getSelf() to my type!" ); aParms.mpEndEvent = makeEvent( boost::bind( &AnimationSetNode::implScheduleDeactivationEvent, - pSelf ) ); + pSelf ), + "AnimationSetNode::implScheduleDeactivationEvent"); } switch (AnimationFactory::classifyAttributeName( attrName )) { diff --git a/slideshow/source/engine/animationnodes/basenode.cxx b/slideshow/source/engine/animationnodes/basenode.cxx index 90eb97d86..8288eb002 100644 --- a/slideshow/source/engine/animationnodes/basenode.cxx +++ b/slideshow/source/engine/animationnodes/basenode.cxx @@ -50,6 +50,7 @@ #include "tools.hxx" #include "nodetools.hxx" #include "generateevent.hxx" +#include "debug.hxx" #include <boost/bind.hpp> #include <vector> @@ -312,6 +313,10 @@ public: mpNode->meCurrState = meToState; clear(); } + + // Uncomment the following line to write the node tree to file on + // every state change of one of its nodes. + // Debug_ShowNodeTree(mpNode->mpSelf); } void clear() { @@ -488,7 +493,9 @@ bool BaseNode::resolve() // schedule delayed activation event. Take iterate node // timeout into account mpCurrentEvent = makeDelay( - boost::bind( &AnimationNode::activate, mpSelf ), mnStartDelay ); + boost::bind( &AnimationNode::activate, mpSelf ), + mnStartDelay, + "AnimationNode::activate with delay"); maContext.mrEventQueue.addEvent( mpCurrentEvent ); } diff --git a/slideshow/source/engine/animationnodes/basenode.hxx b/slideshow/source/engine/animationnodes/basenode.hxx index a25f6cb92..dbf2f1375 100644 --- a/slideshow/source/engine/animationnodes/basenode.hxx +++ b/slideshow/source/engine/animationnodes/basenode.hxx @@ -136,7 +136,9 @@ public: const AnimationNodeSharedPtr& rNotifee ); // nop: virtual void notifyDeactivating( const AnimationNodeSharedPtr& rNotifier ); - + + bool isMainSequenceRootNode() const { return mbIsMainSequenceRootNode; } + protected: void scheduleDeactivationEvent( EventSharedPtr const& pEvent = EventSharedPtr() ); @@ -144,8 +146,6 @@ protected: SlideShowContext const& getContext() const { return maContext; } ::boost::shared_ptr<BaseNode> const& getSelf() const { return mpSelf; } - bool isMainSequenceRootNode() const { return mbIsMainSequenceRootNode; } - bool checkValidNode() const { ENSURE_OR_THROW( mpSelf, "no self ptr set!" ); bool const bRet = (meCurrState != INVALID); diff --git a/slideshow/source/engine/animationnodes/generateevent.cxx b/slideshow/source/engine/animationnodes/generateevent.cxx index e9bc6736d..33aaa670b 100644 --- a/slideshow/source/engine/animationnodes/generateevent.cxx +++ b/slideshow/source/engine/animationnodes/generateevent.cxx @@ -111,7 +111,9 @@ EventSharedPtr generateEvent( case animations::EventTrigger::BEGIN_EVENT: // try to extract XAnimationNode event source if (aEvent.Source >>= xNode) { - pEvent = makeDelay( rFunctor, nDelay2 + nAdditionalDelay ); + pEvent = makeDelay( rFunctor, + nDelay2 + nAdditionalDelay, + "generateEvent, BEGIN_EVENT"); rContext.mrUserEventQueue.registerAnimationStartEvent( pEvent, xNode ); } @@ -123,7 +125,9 @@ EventSharedPtr generateEvent( case animations::EventTrigger::END_EVENT: // try to extract XAnimationNode event source if (aEvent.Source >>= xNode) { - pEvent = makeDelay( rFunctor, nDelay2 + nAdditionalDelay ); + pEvent = makeDelay( rFunctor, + nDelay2 + nAdditionalDelay, + "generateEvent, END_EVENT"); rContext.mrUserEventQueue.registerAnimationEndEvent( pEvent, xNode ); } @@ -137,7 +141,9 @@ EventSharedPtr generateEvent( if ((aEvent.Source >>= xShape) && (pShape = rContext.mpSubsettableShapeManager->lookupShape(xShape)).get()) { - pEvent = makeDelay( rFunctor, nDelay2 + nAdditionalDelay ); + pEvent = makeDelay( rFunctor, + nDelay2 + nAdditionalDelay, + "generateEvent, ON_CLICK"); rContext.mrUserEventQueue.registerShapeClickEvent( pEvent, pShape ); } @@ -151,7 +157,9 @@ EventSharedPtr generateEvent( if ((aEvent.Source >>= xShape) && (pShape = rContext.mpSubsettableShapeManager->lookupShape(xShape)).get()) { - pEvent = makeDelay( rFunctor, nDelay2 + nAdditionalDelay ); + pEvent = makeDelay( rFunctor, + nDelay2 + nAdditionalDelay, + "generateEvent, ON_DBL_CLICK"); rContext.mrUserEventQueue.registerShapeDoubleClickEvent( pEvent, pShape ); } @@ -165,7 +173,9 @@ EventSharedPtr generateEvent( if ((aEvent.Source >>= xShape) && (pShape = rContext.mpSubsettableShapeManager->lookupShape(xShape)).get()) { - pEvent = makeDelay( rFunctor, nDelay2 + nAdditionalDelay ); + pEvent = makeDelay( rFunctor, + nDelay2 + nAdditionalDelay, + "generateEvent, ON_MOUSE_ENTER"); rContext.mrUserEventQueue.registerMouseEnterEvent( pEvent, pShape ); } @@ -179,7 +189,9 @@ EventSharedPtr generateEvent( if ((aEvent.Source >>= xShape) && (pShape = rContext.mpSubsettableShapeManager->lookupShape(xShape)).get()) { - pEvent = makeDelay( rFunctor, nDelay2 + nAdditionalDelay ); + pEvent = makeDelay( rFunctor, + nDelay2 + nAdditionalDelay, + "generateEvent, ON_MOUSE_LEAVE"); rContext.mrUserEventQueue.registerMouseLeaveEvent( pEvent, pShape ); } @@ -193,13 +205,17 @@ EventSharedPtr generateEvent( "mapped to ON_NEXT!" ); // FALLTHROUGH intended case animations::EventTrigger::ON_NEXT: - pEvent = makeDelay( rFunctor, nDelay2 + nAdditionalDelay ); + pEvent = makeDelay( rFunctor, + nDelay2 + nAdditionalDelay, + "generateEvent, ON_NEXT"); rContext.mrUserEventQueue.registerNextEffectEvent( pEvent ); break; case animations::EventTrigger::ON_STOP_AUDIO: // try to extract XAnimationNode event source if (aEvent.Source >>= xNode) { - pEvent = makeDelay( rFunctor, nDelay2 + nAdditionalDelay ); + pEvent = makeDelay( rFunctor, + nDelay2 + nAdditionalDelay, + "generateEvent, ON_STOP_AUDIO"); rContext.mrUserEventQueue.registerAudioStoppedEvent( pEvent, xNode ); } @@ -218,7 +234,9 @@ EventSharedPtr generateEvent( "not yet implemented!" ); } else if (rEventDescription >>= nDelay1) { - pEvent = makeDelay( rFunctor, nDelay1 + nAdditionalDelay ); + pEvent = makeDelay( rFunctor, + nDelay1 + nAdditionalDelay, + "generateEvent with delay"); // schedule delay event rContext.mrEventQueue.addEvent( pEvent ); } diff --git a/slideshow/source/engine/animationnodes/paralleltimecontainer.cxx b/slideshow/source/engine/animationnodes/paralleltimecontainer.cxx index 98f4f909a..b9e39ef67 100644 --- a/slideshow/source/engine/animationnodes/paralleltimecontainer.cxx +++ b/slideshow/source/engine/animationnodes/paralleltimecontainer.cxx @@ -53,7 +53,8 @@ void ParallelTimeContainer::activate_st() if (isDurationIndefinite() && maChildren.empty()) { // deactivate ASAP: scheduleDeactivationEvent( - makeEvent( boost::bind( &AnimationNode::deactivate, getSelf() ) ) ); + makeEvent( boost::bind( &AnimationNode::deactivate, getSelf() ), + "ParallelTimeContainer::deactivate") ); } else { // use default scheduleDeactivationEvent(); diff --git a/slideshow/source/engine/animationnodes/sequentialtimecontainer.cxx b/slideshow/source/engine/animationnodes/sequentialtimecontainer.cxx index de6332114..9e64d421e 100644 --- a/slideshow/source/engine/animationnodes/sequentialtimecontainer.cxx +++ b/slideshow/source/engine/animationnodes/sequentialtimecontainer.cxx @@ -34,11 +34,11 @@ #include <canvas/debug.hxx> #include <canvas/verbosetrace.hxx> +#include "delayevent.hxx" #include "eventqueue.hxx" #include "usereventqueue.hxx" #include "sequentialtimecontainer.hxx" #include "tools.hxx" -#include "delayevent.hxx" #include <boost/bind.hpp> #include <algorithm> @@ -63,7 +63,9 @@ void SequentialTimeContainer::activate_st() { // deactivate ASAP: scheduleDeactivationEvent( - makeEvent( boost::bind( &AnimationNode::deactivate, getSelf() ) ) ); + makeEvent( + boost::bind< void >( boost::mem_fn( &AnimationNode::deactivate ), getSelf() ), + "SequentialTimeContainer::deactivate") ); } else // use default scheduleDeactivationEvent(); @@ -88,8 +90,10 @@ void SequentialTimeContainer::skipEffect( if (isChildNode(pChildNode)) { // empty all events ignoring timings => until next effect getContext().mrEventQueue.forceEmpty(); - getContext().mrEventQueue.addEventForNextRound( - makeEvent( boost::bind(&AnimationNode::deactivate, pChildNode) ) ); + getContext().mrEventQueue.addEvent( + makeEvent( + boost::bind<void>( boost::mem_fn( &AnimationNode::deactivate ), pChildNode ), + "SequentialTimeContainer::deactivate, skipEffect with delay") ); } else OSL_ENSURE( false, "unknown notifier!" ); @@ -116,16 +120,19 @@ bool SequentialTimeContainer::resolveChild( mpCurrentSkipEvent = makeEvent( boost::bind( &SequentialTimeContainer::skipEffect, boost::dynamic_pointer_cast<SequentialTimeContainer>( getSelf() ), - pChildNode ) ); + pChildNode ), + "SequentialTimeContainer::skipEffect, resolveChild"); // event that will reresolve the resolved/activated child: mpCurrentRewindEvent = makeEvent( boost::bind( &SequentialTimeContainer::rewindEffect, boost::dynamic_pointer_cast<SequentialTimeContainer>( getSelf() ), - pChildNode ) ); + pChildNode ), + "SequentialTimeContainer::rewindEffect, resolveChild"); // deactivate child node when skip event occurs: getContext().mrUserEventQueue.registerSkipEffectEvent( - mpCurrentSkipEvent ); + mpCurrentSkipEvent, + mnFinishedChildren+1<maChildren.size()); // rewind to previous child: getContext().mrUserEventQueue.registerRewindEffectEvent( mpCurrentRewindEvent ); diff --git a/slideshow/source/engine/debug.cxx b/slideshow/source/engine/debug.cxx new file mode 100644 index 000000000..f529828be --- /dev/null +++ b/slideshow/source/engine/debug.cxx @@ -0,0 +1,327 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: layer.hxx,v $ + * $Revision: 1.3 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include "precompiled_slideshow.hxx" + +#include "debug.hxx" + +#if OSL_DEBUG_LEVEL > 1 + +#include "animationnodes/basecontainernode.hxx" +#include "animationnodes/paralleltimecontainer.hxx" +#include "animationnodes/sequentialtimecontainer.hxx" +#include "animationnodes/animationtransitionfilternode.hxx" +#include "animationnodes/animationaudionode.hxx" +#include "animationnodes/animationcolornode.hxx" +#include "animationnodes/animationcommandnode.hxx" +#include "animationnodes/animationpathmotionnode.hxx" +#include "animationnodes/animationsetnode.hxx" +#include "animationnodes/animationtransformnode.hxx" +#include "animationnodes/propertyanimationnode.hxx" + +#include <com/sun/star/animations/XAnimationNode.hpp> +#include <com/sun/star/animations/Event.hpp> + +#include <cstdio> +#include <cstdarg> + +using ::rtl::OUString; +using namespace ::com::sun::star; + + +namespace slideshow { namespace internal { + +namespace { + +class NodeContainer : public BaseContainerNode +{ +public: + void ShowChildrenState (void) const; +}; + + + + +OUString DebugGetDescription (const AnimationNodeSharedPtr& rpNode) +{ + if (::boost::dynamic_pointer_cast<BaseContainerNode>(rpNode)) + { + // Node is a container. + if (::boost::dynamic_pointer_cast<ParallelTimeContainer>(rpNode)) + return OUString::createFromAscii("ParallelTimeContainer"); + else if (::boost::dynamic_pointer_cast<SequentialTimeContainer>(rpNode)) + return OUString::createFromAscii("SequentialTimeContainer"); + else + return OUString::createFromAscii("<unknown container>"); + } + else if (::boost::dynamic_pointer_cast<AnimationTransitionFilterNode>(rpNode)) + return OUString::createFromAscii("AnimationTransitionFilterNode"); + else if (::boost::dynamic_pointer_cast<AnimationAudioNode>(rpNode)) + return OUString::createFromAscii("AnimationAudioNode"); + else if (::boost::dynamic_pointer_cast<AnimationColorNode>(rpNode)) + return OUString::createFromAscii("AnimationColorNode"); + else if (::boost::dynamic_pointer_cast<AnimationCommandNode>(rpNode)) + return OUString::createFromAscii("AnimationCommandNode"); + else if (::boost::dynamic_pointer_cast<AnimationPathMotionNode>(rpNode)) + return OUString::createFromAscii("AnimationPathMotionNode"); + else if (::boost::dynamic_pointer_cast<AnimationSetNode>(rpNode)) + return OUString::createFromAscii("AnimationSetNode"); + else if (::boost::dynamic_pointer_cast<AnimationTransformNode>(rpNode)) + return OUString::createFromAscii("AnimationTransformNode"); + else if (::boost::dynamic_pointer_cast<PropertyAnimationNode>(rpNode)) + return OUString::createFromAscii("PropertyAnimationNode"); + else + return OUString::createFromAscii("<unknown node type>"); +} + + + + +void DebugShowState (const AnimationNodeSharedPtr& rpNode) +{ + if ( ! rpNode) + return; + + OUString sState; + OUString sStateColor; + switch (rpNode->getState()) + { + default: + case AnimationNode::INVALID: + sState = OUString::createFromAscii("Invalid"); + sStateColor = OUString::createFromAscii("firebrick1"); + break; + case AnimationNode::UNRESOLVED: + sState = OUString::createFromAscii("Unresolved"); + sStateColor = OUString::createFromAscii("dodgerblue4"); + break; + case AnimationNode::RESOLVED: + sState = OUString::createFromAscii("Resolved"); + sStateColor = OUString::createFromAscii("dodgerblue"); + break; + case AnimationNode::ACTIVE: + sState = OUString::createFromAscii("Active"); + sStateColor = OUString::createFromAscii("seagreen1"); + break; + case AnimationNode::FROZEN: + sState = OUString::createFromAscii("Frozen"); + sStateColor = OUString::createFromAscii("lightskyblue1"); + break; + case AnimationNode::ENDED: + sState = OUString::createFromAscii("Ended"); + sStateColor = OUString::createFromAscii("slategray3"); + break; + } + + const uno::Any aBegin (rpNode->getXAnimationNode()->getBegin()); + OUString sTrigger; + if (aBegin.hasValue()) + { + animations::Event aEvent; + double nTimeOffset; + const static char* sEventTriggers[] = { + "NONE", "ON_BEGIN", "ON_END", "BEGIN_EVENT", "END_EVENT", "ON_CLICK", + "ON_DBL_CLICK", "ON_MOUSE_ENTER", "ON_MOUSE_LEAVE", "ON_NEXT", "ON_PREV", + "ON_STOP_AUDIO", "REPEAT"}; + if (aBegin >>= aEvent) + { + sTrigger = OUString::createFromAscii(sEventTriggers[aEvent.Trigger]); + } + else if (aBegin >>= nTimeOffset) + { + sTrigger = OUString::valueOf(nTimeOffset); + } + else + { + sTrigger = OUString::createFromAscii("other"); + } + } + else + sTrigger = ::rtl::OUString::createFromAscii("void"); + + TRACE("Node state: n%x [label=\"%x / %x / %s\\n%s\\n%s\",style=filled,fillcolor=\"%s\"]\r", + rpNode.get(), + rpNode.get(), + rpNode->getXAnimationNode().get(), + ::rtl::OUStringToOString(sState, RTL_TEXTENCODING_ASCII_US).getStr(), + ::rtl::OUStringToOString(DebugGetDescription(rpNode), RTL_TEXTENCODING_ASCII_US).getStr(), + ::rtl::OUStringToOString(sTrigger, RTL_TEXTENCODING_ASCII_US).getStr(), + ::rtl::OUStringToOString(sStateColor, RTL_TEXTENCODING_ASCII_US).getStr()); + + BaseContainerNodeSharedPtr pContainer ( + ::boost::dynamic_pointer_cast<BaseContainerNode>(rpNode)); + if (pContainer) + ::boost::static_pointer_cast<NodeContainer>(rpNode)->ShowChildrenState(); +} + + + + +void NodeContainer::ShowChildrenState (void) const +{ + for (std::size_t nIndex=0; nIndex<maChildren.size(); ++nIndex) + { + TRACE("Node connection: n%x -> n%x", this, maChildren[nIndex].get()); + DebugShowState(maChildren[nIndex]); + } +} + + + + +AnimationNodeSharedPtr DebugGetTreeRoot (const BaseNodeSharedPtr& rpNode) +{ + BaseNodeSharedPtr pNode (rpNode); + if (pNode) + { + BaseNodeSharedPtr pParent (pNode->getParentNode()); + while (pParent) + { + pNode = pParent; + pParent = pNode->getParentNode(); + } + } + return pNode; +} + +} // end of anonymous namespace + + + + +void Debug_ShowNodeTree (const AnimationNodeSharedPtr& rpNode) +{ + DebugTraceScope aTraceScope ("NodeTree"); + + DebugShowState(DebugGetTreeRoot(::boost::dynamic_pointer_cast<BaseNode>(rpNode))); +} + + + + +//----- Tracing --------------------------------------------------------------- + +extern "C" { + + namespace { + + class TraceData + { + public: + TraceData (void) + : mnIndentation(0), + mpFile(fopen(TRACE_LOG_FILE_NAME, "w")), + maTime() + { + } + + int mnIndentation; + FILE* mpFile; + ::canvas::tools::ElapsedTime maTime; + }; + static TraceData gTraceData; + + inline void SAL_CALL DebugTrace ( + const int nIndentationOffset, + const sal_Char* sFormat, + va_list args) + { + if (gTraceData.mpFile != NULL) + { + // Write line head with current time and indentation. + // Adapt indentation. + if (nIndentationOffset < 0) + gTraceData.mnIndentation += nIndentationOffset; + fprintf(gTraceData.mpFile, "%10.8f ", gTraceData.maTime.getElapsedTime()); + for (int nIndentation=0; nIndentation<gTraceData.mnIndentation; ++nIndentation) + fprintf(gTraceData.mpFile, " "); + if (nIndentationOffset > 0) + gTraceData.mnIndentation += nIndentationOffset; + + // Write message. + vfprintf(gTraceData.mpFile, sFormat, args); + fprintf(gTraceData.mpFile, "\n"); + fflush(gTraceData.mpFile); + } + } + +} // end of anonymous namespace + + +} // end of extern "C" + +void SAL_CALL DebugTraceBegin (const sal_Char* sFormat, ...) +{ + va_list args; + va_start(args, sFormat); + DebugTrace(+1,sFormat, args); + va_end(args); +} + +void SAL_CALL DebugTraceEnd (const sal_Char* sFormat, ...) +{ + va_list args; + va_start(args, sFormat); + DebugTrace(-1,sFormat, args); + va_end(args); +} + +void SAL_CALL DebugTraceMessage (const sal_Char* sFormat, ...) +{ + va_list args; + va_start(args, sFormat); + DebugTrace(0,sFormat, args); + va_end(args); +} + + + +DebugTraceScope::DebugTraceScope (const sal_Char* sFormat, ...) + : msMessage(new sal_Char[mnBufferSize]) +{ + va_list args; + va_start(args, sFormat); + + msMessage[mnBufferSize-1] = 0; + _vsnprintf(msMessage, mnBufferSize-1, sFormat, args); + TRACE_BEGIN("[ %s", msMessage); + va_end(args); +} + +DebugTraceScope::~DebugTraceScope (void) +{ + TRACE_END("] %s", msMessage); + delete [] msMessage; +} + + +} } + +#endif // OSL_DEBUG_LEVEL > 1 diff --git a/slideshow/source/engine/effectrewinder.cxx b/slideshow/source/engine/effectrewinder.cxx new file mode 100644 index 000000000..3b10405fa --- /dev/null +++ b/slideshow/source/engine/effectrewinder.cxx @@ -0,0 +1,436 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: slideshowimpl.cxx,v $ + * $Revision: 1.10 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include "precompiled_slideshow.hxx" + +#include "effectrewinder.hxx" +#include "eventqueue.hxx" +#include "usereventqueue.hxx" +#include "mouseeventhandler.hxx" +#include "animationnodes/basecontainernode.hxx" +#include "delayevent.hxx" + +#include <com/sun/star/awt/MouseEvent.hpp> +#include <com/sun/star/animations/Event.hpp> +#include <com/sun/star/animations/EventTrigger.hpp> +#include <com/sun/star/container/XEnumerationAccess.hpp> +#include <boost/function.hpp> +#include <boost/bind.hpp> +#include <boost/enable_shared_from_this.hpp> + +using ::com::sun::star::uno::Reference; +using namespace ::com::sun::star; + +namespace slideshow { namespace internal { + + +namespace { + +class RewinderEventHandler : public EventHandler +{ +public: + typedef ::boost::function<bool(void)> Action; + RewinderEventHandler (const Action& rAction) : maAction(rAction) {} + virtual ~RewinderEventHandler (void) {} +private: + const Action maAction; + virtual bool handleEvent (void) { return maAction(); } +}; + + + +class RewinderAnimationEventHandler : public AnimationEventHandler +{ +public: + typedef ::boost::function<bool(const AnimationNodeSharedPtr& rpNode)> Action; + RewinderAnimationEventHandler (const Action& rAction) : maAction(rAction) {} + virtual ~RewinderAnimationEventHandler (void) {} +private: + const Action maAction; + virtual bool handleAnimationEvent (const AnimationNodeSharedPtr& rpNode) + { return maAction(rpNode); } +}; + + + +} // end of anonymous namespace + + +//----- EffectRewinder -------------------------------------------------------------- + +EffectRewinder::EffectRewinder ( + EventMultiplexer& rEventMultiplexer, + EventQueue& rEventQueue, + UserEventQueue& rUserEventQueue) + : mrEventMultiplexer(rEventMultiplexer), + mrEventQueue(rEventQueue), + mrUserEventQueue(rUserEventQueue), + mpSlideStartHandler(), + mpSlideEndHandler(), + mpAnimationStartHandler(), + mnMainSequenceEffectCount(0), + mpAsynchronousRewindEvent(), + mxCurrentAnimationRootNode(), + mbNonUserTriggeredMainSequenceEffectSeen(false) +{ + initialize(); +} + + + + +void EffectRewinder::initialize (void) +{ + // Add some event handlers so that we are informed when + // a) an animation is started (we then check whether that belongs to a + // main sequence effect and if so, increase the respective counter), + // b,c) a slide was started or ended (in which case the effect counter + // is reset. + + mpAnimationStartHandler.reset( + new RewinderAnimationEventHandler( + ::boost::bind(&EffectRewinder::notifyAnimationStart, this, _1))); + mrEventMultiplexer.addAnimationStartHandler(mpAnimationStartHandler); + + mpSlideStartHandler.reset( + new RewinderEventHandler( + ::boost::bind(&EffectRewinder::resetEffectCount, this))); + mrEventMultiplexer.addSlideStartHandler(mpSlideStartHandler); + + mpSlideEndHandler.reset( + new RewinderEventHandler( + ::boost::bind(&EffectRewinder::resetEffectCount, this))); + mrEventMultiplexer.addSlideEndHandler(mpSlideEndHandler); +} + + + + +EffectRewinder::~EffectRewinder (void) +{ + dispose(); +} + + + + +void EffectRewinder::dispose (void) +{ + if (mpAsynchronousRewindEvent) + { + mpAsynchronousRewindEvent->dispose(); + mpAsynchronousRewindEvent.reset(); + } + + if (mpAnimationStartHandler) + { + mrEventMultiplexer.removeAnimationStartHandler(mpAnimationStartHandler); + mpAnimationStartHandler.reset(); + } + + if (mpSlideStartHandler) + { + mrEventMultiplexer.removeSlideStartHandler(mpSlideStartHandler); + mpSlideStartHandler.reset(); + } + + if (mpSlideEndHandler) + { + mrEventMultiplexer.removeSlideEndHandler(mpSlideEndHandler); + mpSlideEndHandler.reset(); + } +} + + + + +void EffectRewinder::setRootAnimationNode ( + const uno::Reference<animations::XAnimationNode>& xRootNode) +{ + mxCurrentAnimationRootNode = xRootNode; +} + + + + +bool EffectRewinder::rewind ( + const ::boost::shared_ptr<ScreenUpdater::UpdateLock>& rpPaintLock, + const ::boost::function<void(void)>& rSlideRewindFunctor, + const ::boost::function<void(void)>& rPreviousSlideFunctor) +{ + mpPaintLock = rpPaintLock; + + // Do not allow nested rewinds. + if (mpAsynchronousRewindEvent) + { + OSL_ASSERT( ! mpAsynchronousRewindEvent); + return false; + } + + // Abort (and skip over the rest of) any currently active animation. + mrUserEventQueue.callSkipEffectEventHandler(); + mrEventQueue.forceEmpty(); + + const int nSkipCount (mnMainSequenceEffectCount - 1); + if (nSkipCount < 0) + { + if ( ! rPreviousSlideFunctor) + { + OSL_ASSERT(rPreviousSlideFunctor); + return false; + } + + // No main sequence effects to rewind on the current slide. + // Go back to the previous slide. + mpAsynchronousRewindEvent = makeEvent( + ::boost::bind( + &EffectRewinder::asynchronousRewindToPreviousSlide, + this, + rPreviousSlideFunctor), + "EffectRewinder::asynchronousRewindToPreviousSlide"); + } + else + { + // The actual rewinding is done asynchronously so that we can safely + // call other methods. + mpAsynchronousRewindEvent = makeEvent( + ::boost::bind( + &EffectRewinder::asynchronousRewind, + this, + nSkipCount, + true, + rSlideRewindFunctor), + "EffectRewinder::asynchronousRewind"); + } + + if (mpAsynchronousRewindEvent) + mrEventQueue.addEvent(mpAsynchronousRewindEvent); + + return mpAsynchronousRewindEvent.get()!=NULL; +} + + + + +void EffectRewinder::skipAllMainSequenceEffects (void) +{ + // Do not allow nested rewinds. + if (mpAsynchronousRewindEvent) + { + OSL_ASSERT(!mpAsynchronousRewindEvent); + return; + } + + const int nTotalMainSequenceEffectCount (countMainSequenceEffects()); + mpAsynchronousRewindEvent = makeEvent( + ::boost::bind( + &EffectRewinder::asynchronousRewind, + this, + nTotalMainSequenceEffectCount, + false, + ::boost::function<void(void)>()), + "EffectRewinder::asynchronousRewind"); + mrEventQueue.addEvent(mpAsynchronousRewindEvent); +} + + + + +sal_Int32 EffectRewinder::countMainSequenceEffects (void) +{ + // Determine the number of main sequence effects. + sal_Int32 nMainSequenceNodeCount (0); + + ::std::queue<uno::Reference<animations::XAnimationNode> > aNodeQueue; + aNodeQueue.push(mxCurrentAnimationRootNode); + while ( ! aNodeQueue.empty()) + { + const uno::Reference<animations::XAnimationNode> xNode (aNodeQueue.front()); + aNodeQueue.pop(); + + // Does the current node belong to the main sequence? + if (xNode.is()) + { + animations::Event aEvent; + if (xNode->getBegin() >>= aEvent) + if (aEvent.Trigger == animations::EventTrigger::ON_NEXT) + ++nMainSequenceNodeCount; + } + + // If the current node is a container then prepare its children for investigation. + uno::Reference<container::XEnumerationAccess> xEnumerationAccess (xNode, uno::UNO_QUERY); + if (xEnumerationAccess.is()) + { + uno::Reference<container::XEnumeration> xEnumeration ( + xEnumerationAccess->createEnumeration()); + if (xEnumeration.is()) + while (xEnumeration->hasMoreElements()) + { + aNodeQueue.push( + uno::Reference<animations::XAnimationNode>( + xEnumeration->nextElement(), uno::UNO_QUERY)); + } + } + } + + return nMainSequenceNodeCount; + + // // Skip all main sequence nodes. + // SkipSomeMainSequenceEffects(nMainSequenceNodeCount); +} + + + + +void EffectRewinder::skipSomeMainSequenceEffects (sal_Int32 nSkipCount) +{ + while (--nSkipCount >= 0) + skipSingleMainSequenceEffects(); +} + + + + +void EffectRewinder::skipSingleMainSequenceEffects (void) +{ + // This basically just starts the next effect and then skips over its + // animation. + mrEventMultiplexer.notifyNextEffect(); + mrEventQueue.forceEmpty(); + mrUserEventQueue.callSkipEffectEventHandler(); + mrEventQueue.forceEmpty(); +} + + + + +bool EffectRewinder::resetEffectCount (void) +{ + mnMainSequenceEffectCount = 0; + return false; +} + + + + +bool EffectRewinder::notifyAnimationStart (const AnimationNodeSharedPtr& rpNode) +{ + // This notification is only relevant for us when the rpNode belongs to + // the main sequence. + BaseNodeSharedPtr pBaseNode (::boost::dynamic_pointer_cast<BaseNode>(rpNode)); + if ( ! pBaseNode) + return false; + + BaseContainerNodeSharedPtr pParent (pBaseNode->getParentNode()); + if ( ! (pParent && pParent->isMainSequenceRootNode())) + return false; + + // This notification is only relevant for us when the effect is user + // triggered. + bool bIsUserTriggered (false); + + Reference<animations::XAnimationNode> xNode (rpNode->getXAnimationNode()); + if (xNode.is()) + { + animations::Event aEvent; + if ((xNode->getBegin() >>= aEvent)) + bIsUserTriggered = (aEvent.Trigger == animations::EventTrigger::ON_NEXT); + } + + if (bIsUserTriggered) + ++mnMainSequenceEffectCount; + else + mbNonUserTriggeredMainSequenceEffectSeen = true; + + return false; +} + + + + +void EffectRewinder::asynchronousRewind ( + sal_Int32 nEffectCount, + const bool bRedisplayCurrentSlide, + const boost::function<void(void)>& rSlideRewindFunctor) +{ + OSL_ASSERT(mpAsynchronousRewindEvent); + + if (bRedisplayCurrentSlide) + { + mpPaintLock->Activate(); + // Re-display the current slide. + if (rSlideRewindFunctor) + rSlideRewindFunctor(); + mpAsynchronousRewindEvent = makeEvent( + ::boost::bind( + &EffectRewinder::asynchronousRewind, + this, + nEffectCount, + false, + rSlideRewindFunctor), + "EffectRewinder::asynchronousRewind"); + mrEventQueue.addEvent(mpAsynchronousRewindEvent); + } + else + { + // Process initial events and skip any animations that are started + // when the slide is shown. + mbNonUserTriggeredMainSequenceEffectSeen = false; + mrEventQueue.forceEmpty(); + if (mbNonUserTriggeredMainSequenceEffectSeen) + { + mrUserEventQueue.callSkipEffectEventHandler(); + mrEventQueue.forceEmpty(); + } + + while (--nEffectCount >= 0) + skipSingleMainSequenceEffects(); + + mpAsynchronousRewindEvent.reset(); + mpPaintLock.reset(); + } +} + + + + +void EffectRewinder::asynchronousRewindToPreviousSlide ( + const ::boost::function<void(void)>& rSlideRewindFunctor) +{ + OSL_ASSERT(mpAsynchronousRewindEvent); + + mpAsynchronousRewindEvent.reset(); + rSlideRewindFunctor(); +} + + + + +} } // end of namespace ::slideshow::internal diff --git a/slideshow/source/engine/effectrewinder.hxx b/slideshow/source/engine/effectrewinder.hxx new file mode 100644 index 000000000..ee49127e0 --- /dev/null +++ b/slideshow/source/engine/effectrewinder.hxx @@ -0,0 +1,185 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: slideshowimpl.cxx,v $ + * $Revision: 1.10 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef INCLUDED_SLIDESHOW_EFFECT_REWINDER_HXX +#define INCLUDED_SLIDESHOW_EFFECT_REWINDER_HXX + +#include "animationnode.hxx" +#include "eventhandler.hxx" +#include "animationeventhandler.hxx" +#include "event.hxx" +#include "screenupdater.hxx" + +#include <com/sun/star/presentation/XSlideShow.hpp> +#include <boost/scoped_ptr.hpp> +#include <boost/function.hpp> +#include <vector> + +namespace css = ::com::sun::star; + +namespace slideshow { namespace internal { + +class EventMultiplexer; +class EventQueue; +class UserEventQueue; + +/** Rewind single effects of the main effect sequence. A rewind is + initiated by calling the Rewind() method. Part of the processing is + done asynchronously. Multiple EventQueue::update() calls may be + necessary to finish a rewind. + + Remember to call SetRootAnimationNode() when switching to a different + slide so that the EffectRewinder can determine the number of main + sequence effects. +*/ +class EffectRewinder +{ +public: + EffectRewinder ( + EventMultiplexer& rEventMultiplexer, + EventQueue& rEventQueue, + UserEventQueue& rUserEventQueue); + ~EffectRewinder (void); + + /** Call Dispose() before the ownder of an EffectRewinder object dies so + that the EffectRewinder can release all references to the owner. + + */ + void dispose (void); + + /** Store the root node of the animation tree. It is used in + CountMainSequenceEffects() to count the number of main sequence + effects (or effect groups.) + */ + void setRootAnimationNode ( + const css::uno::Reference<css::animations::XAnimationNode>& xRootNode); + + /** Rewind one effect of the main effect sequence. When the current + slide has not effects or no main sequence effect has yet been played + then switch to the previous slide and replay all of its main + sequence effects. + The caller has to pass two functors that redisplay the current slide + or switch to the previous slide so that it does not have to expose + its internals to us. Only one of the two functors is called. + @param rpPaintLock + This paint lock is released after the whole asynchronous + procoess of rewinding the current effect is completed. It + prevents intermediate repaints that would show partial replay + of effects. + @param rSlideRewindFunctor + This functor is called when the current slide is to be + redisplayed. When it is called then the other functor is not + called. + @param rPreviousSlideFunctor + This functor is called to switch to the previous slide. When it + is called then the other functor is not called. + */ + bool rewind ( + const ::boost::shared_ptr<ScreenUpdater::UpdateLock>& rpPaintLock, + const ::boost::function<void(void)>& rSlideRewindFunctor, + const ::boost::function<void(void)>& rPreviousSlideFunctor); + + /** Call this method after gotoPreviousEffect() triggered a slide change + to the previous slide. + */ + void skipAllMainSequenceEffects (void); + +private: + EventMultiplexer& mrEventMultiplexer; + EventQueue& mrEventQueue; + UserEventQueue& mrUserEventQueue; + + EventHandlerSharedPtr mpSlideStartHandler; + EventHandlerSharedPtr mpSlideEndHandler; + AnimationEventHandlerSharedPtr mpAnimationStartHandler; + + /** The number off main sequence effects so far. + */ + sal_Int32 mnMainSequenceEffectCount; + + /** This is the currently scheduled event that executes the asynchronous + part of the effect rewinding. It is also used as flag that prevents + nested rewinds. + */ + EventSharedPtr mpAsynchronousRewindEvent; + + css::uno::Reference<css::animations::XAnimationNode> mxCurrentAnimationRootNode; + ::boost::shared_ptr<ScreenUpdater::UpdateLock> mpPaintLock; + + bool mbNonUserTriggeredMainSequenceEffectSeen; + + void initialize (void); + + bool resetEffectCount (void); + /** Called by listeners when an animation (not necessarily of a main + sequence effect) starts. + */ + bool notifyAnimationStart (const AnimationNodeSharedPtr& rpNode); + + /** Count the number of effects (or effect groups) in the main effect + sequence. + */ + sal_Int32 countMainSequenceEffects (void); + + /** Skip the next main sequence effect. + */ + void skipSingleMainSequenceEffects (void); + + /** Skip the specified number of main sequence effects. + */ + void skipSomeMainSequenceEffects (const sal_Int32 nSkipCount); + + /** Rewind the last effect of the main effect sequence by replaying all + previous effects. + @param nEffectCount + The number of main sequence effects to replay. + @param bRedisplayCurrentSlide + When <TRUE/> then the current slide is redisplayed before the + effects are replayed. + @param rSlideRewindFunctor + This functor is used to redisplay the current slide. + */ + void asynchronousRewind ( + sal_Int32 nEffectCount, + const bool bRedisplayCurrentSlide, + const boost::function<void(void)>& rSlideRewindFunctor); + + /** Go to the previous slide and replay all of its main sequence effects + (or effect groups). + @param rPreviousSlideFunctor + This functor is used to go to the previous slide. + */ + void asynchronousRewindToPreviousSlide ( + const ::boost::function<void(void)>& rPreviousSlideFunctor); +}; + +} } // end of namespace ::slideshow::internal + +#endif diff --git a/slideshow/source/engine/eventmultiplexer.cxx b/slideshow/source/engine/eventmultiplexer.cxx index cf39933c7..822eac50f 100644 --- a/slideshow/source/engine/eventmultiplexer.cxx +++ b/slideshow/source/engine/eventmultiplexer.cxx @@ -368,7 +368,8 @@ void SAL_CALL EventMultiplexerListener::mousePressed( mpEventQueue->addEvent( makeEvent( boost::bind( &EventMultiplexerImpl::mousePressed, mpEventMultiplexer, - e ) ) ); + e ), + "EventMultiplexerImpl::mousePressed") ); } void SAL_CALL EventMultiplexerListener::mouseReleased( @@ -382,7 +383,8 @@ void SAL_CALL EventMultiplexerListener::mouseReleased( mpEventQueue->addEvent( makeEvent( boost::bind( &EventMultiplexerImpl::mouseReleased, mpEventMultiplexer, - e ) ) ); + e ), + "EventMultiplexerImpl::mouseReleased") ); } void SAL_CALL EventMultiplexerListener::mouseEntered( @@ -409,7 +411,8 @@ void SAL_CALL EventMultiplexerListener::mouseDragged( mpEventQueue->addEvent( makeEvent( boost::bind( &EventMultiplexerImpl::mouseDragged, mpEventMultiplexer, - e )) ); + e ), + "EventMultiplexerImpl::mouseDragged") ); } void SAL_CALL EventMultiplexerListener::mouseMoved( @@ -423,7 +426,8 @@ void SAL_CALL EventMultiplexerListener::mouseMoved( mpEventQueue->addEvent( makeEvent( boost::bind( &EventMultiplexerImpl::mouseMoved, mpEventMultiplexer, - e )) ); + e ), + "EventMultiplexerImpl::mouseMoved") ); } @@ -447,7 +451,15 @@ void EventMultiplexerImpl::forEachView( XSlideShowViewFunc pViewMethod ) for( UnoViewVector::const_iterator aIter( mrViewContainer.begin() ), aEnd( mrViewContainer.end() ); aIter != aEnd; ++aIter ) { - ((*aIter)->getUnoView().get()->*pViewMethod)( mxListener.get() ); + uno::Reference<presentation::XSlideShowView> xView ((*aIter)->getUnoView()); + if (xView.is()) + { + (xView.get()->*pViewMethod)( mxListener.get() ); + } + else + { + OSL_ASSERT(xView.is()); + } } } } @@ -519,7 +531,8 @@ void EventMultiplexerImpl::scheduleTick() EventSharedPtr pEvent( makeDelay( boost::bind( &EventMultiplexerImpl::tick, this ), - mnTimeout )); + mnTimeout, + "EventMultiplexerImpl::tick with delay")); // store weak reference to generated event, to notice when // the event queue gets cleansed (we then have to diff --git a/slideshow/source/engine/eventqueue.cxx b/slideshow/source/engine/eventqueue.cxx index cd1eb3789..3fe997e73 100644 --- a/slideshow/source/engine/eventqueue.cxx +++ b/slideshow/source/engine/eventqueue.cxx @@ -35,6 +35,7 @@ #include <canvas/debug.hxx> #include <tools/diagnose_ex.h> #include <canvas/verbosetrace.hxx> +#include "debug.hxx" #include <comphelper/anytostring.hxx> #include <cppuhelper/exc_hlp.hxx> @@ -66,6 +67,7 @@ namespace slideshow : maMutex(), maEvents(), maNextEvents(), + maNextNextEvents(), mpTimer( pPresTimer ) { } @@ -103,6 +105,13 @@ namespace slideshow { ::osl::MutexGuard aGuard( maMutex ); +#if OSL_DEBUG_LEVEL > 1 && defined (SLIDESHOW_ADD_DESCRIPTIONS_TO_EVENTS) + OSL_TRACE("adding at %f event [%s] at %x with delay %f\r", + mpTimer->getElapsedTime(), + OUStringToOString(rEvent->GetDescription(), RTL_TEXTENCODING_UTF8).getStr(), + rEvent.get(), + rEvent->getActivationTime(0.0)); +#endif ENSURE_OR_RETURN( rEvent, "EventQueue::addEvent: event ptr NULL" ); @@ -123,6 +132,14 @@ namespace slideshow bool EventQueue::addEventForNextRound( EventSharedPtr const& rEvent ) { ::osl::MutexGuard aGuard( maMutex ); + +#if OSL_DEBUG_LEVEL > 1 && defined (SLIDESHOW_ADD_DESCRIPTIONS_TO_EVENTS) + OSL_TRACE("adding at %f event [%s] at %x for next round with delay %f\r", + mpTimer->getElapsedTime(), + OUStringToOString(rEvent->GetDescription(), RTL_TEXTENCODING_UTF8).getStr(), + rEvent.get(), + rEvent->getActivationTime(0.0)); +#endif ENSURE_OR_RETURN( rEvent.get() != NULL, "EventQueue::addEvent: event ptr NULL" ); @@ -131,6 +148,30 @@ namespace slideshow mpTimer->getElapsedTime()) ) ); return true; } + + bool EventQueue::addEventWhenQueueIsEmpty (const EventSharedPtr& rpEvent) + { + ::osl::MutexGuard aGuard( maMutex ); + +#if OSL_DEBUG_LEVEL > 1 && defined (SLIDESHOW_ADD_DESCRIPTIONS_TO_EVENTS) + OSL_TRACE("adding at %f event [%s] at %x for execution when queue is empty with delay %f\r", + mpTimer->getElapsedTime(), + OUStringToOString(rpEvent->GetDescription(), RTL_TEXTENCODING_UTF8).getStr(), + rpEvent.get(), + rpEvent->getActivationTime(0.0)); +#endif + + ENSURE_OR_RETURN( + rpEvent.get() != NULL, + "EventQueue::addEvent: event ptr NULL"); + + maNextNextEvents.push( + EventEntry( + rpEvent, + rpEvent->getActivationTime(mpTimer->getElapsedTime()))); + + return true; + } void EventQueue::forceEmpty() { @@ -157,12 +198,23 @@ namespace slideshow maEvents.push(*iPos); } EventEntryVector().swap( maNextEvents ); - + // perform topmost, ready-to-execute event // ======================================= const double nCurrTime( mpTimer->getElapsedTime() ); - + + // When maEvents does not contain any events that are due now + // then process one event from maNextNextEvents. + if (!maNextNextEvents.empty() + && !bFireAllEvents + && (maEvents.empty() || maEvents.top().nTime > nCurrTime)) + { + const EventEntry aEvent (maNextNextEvents.top()); + maNextNextEvents.pop(); + maEvents.push(aEvent); + } + // process ready/elapsed events. Note that the 'perceived' // current time remains constant for this loop, thus we're // processing only those events which where ready when we @@ -189,6 +241,14 @@ namespace slideshow event.pEvent.get(), event.pEvent->getActivationTime(0.0) ); #endif +#if OSL_DEBUG_LEVEL > 1 && defined (SLIDESHOW_ADD_DESCRIPTIONS_TO_EVENTS) + OSL_TRACE("firing at %f event [%s] at %x with delay %f\r", + mpTimer->getElapsedTime(), + OUStringToOString(event.pEvent->GetDescription(), + RTL_TEXTENCODING_UTF8).getStr(), + event.pEvent.get(), + event.pEvent->getActivationTime(0.0)); +#endif event.pEvent->fire(); } @@ -243,7 +303,7 @@ namespace slideshow { ::osl::MutexGuard aGuard( maMutex ); - return maEvents.empty(); + return maEvents.empty() && maNextEvents.empty() && maNextNextEvents.empty(); } double EventQueue::nextTimeout() const @@ -251,9 +311,16 @@ namespace slideshow ::osl::MutexGuard aGuard( maMutex ); // return time for next entry (if any) - return isEmpty() ? - ::std::numeric_limits<double>::max() : - maEvents.top().nTime - mpTimer->getElapsedTime(); + double nTimeout (::std::numeric_limits<double>::max()); + const double nCurrentTime (mpTimer->getElapsedTime()); + if ( ! maEvents.empty()) + nTimeout = maEvents.top().nTime - nCurrentTime; + if ( ! maNextEvents.empty()) + nTimeout = ::std::min(nTimeout, maNextEvents.front().nTime - nCurrentTime); + if ( ! maNextNextEvents.empty()) + nTimeout = ::std::min(nTimeout, maNextNextEvents.top().nTime - nCurrentTime); + + return nTimeout; } void EventQueue::clear() @@ -263,6 +330,9 @@ namespace slideshow // TODO(P1): Maybe a plain vector and vector.swap will // be faster here. Profile. maEvents = ImplQueueType(); + + maNextEvents.clear(); + maNextNextEvents = ImplQueueType(); } } } diff --git a/slideshow/source/engine/makefile.mk b/slideshow/source/engine/makefile.mk index 6d4b182f2..2b640b6e8 100644 --- a/slideshow/source/engine/makefile.mk +++ b/slideshow/source/engine/makefile.mk @@ -47,6 +47,8 @@ CDEFS+=-DENABLE_PRESENTER_EXTRA_UI # --- Common ---------------------------------------------------------- +ENVCFLAGS += -DBOOST_SPIRIT_USE_OLD_NAMESPACE + .IF "$(OS)"=="SOLARIS" .IF "$(CCNUMVER)"=="00050009" # SunStudio12: anachronism warning in boost code (smilfunctionparser.cxx) @@ -74,6 +76,7 @@ SLOFILES = $(SLO)$/activitiesqueue.obj \ $(SLO)$/attributemap.obj \ $(SLO)$/color.obj \ $(SLO)$/delayevent.obj \ + $(SLO)$/effectrewinder.obj \ $(SLO)$/eventmultiplexer.obj \ $(SLO)$/eventqueue.obj \ $(SLO)$/expressionnodefactory.obj \ @@ -91,7 +94,8 @@ SLOFILES = $(SLO)$/activitiesqueue.obj \ $(SLO)$/unoviewcontainer.obj \ $(SLO)$/usereventqueue.obj \ $(SLO)$/waitsymbol.obj \ - $(SLO)$/wakeupevent.obj + $(SLO)$/wakeupevent.obj \ + $(SLO)$/debug.obj .IF "$(debug)"!="" || "$(DEBUG)"!="" SLOFILES += $(SLO)$/sp_debug.obj diff --git a/slideshow/source/engine/rehearsetimingsactivity.cxx b/slideshow/source/engine/rehearsetimingsactivity.cxx index 26fb0c5ea..c885e7ac0 100644 --- a/slideshow/source/engine/rehearsetimingsactivity.cxx +++ b/slideshow/source/engine/rehearsetimingsactivity.cxx @@ -72,6 +72,9 @@ public: WakeupEvent( boost::shared_ptr< ::canvas::tools::ElapsedTime > const& pTimeBase, ActivitySharedPtr const& rActivity, ActivitiesQueue & rActivityQueue ) : +#if OSL_DEBUG_LEVEL > 1 + Event(::rtl::OUString::createFromAscii("WakeupEvent")), +#endif maTimer(pTimeBase), mnNextTime(0.0), mpActivity(rActivity), diff --git a/slideshow/source/engine/screenupdater.cxx b/slideshow/source/engine/screenupdater.cxx index bf3ca0c5b..8cfaaddf2 100644 --- a/slideshow/source/engine/screenupdater.cxx +++ b/slideshow/source/engine/screenupdater.cxx @@ -36,6 +36,19 @@ #include <vector> #include <algorithm> +namespace { + class UpdateLock : public ::slideshow::internal::ScreenUpdater::UpdateLock + { + public: + UpdateLock (::slideshow::internal::ScreenUpdater& rUpdater, const bool bStartLocked); + virtual ~UpdateLock (void); + virtual void Activate (void); + private: + ::slideshow::internal::ScreenUpdater& mrUpdater; + bool mbIsActivated; + }; +} + namespace slideshow { namespace internal @@ -64,12 +77,16 @@ namespace internal /// True, if at least one notifyUpdate() call had bViewClobbered set bool mbViewClobbered; + /// The screen is updated only when mnLockCount==0 + sal_Int32 mnLockCount; + explicit ImplScreenUpdater( UnoViewContainer const& rViewContainer ) : maUpdaters(), maViewUpdateRequests(), mrViewContainer(rViewContainer), mbUpdateAllRequest(false), - mbViewClobbered(false) + mbViewClobbered(false), + mnLockCount(0) {} }; @@ -100,6 +117,9 @@ namespace internal void ScreenUpdater::commitUpdates() { + if (mpImpl->mnLockCount > 0) + return; + // cases: // // (a) no update necessary at all @@ -178,6 +198,9 @@ namespace internal void ScreenUpdater::requestImmediateUpdate() { + if (mpImpl->mnLockCount > 0) + return; + // TODO(F2): This will interfere with other updates, since it // happens out-of-sync with main animation loop. Might cause // artifacts. @@ -186,5 +209,63 @@ namespace internal boost::mem_fn(&View::updateScreen) ); } + void ScreenUpdater::lockUpdates (void) + { + ++mpImpl->mnLockCount; + OSL_ASSERT(mpImpl->mnLockCount>0); + } + + void ScreenUpdater::unlockUpdates (void) + { + OSL_ASSERT(mpImpl->mnLockCount>0); + if (mpImpl->mnLockCount > 0) + { + --mpImpl->mnLockCount; + if (mpImpl->mnLockCount) + commitUpdates(); + } + } + + ::boost::shared_ptr<ScreenUpdater::UpdateLock> ScreenUpdater::createLock (const bool bStartLocked) + { + return ::boost::shared_ptr<ScreenUpdater::UpdateLock>(new ::UpdateLock(*this, bStartLocked)); + } + + } // namespace internal } // namespace slideshow + +namespace { + +UpdateLock::UpdateLock ( + ::slideshow::internal::ScreenUpdater& rUpdater, + const bool bStartLocked) + : mrUpdater(rUpdater), + mbIsActivated(false) +{ + if (bStartLocked) + Activate(); +} + + + + +UpdateLock::~UpdateLock (void) +{ + if (mbIsActivated) + mrUpdater.unlockUpdates(); +} + + + + +void UpdateLock::Activate (void) +{ + if ( ! mbIsActivated) + { + mbIsActivated = true; + mrUpdater.lockUpdates(); + } +} + +} diff --git a/slideshow/source/engine/shapes/drawshape.cxx b/slideshow/source/engine/shapes/drawshape.cxx index fff5218df..c94b73f3a 100644 --- a/slideshow/source/engine/shapes/drawshape.cxx +++ b/slideshow/source/engine/shapes/drawshape.cxx @@ -262,7 +262,8 @@ namespace slideshow void DrawShape::updateStateIds() const { - // update the states, we've just redrawn + // Update the states, we've just redrawn or created a new + // attribute layer. if( mpAttributeLayer ) { mnAttributeTransformationState = mpAttributeLayer->getTransformationState(); @@ -1277,6 +1278,9 @@ namespace slideshow // create new layer, with last as its new child mpAttributeLayer.reset( new ShapeAttributeLayer( mpAttributeLayer ) ); + // Update the local state ids to reflect those of the new layer. + updateStateIds(); + return mpAttributeLayer; } diff --git a/slideshow/source/engine/shapes/viewmediashape.cxx b/slideshow/source/engine/shapes/viewmediashape.cxx index d0876d219..609566f73 100644 --- a/slideshow/source/engine/shapes/viewmediashape.cxx +++ b/slideshow/source/engine/shapes/viewmediashape.cxx @@ -71,6 +71,7 @@ #include "viewmediashape.hxx" #include "mediashape.hxx" #include "tools.hxx" +#include "unoview.hxx" using namespace ::com::sun::star; @@ -88,12 +89,19 @@ namespace slideshow mxShape( rxShape ), mxPlayer(), mxPlayerWindow(), - mxComponentContext( rxContext ) + mxComponentContext( rxContext ), + mbIsSoundEnabled(true) { ENSURE_OR_THROW( mxShape.is(), "ViewMediaShape::ViewMediaShape(): Invalid Shape" ); ENSURE_OR_THROW( mpViewLayer, "ViewMediaShape::ViewMediaShape(): Invalid View" ); ENSURE_OR_THROW( mpViewLayer->getCanvas(), "ViewMediaShape::ViewMediaShape(): Invalid ViewLayer canvas" ); ENSURE_OR_THROW( mxComponentContext.is(), "ViewMediaShape::ViewMediaShape(): Invalid component context" ); + + UnoViewSharedPtr pUnoView (::boost::dynamic_pointer_cast<UnoView>(rViewLayer)); + if (pUnoView) + { + mbIsSoundEnabled = pUnoView->isSoundEnabled(); + } } // --------------------------------------------------------------------- @@ -352,7 +360,7 @@ namespace slideshow getPropertyValue( bMute, rxProps, ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Mute" ))); - mxPlayer->setMute( bMute ); + mxPlayer->setMute( bMute || !mbIsSoundEnabled); sal_Int16 nVolumeDB(0); getPropertyValue( nVolumeDB, diff --git a/slideshow/source/engine/shapes/viewmediashape.hxx b/slideshow/source/engine/shapes/viewmediashape.hxx index 4b26359b6..68611e85d 100644 --- a/slideshow/source/engine/shapes/viewmediashape.hxx +++ b/slideshow/source/engine/shapes/viewmediashape.hxx @@ -166,6 +166,7 @@ namespace slideshow ::com::sun::star::uno::Reference< ::com::sun::star::media::XPlayer > mxPlayer; ::com::sun::star::uno::Reference< ::com::sun::star::media::XPlayerWindow > mxPlayerWindow; ::com::sun::star::uno::Reference< ::com::sun::star::uno::XComponentContext> mxComponentContext; + bool mbIsSoundEnabled; }; typedef ::boost::shared_ptr< ViewMediaShape > ViewMediaShapeSharedPtr; diff --git a/slideshow/source/engine/slide/layermanager.cxx b/slideshow/source/engine/slide/layermanager.cxx index 999d09843..f55bef6ba 100644 --- a/slideshow/source/engine/slide/layermanager.cxx +++ b/slideshow/source/engine/slide/layermanager.cxx @@ -67,11 +67,11 @@ namespace slideshow { LayerSharedPtr pCurrLayer; ViewLayerSharedPtr pCurrViewLayer; - LayerShapeSet::const_iterator aIter( maAllShapes.begin() ); - const LayerShapeSet::const_iterator aEnd ( maAllShapes.end() ); + LayerShapeMap::const_iterator aIter( maAllShapes.begin() ); + const LayerShapeMap::const_iterator aEnd ( maAllShapes.end() ); while( aIter != aEnd ) { - LayerSharedPtr pLayer = aIter->mpLayer.lock(); + LayerSharedPtr pLayer = aIter->second.lock(); if( pLayer && pLayer != pCurrLayer ) { pCurrLayer = pLayer; @@ -79,7 +79,7 @@ namespace slideshow } if( pCurrViewLayer ) - shapeFunc(aIter->mpShape,pCurrViewLayer); + shapeFunc(aIter->first,pCurrViewLayer); ++aIter; } @@ -164,8 +164,17 @@ namespace slideshow std::for_each(maAllShapes.begin(), maAllShapes.end(), boost::bind( &Shape::clearAllViewLayers, - boost::bind( &ShapeEntry::getShape, + boost::bind( std::select1st<LayerShapeMap::value_type>(), _1 ))); + + for (LayerShapeMap::iterator + iShape (maAllShapes.begin()), + iEnd (maAllShapes.end()); + iShape!=iEnd; + ++iShape) + { + iShape->second.reset(); + } if( bMoreThanOneLayer ) maLayers.erase(maLayers.begin()+1, @@ -265,8 +274,7 @@ namespace slideshow std::for_each( maAllShapes.begin(), maAllShapes.end(), boost::bind(&Shape::render, - boost::bind(&ShapeEntry::getShape, - _1)) ); + boost::bind( ::std::select1st<LayerShapeMap::value_type>(), _1)) ); } void LayerManager::addShape( const ShapeSharedPtr& rShape ) @@ -287,13 +295,11 @@ namespace slideshow implAddShape( rShape ); } - void LayerManager::putShape2BackgroundLayer( const ShapeEntry& rShapeEntry ) + void LayerManager::putShape2BackgroundLayer( LayerShapeMap::value_type& rShapeEntry ) { LayerSharedPtr& rBgLayer( maLayers.front() ); - rBgLayer->setShapeViews(rShapeEntry.mpShape); - // changing a part of the ShapeEntry irrelevant for the - // set sort order - const_cast<ShapeEntry&>(rShapeEntry).mpLayer = rBgLayer; + rBgLayer->setShapeViews(rShapeEntry.first); + rShapeEntry.second = rBgLayer; } void LayerManager::implAddShape( const ShapeSharedPtr& rShape ) @@ -301,16 +307,16 @@ namespace slideshow OSL_ASSERT( !maLayers.empty() ); // always at least background layer ENSURE_OR_THROW( rShape, "LayerManager::implAddShape(): invalid Shape" ); - ShapeEntry aShapeEntry(rShape); + LayerShapeMap::value_type aValue (rShape, LayerWeakPtr()); - OSL_ASSERT( maAllShapes.find(aShapeEntry) == maAllShapes.end() ); // shape must not be added already + OSL_ASSERT( maAllShapes.find(rShape) == maAllShapes.end() ); // shape must not be added already mbLayerAssociationDirty = true; if( mbDisableAnimationZOrder ) putShape2BackgroundLayer( - *maAllShapes.insert(aShapeEntry).first ); + *maAllShapes.insert(aValue).first ); else - maAllShapes.insert(aShapeEntry); + maAllShapes.insert(aValue); // update shape, it's just added and not yet painted if( rShape->isVisible() ) @@ -323,8 +329,7 @@ namespace slideshow if( maXShapeHash.erase( rShape->getXShape() ) == 0 ) return false; // shape not in map - OSL_ASSERT( maAllShapes.find( - ShapeEntry(rShape)) != maAllShapes.end() ); + OSL_ASSERT( maAllShapes.find(rShape) != maAllShapes.end() ); implRemoveShape( rShape ); @@ -336,9 +341,7 @@ namespace slideshow OSL_ASSERT( !maLayers.empty() ); // always at least background layer ENSURE_OR_THROW( rShape, "LayerManager::implRemoveShape(): invalid Shape" ); - const LayerShapeSet::iterator aShapeEntry( - maAllShapes.find( - ShapeEntry(rShape)) ); + const LayerShapeMap::iterator aShapeEntry( maAllShapes.find(rShape) ); if( aShapeEntry == maAllShapes.end() ) return; @@ -354,7 +357,7 @@ namespace slideshow (rShape->isVisible() && !rShape->isBackgroundDetached()) ) { - LayerSharedPtr pLayer = aShapeEntry->mpLayer.lock(); + LayerSharedPtr pLayer = aShapeEntry->second.lock(); if( pLayer ) { // store area early, once the shape is removed from @@ -419,8 +422,7 @@ namespace slideshow if( rOrigShape->revokeSubset( rSubsetShape ) ) { - OSL_ASSERT( maAllShapes.find( - ShapeEntry(rSubsetShape)) != maAllShapes.end() ); + OSL_ASSERT( maAllShapes.find(rSubsetShape) != maAllShapes.end() ); implRemoveShape( rSubsetShape ); @@ -584,11 +586,11 @@ namespace slideshow bool bIsCurrLayerUpdating(false); Layer::EndUpdater aEndUpdater; LayerSharedPtr pCurrLayer; - LayerShapeSet::const_iterator aIter( maAllShapes.begin() ); - const LayerShapeSet::const_iterator aEnd ( maAllShapes.end() ); + LayerShapeMap::const_iterator aIter( maAllShapes.begin() ); + const LayerShapeMap::const_iterator aEnd ( maAllShapes.end() ); while( aIter != aEnd ) { - LayerSharedPtr pLayer = aIter->mpLayer.lock(); + LayerSharedPtr pLayer = aIter->second.lock(); if( pLayer != pCurrLayer ) { pCurrLayer = pLayer; @@ -599,10 +601,10 @@ namespace slideshow } if( bIsCurrLayerUpdating && - !aIter->mpShape->isBackgroundDetached() && - pCurrLayer->isInsideUpdateArea(aIter->mpShape) ) + !aIter->first->isBackgroundDetached() && + pCurrLayer->isInsideUpdateArea(aIter->first) ) { - if( !aIter->mpShape->render() ) + if( !aIter->first->render() ) bRet = false; } @@ -694,8 +696,8 @@ namespace slideshow bool bRet( true ); ViewLayerSharedPtr pTmpLayer( new DummyLayer( rTargetCanvas ) ); - LayerShapeSet::const_iterator aIter( maAllShapes.begin() ); - const LayerShapeSet::const_iterator aEnd ( maAllShapes.end() ); + LayerShapeMap::const_iterator aIter( maAllShapes.begin() ); + const LayerShapeMap::const_iterator aEnd ( maAllShapes.end() ); while( aIter != aEnd ) { try @@ -705,11 +707,11 @@ namespace slideshow // ViewLayer. Since we add the shapes in the // maShapeSet order (which is also the render order), // this is equivalent to a subsequent render() call) - aIter->mpShape->addViewLayer( pTmpLayer, - true ); + aIter->first->addViewLayer( pTmpLayer, + true ); // and remove again, this is only temporary - aIter->mpShape->removeViewLayer( pTmpLayer ); + aIter->first->removeViewLayer( pTmpLayer ); } catch( uno::Exception& ) { @@ -735,21 +737,19 @@ namespace slideshow OSL_ASSERT( !maLayers.empty() ); // always at least background layer ENSURE_OR_THROW( rShape, "LayerManager::addUpdateArea(): invalid Shape" ); - const LayerShapeSet::const_iterator aShapeEntry( - maAllShapes.find( - ShapeEntry(rShape)) ); + const LayerShapeMap::const_iterator aShapeEntry( maAllShapes.find(rShape) ); if( aShapeEntry == maAllShapes.end() ) return; - LayerSharedPtr pLayer = aShapeEntry->mpLayer.lock(); + LayerSharedPtr pLayer = aShapeEntry->second.lock(); if( pLayer ) pLayer->addUpdateRange( rShape->getUpdateArea() ); } void LayerManager::commitLayerChanges( std::size_t nCurrLayerIndex, - LayerShapeSet::const_iterator aFirstLayerShape, - LayerShapeSet::const_iterator aEndLayerShapes ) + LayerShapeMap::const_iterator aFirstLayerShape, + LayerShapeMap::const_iterator aEndLayerShapes ) { const bool bLayerExists( maLayers.size() > nCurrLayerIndex ); if( bLayerExists ) @@ -768,8 +768,8 @@ namespace slideshow // render and remove from update set while( aFirstLayerShape != aEndLayerShapes ) { - maUpdateShapes.erase(aFirstLayerShape->mpShape); - aFirstLayerShape->mpShape->render(); + maUpdateShapes.erase(aFirstLayerShape->first); + aFirstLayerShape->first->render(); ++aFirstLayerShape; } } @@ -825,13 +825,13 @@ namespace slideshow std::size_t nCurrLayerIndex(0); bool bIsBackgroundLayer(true); bool bLastWasBackgroundDetached(false); // last shape sprite state - LayerShapeSet::iterator aCurrShapeEntry( maAllShapes.begin() ); - LayerShapeSet::iterator aCurrLayerFirstShapeEntry( maAllShapes.begin() ); - const LayerShapeSet::iterator aEndShapeEntry ( maAllShapes.end() ); + LayerShapeMap::iterator aCurrShapeEntry( maAllShapes.begin() ); + LayerShapeMap::iterator aCurrLayerFirstShapeEntry( maAllShapes.begin() ); + const LayerShapeMap::iterator aEndShapeEntry ( maAllShapes.end() ); ShapeUpdateSet aUpdatedShapes; // shapes that need update while( aCurrShapeEntry != aEndShapeEntry ) { - const ShapeSharedPtr pCurrShape( aCurrShapeEntry->mpShape ); + const ShapeSharedPtr pCurrShape( aCurrShapeEntry->first ); const bool bThisIsBackgroundDetached( pCurrShape->isBackgroundDetached() ); @@ -851,7 +851,7 @@ namespace slideshow bIsBackgroundLayer = false; if( aWeakLayers.size() <= nCurrLayerIndex || - aWeakLayers.at(nCurrLayerIndex) != aCurrShapeEntry->mpLayer ) + aWeakLayers.at(nCurrLayerIndex) != aCurrShapeEntry->second ) { // no more layers left, or shape was not // member of this layer - create a new one @@ -868,7 +868,7 @@ namespace slideshow // above invalidates iterators LayerSharedPtr& rCurrLayer( maLayers.at(nCurrLayerIndex) ); LayerWeakPtr& rCurrWeakLayer( aWeakLayers.at(nCurrLayerIndex) ); - if( rCurrWeakLayer != aCurrShapeEntry->mpLayer ) + if( rCurrWeakLayer != aCurrShapeEntry->second ) { // mismatch: shape is not contained in current // layer - move shape to that layer, then. @@ -879,7 +879,7 @@ namespace slideshow // non-sprite shape if( !bThisIsBackgroundDetached && pCurrShape->isVisible() ) { - LayerSharedPtr pOldLayer( aCurrShapeEntry->mpLayer.lock() ); + LayerSharedPtr pOldLayer( aCurrShapeEntry->second.lock() ); if( pOldLayer ) { // old layer still valid? then we need to @@ -894,10 +894,7 @@ namespace slideshow maUpdateShapes.insert( pCurrShape ); } - // std::set iterators are const for a reason - but - // here, we need modify an aspect of the - // ShapeEntry that has no influence on sort order - const_cast<ShapeEntry&>(*aCurrShapeEntry).mpLayer = rCurrWeakLayer; + aCurrShapeEntry->second = rCurrWeakLayer; } // update layerbounds regardless of the fact that the diff --git a/slideshow/source/engine/slide/layermanager.hxx b/slideshow/source/engine/slide/layermanager.hxx index 8dc068fb4..083ab8194 100644 --- a/slideshow/source/engine/slide/layermanager.hxx +++ b/slideshow/source/engine/slide/layermanager.hxx @@ -254,33 +254,18 @@ namespace slideshow hash< ::com::sun::star::uno::Reference< ::com::sun::star::drawing::XShape > > > XShapeHash; - /** Element of all-shapes set - */ - struct ShapeEntry + class ShapeComparator { - /// Shape this entry stands for - ShapeSharedPtr mpShape; - - /// Layer this shape is currently displayed on - LayerWeakPtr mpLayer; - - explicit ShapeEntry( ShapeSharedPtr const& rShape ) : - mpShape(rShape), - mpLayer() - {} - - ShapeSharedPtr const& getShape() const { return mpShape; } - - bool operator<( const ShapeEntry& rRHS ) const + public: + bool operator() (const ShapeSharedPtr& rpS1, const ShapeSharedPtr& rpS2 ) const { - return Shape::lessThanShape::compare(mpShape.get(), - rRHS.mpShape.get()); + return Shape::lessThanShape::compare(rpS1.get(), rpS2.get()); } }; - /** Set of all shapes */ - typedef ::std::set< ShapeEntry > LayerShapeSet; + private: + typedef ::std::map< ShapeSharedPtr, LayerWeakPtr, ShapeComparator > LayerShapeMap; typedef ::std::set< ShapeSharedPtr > ShapeUpdateSet; @@ -309,12 +294,12 @@ namespace slideshow denoting one-behind-the-last shape of nCurrLayerIndex */ void commitLayerChanges( std::size_t nCurrLayerIndex, - LayerShapeSet::const_iterator aFirstLayerShape, - LayerShapeSet::const_iterator aEndLayerShapes ); + LayerShapeMap::const_iterator aFirstLayerShape, + LayerShapeMap::const_iterator aEndLayerShapes ); /** Init Shape layers with background layer. */ - void putShape2BackgroundLayer( const ShapeEntry& rShapeEntry ); + void putShape2BackgroundLayer( LayerShapeMap::value_type& rShapeEntry ); /** Commits any pending layer reorg, due to shapes either entering or leaving animation mode @@ -364,7 +349,7 @@ namespace slideshow for buffering animation enable/disable changes, and shape update requests. */ - LayerShapeSet maAllShapes; + LayerShapeMap maAllShapes; /** Set of shapes that have requested an update diff --git a/slideshow/source/engine/slideshowimpl.cxx b/slideshow/source/engine/slideshowimpl.cxx index e9014bcd3..b2a3ca50a 100644 --- a/slideshow/source/engine/slideshowimpl.cxx +++ b/slideshow/source/engine/slideshowimpl.cxx @@ -73,6 +73,7 @@ #include <com/sun/star/animations/TransitionType.hpp> #include <com/sun/star/animations/TransitionSubType.hpp> #include <com/sun/star/presentation/XSlideShow.hpp> +#include <com/sun/star/presentation/XSlideShowListener.hpp> #include <com/sun/star/lang/XServiceInfo.hpp> #include <com/sun/star/lang/XServiceName.hpp> #include <com/sun/star/lang/XComponent.hpp> @@ -105,6 +106,7 @@ #include "slidebitmap.hxx" #include "rehearsetimingsactivity.hxx" #include "waitsymbol.hxx" +#include "effectrewinder.hxx" #include "framerate.hxx" #include <boost/noncopyable.hpp> @@ -123,6 +125,73 @@ using namespace ::slideshow::internal; namespace { +/** During animations the update() method tells its caller to call it as + soon as possible. This gives us more time to render the next frame and + still maintain a steady frame rate. This class is responsible for + synchronizing the display of new frames and thus keeping the frame rate + steady. +*/ +class FrameSynchronization +{ +public: + /** Create new object with a predefined duration between two frames. + @param nFrameDuration + The preferred duration between the display of two frames in + seconds. + */ + FrameSynchronization (const double nFrameDuration); + + /** Set the current time as the time at which the current frame is + displayed. From this the target time of the next frame is derived. + */ + void MarkCurrentFrame (void); + + /** When there is time left until the next frame is due then wait. + Otherwise return without delay. + */ + void Synchronize (void); + + /** Activate frame synchronization when an animation is active and + frames are to be displayed in a steady rate. While active + Synchronize() will wait until the frame duration time has passed. + */ + void Activate (void); + + /** Deactivate frame sychronization when no animation is active and the + time between frames depends on user actions and other external + sources. While deactivated Synchronize() will return without delay. + */ + void Deactivate (void); + + /** Return the current time of the timer. It is not synchronized with + any other timer so its absolute values are of no concern. Typically + used during debugging to measure durations. + */ + double GetCurrentTime (void) const; + +private: + /** The timer that is used for synchronization is independent from the + one used by SlideShowImpl: it is not paused or modified by + animations. + */ + canvas::tools::ElapsedTime maTimer; + /** Time between the display of frames. Enforced only when mbIsActive + is <TRUE/>. + */ + const double mnFrameDuration; + /** Time (of maTimer) when the next frame shall be displayed. + Synchronize() will wait until this time. + */ + double mnNextFrameTargetTime; + /** Synchronize() will wait only when this flag is <TRUE/>. Otherwise + it returns immediately. + */ + bool mbIsActive; +}; + + + + /****************************************************************************** SlideShowImpl @@ -217,7 +286,7 @@ public: This method notifies the end of the third phase. */ - void notifySlideEnded(); + void notifySlideEnded (const bool bReverse); /** Notification from eventmultiplexer that a hyperlink has been clicked. @@ -232,6 +301,7 @@ public: private: // XSlideShow: virtual sal_Bool SAL_CALL nextEffect() throw (uno::RuntimeException); + virtual sal_Bool SAL_CALL previousEffect() throw (uno::RuntimeException); virtual sal_Bool SAL_CALL startShapeActivity( uno::Reference<drawing::XShape> const& xShape ) throw (uno::RuntimeException); @@ -284,6 +354,12 @@ private: virtual bool requestCursor( sal_Int16 nCursorShape ); virtual void resetCursor(); + /** This is somewhat similar to displaySlide when called for the current + slide. It has been simplified to take advantage of that no slide + change takes place. Furthermore it does not show the slide + transition. + */ + void redisplayCurrentSlide (void); protected: // WeakComponentImplHelperBase @@ -343,12 +419,32 @@ private: const SlideSharedPtr& rEnteringSlide, const EventSharedPtr& rTransitionEndEvent ); - /// Display/hide wait symbol on all views - void setWaitState( bool bOn ); + /** Request/release the wait symbol. The wait symbol is displayed when + there are more requests then releases. Locking the wait symbol + helps to avoid intermediate repaints. + + Do not call this method directly. Use WaitSymbolLock instead. + */ + void requestWaitSymbol (void); + void releaseWaitSymbol (void); + + class WaitSymbolLock {public: + WaitSymbolLock(SlideShowImpl& rSlideShowImpl) : mrSlideShowImpl(rSlideShowImpl) + { mrSlideShowImpl.requestWaitSymbol(); } + ~WaitSymbolLock(void) + { mrSlideShowImpl.releaseWaitSymbol(); } + private: SlideShowImpl& mrSlideShowImpl; + }; + /// Filter requested cursor shape against hard slideshow cursors (wait, etc.) sal_Int16 calcActiveCursor( sal_Int16 nCursorShape ) const; + /** This method is called asynchronously to finish the rewinding of an + effect to the previous slide that was initiated earlier. + */ + void rewindEffectToPreviousSlide (void); + /// all registered views UnoViewContainer maViewContainer; @@ -409,7 +505,7 @@ private: sal_Int16 mnCurrentCursor; - bool mbWaitState; + sal_Int32 mnWaitSymbolRequestCount; bool mbAutomaticAdvancementMode; bool mbImageAnimationsAllowed; bool mbNoSlideTransitions; @@ -418,6 +514,9 @@ private: bool mbShowPaused; bool mbSlideShowIdle; bool mbDisableAnimationZOrder; + + EffectRewinder maEffectRewinder; + FrameSynchronization maFrameSynchronization; }; @@ -452,10 +551,14 @@ struct SlideShowImpl::SeparateListenerImpl : public EventHandler, // directly, but queue an event. handleEvent() // might be called from e.g. // showNext(), and notifySlideAnimationsEnded() must not be called - // in recursion. - mrEventQueue.addEvent( - makeEvent( boost::bind( &SlideShowImpl::notifySlideAnimationsEnded, - boost::ref(mrShow) ))); + // in recursion. Note that the event is scheduled for the next + // frame so that its expensive execution does not come in between + // sprite hiding and shape redraw (at the end of the animation of a + // shape), which would cause a flicker. + mrEventQueue.addEventForNextRound( + makeEvent( + boost::bind( &SlideShowImpl::notifySlideAnimationsEnded, boost::ref(mrShow) ), + "SlideShowImpl::notifySlideAnimationsEnded")); return true; } @@ -511,7 +614,7 @@ SlideShowImpl::SlideShowImpl( mxDrawPagesSupplier(), mxPrefetchAnimationNode(), mnCurrentCursor(awt::SystemPointer::ARROW), - mbWaitState(false), + mnWaitSymbolRequestCount(0), mbAutomaticAdvancementMode(false), mbImageAnimationsAllowed( true ), mbNoSlideTransitions( false ), @@ -519,7 +622,10 @@ SlideShowImpl::SlideShowImpl( mbForceManualAdvance( false ), mbShowPaused( false ), mbSlideShowIdle( true ), - mbDisableAnimationZOrder( false ) + mbDisableAnimationZOrder( false ), + maEffectRewinder(maEventMultiplexer, maEventQueue, maUserEventQueue), + maFrameSynchronization(1.0 / FrameRate::PreferredFramesPerSecond) + { // keep care not constructing any UNO references to this inside ctor, // shift that code to create()! @@ -559,6 +665,8 @@ void SlideShowImpl::disposing() { osl::MutexGuard const guard( m_aMutex ); + maEffectRewinder.dispose(); + // stop slide transition sound, if any: stopSlideTransitionSound(); @@ -659,7 +767,7 @@ ActivitySharedPtr SlideShowImpl::createSlideTransition( const uno::Reference< drawing::XDrawPage >& xDrawPage, const SlideSharedPtr& rLeavingSlide, const SlideSharedPtr& rEnteringSlide, - const EventSharedPtr& rTransitionEndEvent ) + const EventSharedPtr& rTransitionEndEvent) { ENSURE_OR_THROW( !maViewContainer.empty(), "createSlideTransition(): No views" ); @@ -744,7 +852,7 @@ ActivitySharedPtr SlideShowImpl::createSlideTransition( bTransitionDirection, aTransitionFadeColor, resetSlideTransitionSound( aSound, bLoopSound ) )); - + if( !pTransition ) return ActivitySharedPtr(); // no transition effect has been // generated. Normally, that means @@ -779,7 +887,8 @@ ActivitySharedPtr SlideShowImpl::createSlideTransition( &::slideshow::internal::Animation::prefetch, pTransition, AnimatableShapeSharedPtr(), - ShapeAttributeLayerSharedPtr()))); + ShapeAttributeLayerSharedPtr()), + "Animation::prefetch")); return ActivitySharedPtr( ActivitiesFactory::createSimpleActivity( @@ -856,20 +965,43 @@ SlideSharedPtr SlideShowImpl::makeSlide( return pSlide; } -void SlideShowImpl::setWaitState( bool bOn ) +void SlideShowImpl::requestWaitSymbol (void) { - mbWaitState = bOn; - if( !mpWaitSymbol ) // fallback to cursor - requestCursor(awt::SystemPointer::WAIT); - else if( mbWaitState ) - mpWaitSymbol->show(); - else - mpWaitSymbol->hide(); + ++mnWaitSymbolRequestCount; + OSL_ASSERT(mnWaitSymbolRequestCount>0); + + if (mnWaitSymbolRequestCount == 1) + { + if( !mpWaitSymbol ) + { + // fall back to cursor + requestCursor(calcActiveCursor(mnCurrentCursor)); + } + else + mpWaitSymbol->show(); + } +} + +void SlideShowImpl::releaseWaitSymbol (void) +{ + --mnWaitSymbolRequestCount; + OSL_ASSERT(mnWaitSymbolRequestCount>=0); + + if (mnWaitSymbolRequestCount == 0) + { + if( !mpWaitSymbol ) + { + // fall back to cursor + requestCursor(calcActiveCursor(mnCurrentCursor)); + } + else + mpWaitSymbol->hide(); + } } sal_Int16 SlideShowImpl::calcActiveCursor( sal_Int16 nCursorShape ) const { - if( mbWaitState && !mpWaitSymbol ) // enforce wait cursor + if( mnWaitSymbolRequestCount>0 && !mpWaitSymbol ) // enforce wait cursor nCursorShape = awt::SystemPointer::WAIT; else if( !mbMouseVisible ) // enforce INVISIBLE nCursorShape = awt::SystemPointer::INVISIBLE; @@ -920,10 +1052,19 @@ void SlideShowImpl::stopShow() } } -struct SlideShowImpl::PrefetchPropertiesFunc + + +class SlideShowImpl::PrefetchPropertiesFunc { - SlideShowImpl *const that; - PrefetchPropertiesFunc( SlideShowImpl * that_ ) : that(that_) {} +public: + PrefetchPropertiesFunc( SlideShowImpl * that_, + bool& rbSkipAllMainSequenceEffects, + bool& rbSkipSlideTransition) + : mpSlideShowImpl(that_), + mrbSkipAllMainSequenceEffects(rbSkipAllMainSequenceEffects), + mrbSkipSlideTransition(rbSkipSlideTransition) + {} + void operator()( beans::PropertyValue const& rProperty ) const { if (rProperty.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("Prefetch") )) @@ -931,16 +1072,30 @@ struct SlideShowImpl::PrefetchPropertiesFunc uno::Sequence<uno::Any> seq; if ((rProperty.Value >>= seq) && seq.getLength() == 2) { - seq[0] >>= that->mxPrefetchSlide; - seq[1] >>= that->mxPrefetchAnimationNode; + seq[0] >>= mpSlideShowImpl->mxPrefetchSlide; + seq[1] >>= mpSlideShowImpl->mxPrefetchAnimationNode; } } + else if (rProperty.Name.equalsAsciiL( + RTL_CONSTASCII_STRINGPARAM("SkipAllMainSequenceEffects") )) + { + rProperty.Value >>= mrbSkipAllMainSequenceEffects; + } + else if (rProperty.Name.equalsAsciiL( + RTL_CONSTASCII_STRINGPARAM("SkipSlideTransition") )) + { + rProperty.Value >>= mrbSkipSlideTransition; + } else { OSL_ENSURE( false, rtl::OUStringToOString( rProperty.Name, RTL_TEXTENCODING_UTF8 ).getStr() ); } } +private: + SlideShowImpl *const mpSlideShowImpl; + bool& mrbSkipAllMainSequenceEffects; + bool& mrbSkipSlideTransition; }; void SlideShowImpl::displaySlide( @@ -954,7 +1109,9 @@ void SlideShowImpl::displaySlide( if (isDisposed()) return; - + + maEffectRewinder.setRootAnimationNode(xRootNode); + // precondition: must only be called from the main thread! DBG_TESTSOLARMUTEX(); @@ -972,20 +1129,20 @@ void SlideShowImpl::displaySlide( // unconditionally. Otherwise, genuine // shape animations (drawing layer and // GIF) will not be stopped. - + + bool bSkipAllMainSequenceEffects (false); + bool bSkipSlideTransition (false); std::for_each( rProperties.getConstArray(), rProperties.getConstArray() + rProperties.getLength(), - PrefetchPropertiesFunc(this) ); + PrefetchPropertiesFunc(this, bSkipAllMainSequenceEffects, bSkipSlideTransition) ); OSL_ENSURE( !maViewContainer.empty(), "### no views!" ); if (maViewContainer.empty()) return; - + // this here might take some time { - comphelper::ScopeGuard const scopeGuard( - boost::bind( &SlideShowImpl::setWaitState, this, false ) ); - setWaitState(true); + WaitSymbolLock aLock (*this); mpPreviousSlide = mpCurrentSlide; mpCurrentSlide.reset(); @@ -1025,15 +1182,26 @@ void SlideShowImpl::displaySlide( // create slide transition, and add proper end event // (which then starts the slide effects // via CURRENT_SLIDE.show()) - ActivitySharedPtr const pSlideChangeActivity( - createSlideTransition( mpCurrentSlide->getXDrawPage(), - mpPreviousSlide, - mpCurrentSlide, - makeEvent( - boost::bind( - &SlideShowImpl::notifySlideTransitionEnded, - this, - false )))); + ActivitySharedPtr pSlideChangeActivity ( + createSlideTransition( + mpCurrentSlide->getXDrawPage(), + mpPreviousSlide, + mpCurrentSlide, + makeEvent( + boost::bind( + &SlideShowImpl::notifySlideTransitionEnded, + this, + false ), + "SlideShowImpl::notifySlideTransitionEnded"))); + + if (bSkipSlideTransition) + { + // The transition activity was created for the side effects + // (like sound transitions). Because we want to skip the + // acutual transition animation we do not need the activity + // anymore. + pSlideChangeActivity.reset(); + } if (pSlideChangeActivity) { @@ -1049,7 +1217,8 @@ void SlideShowImpl::displaySlide( boost::bind( &SlideShowImpl::notifySlideTransitionEnded, this, - true ))); + true ), + "SlideShowImpl::notifySlideTransitionEnded")); } } } // finally @@ -1057,6 +1226,42 @@ void SlideShowImpl::displaySlide( maEventMultiplexer.notifySlideTransitionStarted(); maListenerContainer.forEach<presentation::XSlideShowListener>( boost::mem_fn( &presentation::XSlideShowListener::slideTransitionStarted ) ); + + // We are currently rewinding an effect. This lead us from the next + // slide to this one. To complete this we have to play back all main + // sequence effects on this slide. + if (bSkipAllMainSequenceEffects) + maEffectRewinder.skipAllMainSequenceEffects(); +} + +void SlideShowImpl::redisplayCurrentSlide (void) +{ + osl::MutexGuard const guard( m_aMutex ); + + if (isDisposed()) + return; + + // precondition: must only be called from the main thread! + DBG_TESTSOLARMUTEX(); + stopShow(); + + OSL_ENSURE( !maViewContainer.empty(), "### no views!" ); + if (maViewContainer.empty()) + return; + + // No transition effect on this slide - schedule slide + // effect start event right away. + maEventQueue.addEvent( + makeEvent( + boost::bind( + &SlideShowImpl::notifySlideTransitionEnded, + this, + true ), + "SlideShowImpl::notifySlideTransitionEnded")); + + maEventMultiplexer.notifySlideTransitionStarted(); + maListenerContainer.forEach<presentation::XSlideShowListener>( + boost::mem_fn( &presentation::XSlideShowListener::slideTransitionStarted ) ); } sal_Bool SlideShowImpl::nextEffect() throw (uno::RuntimeException) @@ -1075,6 +1280,50 @@ sal_Bool SlideShowImpl::nextEffect() throw (uno::RuntimeException) return maEventMultiplexer.notifyNextEffect(); } + +sal_Bool SlideShowImpl::previousEffect() throw (uno::RuntimeException) +{ + osl::MutexGuard const guard( m_aMutex ); + + if (isDisposed()) + return false; + + // precondition: must only be called from the main thread! + DBG_TESTSOLARMUTEX(); + + if (mbShowPaused) + return true; + else + { + return maEffectRewinder.rewind( + maScreenUpdater.createLock(false), + ::boost::bind<void>(::boost::mem_fn(&SlideShowImpl::redisplayCurrentSlide), this), + ::boost::bind<void>(::boost::mem_fn(&SlideShowImpl::rewindEffectToPreviousSlide), this)); + } +} + +void SlideShowImpl::rewindEffectToPreviousSlide (void) +{ + // Show the wait symbol now and prevent it from showing temporary slide + // content while effects are played back. + WaitSymbolLock aLock (*this); + + // A previous call to EffectRewinder::Rewind could not rewind the current + // effect because there are no effects on the current slide or none has + // yet been displayed. Go to the previous slide. + notifySlideEnded(true); + + // Process pending events once more in order to have the following + // screen update show the last effect. Not sure whether this should be + // necessary. + maEventQueue.forceEmpty(); + + // We have to call the screen updater before the wait symbol is turned + // off. Otherwise the wait symbol would force the display of an + // intermediate state of the slide (before the effects are replayed.) + maScreenUpdater.commitUpdates(); +} + sal_Bool SlideShowImpl::startShapeActivity( uno::Reference<drawing::XShape> const& /*xShape*/ ) throw (uno::RuntimeException) @@ -1614,6 +1863,34 @@ sal_Bool SlideShowImpl::setProperty( beans::PropertyValue const& rProperty ) return (rProperty.Value >>= mbNoSlideTransitions); } + if (rProperty.Name.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("IsSoundEnabled"))) + { + uno::Sequence<uno::Any> aValues; + uno::Reference<presentation::XSlideShowView> xView; + sal_Bool bValue (false); + if ((rProperty.Value >>= aValues) + && aValues.getLength()==2 + && (aValues[0] >>= xView) + && (aValues[1] >>= bValue)) + { + // Look up the view. + for (UnoViewVector::const_iterator + iView (maViewContainer.begin()), + iEnd (maViewContainer.end()); + iView!=iEnd; + ++iView) + { + if (*iView && (*iView)->getUnoView()==xView) + { + // Store the flag at the view so that media shapes have + // access to it. + (*iView)->setIsSoundEnabled(bValue); + return true; + } + } + } + } + return false; } @@ -1795,25 +2072,24 @@ sal_Bool SlideShowImpl::update( double & nNextTimeout ) // TODO(F2): re-evaluate whether that timer lagging makes // sense. - // hold timer, while processing the queues (ensures - // same time for all activities and events) + // hold timer, while processing the queues: + // 1. when there is more than one active activity this ensures the + // same time for all activities and events + // 2. processing of events may lead to creation of further events + // that have zero delay. While the timer is stopped these events + // are processed in the same run. { comphelper::ScopeGuard scopeGuard( boost::bind( &canvas::tools::ElapsedTime::releaseTimer, boost::cref(mpPresTimer) ) ); + mpPresTimer->holdTimer(); - // no need to hold timer for only one active animation - - // it's only meant to keep multiple ones in sync - if( maActivitiesQueue.size() > 1 ) - mpPresTimer->holdTimer(); - else - scopeGuard.dismiss(); // we're not holding the timer - // process queues maEventQueue.process(); maActivitiesQueue.process(); // commit frame to screen + maFrameSynchronization.Synchronize(); maScreenUpdater.commitUpdates(); // TODO(Q3): remove need to call dequeued() from @@ -1857,7 +2133,13 @@ sal_Bool SlideShowImpl::update( double & nNextTimeout ) { // Activity queue is not empty. Tell caller that we would // like to render another frame. - nNextTimeout = 1.0 / FrameRate::PreferredFramesPerSecond; + + // Return a zero time-out to signal our caller to call us + // back as soon as possible. The actual timing, waiting the + // appropriate amount of time between frames, is then done + // by the maFrameSynchronization object. + nNextTimeout = 0; + maFrameSynchronization.Activate(); } else { @@ -1871,6 +2153,10 @@ sal_Bool SlideShowImpl::update( double & nNextTimeout ) // ensure positive value: nNextTimeout = std::max( 0.0, maEventQueue.nextTimeout() ); + + // There is no active animation so the frame rate does not + // need to be synchronized. + maFrameSynchronization.Deactivate(); } mbSlideShowIdle = false; @@ -1915,7 +2201,7 @@ sal_Bool SlideShowImpl::update( double & nNextTimeout ) mbSlideShowIdle = true; } #endif - + return bRet; } } @@ -1995,7 +2281,7 @@ void SlideShowImpl::notifySlideAnimationsEnded() // schedule a slide end event, with automatic mode's // delay aNotificationEvents = makeInterruptableDelay( - boost::bind( &SlideShowImpl::notifySlideEnded, this ), + boost::bind<void>( boost::mem_fn(&SlideShowImpl::notifySlideEnded), this, false ), maEventMultiplexer.getAutomaticTimeout() ); } else @@ -2020,7 +2306,7 @@ void SlideShowImpl::notifySlideAnimationsEnded() bHasAutomaticNextSlide ) { aNotificationEvents = makeInterruptableDelay( - boost::bind( &SlideShowImpl::notifySlideEnded, this ), + boost::bind<void>( boost::mem_fn(&SlideShowImpl::notifySlideEnded), this, false ), nAutomaticNextSlideTimeout); // TODO(F2): Provide a mechanism to let the user override @@ -2036,8 +2322,9 @@ void SlideShowImpl::notifySlideAnimationsEnded() // generate interruptable event here, there's no // timeout involved. aNotificationEvents.mpImmediateEvent = - makeEvent( boost::bind( - &SlideShowImpl::notifySlideEnded, this ) ); + makeEvent( boost::bind<void>( + boost::mem_fn(&SlideShowImpl::notifySlideEnded), this, false ), + "SlideShowImpl::notifySlideEnded"); } } @@ -2058,9 +2345,7 @@ void SlideShowImpl::notifySlideAnimationsEnded() // change setup time a lot). Show the wait cursor, this // indeed might take some seconds. { - comphelper::ScopeGuard const scopeGuard( - boost::bind( &SlideShowImpl::setWaitState, this, false ) ); - setWaitState(true); + WaitSymbolLock aLock (*this); if (! matches( mpPrefetchSlide, mxPrefetchSlide, mxPrefetchAnimationNode )) @@ -2082,13 +2367,13 @@ void SlideShowImpl::notifySlideAnimationsEnded() boost::mem_fn( &presentation::XSlideShowListener::slideAnimationsEnded ) ); } -void SlideShowImpl::notifySlideEnded() +void SlideShowImpl::notifySlideEnded (const bool bReverse) { osl::MutexGuard const guard( m_aMutex ); OSL_ENSURE( !isDisposed(), "### already disposed!" ); - if (mpRehearseTimingsActivity) + if (mpRehearseTimingsActivity && !bReverse) { const double time = mpRehearseTimingsActivity->stop(); if (mpRehearseTimingsActivity->hasBeenClicked()) @@ -2108,8 +2393,9 @@ void SlideShowImpl::notifySlideEnded() } } } - - maEventMultiplexer.notifySlideEndEvent(); + + if (bReverse) + maEventMultiplexer.notifySlideEndEvent(); stopShow(); // MUST call that: results in // maUserEventQueue.clear(). What's more, @@ -2119,9 +2405,12 @@ void SlideShowImpl::notifySlideEnded() // unconditionally. Otherwise, genuine // shape animations (drawing layer and // GIF) will not be stopped. - + maListenerContainer.forEach<presentation::XSlideShowListener>( - boost::mem_fn( &presentation::XSlideShowListener::slideEnded ) ); + boost::bind<void>( + ::boost::mem_fn(&presentation::XSlideShowListener::slideEnded), + _1, + sal_Bool(bReverse))); } bool SlideShowImpl::notifyHyperLinkClicked( rtl::OUString const& hyperLink ) @@ -2169,6 +2458,66 @@ bool SlideShowImpl::handleAnimationEvent( const AnimationNodeSharedPtr& rNode ) return true; } + +//===== FrameSynchronization ================================================== + +FrameSynchronization::FrameSynchronization (const double nFrameDuration) + : maTimer(), + mnFrameDuration(nFrameDuration), + mnNextFrameTargetTime(0), + mbIsActive(false) +{ + MarkCurrentFrame(); +} + + + + +void FrameSynchronization::MarkCurrentFrame (void) +{ + mnNextFrameTargetTime = maTimer.getElapsedTime() + mnFrameDuration; +} + + + + +void FrameSynchronization::Synchronize (void) +{ + if (mbIsActive) + { + // Do busy waiting for now. + while (maTimer.getElapsedTime() < mnNextFrameTargetTime) + ; + } + + MarkCurrentFrame(); +} + + + + +void FrameSynchronization::Activate (void) +{ + mbIsActive = true; +} + + + + +void FrameSynchronization::Deactivate (void) +{ + mbIsActive = false; +} + + + + +double FrameSynchronization::GetCurrentTime (void) const +{ + return maTimer.getElapsedTime(); +} + + } // anon namespace namespace sdecl = comphelper::service_decl; diff --git a/slideshow/source/engine/slideview.cxx b/slideshow/source/engine/slideview.cxx index a5d4d6bb2..b6faaca69 100644 --- a/slideshow/source/engine/slideview.cxx +++ b/slideshow/source/engine/slideview.cxx @@ -715,6 +715,8 @@ private: // UnoView: virtual void _dispose(); virtual uno::Reference<presentation::XSlideShowView> getUnoView()const; + virtual void setIsSoundEnabled (const bool bValue); + virtual bool isSoundEnabled (void) const; // XEventListener: virtual void SAL_CALL disposing( lang::EventObject const& evt ) @@ -755,6 +757,7 @@ private: basegfx::B2DHomMatrix maViewTransform; basegfx::B2DSize maUserSize; + bool mbIsSoundEnabled; }; @@ -770,7 +773,8 @@ SlideView::SlideView( const uno::Reference<presentation::XSlideShowView>& xView, maViewLayers(), maClip(), maViewTransform(), - maUserSize( 1.0, 1.0 ) // default size: one-by-one rectangle + maUserSize( 1.0, 1.0 ), // default size: one-by-one rectangle + mbIsSoundEnabled(true) { // take care not constructing any UNO references to this _inside_ // ctor, shift that code to createSlideView()! @@ -1001,6 +1005,16 @@ uno::Reference<presentation::XSlideShowView> SlideView::getUnoView() const return mxView; } +void SlideView::setIsSoundEnabled (const bool bValue) +{ + mbIsSoundEnabled = bValue; +} + +bool SlideView::isSoundEnabled (void) const +{ + return mbIsSoundEnabled; +} + void SlideView::_dispose() { dispose(); @@ -1071,7 +1085,8 @@ void SlideView::modified( const lang::EventObject& /*aEvent*/ ) makeEvent( boost::bind( (bool (EventMultiplexer::*)( const uno::Reference<presentation::XSlideShowView>&)) &EventMultiplexer::notifyViewChanged, - boost::ref(mrEventMultiplexer), mxView ))); + boost::ref(mrEventMultiplexer), mxView ), + "EventMultiplexer::notifyViewChanged")); } // XPaintListener @@ -1086,7 +1101,8 @@ void SlideView::windowPaint( const awt::PaintEvent& /*e*/ ) // this might not be the main thread! mrEventQueue.addEvent( makeEvent( boost::bind( &EventMultiplexer::notifyViewClobbered, - boost::ref(mrEventMultiplexer), mxView ) ) ); + boost::ref(mrEventMultiplexer), mxView ), + "EventMultiplexer::notifyViewClobbered") ); } void SlideView::updateCanvas() diff --git a/slideshow/source/engine/smilfunctionparser.cxx b/slideshow/source/engine/smilfunctionparser.cxx index 4e6e30c08..7a06548f1 100644 --- a/slideshow/source/engine/smilfunctionparser.cxx +++ b/slideshow/source/engine/smilfunctionparser.cxx @@ -55,7 +55,7 @@ #include <typeinfo> #define BOOST_SPIRIT_DEBUG #endif -#include <boost/spirit/core.hpp> +#include <boost/spirit/include/classic_core.hpp> #if OSL_DEBUG_LEVEL > 0 #include <iostream> diff --git a/slideshow/source/engine/usereventqueue.cxx b/slideshow/source/engine/usereventqueue.cxx index 1ead45a9d..2a08faa0c 100644 --- a/slideshow/source/engine/usereventqueue.cxx +++ b/slideshow/source/engine/usereventqueue.cxx @@ -306,26 +306,45 @@ public: EventMultiplexer & rEventMultiplexer ) : ClickEventHandler(rEventQueue), mrEventQueue(rEventQueue), - mrEventMultiplexer(rEventMultiplexer) {} + mrEventMultiplexer(rEventMultiplexer), + mbSkipTriggersNextEffect(true) {} + + /** Remember to trigger (or not to trigger) the next effect after the + current effect is skiped. + */ + void setSkipTriggersNextEffect (const bool bSkipTriggersNextEffect) + { mbSkipTriggersNextEffect = bSkipTriggersNextEffect; } + + /// Skip the current effect but do not triggere the next effect. + void skipEffect (void) { handleEvent_impl(false); } private: virtual bool handleEvent_impl() { + return handleEvent_impl(true); + } + + bool handleEvent_impl (bool bNotifyNextEffect) + { // fire all events, so animation nodes can register their // next effect listeners: if(fireAllEvents( maEvents, mrEventQueue )) { - // then simulate a next effect event: - // this skip effect handler is triggered upon next effect - // events (multiplexer prio=-1)! - // Posting a notifyNextEffect() here is only safe - // (we don't run into busy loop), because we assume that - // someone has registerered above for next effects - // (multiplexer prio=0) at the user event queue. - return mrEventQueue.addEventForNextRound( - makeEvent( boost::bind( - &EventMultiplexer::notifyNextEffect, - boost::ref(mrEventMultiplexer) ) ) ); + if (mbSkipTriggersNextEffect && bNotifyNextEffect) + { + // then simulate a next effect event: this skip effect + // handler is triggered upon next effect events (multiplexer + // prio=-1)! Posting a notifyNextEffect() here is only safe + // (we don't run into busy loop), because we assume that + // someone has registerered above for next effects + // (multiplexer prio=0) at the user event queue. + return mrEventQueue.addEventWhenQueueIsEmpty( + makeEvent( boost::bind( &EventMultiplexer::notifyNextEffect, + boost::ref(mrEventMultiplexer) ), + "EventMultiplexer::notifyNextEffect") ); + } + else + return true; } return false; } @@ -333,6 +352,7 @@ private: private: EventQueue & mrEventQueue; EventMultiplexer & mrEventMultiplexer; + bool mbSkipTriggersNextEffect; }; class RewindEffectEventHandler : public MouseEventHandler_, @@ -772,6 +792,7 @@ void UserEventQueue::setAdvanceOnClick( bool bAdvanceOnClick ) mpClickEventHandler->setAdvanceOnClick( bAdvanceOnClick ); } + void UserEventQueue::registerSlideStartEvent( const EventSharedPtr& rEvent ) { registerEvent( mpStartEventHandler, @@ -888,7 +909,9 @@ void UserEventQueue::registerNextEffectEvent( const EventSharedPtr& rEvent ) mbAdvanceOnClick ) ); } -void UserEventQueue::registerSkipEffectEvent( EventSharedPtr const & pEvent ) +void UserEventQueue::registerSkipEffectEvent( + EventSharedPtr const & pEvent, + const bool bSkipTriggersNextEffect) { if(!mpSkipEffectEventHandler) { @@ -905,6 +928,7 @@ void UserEventQueue::registerSkipEffectEvent( EventSharedPtr const & pEvent ) // we're called here) mpSkipEffectEventHandler->setAdvanceOnClick( mbAdvanceOnClick ); } + mpSkipEffectEventHandler->setSkipTriggersNextEffect(bSkipTriggersNextEffect); mpSkipEffectEventHandler->addEvent( pEvent ); } @@ -973,6 +997,14 @@ void UserEventQueue::registerMouseLeaveEvent( const EventSharedPtr& rEvent, 0.0 /* default prio */ ) ); } +void UserEventQueue::callSkipEffectEventHandler (void) +{ + ::boost::shared_ptr<SkipEffectEventHandler> pHandler ( + ::boost::dynamic_pointer_cast<SkipEffectEventHandler>(mpSkipEffectEventHandler)); + if (pHandler) + pHandler->skipEffect(); +} + } // namespace internal } // namespace presentation diff --git a/slideshow/source/engine/wakeupevent.cxx b/slideshow/source/engine/wakeupevent.cxx index 249b07eb4..b36117a7e 100644 --- a/slideshow/source/engine/wakeupevent.cxx +++ b/slideshow/source/engine/wakeupevent.cxx @@ -45,6 +45,9 @@ namespace slideshow WakeupEvent::WakeupEvent( boost::shared_ptr<canvas::tools::ElapsedTime> const & pTimeBase, ActivitiesQueue& rActivityQueue ) : +#if OSL_DEBUG_LEVEL > 1 + Event(::rtl::OUString::createFromAscii("WakeupEvent")), +#endif maTimer(pTimeBase), mnNextTime(0.0), mpActivity(), |