summaryrefslogtreecommitdiff
path: root/slideshow/source/engine
diff options
context:
space:
mode:
Diffstat (limited to 'slideshow/source/engine')
-rw-r--r--slideshow/source/engine/activities/activitiesfactory.cxx9
-rw-r--r--slideshow/source/engine/animationnodes/animationaudionode.cxx9
-rw-r--r--slideshow/source/engine/animationnodes/animationbasenode.cxx3
-rw-r--r--slideshow/source/engine/animationnodes/animationcommandnode.cxx3
-rw-r--r--slideshow/source/engine/animationnodes/animationsetnode.cxx3
-rw-r--r--slideshow/source/engine/animationnodes/basenode.cxx9
-rw-r--r--slideshow/source/engine/animationnodes/basenode.hxx4
-rw-r--r--slideshow/source/engine/animationnodes/generateevent.cxx36
-rw-r--r--slideshow/source/engine/animationnodes/paralleltimecontainer.cxx3
-rw-r--r--slideshow/source/engine/animationnodes/sequentialtimecontainer.cxx19
-rw-r--r--slideshow/source/engine/debug.cxx327
-rw-r--r--slideshow/source/engine/effectrewinder.cxx436
-rw-r--r--slideshow/source/engine/effectrewinder.hxx185
-rw-r--r--slideshow/source/engine/eventmultiplexer.cxx25
-rw-r--r--slideshow/source/engine/eventqueue.cxx78
-rw-r--r--slideshow/source/engine/makefile.mk4
-rw-r--r--slideshow/source/engine/rehearsetimingsactivity.cxx3
-rw-r--r--slideshow/source/engine/screenupdater.cxx83
-rw-r--r--slideshow/source/engine/shapes/drawshape.cxx6
-rw-r--r--slideshow/source/engine/shapes/viewmediashape.cxx12
-rw-r--r--slideshow/source/engine/shapes/viewmediashape.hxx1
-rw-r--r--slideshow/source/engine/slide/layermanager.cxx105
-rw-r--r--slideshow/source/engine/slide/layermanager.hxx35
-rw-r--r--slideshow/source/engine/slideshowimpl.cxx469
-rw-r--r--slideshow/source/engine/slideview.cxx22
-rw-r--r--slideshow/source/engine/usereventqueue.cxx58
-rw-r--r--slideshow/source/engine/wakeupevent.cxx3
27 files changed, 1754 insertions, 196 deletions
diff --git a/slideshow/source/engine/activities/activitiesfactory.cxx b/slideshow/source/engine/activities/activitiesfactory.cxx
index 39cd7d733110..398a813a62aa 100644
--- a/slideshow/source/engine/activities/activitiesfactory.cxx
+++ b/slideshow/source/engine/activities/activitiesfactory.cxx
@@ -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 634dc0342515..24c3a95382bf 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 82868063fefa..19a6df2a8244 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 f9104f37a45a..2adbe2b9b75a 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 f7669b7214d3..ba1f015cd4fe 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 9f74a75fd571..6ad15e43462f 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 68ec92f0fea7..3a0b25e7ea94 100644
--- a/slideshow/source/engine/animationnodes/basenode.hxx
+++ b/slideshow/source/engine/animationnodes/basenode.hxx
@@ -137,6 +137,8 @@ public:
// 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 0983019dafef..015db5586834 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 0bd4c524586e..0581c78e9c47 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 683fe7bd9707..79046f50251a 100644
--- a/slideshow/source/engine/animationnodes/sequentialtimecontainer.cxx
+++ b/slideshow/source/engine/animationnodes/sequentialtimecontainer.cxx
@@ -63,7 +63,9 @@ void SequentialTimeContainer::activate_st()
{
// deactivate ASAP:
scheduleDeactivationEvent(
- makeEvent( boost::bind< void >( boost::mem_fn( &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<void>( boost::mem_fn( &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 000000000000..75d1c30d5013
--- /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 000000000000..3f6ceb54bb8e
--- /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 000000000000..804696c99c92
--- /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 181a787301b7..d62a7946d3c7 100644
--- a/slideshow/source/engine/eventmultiplexer.cxx
+++ b/slideshow/source/engine/eventmultiplexer.cxx
@@ -369,7 +369,8 @@ void SAL_CALL EventMultiplexerListener::mousePressed(
mpEventQueue->addEvent(
makeEvent( boost::bind( &EventMultiplexerImpl::mousePressed,
mpEventMultiplexer,
- e ) ) );
+ e ),
+ "EventMultiplexerImpl::mousePressed") );
}
void SAL_CALL EventMultiplexerListener::mouseReleased(
@@ -383,7 +384,8 @@ void SAL_CALL EventMultiplexerListener::mouseReleased(
mpEventQueue->addEvent(
makeEvent( boost::bind( &EventMultiplexerImpl::mouseReleased,
mpEventMultiplexer,
- e ) ) );
+ e ),
+ "EventMultiplexerImpl::mouseReleased") );
}
void SAL_CALL EventMultiplexerListener::mouseEntered(
@@ -410,7 +412,8 @@ void SAL_CALL EventMultiplexerListener::mouseDragged(
mpEventQueue->addEvent(
makeEvent( boost::bind( &EventMultiplexerImpl::mouseDragged,
mpEventMultiplexer,
- e )) );
+ e ),
+ "EventMultiplexerImpl::mouseDragged") );
}
void SAL_CALL EventMultiplexerListener::mouseMoved(
@@ -424,7 +427,8 @@ void SAL_CALL EventMultiplexerListener::mouseMoved(
mpEventQueue->addEvent(
makeEvent( boost::bind( &EventMultiplexerImpl::mouseMoved,
mpEventMultiplexer,
- e )) );
+ e ),
+ "EventMultiplexerImpl::mouseMoved") );
}
@@ -448,7 +452,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());
+ }
}
}
}
@@ -520,7 +532,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 9d7b7ed65eb1..44e1cad2f45e 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" );
@@ -124,6 +133,14 @@ 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 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" );
maNextEvents.push_back(
@@ -132,6 +149,30 @@ namespace slideshow
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()
{
::osl::MutexGuard aGuard( maMutex );
@@ -163,6 +204,17 @@ namespace slideshow
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 b064ad7b94cb..84afa7fb657e 100644
--- a/slideshow/source/engine/makefile.mk
+++ b/slideshow/source/engine/makefile.mk
@@ -72,6 +72,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 \
@@ -89,7 +90,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 f8e9cf2f8e6c..90efe8b5aedd 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 bf512dfca5db..940d45337919 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 e588a8fb4e58..899804fe1896 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 a02c795d4943..03fdff737e7c 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 09f4d4fa53ef..f28fe3d74f75 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 ceff661890bf..251cff3a73b3 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,9 +164,18 @@ 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,
maLayers.end());
@@ -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 2fb72af1be18..5a1512bb4f7e 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 d8dc7931add1..109e64ca63c1 100644
--- a/slideshow/source/engine/slideshowimpl.cxx
+++ b/slideshow/source/engine/slideshowimpl.cxx
@@ -71,6 +71,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/loader/CannotActivateFactoryException.hpp>
@@ -93,6 +94,7 @@
#include "slidebitmap.hxx"
#include "rehearsetimingsactivity.hxx"
#include "waitsymbol.hxx"
+#include "effectrewinder.hxx"
#include "framerate.hxx"
#include <boost/noncopyable.hpp>
@@ -109,6 +111,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
@@ -196,7 +265,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.
@@ -211,6 +280,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);
@@ -261,6 +331,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
@@ -316,12 +392,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;
@@ -368,7 +464,7 @@ private:
sal_Int16 mnCurrentCursor;
- bool mbWaitState;
+ sal_Int32 mnWaitSymbolRequestCount;
bool mbAutomaticAdvancementMode;
bool mbImageAnimationsAllowed;
bool mbNoSlideTransitions;
@@ -377,6 +473,9 @@ private:
bool mbShowPaused;
bool mbSlideShowIdle;
bool mbDisableAnimationZOrder;
+
+ EffectRewinder maEffectRewinder;
+ FrameSynchronization maFrameSynchronization;
};
@@ -411,10 +510,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;
}
@@ -468,7 +571,7 @@ SlideShowImpl::SlideShowImpl(
mxPrefetchSlide(),
mxPrefetchAnimationNode(),
mnCurrentCursor(awt::SystemPointer::ARROW),
- mbWaitState(false),
+ mnWaitSymbolRequestCount(0),
mbAutomaticAdvancementMode(false),
mbImageAnimationsAllowed( true ),
mbNoSlideTransitions( false ),
@@ -476,7 +579,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()!
@@ -516,6 +622,8 @@ void SlideShowImpl::disposing()
{
osl::MutexGuard const guard( m_aMutex );
+ maEffectRewinder.dispose();
+
// stop slide transition sound, if any:
stopSlideTransitionSound();
@@ -616,7 +724,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" );
@@ -736,7 +844,8 @@ ActivitySharedPtr SlideShowImpl::createSlideTransition(
&::slideshow::internal::Animation::prefetch,
pTransition,
AnimatableShapeSharedPtr(),
- ShapeAttributeLayerSharedPtr())));
+ ShapeAttributeLayerSharedPtr()),
+ "Animation::prefetch"));
return ActivitySharedPtr(
ActivitiesFactory::createSimpleActivity(
@@ -787,20 +896,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;
@@ -844,10 +976,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") ))
@@ -855,16 +996,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(
@@ -878,6 +1033,8 @@ void SlideShowImpl::displaySlide(
if (isDisposed())
return;
+ maEffectRewinder.setRootAnimationNode(xRootNode);
+
// precondition: must only be called from the main thread!
DBG_TESTSOLARMUTEX();
@@ -890,9 +1047,11 @@ void SlideShowImpl::displaySlide(
// 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())
@@ -900,9 +1059,7 @@ void SlideShowImpl::displaySlide(
// 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();
@@ -944,15 +1101,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)
{
@@ -968,7 +1136,8 @@ void SlideShowImpl::displaySlide(
boost::bind(
&SlideShowImpl::notifySlideTransitionEnded,
this,
- true )));
+ true ),
+ "SlideShowImpl::notifySlideTransitionEnded"));
}
}
} // finally
@@ -976,6 +1145,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)
@@ -994,6 +1199,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)
@@ -1290,6 +1539,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;
}
@@ -1471,25 +1748,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) ) );
-
- // 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
+ mpPresTimer->holdTimer();
// process queues
maEventQueue.process();
maActivitiesQueue.process();
// commit frame to screen
+ maFrameSynchronization.Synchronize();
maScreenUpdater.commitUpdates();
// TODO(Q3): remove need to call dequeued() from
@@ -1533,7 +1809,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
{
@@ -1547,6 +1829,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;
@@ -1668,7 +1954,7 @@ void SlideShowImpl::notifySlideAnimationsEnded()
// schedule a slide end event, with automatic mode's
// delay
aNotificationEvents = makeInterruptableDelay(
- boost::bind<void>( boost::mem_fn(&SlideShowImpl::notifySlideEnded), this ),
+ boost::bind<void>( boost::mem_fn(&SlideShowImpl::notifySlideEnded), this, false ),
maEventMultiplexer.getAutomaticTimeout() );
}
else
@@ -1693,7 +1979,7 @@ void SlideShowImpl::notifySlideAnimationsEnded()
bHasAutomaticNextSlide )
{
aNotificationEvents = makeInterruptableDelay(
- boost::bind<void>( boost::mem_fn(&SlideShowImpl::notifySlideEnded), this ),
+ boost::bind<void>( boost::mem_fn(&SlideShowImpl::notifySlideEnded), this, false ),
nAutomaticNextSlideTimeout);
// TODO(F2): Provide a mechanism to let the user override
@@ -1710,7 +1996,8 @@ void SlideShowImpl::notifySlideAnimationsEnded()
// timeout involved.
aNotificationEvents.mpImmediateEvent =
makeEvent( boost::bind<void>(
- boost::mem_fn(&SlideShowImpl::notifySlideEnded), this ) );
+ boost::mem_fn(&SlideShowImpl::notifySlideEnded), this, false ),
+ "SlideShowImpl::notifySlideEnded");
}
}
@@ -1731,9 +2018,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 ))
@@ -1755,13 +2040,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())
@@ -1782,7 +2067,8 @@ void SlideShowImpl::notifySlideEnded()
}
}
- maEventMultiplexer.notifySlideEndEvent();
+ if (bReverse)
+ maEventMultiplexer.notifySlideEndEvent();
stopShow(); // MUST call that: results in
// maUserEventQueue.clear(). What's more,
@@ -1794,7 +2080,10 @@ void SlideShowImpl::notifySlideEnded()
// 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 )
@@ -1840,6 +2129,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 9d48612457b3..dcca4a51b4d7 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/usereventqueue.cxx b/slideshow/source/engine/usereventqueue.cxx
index ac7f39174118..4415599f84d8 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 0b30510f9cb3..d2e13d188e14 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(),