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