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