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 for_each_sprite( boost::bind( &cppcanvas::Sprite::move, 412 _1, 413 boost::cref(maSpriteRectangle.getMinimum())) ); 414 415 // sprites changed, need screen update 416 mrScreenUpdater.notifyUpdate(); 417 } 418 } 419 420 void RehearseTimingsActivity::paintAllSprites() const 421 { 422 for_each_sprite( 423 boost::bind( &RehearseTimingsActivity::paint, this, 424 // call getContentCanvas() on each sprite: 425 boost::bind( 426 &cppcanvas::CustomSprite::getContentCanvas, _1 ) ) ); 427 } 428 429 void RehearseTimingsActivity::paint( cppcanvas::CanvasSharedPtr const & canvas ) const 430 { 431 // build timer string: 432 const sal_Int32 nTimeSecs = 433 static_cast<sal_Int32>(maElapsedTime.getElapsedTime()); 434 rtl::OUStringBuffer buf; 435 sal_Int32 n = (nTimeSecs / 3600); 436 if (n < 10) 437 buf.append( static_cast<sal_Unicode>('0') ); 438 buf.append( n ); 439 buf.append( static_cast<sal_Unicode>(':') ); 440 n = ((nTimeSecs % 3600) / 60); 441 if (n < 10) 442 buf.append( static_cast<sal_Unicode>('0') ); 443 buf.append( n ); 444 buf.append( static_cast<sal_Unicode>(':') ); 445 n = (nTimeSecs % 60); 446 if (n < 10) 447 buf.append( static_cast<sal_Unicode>('0') ); 448 buf.append( n ); 449 const rtl::OUString time = buf.makeStringAndClear(); 450 451 // create the MetaFile: 452 GDIMetaFile metaFile; 453 VirtualDevice blackHole; 454 metaFile.Record( &blackHole ); 455 metaFile.SetPrefSize( Size( 1, 1 ) ); 456 blackHole.EnableOutput(false); 457 blackHole.SetMapMode( MAP_PIXEL ); 458 blackHole.SetFont( maFont ); 459 Rectangle rect = Rectangle( 0,0, 460 maSpriteSizePixel.getX(), 461 maSpriteSizePixel.getY()); 462 if (mbDrawPressed) 463 { 464 blackHole.SetTextColor( COL_BLACK ); 465 blackHole.SetFillColor( COL_LIGHTGRAY ); 466 blackHole.SetLineColor( COL_GRAY ); 467 } 468 else 469 { 470 blackHole.SetTextColor( COL_BLACK ); 471 blackHole.SetFillColor( COL_WHITE ); 472 blackHole.SetLineColor( COL_GRAY ); 473 } 474 blackHole.DrawRect( rect ); 475 blackHole.GetTextBoundRect( rect, time ); 476 blackHole.DrawText( 477 Point( (maSpriteSizePixel.getX() - rect.getWidth()) / 2, 478 mnYOffset ), time ); 479 480 metaFile.Stop(); 481 metaFile.WindStart(); 482 483 cppcanvas::RendererSharedPtr renderer( 484 cppcanvas::VCLFactory::getInstance().createRenderer( 485 canvas, metaFile, cppcanvas::Renderer::Parameters() ) ); 486 const bool succ = renderer->draw(); 487 OSL_ASSERT( succ ); 488 (void)succ; 489 } 490 491 492 RehearseTimingsActivity::MouseHandler::MouseHandler( RehearseTimingsActivity& rta ) : 493 mrActivity(rta), 494 mbHasBeenClicked(false), 495 mbMouseStartedInArea(false) 496 {} 497 498 void RehearseTimingsActivity::MouseHandler::reset() 499 { 500 mbHasBeenClicked = false; 501 mbMouseStartedInArea = false; 502 } 503 504 bool RehearseTimingsActivity::MouseHandler::isInArea( 505 awt::MouseEvent const & evt ) const 506 { 507 return mrActivity.maSpriteRectangle.isInside( 508 basegfx::B2DPoint( evt.X, evt.Y ) ); 509 } 510 511 void RehearseTimingsActivity::MouseHandler::updatePressedState( 512 const bool pressedState ) const 513 { 514 if( pressedState != mrActivity.mbDrawPressed ) 515 { 516 mrActivity.mbDrawPressed = pressedState; 517 mrActivity.paintAllSprites(); 518 519 mrActivity.mrScreenUpdater.notifyUpdate(); 520 } 521 } 522 523 // MouseEventHandler 524 bool RehearseTimingsActivity::MouseHandler::handleMousePressed( 525 awt::MouseEvent const & evt ) 526 { 527 if( evt.Buttons == awt::MouseButton::LEFT && isInArea(evt) ) 528 { 529 mbMouseStartedInArea = true; 530 updatePressedState(true); 531 return true; // consume event 532 } 533 return false; 534 } 535 536 bool RehearseTimingsActivity::MouseHandler::handleMouseReleased( 537 awt::MouseEvent const & evt ) 538 { 539 if( evt.Buttons == awt::MouseButton::LEFT && mbMouseStartedInArea ) 540 { 541 mbHasBeenClicked = isInArea(evt); // fini if in 542 mbMouseStartedInArea = false; 543 updatePressedState(false); 544 if( !mbHasBeenClicked ) 545 return true; // consume event, else next slide (manual advance) 546 } 547 return false; 548 } 549 550 bool RehearseTimingsActivity::MouseHandler::handleMouseEntered( 551 awt::MouseEvent const & /*evt*/ ) 552 { 553 return false; 554 } 555 556 bool RehearseTimingsActivity::MouseHandler::handleMouseExited( 557 awt::MouseEvent const & /*evt*/ ) 558 { 559 return false; 560 } 561 562 bool RehearseTimingsActivity::MouseHandler::handleMouseDragged( 563 awt::MouseEvent const & evt ) 564 { 565 if( mbMouseStartedInArea ) 566 updatePressedState( isInArea(evt) ); 567 return false; 568 } 569 570 bool RehearseTimingsActivity::MouseHandler::handleMouseMoved( 571 awt::MouseEvent const & /*evt*/ ) 572 { 573 return false; 574 } 575 576 } // namespace internal 577 } // namespace presentation 578