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 #include <boost/current_function.hpp> 32 #include <rtl/ustrbuf.hxx> 33 #include <vcl/svapp.hxx> 34 #include <vcl/gdimtf.hxx> 35 #include <vcl/virdev.hxx> 36 #include <vcl/metric.hxx> 37 #include <cppcanvas/vclfactory.hxx> 38 #include <cppcanvas/basegfxfactory.hxx> 39 #include <basegfx/range/b2drange.hxx> 40 41 #include <comphelper/anytostring.hxx> 42 #include <cppuhelper/exc_hlp.hxx> 43 44 #include <com/sun/star/awt/MouseButton.hpp> 45 #include <com/sun/star/awt/MouseEvent.hpp> 46 #include <com/sun/star/rendering/XBitmap.hpp> 47 48 #include "eventqueue.hxx" 49 #include "screenupdater.hxx" 50 #include "eventmultiplexer.hxx" 51 #include "activitiesqueue.hxx" 52 #include "slideshowcontext.hxx" 53 #include "mouseeventhandler.hxx" 54 #include "rehearsetimingsactivity.hxx" 55 56 #include <boost/bind.hpp> 57 #include <algorithm> 58 59 using namespace com::sun::star; 60 using namespace com::sun::star::uno; 61 62 namespace slideshow { 63 namespace internal { 64 65 class RehearseTimingsActivity::WakeupEvent : public Event, 66 private ::boost::noncopyable 67 { 68 public: 69 WakeupEvent( boost::shared_ptr< ::canvas::tools::ElapsedTime > const& pTimeBase, 70 ActivitySharedPtr const& rActivity, 71 ActivitiesQueue & rActivityQueue ) : 72 #if OSL_DEBUG_LEVEL > 1 73 Event(::rtl::OUString::createFromAscii("WakeupEvent")), 74 #endif 75 maTimer(pTimeBase), 76 mnNextTime(0.0), 77 mpActivity(rActivity), 78 mrActivityQueue( rActivityQueue ) 79 {} 80 81 virtual void dispose() {} 82 virtual bool fire() 83 { 84 ActivitySharedPtr pActivity( mpActivity.lock() ); 85 if( !pActivity ) 86 return false; 87 88 return mrActivityQueue.addActivity( pActivity ); 89 } 90 91 virtual bool isCharged() const { return true; } 92 virtual double getActivationTime( double nCurrentTime ) const 93 { 94 const double nElapsedTime( maTimer.getElapsedTime() ); 95 96 return ::std::max( nCurrentTime, 97 nCurrentTime - nElapsedTime + mnNextTime ); 98 } 99 100 /// Start the internal timer 101 void start() { maTimer.reset(); } 102 103 /** Set the next timeout this object should generate. 104 105 @param nextTime 106 Absolute time, measured from the last start() call, 107 when this event should wakeup the Activity again. If 108 your time is relative, simply call start() just before 109 every setNextTimeout() call. 110 */ 111 void setNextTimeout( double nextTime ) { mnNextTime = nextTime; } 112 113 private: 114 ::canvas::tools::ElapsedTime maTimer; 115 double mnNextTime; 116 boost::weak_ptr<Activity> mpActivity; 117 ActivitiesQueue& mrActivityQueue; 118 }; 119 120 class RehearseTimingsActivity::MouseHandler : public MouseEventHandler, 121 private boost::noncopyable 122 { 123 public: 124 explicit MouseHandler( RehearseTimingsActivity& rta ); 125 126 void reset(); 127 bool hasBeenClicked() const { return mbHasBeenClicked; } 128 129 // MouseEventHandler 130 virtual bool handleMousePressed( awt::MouseEvent const & evt ); 131 virtual bool handleMouseReleased( awt::MouseEvent const & evt ); 132 virtual bool handleMouseEntered( awt::MouseEvent const & evt ); 133 virtual bool handleMouseExited( awt::MouseEvent const & evt ); 134 virtual bool handleMouseDragged( awt::MouseEvent const & evt ); 135 virtual bool handleMouseMoved( awt::MouseEvent const & evt ); 136 137 private: 138 bool isInArea( com::sun::star::awt::MouseEvent const & evt ) const; 139 void updatePressedState( const bool pressedState ) const; 140 141 RehearseTimingsActivity& mrActivity; 142 bool mbHasBeenClicked; 143 bool mbMouseStartedInArea; 144 }; 145 146 const sal_Int32 LEFT_BORDER_SPACE = 10; 147 const sal_Int32 LOWER_BORDER_SPACE = 30; 148 149 RehearseTimingsActivity::RehearseTimingsActivity( const SlideShowContext& rContext ) : 150 mrEventQueue(rContext.mrEventQueue), 151 mrScreenUpdater(rContext.mrScreenUpdater), 152 mrEventMultiplexer(rContext.mrEventMultiplexer), 153 mrActivitiesQueue(rContext.mrActivitiesQueue), 154 maElapsedTime( rContext.mrEventQueue.getTimer() ), 155 maViews(), 156 maSpriteRectangle(), 157 maFont( Application::GetSettings().GetStyleSettings().GetInfoFont() ), 158 mpWakeUpEvent(), 159 mpMouseHandler(), 160 maSpriteSizePixel(), 161 mnYOffset(0), 162 mbActive(false), 163 mbDrawPressed(false) 164 { 165 maFont.SetHeight( maFont.GetHeight() * 2 ); 166 maFont.SetWidth( maFont.GetWidth() * 2 ); 167 maFont.SetAlign( ALIGN_BASELINE ); 168 maFont.SetColor( COL_BLACK ); 169 170 // determine sprite size (in pixel): 171 VirtualDevice blackHole; 172 blackHole.EnableOutput(false); 173 blackHole.SetFont( maFont ); 174 blackHole.SetMapMode( MAP_PIXEL ); 175 Rectangle rect; 176 const FontMetric metric( blackHole.GetFontMetric() ); 177 blackHole.GetTextBoundRect( 178 rect, String(RTL_CONSTASCII_USTRINGPARAM("XX:XX:XX")) ); 179 maSpriteSizePixel.setX( rect.getWidth() * 12 / 10 ); 180 maSpriteSizePixel.setY( metric.GetLineHeight() * 11 / 10 ); 181 mnYOffset = (metric.GetAscent() + (metric.GetLineHeight() / 20)); 182 183 std::for_each( rContext.mrViewContainer.begin(), 184 rContext.mrViewContainer.end(), 185 boost::bind( &RehearseTimingsActivity::viewAdded, 186 this, 187 _1 )); 188 } 189 190 RehearseTimingsActivity::~RehearseTimingsActivity() 191 { 192 try 193 { 194 stop(); 195 } 196 catch (uno::Exception &) 197 { 198 OSL_ENSURE( false, rtl::OUStringToOString( 199 comphelper::anyToString( 200 cppu::getCaughtException() ), 201 RTL_TEXTENCODING_UTF8 ).getStr() ); 202 } 203 } 204 205 boost::shared_ptr<RehearseTimingsActivity> RehearseTimingsActivity::create( 206 const SlideShowContext& rContext ) 207 { 208 boost::shared_ptr<RehearseTimingsActivity> pActivity( 209 new RehearseTimingsActivity( rContext )); 210 211 pActivity->mpMouseHandler.reset( 212 new MouseHandler(*pActivity.get()) ); 213 pActivity->mpWakeUpEvent.reset( 214 new WakeupEvent( rContext.mrEventQueue.getTimer(), 215 pActivity, 216 rContext.mrActivitiesQueue )); 217 218 rContext.mrEventMultiplexer.addViewHandler( pActivity ); 219 220 return pActivity; 221 } 222 223 void RehearseTimingsActivity::start() 224 { 225 maElapsedTime.reset(); 226 mbDrawPressed = false; 227 mbActive = true; 228 229 // paint and show all sprites: 230 paintAllSprites(); 231 for_each_sprite( boost::bind( &cppcanvas::Sprite::show, _1 ) ); 232 233 mrActivitiesQueue.addActivity( shared_from_this() ); 234 235 mpMouseHandler->reset(); 236 mrEventMultiplexer.addClickHandler( 237 mpMouseHandler, 42 /* highest prio of all, > 3.0 */ ); 238 mrEventMultiplexer.addMouseMoveHandler( 239 mpMouseHandler, 42 /* highest prio of all, > 3.0 */ ); 240 } 241 242 double RehearseTimingsActivity::stop() 243 { 244 mrEventMultiplexer.removeMouseMoveHandler( mpMouseHandler ); 245 mrEventMultiplexer.removeClickHandler( mpMouseHandler ); 246 247 mbActive = false; // will be removed from queue 248 249 for_each_sprite( boost::bind( &cppcanvas::Sprite::hide, _1 ) ); 250 251 return maElapsedTime.getElapsedTime(); 252 } 253 254 bool RehearseTimingsActivity::hasBeenClicked() const 255 { 256 if (mpMouseHandler) 257 return mpMouseHandler->hasBeenClicked(); 258 return false; 259 } 260 261 // Disposable: 262 void RehearseTimingsActivity::dispose() 263 { 264 stop(); 265 266 mpWakeUpEvent.reset(); 267 mpMouseHandler.reset(); 268 269 ViewsVecT().swap( maViews ); 270 } 271 272 // Activity: 273 double RehearseTimingsActivity::calcTimeLag() const 274 { 275 return 0.0; 276 } 277 278 bool RehearseTimingsActivity::perform() 279 { 280 if( !isActive() ) 281 return false; 282 283 if( !mpWakeUpEvent ) 284 return false; 285 286 mpWakeUpEvent->start(); 287 mpWakeUpEvent->setNextTimeout( 0.5 ); 288 mrEventQueue.addEvent( mpWakeUpEvent ); 289 290 paintAllSprites(); 291 292 // sprites changed, need screen update 293 mrScreenUpdater.notifyUpdate(); 294 295 return false; // don't reinsert, WakeupEvent will perform 296 // that after the given timeout 297 } 298 299 bool RehearseTimingsActivity::isActive() const 300 { 301 return mbActive; 302 } 303 304 void RehearseTimingsActivity::dequeued() 305 { 306 // not used here 307 } 308 309 void RehearseTimingsActivity::end() 310 { 311 if (isActive()) 312 { 313 stop(); 314 mbActive = false; 315 } 316 } 317 318 basegfx::B2DRange RehearseTimingsActivity::calcSpriteRectangle( UnoViewSharedPtr const& rView ) const 319 { 320 const Reference<rendering::XBitmap> xBitmap( rView->getCanvas()->getUNOCanvas(), 321 UNO_QUERY ); 322 if( !xBitmap.is() ) 323 return basegfx::B2DRange(); 324 325 const geometry::IntegerSize2D realSize( xBitmap->getSize() ); 326 // pixel: 327 basegfx::B2DPoint spritePos( 328 std::min<sal_Int32>( realSize.Width, LEFT_BORDER_SPACE ), 329 std::max<sal_Int32>( 0, realSize.Height - maSpriteSizePixel.getY() 330 - LOWER_BORDER_SPACE ) ); 331 basegfx::B2DHomMatrix transformation( rView->getTransformation() ); 332 transformation.invert(); 333 spritePos *= transformation; 334 basegfx::B2DSize spriteSize( maSpriteSizePixel.getX(), 335 maSpriteSizePixel.getY() ); 336 spriteSize *= transformation; 337 return basegfx::B2DRange( 338 spritePos.getX(), spritePos.getY(), 339 spritePos.getX() + spriteSize.getX(), 340 spritePos.getY() + spriteSize.getY() ); 341 } 342 343 void RehearseTimingsActivity::viewAdded( const UnoViewSharedPtr& rView ) 344 { 345 cppcanvas::CustomSpriteSharedPtr sprite( 346 rView->createSprite( basegfx::B2DSize( 347 maSpriteSizePixel.getX()+2, 348 maSpriteSizePixel.getY()+2 ), 349 1001.0 )); // sprite should be in front of all 350 // other sprites 351 sprite->setAlpha( 0.8 ); 352 const basegfx::B2DRange spriteRectangle( 353 calcSpriteRectangle( rView ) ); 354 sprite->move( basegfx::B2DPoint( 355 spriteRectangle.getMinX(), 356 spriteRectangle.getMinY() ) ); 357 358 if( maViews.empty() ) 359 maSpriteRectangle = spriteRectangle; 360 361 maViews.push_back( ViewsVecT::value_type( rView, sprite ) ); 362 363 if (isActive()) 364 sprite->show(); 365 } 366 367 void RehearseTimingsActivity::viewRemoved( const UnoViewSharedPtr& rView ) 368 { 369 maViews.erase( 370 std::remove_if( 371 maViews.begin(), maViews.end(), 372 boost::bind( 373 std::equal_to<UnoViewSharedPtr>(), 374 rView, 375 // select view: 376 boost::bind( std::select1st<ViewsVecT::value_type>(), _1 ))), 377 maViews.end() ); 378 } 379 380 void RehearseTimingsActivity::viewChanged( const UnoViewSharedPtr& rView ) 381 { 382 // find entry corresponding to modified view 383 ViewsVecT::iterator aModifiedEntry( 384 std::find_if( 385 maViews.begin(), 386 maViews.end(), 387 boost::bind( 388 std::equal_to<UnoViewSharedPtr>(), 389 rView, 390 // select view: 391 boost::bind( std::select1st<ViewsVecT::value_type>(), _1 )))); 392 393 OSL_ASSERT( aModifiedEntry != maViews.end() ); 394 if( aModifiedEntry == maViews.end() ) 395 return; 396 397 // new sprite pos, transformation might have changed: 398 maSpriteRectangle = calcSpriteRectangle( rView ); 399 400 // reposition sprite: 401 aModifiedEntry->second->move( maSpriteRectangle.getMinimum() ); 402 403 // sprites changed, need screen update 404 mrScreenUpdater.notifyUpdate( rView ); 405 } 406 407 void RehearseTimingsActivity::viewsChanged() 408 { 409 if( !maViews.empty() ) 410 { 411 // new sprite pos, transformation might have changed: 412 maSpriteRectangle = calcSpriteRectangle( maViews.front().first ); 413 414 // reposition sprites 415 for_each_sprite( boost::bind( &cppcanvas::Sprite::move, 416 _1, 417 boost::cref(maSpriteRectangle.getMinimum())) ); 418 419 // sprites changed, need screen update 420 mrScreenUpdater.notifyUpdate(); 421 } 422 } 423 424 void RehearseTimingsActivity::paintAllSprites() const 425 { 426 for_each_sprite( 427 boost::bind( &RehearseTimingsActivity::paint, this, 428 // call getContentCanvas() on each sprite: 429 boost::bind( 430 &cppcanvas::CustomSprite::getContentCanvas, _1 ) ) ); 431 } 432 433 void RehearseTimingsActivity::paint( cppcanvas::CanvasSharedPtr const & canvas ) const 434 { 435 // build timer string: 436 const sal_Int32 nTimeSecs = 437 static_cast<sal_Int32>(maElapsedTime.getElapsedTime()); 438 rtl::OUStringBuffer buf; 439 sal_Int32 n = (nTimeSecs / 3600); 440 if (n < 10) 441 buf.append( static_cast<sal_Unicode>('0') ); 442 buf.append( n ); 443 buf.append( static_cast<sal_Unicode>(':') ); 444 n = ((nTimeSecs % 3600) / 60); 445 if (n < 10) 446 buf.append( static_cast<sal_Unicode>('0') ); 447 buf.append( n ); 448 buf.append( static_cast<sal_Unicode>(':') ); 449 n = (nTimeSecs % 60); 450 if (n < 10) 451 buf.append( static_cast<sal_Unicode>('0') ); 452 buf.append( n ); 453 const rtl::OUString time = buf.makeStringAndClear(); 454 455 // create the MetaFile: 456 GDIMetaFile metaFile; 457 VirtualDevice blackHole; 458 metaFile.Record( &blackHole ); 459 metaFile.SetPrefSize( Size( 1, 1 ) ); 460 blackHole.EnableOutput(false); 461 blackHole.SetMapMode( MAP_PIXEL ); 462 blackHole.SetFont( maFont ); 463 Rectangle rect = Rectangle( 0,0, 464 maSpriteSizePixel.getX(), 465 maSpriteSizePixel.getY()); 466 if (mbDrawPressed) 467 { 468 blackHole.SetTextColor( COL_BLACK ); 469 blackHole.SetFillColor( COL_LIGHTGRAY ); 470 blackHole.SetLineColor( COL_GRAY ); 471 } 472 else 473 { 474 blackHole.SetTextColor( COL_BLACK ); 475 blackHole.SetFillColor( COL_WHITE ); 476 blackHole.SetLineColor( COL_GRAY ); 477 } 478 blackHole.DrawRect( rect ); 479 blackHole.GetTextBoundRect( rect, time ); 480 blackHole.DrawText( 481 Point( (maSpriteSizePixel.getX() - rect.getWidth()) / 2, 482 mnYOffset ), time ); 483 484 metaFile.Stop(); 485 metaFile.WindStart(); 486 487 cppcanvas::RendererSharedPtr renderer( 488 cppcanvas::VCLFactory::getInstance().createRenderer( 489 canvas, metaFile, cppcanvas::Renderer::Parameters() ) ); 490 const bool succ = renderer->draw(); 491 OSL_ASSERT( succ ); 492 (void)succ; 493 } 494 495 496 RehearseTimingsActivity::MouseHandler::MouseHandler( RehearseTimingsActivity& rta ) : 497 mrActivity(rta), 498 mbHasBeenClicked(false), 499 mbMouseStartedInArea(false) 500 {} 501 502 void RehearseTimingsActivity::MouseHandler::reset() 503 { 504 mbHasBeenClicked = false; 505 mbMouseStartedInArea = false; 506 } 507 508 bool RehearseTimingsActivity::MouseHandler::isInArea( 509 awt::MouseEvent const & evt ) const 510 { 511 return mrActivity.maSpriteRectangle.isInside( 512 basegfx::B2DPoint( evt.X, evt.Y ) ); 513 } 514 515 void RehearseTimingsActivity::MouseHandler::updatePressedState( 516 const bool pressedState ) const 517 { 518 if( pressedState != mrActivity.mbDrawPressed ) 519 { 520 mrActivity.mbDrawPressed = pressedState; 521 mrActivity.paintAllSprites(); 522 523 mrActivity.mrScreenUpdater.notifyUpdate(); 524 } 525 } 526 527 // MouseEventHandler 528 bool RehearseTimingsActivity::MouseHandler::handleMousePressed( 529 awt::MouseEvent const & evt ) 530 { 531 if( evt.Buttons == awt::MouseButton::LEFT && isInArea(evt) ) 532 { 533 mbMouseStartedInArea = true; 534 updatePressedState(true); 535 return true; // consume event 536 } 537 return false; 538 } 539 540 bool RehearseTimingsActivity::MouseHandler::handleMouseReleased( 541 awt::MouseEvent const & evt ) 542 { 543 if( evt.Buttons == awt::MouseButton::LEFT && mbMouseStartedInArea ) 544 { 545 mbHasBeenClicked = isInArea(evt); // fini if in 546 mbMouseStartedInArea = false; 547 updatePressedState(false); 548 if( !mbHasBeenClicked ) 549 return true; // consume event, else next slide (manual advance) 550 } 551 return false; 552 } 553 554 bool RehearseTimingsActivity::MouseHandler::handleMouseEntered( 555 awt::MouseEvent const & /*evt*/ ) 556 { 557 return false; 558 } 559 560 bool RehearseTimingsActivity::MouseHandler::handleMouseExited( 561 awt::MouseEvent const & /*evt*/ ) 562 { 563 return false; 564 } 565 566 bool RehearseTimingsActivity::MouseHandler::handleMouseDragged( 567 awt::MouseEvent const & evt ) 568 { 569 if( mbMouseStartedInArea ) 570 updatePressedState( isInArea(evt) ); 571 return false; 572 } 573 574 bool RehearseTimingsActivity::MouseHandler::handleMouseMoved( 575 awt::MouseEvent const & /*evt*/ ) 576 { 577 return false; 578 } 579 580 } // namespace internal 581 } // namespace presentation 582