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 #include "precompiled_slideshow.hxx" 25 26 #include <canvas/debug.hxx> 27 #include <tools/diagnose_ex.h> 28 #include <canvas/canvastools.hxx> 29 30 #include "eventqueue.hxx" 31 #include "eventmultiplexer.hxx" 32 #include "slideview.hxx" 33 #include "delayevent.hxx" 34 #include "unoview.hxx" 35 36 #include <rtl/instance.hxx> 37 #include <cppuhelper/basemutex.hxx> 38 #include <cppuhelper/compbase2.hxx> 39 #include <cppuhelper/implementationentry.hxx> 40 #include <cppuhelper/interfacecontainer.h> 41 #include <comphelper/make_shared_from_uno.hxx> 42 43 #include <cppcanvas/spritecanvas.hxx> 44 #include <cppcanvas/customsprite.hxx> 45 #include <cppcanvas/vclfactory.hxx> 46 #include <cppcanvas/basegfxfactory.hxx> 47 48 #include <tools/debug.hxx> 49 50 #include <basegfx/range/b1drange.hxx> 51 #include <basegfx/range/b2drange.hxx> 52 #include <basegfx/range/b2irange.hxx> 53 #include <basegfx/point/b2dpoint.hxx> 54 #include <basegfx/polygon/b2dpolygon.hxx> 55 #include <basegfx/matrix/b2dhommatrix.hxx> 56 #include <basegfx/polygon/b2dpolygontools.hxx> 57 #include <basegfx/polygon/b2dpolypolygontools.hxx> 58 #include <basegfx/tools/canvastools.hxx> 59 #include <basegfx/polygon/b2dpolygonclipper.hxx> 60 #include <basegfx/polygon/b2dpolypolygoncutter.hxx> 61 62 #include <com/sun/star/presentation/XSlideShow.hpp> 63 64 #include <boost/noncopyable.hpp> 65 #include <boost/bind.hpp> 66 #include <boost/weak_ptr.hpp> 67 68 #include <vector> 69 #include <iterator> 70 #include <algorithm> 71 72 using namespace com::sun::star; 73 74 namespace slideshow { 75 namespace internal { 76 77 namespace { 78 79 struct StaticUnitRectPoly : public rtl::StaticWithInit<basegfx::B2DPolygon, StaticUnitRectPoly> 80 { 81 basegfx::B2DPolygon operator()() 82 { 83 return basegfx::tools::createUnitPolygon(); 84 } 85 }; 86 87 /** Sprite entry, to store sprite plus priority 88 89 The operator<() defines a strict weak ordering of sprites, sort 90 key is the sprite priority. 91 */ 92 struct SpriteEntry 93 { 94 SpriteEntry( const cppcanvas::CustomSpriteSharedPtr& rSprite, 95 double nPrio ) : 96 mpSprite( rSprite ), 97 mnPriority( nPrio ) 98 { 99 } 100 101 bool operator<(const SpriteEntry& rRHS) const 102 { 103 return mnPriority < rRHS.mnPriority; 104 } 105 106 boost::weak_ptr< cppcanvas::CustomSprite > mpSprite; 107 double mnPriority; 108 }; 109 110 typedef std::vector< SpriteEntry > SpriteVector; 111 112 113 /** Create a clip polygon for slide views 114 115 @param rClip 116 Clip to set (can be empty) 117 118 @param rCanvas 119 Canvas to create the clip polygon for 120 121 @param rUserSize 122 The size of the view. Note that the returned clip will 123 <em>always</em> clip to at least the rect defined herein. 124 125 @return the view clip polygon, in view coordinates, which is 126 guaranteed to at least clip to the view size. 127 */ 128 basegfx::B2DPolyPolygon createClipPolygon( const basegfx::B2DPolyPolygon& rClip, 129 const cppcanvas::CanvasSharedPtr& /*rCanvas*/, 130 const basegfx::B2DSize& rUserSize ) 131 { 132 // setup canvas clipping 133 // ===================== 134 135 // AW: Simplified 136 const basegfx::B2DRange aClipRange(0, 0, rUserSize.getX(), rUserSize.getY()); 137 138 if(rClip.count()) 139 { 140 return basegfx::tools::clipPolyPolygonOnRange(rClip, aClipRange, true, false); 141 } 142 else 143 { 144 return basegfx::B2DPolyPolygon(basegfx::tools::createPolygonFromRect(aClipRange)); 145 } 146 } 147 148 /** Prepare given clip polygon to be stored as the current clip 149 150 Note that this is separate from createClipPolygon(), to allow 151 SlideView implementations to store this intermediate result 152 (createClipPolygon() has to be called every time the view size 153 changes) 154 */ 155 basegfx::B2DPolyPolygon prepareClip( const basegfx::B2DPolyPolygon& rClip ) 156 { 157 basegfx::B2DPolyPolygon aClip( rClip ); 158 159 // TODO(P2): unnecessary, once XCanvas is correctly handling this 160 // AW: Should be no longer necessary; tools are now bezier-safe 161 if( aClip.areControlPointsUsed() ) 162 aClip = basegfx::tools::adaptiveSubdivideByAngle( aClip ); 163 164 // normalize polygon, preparation for clipping 165 // in updateCanvas() 166 aClip = basegfx::tools::correctOrientations(aClip); 167 aClip = basegfx::tools::solveCrossovers(aClip); 168 aClip = basegfx::tools::stripNeutralPolygons(aClip); 169 aClip = basegfx::tools::stripDispensablePolygons(aClip, false); 170 171 return aClip; 172 } 173 174 175 void clearRect( ::cppcanvas::CanvasSharedPtr const& pCanvas, 176 basegfx::B2IRange const& rArea ) 177 { 178 // convert clip polygon to device coordinate system 179 ::basegfx::B2DPolyPolygon const* pClipPoly( pCanvas->getClip() ); 180 if( pClipPoly ) 181 { 182 ::basegfx::B2DPolyPolygon aClipPoly( *pClipPoly ); 183 aClipPoly.transform( pCanvas->getTransformation() ); 184 pCanvas->setClip( aClipPoly ); 185 } 186 187 // set transformation to identity (->device pixel) 188 pCanvas->setTransformation( ::basegfx::B2DHomMatrix() ); 189 190 // #i42440# Fill the _full_ background in 191 // black. Since we had to extend the bitmap by one 192 // pixel, and the bitmap is initialized white, 193 // depending on the slide content a one pixel wide 194 // line will show to the bottom and the right. 195 const ::basegfx::B2DPolygon aPoly( 196 ::basegfx::tools::createPolygonFromRect( 197 basegfx::B2DRange(rArea))); 198 199 ::cppcanvas::PolyPolygonSharedPtr pPolyPoly( 200 ::cppcanvas::BaseGfxFactory::getInstance().createPolyPolygon( pCanvas, 201 aPoly ) ); 202 203 if( pPolyPoly ) 204 { 205 pPolyPoly->setCompositeOp( cppcanvas::CanvasGraphic::SOURCE ); 206 pPolyPoly->setRGBAFillColor( 0x00000000U ); 207 pPolyPoly->draw(); 208 } 209 210 #if defined(VERBOSE) && defined(DBG_UTIL) 211 ::cppcanvas::CanvasSharedPtr pCliplessCanvas( pCanvas->clone() ); 212 pCliplessCanvas->setClip(); 213 214 if( pCanvas->getClip() ) 215 { 216 ::cppcanvas::PolyPolygonSharedPtr pPolyPoly2( 217 ::cppcanvas::BaseGfxFactory::getInstance().createPolyPolygon( pCliplessCanvas, 218 *(pCanvas->getClip()) )); 219 if( pPolyPoly2 ) 220 { 221 pPolyPoly2->setRGBALineColor( 0x008000FFU ); 222 pPolyPoly2->draw(); 223 } 224 } 225 #endif 226 } 227 228 /** Get bounds in pixel 229 230 @param rLayerBounds 231 Bound rect, in user space coordinates 232 233 @param rTransformation 234 User space to device pixel transformation 235 236 @return the layer bounds in pixel, extended by one pixel to the 237 right and bottom 238 */ 239 basegfx::B2IRange getLayerBoundsPixel( basegfx::B2DRange const& rLayerBounds, 240 basegfx::B2DHomMatrix const& rTransformation ) 241 { 242 ::basegfx::B2DRange aTmpRect; 243 ::canvas::tools::calcTransformedRectBounds( aTmpRect, 244 rLayerBounds, 245 rTransformation ); 246 247 if( aTmpRect.isEmpty() ) 248 return ::basegfx::B2IRange(); 249 250 // #i42440# Returned layer size is one pixel too small, as 251 // rendering happens one pixel to the right and below the 252 // actual bound rect. 253 return ::basegfx::B2IRange( ::basegfx::fround(aTmpRect.getMinX()), 254 ::basegfx::fround(aTmpRect.getMinY()), 255 ::basegfx::fround(aTmpRect.getMaxX()) + 1, 256 ::basegfx::fround(aTmpRect.getMaxY()) + 1 ); 257 } 258 259 260 // ---------------------------------------------------------------- 261 262 /** Container class for sprites issued by a ViewLayer 263 264 This class handles the sprite prioritization issues, that are 265 needed for layer sprites (e.g. the need to re-prioritize sprites 266 when the layer changes prio). 267 */ 268 class LayerSpriteContainer 269 { 270 /** Max fill level of maSprites, before we try to prune it from 271 deceased sprites 272 */ 273 enum{ SPRITE_ULLAGE=256 }; 274 275 /** All sprites that have been issued by this container (pruned 276 from time to time, for invalid references). This vector is 277 kept sorted with increasing sprite priority. 278 */ 279 SpriteVector maSprites; 280 281 // Priority of this layer, relative to other view layers 282 basegfx::B1DRange maLayerPrioRange; 283 284 double getSpritePriority( std::size_t nSpriteNum ) const 285 { 286 // divide the available layer range equally between all 287 // sprites, assign upper bound of individual sprite range as 288 // sprite prio (the layer itself gets assigned the lower bound 289 // of sprite 0's individual range): 290 // 291 // | layer 0 | layer 1 | ... 292 // | sprite 0 | sprite 1 | sprite 0 | sprite 1 | ... 293 return maLayerPrioRange.getMinimum() + maLayerPrioRange.getRange()*(nSpriteNum+1)/(maSprites.size()+1); 294 } 295 296 /** Rescan sprite vector, and remove deceased sprites (and reset 297 sprite prio) 298 299 @param aBegin 300 Iterator to the first entry to rescan 301 */ 302 void updateSprites() 303 { 304 SpriteVector aValidSprites; 305 306 // check all sprites for validity and set new priority 307 SpriteVector::iterator aCurrSprite( maSprites.begin() ); 308 const SpriteVector::iterator aEnd( maSprites.end() ); 309 while( aCurrSprite != aEnd ) 310 { 311 cppcanvas::CustomSpriteSharedPtr pCurrSprite( aCurrSprite->mpSprite.lock() ); 312 313 if( pCurrSprite ) 314 { 315 // only copy still valid sprites over to the refreshed 316 // sprite vector. 317 aValidSprites.push_back( *aCurrSprite ); 318 319 pCurrSprite->setPriority( 320 getSpritePriority( aValidSprites.size()-1 )); 321 } 322 323 ++aCurrSprite; 324 } 325 326 // replace sprite list with pruned one 327 maSprites.swap( aValidSprites ); 328 } 329 330 public: 331 LayerSpriteContainer() : 332 maSprites(), 333 maLayerPrioRange() 334 { 335 } 336 337 basegfx::B1DRange getLayerPriority() const 338 { 339 return maLayerPrioRange; 340 } 341 342 void setLayerPriority( const basegfx::B1DRange& rRange ) 343 { 344 if( rRange != maLayerPrioRange ) 345 { 346 maLayerPrioRange = rRange; 347 348 // prune and recalc sprite prios 349 updateSprites(); 350 } 351 } 352 353 void addSprite( const cppcanvas::CustomSpriteSharedPtr& pSprite, 354 double nPriority ) 355 { 356 if( !pSprite ) 357 return; 358 359 SpriteEntry aEntry( pSprite,nPriority ); 360 361 // insert new sprite, such that vector stays sorted 362 SpriteVector::iterator aInsertPos( 363 maSprites.insert( 364 std::lower_bound( maSprites.begin(), 365 maSprites.end(), 366 aEntry ), 367 aEntry )); 368 369 const std::size_t nNumSprites( maSprites.size() ); 370 if( nNumSprites > SPRITE_ULLAGE || 371 maSprites.end() - aInsertPos > 1 ) 372 { 373 // updateSprites() also updates all sprite prios 374 updateSprites(); 375 } 376 else 377 { 378 // added sprite to the end, and not too many sprites in 379 // vector - perform optimized update (only need to set 380 // prio). This basically caters for the common case of 381 // iterated character animations, which generate lots of 382 // sprites, all added to the end. 383 pSprite->setPriority( 384 getSpritePriority( nNumSprites-1 )); 385 } 386 } 387 388 void clear() 389 { 390 maSprites.clear(); 391 } 392 }; 393 394 395 // ---------------------------------------------------------------- 396 397 398 /** This class provides layers for a slide view 399 400 Layers are used to render animations with the correct z order - 401 because sprites are always in front of the static canvas 402 background, shapes that must appear <em<before</em> an animation 403 must also be displayed as a sprite. 404 405 Each layer has a priority assigned to it (valid range [0,1]), which 406 also affects all sprites created for this specific layer - i.e. if 407 the layer priority changes, the sprites change z order together 408 with their parent. 409 */ 410 class SlideViewLayer : public ViewLayer, 411 private boost::noncopyable 412 { 413 // Smart container for all sprites issued by this layer 414 mutable LayerSpriteContainer maSpriteContainer; 415 416 // Bounds of this layer in user space coordinates 417 basegfx::B2DRange maLayerBounds; 418 419 // Bounds of this layer in device pixel 420 mutable basegfx::B2IRange maLayerBoundsPixel; 421 422 // Current clip polygon in user coordinates 423 basegfx::B2DPolyPolygon maClip; 424 425 // Current size of the view in user coordinates 426 basegfx::B2DSize maUserSize; 427 428 // Current overall view transformation 429 basegfx::B2DHomMatrix maTransformation; 430 431 // 'parent' canvas, this viewlayer is associated with 432 const cppcanvas::SpriteCanvasSharedPtr mpSpriteCanvas; 433 434 /** output surface (necessarily a sprite, won't otherwise be able 435 to display anything <em>before</em> other sprites) 436 */ 437 mutable cppcanvas::CustomSpriteSharedPtr mpSprite; 438 439 // actual output canvas retrieved from a sprite 440 mutable cppcanvas::CanvasSharedPtr mpOutputCanvas; 441 442 // ptr back to owning view. needed for isOnView() method 443 View const* const mpParentView; 444 445 public: 446 /** Create a new layer 447 448 @param pCanvas 449 Sprite canvas to create the layer on 450 451 @param rTransform 452 Initial overall canvas transformation 453 454 @param rLayerBounds 455 Initial layer bounds, in view coordinate system 456 */ 457 SlideViewLayer( const cppcanvas::SpriteCanvasSharedPtr& pCanvas, 458 const basegfx::B2DHomMatrix& rTransform, 459 const basegfx::B2DRange& rLayerBounds, 460 const basegfx::B2DSize& rUserSize, 461 View const* const pParentView) : 462 maSpriteContainer(), 463 maLayerBounds(rLayerBounds), 464 maLayerBoundsPixel(), 465 maClip(), 466 maUserSize(rUserSize), 467 maTransformation(rTransform), 468 mpSpriteCanvas(pCanvas), 469 mpSprite(), 470 mpOutputCanvas(), 471 mpParentView(pParentView) 472 { 473 } 474 475 void updateView( const basegfx::B2DHomMatrix& rMatrix, 476 const basegfx::B2DSize& rUserSize ) 477 { 478 maTransformation = rMatrix; 479 maUserSize = rUserSize; 480 481 // limit layer bounds to visible screen 482 maLayerBounds.intersect( basegfx::B2DRange(0.0, 483 0.0, 484 maUserSize.getX(), 485 maUserSize.getY()) ); 486 487 basegfx::B2IRange const& rNewLayerPixel( 488 getLayerBoundsPixel(maLayerBounds, 489 maTransformation) ); 490 if( rNewLayerPixel != maLayerBoundsPixel ) 491 { 492 // re-gen sprite with new size 493 mpOutputCanvas.reset(); 494 mpSprite.reset(); 495 } 496 } 497 498 private: 499 // ViewLayer interface 500 // ---------------------------------------------- 501 502 virtual cppcanvas::CustomSpriteSharedPtr createSprite( 503 const ::basegfx::B2DSize& rSpriteSizePixel, 504 double nPriority ) const 505 { 506 cppcanvas::CustomSpriteSharedPtr pSprite( 507 mpSpriteCanvas->createCustomSprite( rSpriteSizePixel ) ); 508 509 maSpriteContainer.addSprite( pSprite, 510 nPriority ); 511 512 return pSprite; 513 } 514 515 virtual void setPriority( const basegfx::B1DRange& rRange ) 516 { 517 OSL_ENSURE( !rRange.isEmpty() && 518 rRange.getMinimum() >= 1.0, 519 "SlideViewLayer::setPriority(): prio MUST be larger than 1.0 (because " 520 "the background layer already lies there)" ); 521 522 maSpriteContainer.setLayerPriority( rRange ); 523 524 if( mpSprite ) 525 mpSprite->setPriority( rRange.getMinimum() ); 526 } 527 528 virtual basegfx::B2DHomMatrix getTransformation() const 529 { 530 // Offset given transformation by left, top border of given 531 // range (after transformation through given transformation) 532 basegfx::B2DRectangle aTmpRect; 533 canvas::tools::calcTransformedRectBounds( aTmpRect, 534 maLayerBounds, 535 maTransformation ); 536 537 basegfx::B2DHomMatrix aMatrix( maTransformation ); 538 539 // Add translation according to the origin of aTmpRect. Ignore the 540 // translation when aTmpRect was not properly initialized. 541 if ( ! aTmpRect.isEmpty()) 542 { 543 aMatrix.translate( -basegfx::fround(aTmpRect.getMinX()), 544 -basegfx::fround(aTmpRect.getMinY()) ); 545 } 546 547 return aMatrix; 548 } 549 550 virtual basegfx::B2DHomMatrix getSpriteTransformation() const 551 { 552 return maTransformation; 553 } 554 555 virtual void clear() const 556 { 557 // keep layer clip 558 clearRect(getCanvas()->clone(), 559 maLayerBoundsPixel); 560 } 561 562 virtual void clearAll() const 563 { 564 ::cppcanvas::CanvasSharedPtr pCanvas( getCanvas()->clone() ); 565 566 // clear layer clip, to clear whole area 567 pCanvas->setClip(); 568 569 clearRect(pCanvas, 570 maLayerBoundsPixel); 571 } 572 573 virtual bool isOnView(boost::shared_ptr<View> const& rView) const 574 { 575 return rView.get() == mpParentView; 576 } 577 578 virtual cppcanvas::CanvasSharedPtr getCanvas() const 579 { 580 if( !mpOutputCanvas ) 581 { 582 if( !mpSprite ) 583 { 584 maLayerBoundsPixel = getLayerBoundsPixel(maLayerBounds, 585 maTransformation); 586 587 // HACK: ensure at least 1x1 pixel size. clients might 588 // need an actual canvas (e.g. for bound rect 589 // calculations) without rendering anything. Better 590 // solution: introduce something like a reference 591 // canvas for ViewLayers, which is always available. 592 if( maLayerBoundsPixel.isEmpty() ) 593 maLayerBoundsPixel = basegfx::B2IRange(0,0,1,1); 594 595 const basegfx::B2I64Tuple& rSpriteSize(maLayerBoundsPixel.getRange()); 596 mpSprite = mpSpriteCanvas->createCustomSprite( 597 basegfx::B2DVector(sal::static_int_cast<sal_Int32>(rSpriteSize.getX()), 598 sal::static_int_cast<sal_Int32>(rSpriteSize.getY())) ); 599 600 mpSprite->setPriority( 601 maSpriteContainer.getLayerPriority().getMinimum() ); 602 603 #if defined(VERBOSE) && defined(DBG_UTIL) 604 mpSprite->movePixel( 605 basegfx::B2DPoint(maLayerBoundsPixel.getMinimum()) + 606 basegfx::B2DPoint(10,10) ); 607 608 mpSprite->setAlpha(0.5); 609 #else 610 mpSprite->movePixel( 611 basegfx::B2DPoint(maLayerBoundsPixel.getMinimum()) ); 612 613 mpSprite->setAlpha(1.0); 614 #endif 615 mpSprite->show(); 616 } 617 618 ENSURE_OR_THROW( mpSprite, 619 "SlideViewLayer::getCanvas(): no layer sprite" ); 620 621 mpOutputCanvas = mpSprite->getContentCanvas(); 622 623 ENSURE_OR_THROW( mpOutputCanvas, 624 "SlideViewLayer::getCanvas(): sprite doesn't yield a canvas" ); 625 626 // new canvas retrieved - setup transformation and clip 627 mpOutputCanvas->setTransformation( getTransformation() ); 628 mpOutputCanvas->setClip( 629 createClipPolygon( maClip, 630 mpOutputCanvas, 631 maUserSize )); 632 } 633 634 return mpOutputCanvas; 635 } 636 637 virtual void setClip( const basegfx::B2DPolyPolygon& rClip ) 638 { 639 basegfx::B2DPolyPolygon aNewClip = prepareClip( rClip ); 640 641 if( aNewClip != maClip ) 642 { 643 maClip = aNewClip; 644 645 if(mpOutputCanvas ) 646 mpOutputCanvas->setClip( 647 createClipPolygon( maClip, 648 mpOutputCanvas, 649 maUserSize )); 650 } 651 } 652 653 virtual bool resize( const ::basegfx::B2DRange& rArea ) 654 { 655 const bool bRet( maLayerBounds != rArea ); 656 maLayerBounds = rArea; 657 updateView( maTransformation, 658 maUserSize ); 659 660 return bRet; 661 } 662 }; 663 664 665 // --------------------------------------------------------- 666 667 typedef cppu::WeakComponentImplHelper2< 668 ::com::sun::star::util::XModifyListener, 669 ::com::sun::star::awt::XPaintListener> SlideViewBase; 670 671 /** SlideView class 672 673 This class implements the View interface, encapsulating 674 <em>one</em> view a slideshow is displayed on. 675 */ 676 class SlideView : private cppu::BaseMutex, 677 public SlideViewBase, 678 public UnoView 679 { 680 public: 681 SlideView( const uno::Reference<presentation::XSlideShowView>& xView, 682 EventQueue& rEventQueue, 683 EventMultiplexer& rEventMultiplexer ); 684 void updateCanvas(); 685 686 private: 687 // View: 688 virtual ViewLayerSharedPtr createViewLayer( const basegfx::B2DRange& rLayerBounds ) const; 689 virtual bool updateScreen() const; 690 virtual bool paintScreen() const; 691 virtual void setViewSize( const ::basegfx::B2DSize& ); 692 virtual void setCursorShape( sal_Int16 nPointerShape ); 693 694 // ViewLayer interface 695 virtual bool isOnView(boost::shared_ptr<View> const& rView) const; 696 virtual void clear() const; 697 virtual void clearAll() const; 698 virtual cppcanvas::CanvasSharedPtr getCanvas() const; 699 virtual cppcanvas::CustomSpriteSharedPtr createSprite( const ::basegfx::B2DSize& rSpriteSizePixel, 700 double nPriority ) const; 701 virtual void setPriority( const basegfx::B1DRange& rRange ); 702 virtual ::basegfx::B2DHomMatrix getTransformation() const; 703 virtual basegfx::B2DHomMatrix getSpriteTransformation() const; 704 virtual void setClip( const ::basegfx::B2DPolyPolygon& rClip ); 705 virtual bool resize( const ::basegfx::B2DRange& rArea ); 706 707 // UnoView: 708 virtual void _dispose(); 709 virtual uno::Reference<presentation::XSlideShowView> getUnoView()const; 710 virtual void setIsSoundEnabled (const bool bValue); 711 virtual bool isSoundEnabled (void) const; 712 713 // XEventListener: 714 virtual void SAL_CALL disposing( lang::EventObject const& evt ) 715 throw (uno::RuntimeException); 716 // XModifyListener: 717 virtual void SAL_CALL modified( const lang::EventObject& aEvent ) 718 throw (uno::RuntimeException); 719 // XPaintListener: 720 virtual void SAL_CALL windowPaint( const awt::PaintEvent& e ) 721 throw (uno::RuntimeException); 722 723 // WeakComponentImplHelperBase: 724 virtual void SAL_CALL disposing(); 725 726 void updateClip(); 727 728 private: 729 typedef std::vector< boost::weak_ptr<SlideViewLayer> > ViewLayerVector; 730 731 // Prune viewlayers from deceased ones, optionally update them 732 void pruneLayers( bool bWithViewLayerUpdate=false ) const; 733 734 /** Max fill level of maViewLayers, before we try to prune it from 735 deceased layers 736 */ 737 enum{ LAYER_ULLAGE=8 }; 738 739 uno::Reference<presentation::XSlideShowView> mxView; 740 cppcanvas::SpriteCanvasSharedPtr mpCanvas; 741 742 EventMultiplexer& mrEventMultiplexer; 743 EventQueue& mrEventQueue; 744 745 mutable LayerSpriteContainer maSprites; 746 mutable ViewLayerVector maViewLayers; 747 748 basegfx::B2DPolyPolygon maClip; 749 750 basegfx::B2DHomMatrix maViewTransform; 751 basegfx::B2DSize maUserSize; 752 bool mbIsSoundEnabled; 753 }; 754 755 756 SlideView::SlideView( const uno::Reference<presentation::XSlideShowView>& xView, 757 EventQueue& rEventQueue, 758 EventMultiplexer& rEventMultiplexer ) : 759 SlideViewBase( m_aMutex ), 760 mxView( xView ), 761 mpCanvas(), 762 mrEventMultiplexer( rEventMultiplexer ), 763 mrEventQueue( rEventQueue ), 764 maSprites(), 765 maViewLayers(), 766 maClip(), 767 maViewTransform(), 768 maUserSize( 1.0, 1.0 ), // default size: one-by-one rectangle 769 mbIsSoundEnabled(true) 770 { 771 // take care not constructing any UNO references to this _inside_ 772 // ctor, shift that code to createSlideView()! 773 ENSURE_OR_THROW( mxView.is(), 774 "SlideView::SlideView(): Invalid view" ); 775 776 mpCanvas = cppcanvas::VCLFactory::getInstance().createSpriteCanvas( 777 xView->getCanvas() ); 778 ENSURE_OR_THROW( mpCanvas, 779 "Could not create cppcanvas" ); 780 781 geometry::AffineMatrix2D aViewTransform( 782 xView->getTransformation() ); 783 784 if( basegfx::fTools::equalZero( 785 basegfx::B2DVector(aViewTransform.m00, 786 aViewTransform.m10).getLength()) || 787 basegfx::fTools::equalZero( 788 basegfx::B2DVector(aViewTransform.m01, 789 aViewTransform.m11).getLength()) ) 790 { 791 OSL_ENSURE( false, 792 "SlideView::SlideView(): Singular matrix!" ); 793 794 canvas::tools::setIdentityAffineMatrix2D(aViewTransform); 795 } 796 797 basegfx::unotools::homMatrixFromAffineMatrix( 798 maViewTransform, aViewTransform ); 799 800 // once and forever: set fixed prio to this 'layer' (we're always 801 // the background layer) 802 maSprites.setLayerPriority( basegfx::B1DRange(0.0,1.0) ); 803 } 804 805 void SlideView::disposing() 806 { 807 osl::MutexGuard aGuard( m_aMutex ); 808 809 maViewLayers.clear(); 810 maSprites.clear(); 811 mpCanvas.reset(); 812 813 // additionally, also de-register from XSlideShowView 814 if (mxView.is()) 815 { 816 mxView->removeTransformationChangedListener( this ); 817 mxView->removePaintListener( this ); 818 mxView.clear(); 819 } 820 } 821 822 ViewLayerSharedPtr SlideView::createViewLayer( const basegfx::B2DRange& rLayerBounds ) const 823 { 824 osl::MutexGuard aGuard( m_aMutex ); 825 826 ENSURE_OR_THROW( mpCanvas, 827 "SlideView::createViewLayer(): Disposed" ); 828 829 const std::size_t nNumLayers( maViewLayers.size() ); 830 831 // avoid filling up layer vector with lots of deceased layer weak 832 // ptrs 833 if( nNumLayers > LAYER_ULLAGE ) 834 pruneLayers(); 835 836 boost::shared_ptr<SlideViewLayer> pViewLayer( new SlideViewLayer(mpCanvas, 837 getTransformation(), 838 rLayerBounds, 839 maUserSize, 840 this) ); 841 maViewLayers.push_back( pViewLayer ); 842 843 return pViewLayer; 844 } 845 846 bool SlideView::updateScreen() const 847 { 848 osl::MutexGuard aGuard( m_aMutex ); 849 850 ENSURE_OR_RETURN_FALSE( mpCanvas.get(), 851 "SlideView::updateScreen(): Disposed" ); 852 853 return mpCanvas->updateScreen( false ); 854 } 855 856 bool SlideView::paintScreen() const 857 { 858 osl::MutexGuard aGuard( m_aMutex ); 859 860 ENSURE_OR_RETURN_FALSE( mpCanvas.get(), 861 "SlideView::paintScreen(): Disposed" ); 862 863 return mpCanvas->updateScreen( true ); 864 } 865 866 void SlideView::clear() const 867 { 868 osl::MutexGuard aGuard( m_aMutex ); 869 870 OSL_ENSURE( mxView.is() && mpCanvas, 871 "SlideView::clear(): Disposed" ); 872 if( !mxView.is() || !mpCanvas ) 873 return; 874 875 // keep layer clip 876 clearRect(getCanvas()->clone(), 877 getLayerBoundsPixel( 878 basegfx::B2DRange(0,0, 879 maUserSize.getX(), 880 maUserSize.getY()), 881 getTransformation())); 882 } 883 884 void SlideView::clearAll() const 885 { 886 osl::MutexGuard aGuard( m_aMutex ); 887 888 OSL_ENSURE( mxView.is() && mpCanvas, 889 "SlideView::clear(): Disposed" ); 890 if( !mxView.is() || !mpCanvas ) 891 return; 892 893 // clear whole view 894 mxView->clear(); 895 } 896 897 void SlideView::setViewSize( const basegfx::B2DSize& rSize ) 898 { 899 osl::MutexGuard aGuard( m_aMutex ); 900 901 maUserSize = rSize; 902 updateCanvas(); 903 } 904 905 void SlideView::setCursorShape( sal_Int16 nPointerShape ) 906 { 907 osl::MutexGuard const guard( m_aMutex ); 908 909 if (mxView.is()) 910 mxView->setMouseCursor( nPointerShape ); 911 } 912 913 bool SlideView::isOnView(boost::shared_ptr<View> const& rView) const 914 { 915 return rView.get() == this; 916 } 917 918 cppcanvas::CanvasSharedPtr SlideView::getCanvas() const 919 { 920 osl::MutexGuard aGuard( m_aMutex ); 921 922 ENSURE_OR_THROW( mpCanvas, 923 "SlideView::getCanvas(): Disposed" ); 924 925 return mpCanvas; 926 } 927 928 cppcanvas::CustomSpriteSharedPtr SlideView::createSprite( 929 const basegfx::B2DSize& rSpriteSizePixel, 930 double nPriority ) const 931 { 932 osl::MutexGuard aGuard( m_aMutex ); 933 934 ENSURE_OR_THROW( mpCanvas, "SlideView::createSprite(): Disposed" ); 935 936 cppcanvas::CustomSpriteSharedPtr pSprite( 937 mpCanvas->createCustomSprite( rSpriteSizePixel ) ); 938 939 maSprites.addSprite( pSprite, 940 nPriority ); 941 942 return pSprite; 943 } 944 945 void SlideView::setPriority( const basegfx::B1DRange& /*rRange*/ ) 946 { 947 osl::MutexGuard aGuard( m_aMutex ); 948 949 OSL_ENSURE( false, 950 "SlideView::setPriority() is a NOOP for slide view - " 951 "content will always be shown in the background" ); 952 } 953 954 basegfx::B2DHomMatrix SlideView::getTransformation() const 955 { 956 osl::MutexGuard aGuard( m_aMutex ); 957 958 basegfx::B2DHomMatrix aMatrix; 959 aMatrix.scale( 1.0/maUserSize.getX(), 1.0/maUserSize.getY() ); 960 961 return maViewTransform * aMatrix; 962 } 963 964 basegfx::B2DHomMatrix SlideView::getSpriteTransformation() const 965 { 966 return getTransformation(); 967 } 968 969 void SlideView::setClip( const basegfx::B2DPolyPolygon& rClip ) 970 { 971 osl::MutexGuard aGuard( m_aMutex ); 972 973 basegfx::B2DPolyPolygon aNewClip = prepareClip( rClip ); 974 975 if( aNewClip != maClip ) 976 { 977 maClip = aNewClip; 978 979 updateClip(); 980 } 981 } 982 983 bool SlideView::resize( const ::basegfx::B2DRange& /*rArea*/ ) 984 { 985 osl::MutexGuard aGuard( m_aMutex ); 986 987 OSL_ENSURE( false, 988 "SlideView::resize(): ignored for the View, can't change size " 989 "effectively, anyway" ); 990 991 return false; 992 } 993 994 uno::Reference<presentation::XSlideShowView> SlideView::getUnoView() const 995 { 996 osl::MutexGuard aGuard( m_aMutex ); 997 return mxView; 998 } 999 1000 void SlideView::setIsSoundEnabled (const bool bValue) 1001 { 1002 mbIsSoundEnabled = bValue; 1003 } 1004 1005 bool SlideView::isSoundEnabled (void) const 1006 { 1007 return mbIsSoundEnabled; 1008 } 1009 1010 void SlideView::_dispose() 1011 { 1012 dispose(); 1013 } 1014 1015 // XEventListener 1016 void SlideView::disposing( lang::EventObject const& evt ) 1017 throw (uno::RuntimeException) 1018 { 1019 (void)evt; 1020 1021 // no deregistration necessary anymore, XView has left: 1022 osl::MutexGuard const guard( m_aMutex ); 1023 1024 if (mxView.is()) 1025 { 1026 OSL_ASSERT( evt.Source == mxView ); 1027 mxView.clear(); 1028 } 1029 1030 dispose(); 1031 } 1032 1033 // XModifyListener 1034 void SlideView::modified( const lang::EventObject& /*aEvent*/ ) 1035 throw (uno::RuntimeException) 1036 { 1037 osl::MutexGuard const guard( m_aMutex ); 1038 1039 OSL_ENSURE( mxView.is(), "SlideView::modified(): " 1040 "Disposed, but event received from XSlideShowView?!"); 1041 1042 if( !mxView.is() ) 1043 return; 1044 1045 geometry::AffineMatrix2D aViewTransform( 1046 mxView->getTransformation() ); 1047 1048 if( basegfx::fTools::equalZero( 1049 basegfx::B2DVector(aViewTransform.m00, 1050 aViewTransform.m10).getLength()) || 1051 basegfx::fTools::equalZero( 1052 basegfx::B2DVector(aViewTransform.m01, 1053 aViewTransform.m11).getLength()) ) 1054 { 1055 OSL_ENSURE( false, 1056 "SlideView::modified(): Singular matrix!" ); 1057 1058 canvas::tools::setIdentityAffineMatrix2D(aViewTransform); 1059 } 1060 1061 // view transformation really changed? 1062 basegfx::B2DHomMatrix aNewTransform; 1063 basegfx::unotools::homMatrixFromAffineMatrix( 1064 aNewTransform, 1065 aViewTransform ); 1066 1067 if( aNewTransform == maViewTransform ) 1068 return; // No change, nothing to do 1069 1070 maViewTransform = aNewTransform; 1071 1072 updateCanvas(); 1073 1074 // notify view change. Don't call EventMultiplexer directly, this 1075 // might not be the main thread! 1076 mrEventQueue.addEvent( 1077 makeEvent( boost::bind( (bool (EventMultiplexer::*)( 1078 const uno::Reference<presentation::XSlideShowView>&)) 1079 &EventMultiplexer::notifyViewChanged, 1080 boost::ref(mrEventMultiplexer), mxView ), 1081 "EventMultiplexer::notifyViewChanged")); 1082 } 1083 1084 // XPaintListener 1085 void SlideView::windowPaint( const awt::PaintEvent& /*e*/ ) 1086 throw (uno::RuntimeException) 1087 { 1088 osl::MutexGuard aGuard( m_aMutex ); 1089 1090 OSL_ENSURE( mxView.is() && mpCanvas, "Disposed, but event received?!" ); 1091 1092 // notify view clobbering. Don't call EventMultiplexer directly, 1093 // this might not be the main thread! 1094 mrEventQueue.addEvent( 1095 makeEvent( boost::bind( &EventMultiplexer::notifyViewClobbered, 1096 boost::ref(mrEventMultiplexer), mxView ), 1097 "EventMultiplexer::notifyViewClobbered") ); 1098 } 1099 1100 void SlideView::updateCanvas() 1101 { 1102 OSL_ENSURE( mpCanvas, 1103 "SlideView::updateCanvasTransform(): Disposed" ); 1104 1105 if( !mpCanvas || !mxView.is()) 1106 return; 1107 1108 mpCanvas->clear(); // this is unnecessary, strictly speaking. but 1109 // it makes the SlideView behave exactly like a 1110 // sprite-based SlideViewLayer, because those 1111 // are created from scratch after a resize 1112 clearAll(); 1113 mpCanvas->setTransformation( getTransformation() ); 1114 mpCanvas->setClip( 1115 createClipPolygon( maClip, 1116 mpCanvas, 1117 maUserSize )); 1118 1119 // forward update to viewlayers 1120 pruneLayers( true ); 1121 } 1122 1123 void SlideView::updateClip() 1124 { 1125 OSL_ENSURE( mpCanvas, 1126 "SlideView::updateClip(): Disposed" ); 1127 1128 if( !mpCanvas ) 1129 return; 1130 1131 mpCanvas->setClip( 1132 createClipPolygon( maClip, 1133 mpCanvas, 1134 maUserSize )); 1135 1136 pruneLayers( false ); 1137 } 1138 1139 void SlideView::pruneLayers( bool bWithViewLayerUpdate ) const 1140 { 1141 ViewLayerVector aValidLayers; 1142 1143 const basegfx::B2DHomMatrix& rCurrTransform( 1144 getTransformation() ); 1145 1146 // check all layers for validity, and retain only the live ones 1147 ViewLayerVector::const_iterator aCurr( maViewLayers.begin() ); 1148 const ViewLayerVector::const_iterator aEnd( maViewLayers.end() ); 1149 while( aCurr != aEnd ) 1150 { 1151 boost::shared_ptr< SlideViewLayer > pCurrLayer( aCurr->lock() ); 1152 1153 if( pCurrLayer ) 1154 { 1155 aValidLayers.push_back( pCurrLayer ); 1156 1157 if( bWithViewLayerUpdate ) 1158 pCurrLayer->updateView( rCurrTransform, 1159 maUserSize ); 1160 } 1161 1162 ++aCurr; 1163 } 1164 1165 // replace layer list with pruned one 1166 maViewLayers.swap( aValidLayers ); 1167 } 1168 1169 } // anonymous namespace 1170 1171 UnoViewSharedPtr createSlideView( uno::Reference< presentation::XSlideShowView> const& xView, 1172 EventQueue& rEventQueue, 1173 EventMultiplexer& rEventMultiplexer ) 1174 { 1175 boost::shared_ptr<SlideView> const that( 1176 comphelper::make_shared_from_UNO( 1177 new SlideView(xView, 1178 rEventQueue, 1179 rEventMultiplexer))); 1180 1181 // register listeners with XSlideShowView 1182 xView->addTransformationChangedListener( that.get() ); 1183 xView->addPaintListener( that.get() ); 1184 1185 // set new transformation 1186 that->updateCanvas(); 1187 1188 return that; 1189 } 1190 1191 } // namespace internal 1192 } // namespace slideshow 1193 1194