957 lines
30 KiB
C++
957 lines
30 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*************************************************************************
|
|
*
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* Copyright 2000, 2010 Oracle and/or its affiliates.
|
|
*
|
|
* OpenOffice.org - a multi-platform office productivity suite
|
|
*
|
|
* 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.
|
|
*
|
|
************************************************************************/
|
|
|
|
|
|
// 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_FAIL( rtl::OUStringToOString(
|
|
comphelper::anyToString(
|
|
cppu::getCaughtException() ),
|
|
RTL_TEXTENCODING_UTF8 ).getStr() );
|
|
}
|
|
}
|
|
|
|
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::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::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
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|