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