blob: ea1e0367da9c77b0f4d965941cd2d4ec1c6dc860 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_slideshow.hxx"
// must be first
#include <canvas/debug.hxx>
#include <tools/diagnose_ex.h>
#include <comphelper/anytostring.hxx>
#include <cppuhelper/exc_hlp.hxx>
#include <com/sun/star/awt/SystemPointer.hpp>
#include <com/sun/star/awt/MouseButton.hpp>
#include <com/sun/star/awt/MouseEvent.hpp>
#include <boost/bind.hpp>
#include "delayevent.hxx"
#include "usereventqueue.hxx"
#include "cursormanager.hxx"
#include "slideshowexceptions.hxx"
#include <vector>
#include <queue>
#include <map>
#include <functional>
#include <algorithm>
using namespace com::sun::star;
/* Implementation of UserEventQueue class */
namespace slideshow {
namespace internal {
namespace {
typedef std::vector<EventSharedPtr> ImpEventVector;
typedef std::queue<EventSharedPtr> ImpEventQueue;
typedef std::map<uno::Reference<animations::XAnimationNode>,
ImpEventVector> ImpAnimationEventMap;
typedef std::map<ShapeSharedPtr, ImpEventQueue,
Shape::lessThanShape> ImpShapeEventMap;
// MouseEventHandler base class, not consuming any event:
class MouseEventHandler_ : public MouseEventHandler
{
public:
virtual bool handleMousePressed( awt::MouseEvent const& /*e*/ ) { return false;}
virtual bool handleMouseReleased( awt::MouseEvent const& /*e*/) { return false;}
virtual bool handleMouseEntered( awt::MouseEvent const& /*e*/ ) { return false;}
virtual bool handleMouseExited( awt::MouseEvent const& /*e*/ ) { return false; }
virtual bool handleMouseDragged( awt::MouseEvent const& /*e*/ ) { return false;}
virtual bool handleMouseMoved( awt::MouseEvent const& /*e*/ ) { return false; }
};
/** @return one event has been posted
*/
template <typename ContainerT>
bool fireSingleEvent( ContainerT & rQueue, EventQueue & rEventQueue )
{
// post next event in given queue:
while (! rQueue.empty())
{
EventSharedPtr const pEvent(rQueue.front());
rQueue.pop();
// skip all inactive events (as the purpose of
// nextEventFromQueue() is to activate the next
// event, and events which return false on
// isCharged() will never be activated by the
// EventQueue)
if(pEvent->isCharged())
return rEventQueue.addEvent( pEvent );
}
return false; // no more (active) events in queue
}
/** @return at least one event has been posted
*/
template <typename ContainerT>
bool fireAllEvents( ContainerT & rQueue, EventQueue & rEventQueue )
{
bool bFiredAny = false;
while (fireSingleEvent( rQueue, rEventQueue ))
bFiredAny = true;
return bFiredAny;
}
class EventContainer
{
public:
EventContainer() :
maEvents()
{}
void clearContainer()
{
maEvents = ImpEventQueue();
}
void addEvent( const EventSharedPtr& rEvent )
{
maEvents.push( rEvent );
}
bool isEmpty()
{
return maEvents.empty();
}
protected:
ImpEventQueue maEvents;
};
} // anon namespace
class PlainEventHandler : public EventHandler,
public EventContainer
{
public:
PlainEventHandler( EventQueue & rEventQueue )
: EventContainer(), mrEventQueue(rEventQueue) {}
virtual void dispose()
{
clearContainer();
}
virtual bool handleEvent()
{
return fireAllEvents( maEvents, mrEventQueue );
}
private:
EventQueue & mrEventQueue;
};
class AllAnimationEventHandler : public AnimationEventHandler
{
public:
AllAnimationEventHandler( EventQueue& rEventQueue ) :
mrEventQueue( rEventQueue ),
maAnimationEventMap()
{}
virtual void dispose()
{
maAnimationEventMap.clear();
}
virtual bool handleAnimationEvent( const AnimationNodeSharedPtr& rNode )
{
ENSURE_OR_RETURN_FALSE(
rNode,
"AllAnimationEventHandler::handleAnimationEvent(): Invalid node" );
bool bRet( false );
ImpAnimationEventMap::iterator aIter;
if( (aIter=maAnimationEventMap.find(
rNode->getXAnimationNode() )) != maAnimationEventMap.end() )
{
ImpEventVector& rVec( aIter->second );
bRet = !rVec.empty();
// registered node found -> fire all events in the vector
std::for_each( rVec.begin(), rVec.end(),
boost::bind( &EventQueue::addEvent,
boost::ref( mrEventQueue ), _1 ) );
rVec.clear();
}
return bRet;
}
void addEvent( const EventSharedPtr& rEvent,
const uno::Reference< animations::XAnimationNode >& xNode )
{
ImpAnimationEventMap::iterator aIter;
if( (aIter=maAnimationEventMap.find( xNode )) ==
maAnimationEventMap.end() )
{
// no entry for this animation -> create one
aIter = maAnimationEventMap.insert(
ImpAnimationEventMap::value_type( xNode,
ImpEventVector() ) ).first;
}
// add new event to queue
aIter->second.push_back( rEvent );
}
bool isEmpty()
{
// find at least one animation with a non-empty vector
ImpAnimationEventMap::const_iterator aCurr( maAnimationEventMap.begin() );
const ImpAnimationEventMap::const_iterator aEnd( maAnimationEventMap.end() );
while( aCurr != aEnd )
{
if( !aCurr->second.empty() )
return false; // at least one non-empty entry found
++aCurr;
}
return true; // not a single non-empty entry found
}
private:
EventQueue& mrEventQueue;
ImpAnimationEventMap maAnimationEventMap;
};
class ClickEventHandler : public MouseEventHandler_,
public EventHandler,
public EventContainer
{
public:
ClickEventHandler( EventQueue& rEventQueue ) :
EventContainer(),
mrEventQueue( rEventQueue ),
mbAdvanceOnClick( true )
{}
void setAdvanceOnClick( bool bAdvanceOnClick )
{
mbAdvanceOnClick = bAdvanceOnClick;
}
private:
virtual void dispose()
{
clearContainer();
}
// triggered by API calls, e.g. space bar
virtual bool handleEvent()
{
return handleEvent_impl();
}
// triggered by mouse release:
virtual bool handleMouseReleased( const awt::MouseEvent& evt )
{
if(evt.Buttons != awt::MouseButton::LEFT)
return false;
if( mbAdvanceOnClick ) {
// fire next event
return handleEvent_impl();
}
else {
return false; // advance-on-click disabled
}
}
// triggered by both:
virtual bool handleEvent_impl()
{
// fire next event:
return fireSingleEvent( maEvents, mrEventQueue );
}
private:
EventQueue& mrEventQueue;
bool mbAdvanceOnClick;
};
class SkipEffectEventHandler : public ClickEventHandler
{
public:
SkipEffectEventHandler( EventQueue & rEventQueue,
EventMultiplexer & rEventMultiplexer )
: ClickEventHandler(rEventQueue),
mrEventQueue(rEventQueue),
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 ))
{
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;
}
private:
EventQueue & mrEventQueue;
EventMultiplexer & mrEventMultiplexer;
bool mbSkipTriggersNextEffect;
};
class RewindEffectEventHandler : public MouseEventHandler_,
public EventContainer
{
public:
RewindEffectEventHandler( EventQueue & rEventQueue )
: EventContainer(), mrEventQueue(rEventQueue) {}
private:
virtual void dispose()
{
clearContainer();
}
virtual bool handleMouseReleased( awt::MouseEvent const& evt )
{
if(evt.Buttons != awt::MouseButton::RIGHT)
return false;
return fireAllEvents( maEvents, mrEventQueue );
}
private:
EventQueue & mrEventQueue;
};
/** Base class to share some common code between
ShapeClickEventHandler and MouseMoveHandler
@derive override necessary MouseEventHandler interface methods,
call sendEvent() method to actually process the event.
*/
class MouseHandlerBase : public MouseEventHandler_
{
public:
MouseHandlerBase( EventQueue& rEventQueue ) :
mrEventQueue( rEventQueue ),
maShapeEventMap()
{}
virtual void dispose()
{
// TODO(Q1): Check whether plain vector with swap idiom is
// okay here
maShapeEventMap = ImpShapeEventMap();
}
void addEvent( const EventSharedPtr& rEvent,
const ShapeSharedPtr& rShape )
{
ImpShapeEventMap::iterator aIter;
if( (aIter=maShapeEventMap.find( rShape )) == maShapeEventMap.end() )
{
// no entry for this shape -> create one
aIter = maShapeEventMap.insert(
ImpShapeEventMap::value_type( rShape,
ImpEventQueue() ) ).first;
}
// add new event to queue
aIter->second.push( rEvent );
}
bool isEmpty()
{
// find at least one shape with a non-empty queue
ImpShapeEventMap::reverse_iterator aCurrShape( maShapeEventMap.begin());
ImpShapeEventMap::reverse_iterator aEndShape( maShapeEventMap.end() );
while( aCurrShape != aEndShape )
{
if( !aCurrShape->second.empty() )
return false; // at least one non-empty entry found
++aCurrShape;
}
return true; // not a single non-empty entry found
}
protected:
bool hitTest( const awt::MouseEvent& e,
ImpShapeEventMap::reverse_iterator& o_rHitShape )
{
// find hit shape in map
const basegfx::B2DPoint aPosition( e.X, e.Y );
// find matching shape (scan reversely, to coarsely match
// paint order)
ImpShapeEventMap::reverse_iterator aCurrShape(maShapeEventMap.rbegin());
const ImpShapeEventMap::reverse_iterator aEndShape( maShapeEventMap.rend() );
while( aCurrShape != aEndShape )
{
// TODO(F2): Get proper geometry polygon from the
// shape, to avoid having areas outside the shape
// react on the mouse
if( aCurrShape->first->getBounds().isInside( aPosition ) &&
aCurrShape->first->isVisible() )
{
// shape hit, and shape is visible - report a
// hit
o_rHitShape = aCurrShape;
return true;
}
++aCurrShape;
}
return false; // nothing hit
}
bool sendEvent( ImpShapeEventMap::reverse_iterator& io_rHitShape )
{
// take next event from queue
const bool bRet( fireSingleEvent( io_rHitShape->second,
mrEventQueue ) );
// clear shape entry, if its queue is
// empty. This is important, since the shapes
// are held by shared ptr, and might otherwise
// not get released, even after their owning
// slide is long gone.
if( io_rHitShape->second.empty() )
{
// this looks funny, since ::std::map does
// provide an erase( iterator )
// method. Unfortunately, stlport does not
// declare the obvious erase(
// reverse_iterator ) needed here (missing
// orthogonality, eh?)
maShapeEventMap.erase( io_rHitShape->first );
}
return bRet;
}
bool processEvent( const awt::MouseEvent& e )
{
ImpShapeEventMap::reverse_iterator aCurrShape;
if( hitTest( e, aCurrShape ) )
return sendEvent( aCurrShape );
return false; // did not handle the event
}
private:
EventQueue& mrEventQueue;
ImpShapeEventMap maShapeEventMap;
};
class ShapeClickEventHandler : public MouseHandlerBase
{
public:
ShapeClickEventHandler( CursorManager& rCursorManager,
EventQueue& rEventQueue ) :
MouseHandlerBase( rEventQueue ),
mrCursorManager( rCursorManager )
{}
virtual bool handleMouseReleased( const awt::MouseEvent& e )
{
if(e.Buttons != awt::MouseButton::LEFT)
return false;
return processEvent( e );
}
virtual bool handleMouseMoved( const awt::MouseEvent& e )
{
// TODO(P2): Maybe buffer last shape touched
// if we have a shape click event, and the mouse
// hovers over this shape, change cursor to hand
ImpShapeEventMap::reverse_iterator aDummy;
if( hitTest( e, aDummy ) )
mrCursorManager.requestCursor( awt::SystemPointer::REFHAND );
return false; // we don't /eat/ this event. Lower prio
// handler should see it, too.
}
private:
CursorManager& mrCursorManager;
};
class MouseEnterHandler : public MouseHandlerBase
{
public:
MouseEnterHandler( EventQueue& rEventQueue )
: MouseHandlerBase( rEventQueue ),
mpLastShape() {}
virtual bool handleMouseMoved( const awt::MouseEvent& e )
{
// TODO(P2): Maybe buffer last shape touched, and
// check against that _first_
ImpShapeEventMap::reverse_iterator aCurr;
if( hitTest( e, aCurr ) )
{
if( aCurr->first != mpLastShape )
{
// we actually hit a shape, and it's different
// from the previous one - thus we just
// entered it, raise event
sendEvent( aCurr );
mpLastShape = aCurr->first;
}
}
else
{
// don't hit no shape - thus, last shape is NULL
mpLastShape.reset();
}
return false; // we don't /eat/ this event. Lower prio
// handler should see it, too.
}
private:
ShapeSharedPtr mpLastShape;
};
class MouseLeaveHandler : public MouseHandlerBase
{
public:
MouseLeaveHandler( EventQueue& rEventQueue )
: MouseHandlerBase( rEventQueue ),
maLastIter() {}
virtual bool handleMouseMoved( const awt::MouseEvent& e )
{
// TODO(P2): Maybe buffer last shape touched, and
// check against that _first_
ImpShapeEventMap::reverse_iterator aCurr;
if( hitTest( e, aCurr ) )
{
maLastIter = aCurr;
}
else
{
if( maLastIter->first )
{
// last time, we were over a shape, now we're
// not - we thus just left that shape, raise
// event
sendEvent( maLastIter );
}
// in any case, when we hit this else-branch: no
// shape hit, thus have to clear maLastIter
maLastIter = ImpShapeEventMap::reverse_iterator();
}
return false; // we don't /eat/ this event. Lower prio
// handler should see it, too.
}
private:
ImpShapeEventMap::reverse_iterator maLastIter;
};
template< typename Handler, typename Functor >
void UserEventQueue::registerEvent(
boost::shared_ptr< Handler >& rHandler,
const EventSharedPtr& rEvent,
const Functor& rRegistrationFunctor )
{
ENSURE_OR_THROW( rEvent,
"UserEventQueue::registerEvent(): Invalid event" );
if( !rHandler ) {
// create handler
rHandler.reset( new Handler( mrEventQueue ) );
// register handler on EventMultiplexer
rRegistrationFunctor( rHandler );
}
rHandler->addEvent( rEvent );
}
template< typename Handler, typename Arg, typename Functor >
void UserEventQueue::registerEvent(
boost::shared_ptr< Handler >& rHandler,
const EventSharedPtr& rEvent,
const Arg& rArg,
const Functor& rRegistrationFunctor )
{
ENSURE_OR_THROW( rEvent,
"UserEventQueue::registerEvent(): Invalid event" );
if( !rHandler ) {
// create handler
rHandler.reset( new Handler( mrEventQueue ) );
// register handler on EventMultiplexer
rRegistrationFunctor( rHandler );
}
rHandler->addEvent( rEvent, rArg );
}
// Public methods
// =====================================================
UserEventQueue::UserEventQueue( EventMultiplexer& rMultiplexer,
EventQueue& rEventQueue,
CursorManager& rCursorManager )
: mrMultiplexer( rMultiplexer ),
mrEventQueue( rEventQueue ),
mrCursorManager( rCursorManager ),
mpStartEventHandler(),
mpEndEventHandler(),
mpAnimationStartEventHandler(),
mpAnimationEndEventHandler(),
mpAudioStoppedEventHandler(),
mpClickEventHandler(),
mpSkipEffectEventHandler(),
mpRewindEffectEventHandler(),
mpDoubleClickEventHandler(),
mpMouseEnterHandler(),
mpMouseLeaveHandler(),
mbAdvanceOnClick( true )
{
}
UserEventQueue::~UserEventQueue()
{
try
{
// unregister all handlers
clear();
}
catch (uno::Exception &) {
OSL_ENSURE( false, rtl::OUStringToOString(
comphelper::anyToString(
cppu::getCaughtException() ),
RTL_TEXTENCODING_UTF8 ).getStr() );
}
}
bool UserEventQueue::isEmpty() const
{
// TODO(T2): This is not thread safe, the handlers are all
// only separately synchronized. This poses the danger of
// generating false empty status on XSlideShow::update(), such
// that the last events of a slide are not triggered.
// we're empty iff all handler queues are empty
return
(mpStartEventHandler ? mpStartEventHandler->isEmpty() : true) &&
(mpEndEventHandler ? mpEndEventHandler->isEmpty() : true) &&
(mpAnimationStartEventHandler ? mpAnimationStartEventHandler->isEmpty() : true) &&
(mpAnimationEndEventHandler ? mpAnimationEndEventHandler->isEmpty() : true) &&
(mpAudioStoppedEventHandler ? mpAudioStoppedEventHandler->isEmpty() : true) &&
(mpShapeClickEventHandler ? mpShapeClickEventHandler->isEmpty() : true) &&
(mpClickEventHandler ? mpClickEventHandler->isEmpty() : true) &&
(mpSkipEffectEventHandler ? mpSkipEffectEventHandler->isEmpty() : true) &&
(mpRewindEffectEventHandler ? mpRewindEffectEventHandler->isEmpty() : true) &&
(mpShapeDoubleClickEventHandler ? mpShapeDoubleClickEventHandler->isEmpty() : true) &&
(mpDoubleClickEventHandler ? mpDoubleClickEventHandler->isEmpty() : true) &&
(mpMouseEnterHandler ? mpMouseEnterHandler->isEmpty() : true) &&
(mpMouseLeaveHandler ? mpMouseLeaveHandler->isEmpty() : true);
}
void UserEventQueue::clear()
{
// unregister and delete all handlers
if( mpStartEventHandler ) {
mrMultiplexer.removeSlideStartHandler( mpStartEventHandler );
mpStartEventHandler.reset();
}
if( mpEndEventHandler ) {
mrMultiplexer.removeSlideEndHandler( mpEndEventHandler );
mpEndEventHandler.reset();
}
if( mpAnimationStartEventHandler ) {
mrMultiplexer.removeAnimationStartHandler(
mpAnimationStartEventHandler );
mpAnimationStartEventHandler.reset();
}
if( mpAnimationEndEventHandler ) {
mrMultiplexer.removeAnimationEndHandler( mpAnimationEndEventHandler );
mpAnimationEndEventHandler.reset();
}
if( mpAudioStoppedEventHandler ) {
mrMultiplexer.removeAudioStoppedHandler( mpAudioStoppedEventHandler );
mpAudioStoppedEventHandler.reset();
}
if( mpShapeClickEventHandler ) {
mrMultiplexer.removeClickHandler( mpShapeClickEventHandler );
mrMultiplexer.removeMouseMoveHandler( mpShapeClickEventHandler );
mpShapeClickEventHandler.reset();
}
if( mpClickEventHandler ) {
mrMultiplexer.removeClickHandler( mpClickEventHandler );
mrMultiplexer.removeNextEffectHandler( mpClickEventHandler );
mpClickEventHandler.reset();
}
if(mpSkipEffectEventHandler) {
mrMultiplexer.removeClickHandler( mpSkipEffectEventHandler );
mrMultiplexer.removeNextEffectHandler( mpSkipEffectEventHandler );
mpSkipEffectEventHandler.reset();
}
if(mpRewindEffectEventHandler) {
mrMultiplexer.removeClickHandler( mpRewindEffectEventHandler );
mpRewindEffectEventHandler.reset();
}
if( mpShapeDoubleClickEventHandler ) {
mrMultiplexer.removeDoubleClickHandler( mpShapeDoubleClickEventHandler );
mrMultiplexer.removeMouseMoveHandler( mpShapeDoubleClickEventHandler );
mpShapeDoubleClickEventHandler.reset();
}
if( mpDoubleClickEventHandler ) {
mrMultiplexer.removeDoubleClickHandler( mpDoubleClickEventHandler );
mpDoubleClickEventHandler.reset();
}
if( mpMouseEnterHandler ) {
mrMultiplexer.removeMouseMoveHandler( mpMouseEnterHandler );
mpMouseEnterHandler.reset();
}
if( mpMouseLeaveHandler ) {
mrMultiplexer.removeMouseMoveHandler( mpMouseLeaveHandler );
mpMouseLeaveHandler.reset();
}
}
void UserEventQueue::setAdvanceOnClick( bool bAdvanceOnClick )
{
mbAdvanceOnClick = bAdvanceOnClick;
// forward to handler, if existing. Otherwise, the handler
// creation will do the forwarding.
if( mpClickEventHandler )
mpClickEventHandler->setAdvanceOnClick( bAdvanceOnClick );
}
void UserEventQueue::registerSlideStartEvent( const EventSharedPtr& rEvent )
{
registerEvent( mpStartEventHandler,
rEvent,
boost::bind( &EventMultiplexer::addSlideStartHandler,
boost::ref( mrMultiplexer ), _1 ) );
}
void UserEventQueue::registerSlideEndEvent( const EventSharedPtr& rEvent )
{
registerEvent( mpEndEventHandler,
rEvent,
boost::bind( &EventMultiplexer::addSlideEndHandler,
boost::ref( mrMultiplexer ), _1 ) );
}
void UserEventQueue::registerAnimationStartEvent(
const EventSharedPtr& rEvent,
const uno::Reference< animations::XAnimationNode>& xNode )
{
registerEvent( mpAnimationStartEventHandler,
rEvent,
xNode,
boost::bind( &EventMultiplexer::addAnimationStartHandler,
boost::ref( mrMultiplexer ), _1 ) );
}
void UserEventQueue::registerAnimationEndEvent(
const EventSharedPtr& rEvent,
const uno::Reference<animations::XAnimationNode>& xNode )
{
registerEvent( mpAnimationEndEventHandler,
rEvent,
xNode,
boost::bind( &EventMultiplexer::addAnimationEndHandler,
boost::ref( mrMultiplexer ), _1 ) );
}
void UserEventQueue::registerAudioStoppedEvent(
const EventSharedPtr& rEvent,
const uno::Reference<animations::XAnimationNode>& xNode )
{
registerEvent( mpAudioStoppedEventHandler,
rEvent,
xNode,
boost::bind( &EventMultiplexer::addAudioStoppedHandler,
boost::ref( mrMultiplexer ), _1 ) );
}
void UserEventQueue::registerShapeClickEvent( const EventSharedPtr& rEvent,
const ShapeSharedPtr& rShape )
{
ENSURE_OR_THROW(
rEvent,
"UserEventQueue::registerShapeClickEvent(): Invalid event" );
if( !mpShapeClickEventHandler )
{
// create handler
mpShapeClickEventHandler.reset(
new ShapeClickEventHandler(mrCursorManager,
mrEventQueue) );
// register handler on EventMultiplexer
mrMultiplexer.addClickHandler( mpShapeClickEventHandler, 1.0 );
mrMultiplexer.addMouseMoveHandler( mpShapeClickEventHandler, 1.0 );
}
mpShapeClickEventHandler->addEvent( rEvent, rShape );
}
namespace {
class ClickEventRegistrationFunctor
{
public:
ClickEventRegistrationFunctor( EventMultiplexer& rMultiplexer,
double nPrio,
bool bAdvanceOnClick )
: mrMultiplexer( rMultiplexer ),
mnPrio(nPrio),
mbAdvanceOnClick( bAdvanceOnClick ) {}
void operator()( const boost::shared_ptr<ClickEventHandler>& rHandler )const
{
// register the handler on _two_ sources: we want the
// nextEffect events, e.g. space bar, to trigger clicks, as well!
mrMultiplexer.addClickHandler( rHandler, mnPrio );
mrMultiplexer.addNextEffectHandler( rHandler, mnPrio );
// forward advance-on-click state to newly
// generated handler (that's the only reason why
// we're called here)
rHandler->setAdvanceOnClick( mbAdvanceOnClick );
}
private:
EventMultiplexer& mrMultiplexer;
double const mnPrio;
bool const mbAdvanceOnClick;
};
} // anon namespace
void UserEventQueue::registerNextEffectEvent( const EventSharedPtr& rEvent )
{
// TODO: better name may be mpNextEffectEventHandler? then we have
// next effect (=> waiting to be started)
// skip effect (skipping the currently running one)
// rewind effect (rewinding back running one and waiting (again)
// to be started)
registerEvent( mpClickEventHandler,
rEvent,
ClickEventRegistrationFunctor( mrMultiplexer,
0.0 /* default prio */,
mbAdvanceOnClick ) );
}
void UserEventQueue::registerSkipEffectEvent(
EventSharedPtr const & pEvent,
const bool bSkipTriggersNextEffect)
{
if(!mpSkipEffectEventHandler)
{
mpSkipEffectEventHandler.reset(
new SkipEffectEventHandler( mrEventQueue, mrMultiplexer ) );
// register the handler on _two_ sources: we want the
// nextEffect events, e.g. space bar, to trigger clicks, as well!
mrMultiplexer.addClickHandler( mpSkipEffectEventHandler,
-1.0 /* prio below default */ );
mrMultiplexer.addNextEffectHandler( mpSkipEffectEventHandler,
-1.0 /* prio below default */ );
// forward advance-on-click state to newly
// generated handler (that's the only reason why
// we're called here)
mpSkipEffectEventHandler->setAdvanceOnClick( mbAdvanceOnClick );
}
mpSkipEffectEventHandler->setSkipTriggersNextEffect(bSkipTriggersNextEffect);
mpSkipEffectEventHandler->addEvent( pEvent );
}
void UserEventQueue::registerRewindEffectEvent( EventSharedPtr const& pEvent )
{
registerEvent( mpRewindEffectEventHandler,
pEvent,
boost::bind( &EventMultiplexer::addClickHandler,
boost::ref(mrMultiplexer), _1,
-1.0 /* prio below default */ ) );
}
void UserEventQueue::registerShapeDoubleClickEvent(
const EventSharedPtr& rEvent,
const ShapeSharedPtr& rShape )
{
ENSURE_OR_THROW(
rEvent,
"UserEventQueue::registerShapeDoubleClickEvent(): Invalid event" );
if( !mpShapeDoubleClickEventHandler )
{
// create handler
mpShapeDoubleClickEventHandler.reset(
new ShapeClickEventHandler(mrCursorManager,
mrEventQueue) );
// register handler on EventMultiplexer
mrMultiplexer.addDoubleClickHandler( mpShapeDoubleClickEventHandler,
1.0 );
mrMultiplexer.addMouseMoveHandler( mpShapeDoubleClickEventHandler,
1.0 );
}
mpShapeDoubleClickEventHandler->addEvent( rEvent, rShape );
}
void UserEventQueue::registerDoubleClickEvent( const EventSharedPtr& rEvent )
{
registerEvent( mpDoubleClickEventHandler,
rEvent,
boost::bind( &EventMultiplexer::addDoubleClickHandler,
boost::ref( mrMultiplexer ), _1,
0.0 /* default prio */ ) );
}
void UserEventQueue::registerMouseEnterEvent( const EventSharedPtr& rEvent,
const ShapeSharedPtr& rShape )
{
registerEvent( mpMouseEnterHandler,
rEvent,
rShape,
boost::bind( &EventMultiplexer::addMouseMoveHandler,
boost::ref( mrMultiplexer ), _1,
0.0 /* default prio */ ) );
}
void UserEventQueue::registerMouseLeaveEvent( const EventSharedPtr& rEvent,
const ShapeSharedPtr& rShape )
{
registerEvent( mpMouseLeaveHandler,
rEvent,
rShape,
boost::bind( &EventMultiplexer::addMouseMoveHandler,
boost::ref( mrMultiplexer ), _1,
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