Branch data Line data Source code
1 : : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : : /*
3 : : * This file is part of the LibreOffice project.
4 : : *
5 : : * This Source Code Form is subject to the terms of the Mozilla Public
6 : : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : : *
9 : : * This file incorporates work covered by the following license notice:
10 : : *
11 : : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : : * contributor license agreements. See the NOTICE file distributed
13 : : * with this work for additional information regarding copyright
14 : : * ownership. The ASF licenses this file to you under the Apache
15 : : * License, Version 2.0 (the "License"); you may not use this file
16 : : * except in compliance with the License. You may obtain a copy of
17 : : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : : */
19 : :
20 : :
21 : : // must be first
22 : : #include <canvas/debug.hxx>
23 : : #include <tools/diagnose_ex.h>
24 : :
25 : : #include <comphelper/anytostring.hxx>
26 : : #include <cppuhelper/exc_hlp.hxx>
27 : :
28 : : #include <com/sun/star/awt/SystemPointer.hpp>
29 : : #include <com/sun/star/awt/MouseButton.hpp>
30 : : #include <com/sun/star/awt/MouseEvent.hpp>
31 : :
32 : : #include <boost/bind.hpp>
33 : :
34 : : #include "delayevent.hxx"
35 : : #include "usereventqueue.hxx"
36 : : #include "cursormanager.hxx"
37 : : #include "slideshowexceptions.hxx"
38 : :
39 : : #include <vector>
40 : : #include <queue>
41 : : #include <map>
42 : : #include <functional>
43 : : #include <algorithm>
44 : :
45 : :
46 : : using namespace com::sun::star;
47 : :
48 : : /* Implementation of UserEventQueue class */
49 : :
50 : : namespace slideshow {
51 : : namespace internal {
52 : :
53 : : namespace {
54 : :
55 : : typedef std::vector<EventSharedPtr> ImpEventVector;
56 : : typedef std::queue<EventSharedPtr> ImpEventQueue;
57 : : typedef std::map<uno::Reference<animations::XAnimationNode>,
58 : : ImpEventVector> ImpAnimationEventMap;
59 : : typedef std::map<ShapeSharedPtr, ImpEventQueue,
60 : : Shape::lessThanShape> ImpShapeEventMap;
61 : :
62 : : // MouseEventHandler base class, not consuming any event:
63 : 0 : class MouseEventHandler_ : public MouseEventHandler
64 : : {
65 : : public:
66 : 0 : virtual bool handleMousePressed( awt::MouseEvent const& /*e*/ ) { return false;}
67 : 0 : virtual bool handleMouseReleased( awt::MouseEvent const& /*e*/) { return false;}
68 : 0 : virtual bool handleMouseEntered( awt::MouseEvent const& /*e*/ ) { return false;}
69 : 0 : virtual bool handleMouseExited( awt::MouseEvent const& /*e*/ ) { return false; }
70 : 0 : virtual bool handleMouseDragged( awt::MouseEvent const& /*e*/ ) { return false;}
71 : 0 : virtual bool handleMouseMoved( awt::MouseEvent const& /*e*/ ) { return false; }
72 : : };
73 : :
74 : : /** @return one event has been posted
75 : : */
76 : : template <typename ContainerT>
77 : 0 : bool fireSingleEvent( ContainerT & rQueue, EventQueue & rEventQueue )
78 : : {
79 : : // post next event in given queue:
80 : 0 : while (! rQueue.empty())
81 : : {
82 : 0 : EventSharedPtr const pEvent(rQueue.front());
83 : 0 : rQueue.pop();
84 : :
85 : : // skip all inactive events (as the purpose of
86 : : // nextEventFromQueue() is to activate the next
87 : : // event, and events which return false on
88 : : // isCharged() will never be activated by the
89 : : // EventQueue)
90 : 0 : if(pEvent->isCharged())
91 : 0 : return rEventQueue.addEvent( pEvent );
92 : : }
93 : 0 : return false; // no more (active) events in queue
94 : : }
95 : :
96 : : /** @return at least one event has been posted
97 : : */
98 : : template <typename ContainerT>
99 : 0 : bool fireAllEvents( ContainerT & rQueue, EventQueue & rEventQueue )
100 : : {
101 : 0 : bool bFiredAny = false;
102 : 0 : while (fireSingleEvent( rQueue, rEventQueue ))
103 : 0 : bFiredAny = true;
104 : 0 : return bFiredAny;
105 : : }
106 : :
107 : 0 : class EventContainer
108 : : {
109 : : public:
110 : 0 : EventContainer() :
111 : 0 : maEvents()
112 : 0 : {}
113 : :
114 : 0 : void clearContainer()
115 : : {
116 : 0 : maEvents = ImpEventQueue();
117 : 0 : }
118 : :
119 : 0 : void addEvent( const EventSharedPtr& rEvent )
120 : : {
121 : 0 : maEvents.push( rEvent );
122 : 0 : }
123 : :
124 : : bool isEmpty()
125 : : {
126 : : return maEvents.empty();
127 : : }
128 : :
129 : : protected:
130 : : ImpEventQueue maEvents;
131 : : };
132 : :
133 : : } // anon namespace
134 : :
135 : 0 : class PlainEventHandler : public EventHandler,
136 : : public EventContainer
137 : : {
138 : : public:
139 : : PlainEventHandler( EventQueue & rEventQueue )
140 : : : EventContainer(), mrEventQueue(rEventQueue) {}
141 : :
142 : 0 : virtual void dispose()
143 : : {
144 : 0 : clearContainer();
145 : 0 : }
146 : :
147 : 0 : virtual bool handleEvent()
148 : : {
149 : 0 : return fireAllEvents( maEvents, mrEventQueue );
150 : : }
151 : :
152 : : private:
153 : : EventQueue & mrEventQueue;
154 : : };
155 : :
156 : 0 : class AllAnimationEventHandler : public AnimationEventHandler
157 : : {
158 : : public:
159 : 0 : AllAnimationEventHandler( EventQueue& rEventQueue ) :
160 : : mrEventQueue( rEventQueue ),
161 : 0 : maAnimationEventMap()
162 : 0 : {}
163 : :
164 : 0 : virtual void dispose()
165 : : {
166 : 0 : maAnimationEventMap.clear();
167 : 0 : }
168 : :
169 : 0 : virtual bool handleAnimationEvent( const AnimationNodeSharedPtr& rNode )
170 : : {
171 : 0 : ENSURE_OR_RETURN_FALSE(
172 : : rNode,
173 : : "AllAnimationEventHandler::handleAnimationEvent(): Invalid node" );
174 : :
175 : 0 : bool bRet( false );
176 : :
177 : 0 : ImpAnimationEventMap::iterator aIter;
178 : 0 : if( (aIter=maAnimationEventMap.find(
179 : 0 : rNode->getXAnimationNode() )) != maAnimationEventMap.end() )
180 : : {
181 : 0 : ImpEventVector& rVec( aIter->second );
182 : :
183 : 0 : bRet = !rVec.empty();
184 : :
185 : : // registered node found -> fire all events in the vector
186 : : std::for_each( rVec.begin(), rVec.end(),
187 : : boost::bind( &EventQueue::addEvent,
188 : 0 : boost::ref( mrEventQueue ), _1 ) );
189 : :
190 : 0 : rVec.clear();
191 : : }
192 : :
193 : 0 : return bRet;
194 : : }
195 : :
196 : 0 : void addEvent( const EventSharedPtr& rEvent,
197 : : const uno::Reference< animations::XAnimationNode >& xNode )
198 : : {
199 : 0 : ImpAnimationEventMap::iterator aIter;
200 : 0 : if( (aIter=maAnimationEventMap.find( xNode )) ==
201 : 0 : maAnimationEventMap.end() )
202 : : {
203 : : // no entry for this animation -> create one
204 : : aIter = maAnimationEventMap.insert(
205 : : ImpAnimationEventMap::value_type( xNode,
206 : 0 : ImpEventVector() ) ).first;
207 : : }
208 : :
209 : : // add new event to queue
210 : 0 : aIter->second.push_back( rEvent );
211 : 0 : }
212 : :
213 : : bool isEmpty()
214 : : {
215 : : // find at least one animation with a non-empty vector
216 : : ImpAnimationEventMap::const_iterator aCurr( maAnimationEventMap.begin() );
217 : : const ImpAnimationEventMap::const_iterator aEnd( maAnimationEventMap.end() );
218 : : while( aCurr != aEnd )
219 : : {
220 : : if( !aCurr->second.empty() )
221 : : return false; // at least one non-empty entry found
222 : :
223 : : ++aCurr;
224 : : }
225 : :
226 : : return true; // not a single non-empty entry found
227 : : }
228 : :
229 : : private:
230 : : EventQueue& mrEventQueue;
231 : : ImpAnimationEventMap maAnimationEventMap;
232 : : };
233 : :
234 : 0 : class ClickEventHandler : public MouseEventHandler_,
235 : : public EventHandler,
236 : : public EventContainer
237 : : {
238 : : public:
239 : 0 : ClickEventHandler( EventQueue& rEventQueue ) :
240 : : EventContainer(),
241 : : mrEventQueue( rEventQueue ),
242 : 0 : mbAdvanceOnClick( true )
243 : 0 : {}
244 : :
245 : 0 : void setAdvanceOnClick( bool bAdvanceOnClick )
246 : : {
247 : 0 : mbAdvanceOnClick = bAdvanceOnClick;
248 : 0 : }
249 : :
250 : : private:
251 : 0 : virtual void dispose()
252 : : {
253 : 0 : clearContainer();
254 : 0 : }
255 : :
256 : : // triggered by API calls, e.g. space bar
257 : 0 : virtual bool handleEvent()
258 : : {
259 : 0 : return handleEvent_impl();
260 : : }
261 : :
262 : : // triggered by mouse release:
263 : 0 : virtual bool handleMouseReleased( const awt::MouseEvent& evt )
264 : : {
265 : 0 : if(evt.Buttons != awt::MouseButton::LEFT)
266 : 0 : return false;
267 : :
268 : 0 : if( mbAdvanceOnClick ) {
269 : : // fire next event
270 : 0 : return handleEvent_impl();
271 : : }
272 : : else {
273 : 0 : return false; // advance-on-click disabled
274 : : }
275 : : }
276 : :
277 : : // triggered by both:
278 : 0 : virtual bool handleEvent_impl()
279 : : {
280 : : // fire next event:
281 : 0 : return fireSingleEvent( maEvents, mrEventQueue );
282 : : }
283 : :
284 : : private:
285 : : EventQueue& mrEventQueue;
286 : : bool mbAdvanceOnClick;
287 : : };
288 : :
289 : 0 : class SkipEffectEventHandler : public ClickEventHandler
290 : : {
291 : : public:
292 : 0 : SkipEffectEventHandler( EventQueue & rEventQueue,
293 : : EventMultiplexer & rEventMultiplexer )
294 : : : ClickEventHandler(rEventQueue),
295 : : mrEventQueue(rEventQueue),
296 : : mrEventMultiplexer(rEventMultiplexer),
297 : 0 : mbSkipTriggersNextEffect(true) {}
298 : :
299 : : /** Remember to trigger (or not to trigger) the next effect after the
300 : : current effect is skiped.
301 : : */
302 : 0 : void setSkipTriggersNextEffect (const bool bSkipTriggersNextEffect)
303 : 0 : { mbSkipTriggersNextEffect = bSkipTriggersNextEffect; }
304 : :
305 : : /// Skip the current effect but do not triggere the next effect.
306 : 0 : void skipEffect (void) { handleEvent_impl(false); }
307 : :
308 : : private:
309 : 0 : virtual bool handleEvent_impl()
310 : : {
311 : 0 : return handleEvent_impl(true);
312 : : }
313 : :
314 : 0 : bool handleEvent_impl (bool bNotifyNextEffect)
315 : : {
316 : : // fire all events, so animation nodes can register their
317 : : // next effect listeners:
318 : 0 : if(fireAllEvents( maEvents, mrEventQueue ))
319 : : {
320 : 0 : if (mbSkipTriggersNextEffect && bNotifyNextEffect)
321 : : {
322 : : // then simulate a next effect event: this skip effect
323 : : // handler is triggered upon next effect events (multiplexer
324 : : // prio=-1)! Posting a notifyNextEffect() here is only safe
325 : : // (we don't run into busy loop), because we assume that
326 : : // someone has registerered above for next effects
327 : : // (multiplexer prio=0) at the user event queue.
328 : : return mrEventQueue.addEventWhenQueueIsEmpty(
329 : 0 : makeEvent( boost::bind( &EventMultiplexer::notifyNextEffect,
330 : : boost::ref(mrEventMultiplexer) ),
331 : 0 : "EventMultiplexer::notifyNextEffect") );
332 : : }
333 : : else
334 : 0 : return true;
335 : : }
336 : 0 : return false;
337 : : }
338 : :
339 : : private:
340 : : EventQueue & mrEventQueue;
341 : : EventMultiplexer & mrEventMultiplexer;
342 : : bool mbSkipTriggersNextEffect;
343 : : };
344 : :
345 : 0 : class RewindEffectEventHandler : public MouseEventHandler_,
346 : : public EventContainer
347 : : {
348 : : public:
349 : 0 : RewindEffectEventHandler( EventQueue & rEventQueue )
350 : 0 : : EventContainer(), mrEventQueue(rEventQueue) {}
351 : :
352 : : private:
353 : 0 : virtual void dispose()
354 : : {
355 : 0 : clearContainer();
356 : 0 : }
357 : :
358 : 0 : virtual bool handleMouseReleased( awt::MouseEvent const& evt )
359 : : {
360 : 0 : if(evt.Buttons != awt::MouseButton::RIGHT)
361 : 0 : return false;
362 : :
363 : 0 : return fireAllEvents( maEvents, mrEventQueue );
364 : : }
365 : :
366 : : private:
367 : : EventQueue & mrEventQueue;
368 : : };
369 : :
370 : : /** Base class to share some common code between
371 : : ShapeClickEventHandler and MouseMoveHandler
372 : :
373 : : @derive override necessary MouseEventHandler interface methods,
374 : : call sendEvent() method to actually process the event.
375 : : */
376 : 0 : class MouseHandlerBase : public MouseEventHandler_
377 : : {
378 : : public:
379 : 0 : MouseHandlerBase( EventQueue& rEventQueue ) :
380 : : mrEventQueue( rEventQueue ),
381 : 0 : maShapeEventMap()
382 : 0 : {}
383 : :
384 : 0 : virtual void dispose()
385 : : {
386 : : // TODO(Q1): Check whether plain vector with swap idiom is
387 : : // okay here
388 : 0 : maShapeEventMap = ImpShapeEventMap();
389 : 0 : }
390 : :
391 : 0 : void addEvent( const EventSharedPtr& rEvent,
392 : : const ShapeSharedPtr& rShape )
393 : : {
394 : 0 : ImpShapeEventMap::iterator aIter;
395 : 0 : if( (aIter=maShapeEventMap.find( rShape )) == maShapeEventMap.end() )
396 : : {
397 : : // no entry for this shape -> create one
398 : : aIter = maShapeEventMap.insert(
399 : : ImpShapeEventMap::value_type( rShape,
400 : 0 : ImpEventQueue() ) ).first;
401 : : }
402 : :
403 : : // add new event to queue
404 : 0 : aIter->second.push( rEvent );
405 : 0 : }
406 : :
407 : : bool isEmpty()
408 : : {
409 : : // find at least one shape with a non-empty queue
410 : : ImpShapeEventMap::reverse_iterator aCurrShape( maShapeEventMap.begin());
411 : : ImpShapeEventMap::reverse_iterator aEndShape( maShapeEventMap.end() );
412 : : while( aCurrShape != aEndShape )
413 : : {
414 : : if( !aCurrShape->second.empty() )
415 : : return false; // at least one non-empty entry found
416 : :
417 : : ++aCurrShape;
418 : : }
419 : :
420 : : return true; // not a single non-empty entry found
421 : : }
422 : :
423 : : protected:
424 : 0 : bool hitTest( const awt::MouseEvent& e,
425 : : ImpShapeEventMap::reverse_iterator& o_rHitShape )
426 : : {
427 : : // find hit shape in map
428 : 0 : const basegfx::B2DPoint aPosition( e.X, e.Y );
429 : :
430 : : // find matching shape (scan reversely, to coarsely match
431 : : // paint order)
432 : 0 : ImpShapeEventMap::reverse_iterator aCurrShape(maShapeEventMap.rbegin());
433 : 0 : const ImpShapeEventMap::reverse_iterator aEndShape( maShapeEventMap.rend() );
434 : 0 : while( aCurrShape != aEndShape )
435 : : {
436 : : // TODO(F2): Get proper geometry polygon from the
437 : : // shape, to avoid having areas outside the shape
438 : : // react on the mouse
439 : 0 : if( aCurrShape->first->getBounds().isInside( aPosition ) &&
440 : 0 : aCurrShape->first->isVisible() )
441 : : {
442 : : // shape hit, and shape is visible - report a
443 : : // hit
444 : 0 : o_rHitShape = aCurrShape;
445 : 0 : return true;
446 : : }
447 : :
448 : 0 : ++aCurrShape;
449 : : }
450 : :
451 : 0 : return false; // nothing hit
452 : : }
453 : :
454 : 0 : bool sendEvent( ImpShapeEventMap::reverse_iterator& io_rHitShape )
455 : : {
456 : : // take next event from queue
457 : 0 : const bool bRet( fireSingleEvent( io_rHitShape->second,
458 : 0 : mrEventQueue ) );
459 : :
460 : : // clear shape entry, if its queue is
461 : : // empty. This is important, since the shapes
462 : : // are held by shared ptr, and might otherwise
463 : : // not get released, even after their owning
464 : : // slide is long gone.
465 : 0 : if( io_rHitShape->second.empty() )
466 : : {
467 : : // this looks funny, since ::std::map does
468 : : // provide an erase( iterator )
469 : : // method. Unfortunately, stlport does not
470 : : // declare the obvious erase(
471 : : // reverse_iterator ) needed here (missing
472 : : // orthogonality, eh?)
473 : 0 : maShapeEventMap.erase( io_rHitShape->first );
474 : : }
475 : :
476 : 0 : return bRet;
477 : : }
478 : :
479 : 0 : bool processEvent( const awt::MouseEvent& e )
480 : : {
481 : 0 : ImpShapeEventMap::reverse_iterator aCurrShape;
482 : :
483 : 0 : if( hitTest( e, aCurrShape ) )
484 : 0 : return sendEvent( aCurrShape );
485 : :
486 : 0 : return false; // did not handle the event
487 : : }
488 : :
489 : : private:
490 : : EventQueue& mrEventQueue;
491 : : ImpShapeEventMap maShapeEventMap;
492 : : };
493 : :
494 : 0 : class ShapeClickEventHandler : public MouseHandlerBase
495 : : {
496 : : public:
497 : 0 : ShapeClickEventHandler( CursorManager& rCursorManager,
498 : : EventQueue& rEventQueue ) :
499 : : MouseHandlerBase( rEventQueue ),
500 : 0 : mrCursorManager( rCursorManager )
501 : 0 : {}
502 : :
503 : 0 : virtual bool handleMouseReleased( const awt::MouseEvent& e )
504 : : {
505 : 0 : if(e.Buttons != awt::MouseButton::LEFT)
506 : 0 : return false;
507 : 0 : return processEvent( e );
508 : : }
509 : :
510 : 0 : virtual bool handleMouseMoved( const awt::MouseEvent& e )
511 : : {
512 : : // TODO(P2): Maybe buffer last shape touched
513 : :
514 : : // if we have a shape click event, and the mouse
515 : : // hovers over this shape, change cursor to hand
516 : 0 : ImpShapeEventMap::reverse_iterator aDummy;
517 : 0 : if( hitTest( e, aDummy ) )
518 : 0 : mrCursorManager.requestCursor( awt::SystemPointer::REFHAND );
519 : :
520 : 0 : return false; // we don't /eat/ this event. Lower prio
521 : : // handler should see it, too.
522 : : }
523 : :
524 : : private:
525 : : CursorManager& mrCursorManager;
526 : : };
527 : :
528 : 0 : class MouseEnterHandler : public MouseHandlerBase
529 : : {
530 : : public:
531 : 0 : MouseEnterHandler( EventQueue& rEventQueue )
532 : : : MouseHandlerBase( rEventQueue ),
533 : 0 : mpLastShape() {}
534 : :
535 : 0 : virtual bool handleMouseMoved( const awt::MouseEvent& e )
536 : : {
537 : : // TODO(P2): Maybe buffer last shape touched, and
538 : : // check against that _first_
539 : :
540 : 0 : ImpShapeEventMap::reverse_iterator aCurr;
541 : 0 : if( hitTest( e, aCurr ) )
542 : : {
543 : 0 : if( aCurr->first != mpLastShape )
544 : : {
545 : : // we actually hit a shape, and it's different
546 : : // from the previous one - thus we just
547 : : // entered it, raise event
548 : 0 : sendEvent( aCurr );
549 : 0 : mpLastShape = aCurr->first;
550 : : }
551 : : }
552 : : else
553 : : {
554 : : // don't hit no shape - thus, last shape is NULL
555 : 0 : mpLastShape.reset();
556 : : }
557 : :
558 : 0 : return false; // we don't /eat/ this event. Lower prio
559 : : // handler should see it, too.
560 : : }
561 : :
562 : : private:
563 : : ShapeSharedPtr mpLastShape;
564 : : };
565 : :
566 : 0 : class MouseLeaveHandler : public MouseHandlerBase
567 : : {
568 : : public:
569 : 0 : MouseLeaveHandler( EventQueue& rEventQueue )
570 : : : MouseHandlerBase( rEventQueue ),
571 : 0 : maLastIter() {}
572 : :
573 : 0 : virtual bool handleMouseMoved( const awt::MouseEvent& e )
574 : : {
575 : : // TODO(P2): Maybe buffer last shape touched, and
576 : : // check against that _first_
577 : :
578 : 0 : ImpShapeEventMap::reverse_iterator aCurr;
579 : 0 : if( hitTest( e, aCurr ) )
580 : : {
581 : 0 : maLastIter = aCurr;
582 : : }
583 : : else
584 : : {
585 : 0 : if( maLastIter->first )
586 : : {
587 : : // last time, we were over a shape, now we're
588 : : // not - we thus just left that shape, raise
589 : : // event
590 : 0 : sendEvent( maLastIter );
591 : : }
592 : :
593 : : // in any case, when we hit this else-branch: no
594 : : // shape hit, thus have to clear maLastIter
595 : 0 : maLastIter = ImpShapeEventMap::reverse_iterator();
596 : : }
597 : :
598 : 0 : return false; // we don't /eat/ this event. Lower prio
599 : : // handler should see it, too.
600 : : }
601 : :
602 : : private:
603 : : ImpShapeEventMap::reverse_iterator maLastIter;
604 : : };
605 : :
606 : : template< typename Handler, typename Functor >
607 : 0 : void UserEventQueue::registerEvent(
608 : : boost::shared_ptr< Handler >& rHandler,
609 : : const EventSharedPtr& rEvent,
610 : : const Functor& rRegistrationFunctor )
611 : : {
612 : 0 : ENSURE_OR_THROW( rEvent,
613 : : "UserEventQueue::registerEvent(): Invalid event" );
614 : :
615 : 0 : if( !rHandler ) {
616 : : // create handler
617 : 0 : rHandler.reset( new Handler( mrEventQueue ) );
618 : : // register handler on EventMultiplexer
619 : 0 : rRegistrationFunctor( rHandler );
620 : : }
621 : :
622 : 0 : rHandler->addEvent( rEvent );
623 : 0 : }
624 : :
625 : : template< typename Handler, typename Arg, typename Functor >
626 : 0 : void UserEventQueue::registerEvent(
627 : : boost::shared_ptr< Handler >& rHandler,
628 : : const EventSharedPtr& rEvent,
629 : : const Arg& rArg,
630 : : const Functor& rRegistrationFunctor )
631 : : {
632 : 0 : ENSURE_OR_THROW( rEvent,
633 : : "UserEventQueue::registerEvent(): Invalid event" );
634 : :
635 : 0 : if( !rHandler ) {
636 : : // create handler
637 : 0 : rHandler.reset( new Handler( mrEventQueue ) );
638 : :
639 : : // register handler on EventMultiplexer
640 : 0 : rRegistrationFunctor( rHandler );
641 : : }
642 : :
643 : 0 : rHandler->addEvent( rEvent, rArg );
644 : 0 : }
645 : :
646 : :
647 : : // Public methods
648 : : // =====================================================
649 : :
650 : 0 : UserEventQueue::UserEventQueue( EventMultiplexer& rMultiplexer,
651 : : EventQueue& rEventQueue,
652 : : CursorManager& rCursorManager )
653 : : : mrMultiplexer( rMultiplexer ),
654 : : mrEventQueue( rEventQueue ),
655 : : mrCursorManager( rCursorManager ),
656 : : mpStartEventHandler(),
657 : : mpEndEventHandler(),
658 : : mpAnimationStartEventHandler(),
659 : : mpAnimationEndEventHandler(),
660 : : mpAudioStoppedEventHandler(),
661 : : mpClickEventHandler(),
662 : : mpSkipEffectEventHandler(),
663 : : mpRewindEffectEventHandler(),
664 : : mpDoubleClickEventHandler(),
665 : : mpMouseEnterHandler(),
666 : : mpMouseLeaveHandler(),
667 : 0 : mbAdvanceOnClick( true )
668 : : {
669 : 0 : }
670 : :
671 : 0 : UserEventQueue::~UserEventQueue()
672 : : {
673 : : try
674 : : {
675 : : // unregister all handlers
676 : 0 : clear();
677 : : }
678 : 0 : catch (uno::Exception &) {
679 : : OSL_FAIL( rtl::OUStringToOString(
680 : : comphelper::anyToString(
681 : : cppu::getCaughtException() ),
682 : : RTL_TEXTENCODING_UTF8 ).getStr() );
683 : : }
684 : 0 : }
685 : :
686 : 0 : void UserEventQueue::clear()
687 : : {
688 : : // unregister and delete all handlers
689 : 0 : if( mpStartEventHandler ) {
690 : 0 : mrMultiplexer.removeSlideStartHandler( mpStartEventHandler );
691 : 0 : mpStartEventHandler.reset();
692 : : }
693 : 0 : if( mpEndEventHandler ) {
694 : 0 : mrMultiplexer.removeSlideEndHandler( mpEndEventHandler );
695 : 0 : mpEndEventHandler.reset();
696 : : }
697 : 0 : if( mpAnimationStartEventHandler ) {
698 : : mrMultiplexer.removeAnimationStartHandler(
699 : 0 : mpAnimationStartEventHandler );
700 : 0 : mpAnimationStartEventHandler.reset();
701 : : }
702 : 0 : if( mpAnimationEndEventHandler ) {
703 : 0 : mrMultiplexer.removeAnimationEndHandler( mpAnimationEndEventHandler );
704 : 0 : mpAnimationEndEventHandler.reset();
705 : : }
706 : 0 : if( mpAudioStoppedEventHandler ) {
707 : 0 : mrMultiplexer.removeAudioStoppedHandler( mpAudioStoppedEventHandler );
708 : 0 : mpAudioStoppedEventHandler.reset();
709 : : }
710 : 0 : if( mpShapeClickEventHandler ) {
711 : 0 : mrMultiplexer.removeClickHandler( mpShapeClickEventHandler );
712 : 0 : mrMultiplexer.removeMouseMoveHandler( mpShapeClickEventHandler );
713 : 0 : mpShapeClickEventHandler.reset();
714 : : }
715 : 0 : if( mpClickEventHandler ) {
716 : 0 : mrMultiplexer.removeClickHandler( mpClickEventHandler );
717 : 0 : mrMultiplexer.removeNextEffectHandler( mpClickEventHandler );
718 : 0 : mpClickEventHandler.reset();
719 : : }
720 : 0 : if(mpSkipEffectEventHandler) {
721 : 0 : mrMultiplexer.removeClickHandler( mpSkipEffectEventHandler );
722 : 0 : mrMultiplexer.removeNextEffectHandler( mpSkipEffectEventHandler );
723 : 0 : mpSkipEffectEventHandler.reset();
724 : : }
725 : 0 : if(mpRewindEffectEventHandler) {
726 : 0 : mrMultiplexer.removeClickHandler( mpRewindEffectEventHandler );
727 : 0 : mpRewindEffectEventHandler.reset();
728 : : }
729 : 0 : if( mpShapeDoubleClickEventHandler ) {
730 : 0 : mrMultiplexer.removeDoubleClickHandler( mpShapeDoubleClickEventHandler );
731 : 0 : mrMultiplexer.removeMouseMoveHandler( mpShapeDoubleClickEventHandler );
732 : 0 : mpShapeDoubleClickEventHandler.reset();
733 : : }
734 : 0 : if( mpDoubleClickEventHandler ) {
735 : 0 : mrMultiplexer.removeDoubleClickHandler( mpDoubleClickEventHandler );
736 : 0 : mpDoubleClickEventHandler.reset();
737 : : }
738 : 0 : if( mpMouseEnterHandler ) {
739 : 0 : mrMultiplexer.removeMouseMoveHandler( mpMouseEnterHandler );
740 : 0 : mpMouseEnterHandler.reset();
741 : : }
742 : 0 : if( mpMouseLeaveHandler ) {
743 : 0 : mrMultiplexer.removeMouseMoveHandler( mpMouseLeaveHandler );
744 : 0 : mpMouseLeaveHandler.reset();
745 : : }
746 : 0 : }
747 : :
748 : 0 : void UserEventQueue::setAdvanceOnClick( bool bAdvanceOnClick )
749 : : {
750 : 0 : mbAdvanceOnClick = bAdvanceOnClick;
751 : :
752 : : // forward to handler, if existing. Otherwise, the handler
753 : : // creation will do the forwarding.
754 : 0 : if( mpClickEventHandler )
755 : 0 : mpClickEventHandler->setAdvanceOnClick( bAdvanceOnClick );
756 : 0 : }
757 : :
758 : 0 : void UserEventQueue::registerAnimationStartEvent(
759 : : const EventSharedPtr& rEvent,
760 : : const uno::Reference< animations::XAnimationNode>& xNode )
761 : : {
762 : : registerEvent( mpAnimationStartEventHandler,
763 : : rEvent,
764 : : xNode,
765 : : boost::bind( &EventMultiplexer::addAnimationStartHandler,
766 : 0 : boost::ref( mrMultiplexer ), _1 ) );
767 : 0 : }
768 : :
769 : 0 : void UserEventQueue::registerAnimationEndEvent(
770 : : const EventSharedPtr& rEvent,
771 : : const uno::Reference<animations::XAnimationNode>& xNode )
772 : : {
773 : : registerEvent( mpAnimationEndEventHandler,
774 : : rEvent,
775 : : xNode,
776 : : boost::bind( &EventMultiplexer::addAnimationEndHandler,
777 : 0 : boost::ref( mrMultiplexer ), _1 ) );
778 : 0 : }
779 : :
780 : 0 : void UserEventQueue::registerAudioStoppedEvent(
781 : : const EventSharedPtr& rEvent,
782 : : const uno::Reference<animations::XAnimationNode>& xNode )
783 : : {
784 : : registerEvent( mpAudioStoppedEventHandler,
785 : : rEvent,
786 : : xNode,
787 : : boost::bind( &EventMultiplexer::addAudioStoppedHandler,
788 : 0 : boost::ref( mrMultiplexer ), _1 ) );
789 : 0 : }
790 : :
791 : 0 : void UserEventQueue::registerShapeClickEvent( const EventSharedPtr& rEvent,
792 : : const ShapeSharedPtr& rShape )
793 : : {
794 : 0 : ENSURE_OR_THROW(
795 : : rEvent,
796 : : "UserEventQueue::registerShapeClickEvent(): Invalid event" );
797 : :
798 : 0 : if( !mpShapeClickEventHandler )
799 : : {
800 : : // create handler
801 : : mpShapeClickEventHandler.reset(
802 : : new ShapeClickEventHandler(mrCursorManager,
803 : 0 : mrEventQueue) );
804 : :
805 : : // register handler on EventMultiplexer
806 : 0 : mrMultiplexer.addClickHandler( mpShapeClickEventHandler, 1.0 );
807 : 0 : mrMultiplexer.addMouseMoveHandler( mpShapeClickEventHandler, 1.0 );
808 : : }
809 : :
810 : 0 : mpShapeClickEventHandler->addEvent( rEvent, rShape );
811 : 0 : }
812 : :
813 : : namespace {
814 : : class ClickEventRegistrationFunctor
815 : : {
816 : : public:
817 : 0 : ClickEventRegistrationFunctor( EventMultiplexer& rMultiplexer,
818 : : double nPrio,
819 : : bool bAdvanceOnClick )
820 : : : mrMultiplexer( rMultiplexer ),
821 : : mnPrio(nPrio),
822 : 0 : mbAdvanceOnClick( bAdvanceOnClick ) {}
823 : :
824 : 0 : void operator()( const boost::shared_ptr<ClickEventHandler>& rHandler )const
825 : : {
826 : : // register the handler on _two_ sources: we want the
827 : : // nextEffect events, e.g. space bar, to trigger clicks, as well!
828 : 0 : mrMultiplexer.addClickHandler( rHandler, mnPrio );
829 : 0 : mrMultiplexer.addNextEffectHandler( rHandler, mnPrio );
830 : :
831 : : // forward advance-on-click state to newly
832 : : // generated handler (that's the only reason why
833 : : // we're called here)
834 : 0 : rHandler->setAdvanceOnClick( mbAdvanceOnClick );
835 : 0 : }
836 : :
837 : : private:
838 : : EventMultiplexer& mrMultiplexer;
839 : : double const mnPrio;
840 : : bool const mbAdvanceOnClick;
841 : : };
842 : : } // anon namespace
843 : :
844 : 0 : void UserEventQueue::registerNextEffectEvent( const EventSharedPtr& rEvent )
845 : : {
846 : : // TODO: better name may be mpNextEffectEventHandler? then we have
847 : : // next effect (=> waiting to be started)
848 : : // skip effect (skipping the currently running one)
849 : : // rewind effect (rewinding back running one and waiting (again)
850 : : // to be started)
851 : : registerEvent( mpClickEventHandler,
852 : : rEvent,
853 : : ClickEventRegistrationFunctor( mrMultiplexer,
854 : : 0.0 /* default prio */,
855 : 0 : mbAdvanceOnClick ) );
856 : 0 : }
857 : :
858 : 0 : void UserEventQueue::registerSkipEffectEvent(
859 : : EventSharedPtr const & pEvent,
860 : : const bool bSkipTriggersNextEffect)
861 : : {
862 : 0 : if(!mpSkipEffectEventHandler)
863 : : {
864 : : mpSkipEffectEventHandler.reset(
865 : 0 : new SkipEffectEventHandler( mrEventQueue, mrMultiplexer ) );
866 : : // register the handler on _two_ sources: we want the
867 : : // nextEffect events, e.g. space bar, to trigger clicks, as well!
868 : : mrMultiplexer.addClickHandler( mpSkipEffectEventHandler,
869 : 0 : -1.0 /* prio below default */ );
870 : : mrMultiplexer.addNextEffectHandler( mpSkipEffectEventHandler,
871 : 0 : -1.0 /* prio below default */ );
872 : : // forward advance-on-click state to newly
873 : : // generated handler (that's the only reason why
874 : : // we're called here)
875 : 0 : mpSkipEffectEventHandler->setAdvanceOnClick( mbAdvanceOnClick );
876 : : }
877 : 0 : mpSkipEffectEventHandler->setSkipTriggersNextEffect(bSkipTriggersNextEffect);
878 : 0 : mpSkipEffectEventHandler->addEvent( pEvent );
879 : 0 : }
880 : :
881 : 0 : void UserEventQueue::registerRewindEffectEvent( EventSharedPtr const& pEvent )
882 : : {
883 : : registerEvent( mpRewindEffectEventHandler,
884 : : pEvent,
885 : : boost::bind( &EventMultiplexer::addClickHandler,
886 : : boost::ref(mrMultiplexer), _1,
887 : 0 : -1.0 /* prio below default */ ) );
888 : 0 : }
889 : :
890 : 0 : void UserEventQueue::registerShapeDoubleClickEvent(
891 : : const EventSharedPtr& rEvent,
892 : : const ShapeSharedPtr& rShape )
893 : : {
894 : 0 : ENSURE_OR_THROW(
895 : : rEvent,
896 : : "UserEventQueue::registerShapeDoubleClickEvent(): Invalid event" );
897 : :
898 : 0 : if( !mpShapeDoubleClickEventHandler )
899 : : {
900 : : // create handler
901 : : mpShapeDoubleClickEventHandler.reset(
902 : : new ShapeClickEventHandler(mrCursorManager,
903 : 0 : mrEventQueue) );
904 : :
905 : : // register handler on EventMultiplexer
906 : : mrMultiplexer.addDoubleClickHandler( mpShapeDoubleClickEventHandler,
907 : 0 : 1.0 );
908 : : mrMultiplexer.addMouseMoveHandler( mpShapeDoubleClickEventHandler,
909 : 0 : 1.0 );
910 : : }
911 : :
912 : 0 : mpShapeDoubleClickEventHandler->addEvent( rEvent, rShape );
913 : 0 : }
914 : :
915 : 0 : void UserEventQueue::registerMouseEnterEvent( const EventSharedPtr& rEvent,
916 : : const ShapeSharedPtr& rShape )
917 : : {
918 : : registerEvent( mpMouseEnterHandler,
919 : : rEvent,
920 : : rShape,
921 : : boost::bind( &EventMultiplexer::addMouseMoveHandler,
922 : : boost::ref( mrMultiplexer ), _1,
923 : 0 : 0.0 /* default prio */ ) );
924 : 0 : }
925 : :
926 : 0 : void UserEventQueue::registerMouseLeaveEvent( const EventSharedPtr& rEvent,
927 : : const ShapeSharedPtr& rShape )
928 : : {
929 : : registerEvent( mpMouseLeaveHandler,
930 : : rEvent,
931 : : rShape,
932 : : boost::bind( &EventMultiplexer::addMouseMoveHandler,
933 : : boost::ref( mrMultiplexer ), _1,
934 : 0 : 0.0 /* default prio */ ) );
935 : 0 : }
936 : :
937 : 0 : void UserEventQueue::callSkipEffectEventHandler (void)
938 : : {
939 : : ::boost::shared_ptr<SkipEffectEventHandler> pHandler (
940 : 0 : ::boost::dynamic_pointer_cast<SkipEffectEventHandler>(mpSkipEffectEventHandler));
941 : 0 : if (pHandler)
942 : 0 : pHandler->skipEffect();
943 : 0 : }
944 : :
945 : : } // namespace internal
946 : 0 : } // namespace presentation
947 : :
948 : : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|