diff options
Diffstat (limited to 'slideshow/source/engine/slideview.cxx')
-rw-r--r-- | slideshow/source/engine/slideview.cxx | 1201 |
1 files changed, 1201 insertions, 0 deletions
diff --git a/slideshow/source/engine/slideview.cxx b/slideshow/source/engine/slideview.cxx new file mode 100644 index 000000000000..dcca4a51b4d7 --- /dev/null +++ b/slideshow/source/engine/slideview.cxx @@ -0,0 +1,1201 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: slideview.cxx,v $ + * $Revision: 1.9 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +#include "precompiled_slideshow.hxx" + +#include <canvas/debug.hxx> +#include <tools/diagnose_ex.h> +#include <canvas/canvastools.hxx> + +#include "eventqueue.hxx" +#include "eventmultiplexer.hxx" +#include "slideview.hxx" +#include "delayevent.hxx" +#include "unoview.hxx" + +#include <rtl/instance.hxx> +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/compbase2.hxx> +#include <cppuhelper/implementationentry.hxx> +#include <cppuhelper/interfacecontainer.h> +#include <comphelper/make_shared_from_uno.hxx> + +#include <cppcanvas/spritecanvas.hxx> +#include <cppcanvas/customsprite.hxx> +#include <cppcanvas/vclfactory.hxx> +#include <cppcanvas/basegfxfactory.hxx> + +#include <tools/debug.hxx> + +#include <basegfx/range/b1drange.hxx> +#include <basegfx/range/b2drange.hxx> +#include <basegfx/range/b2irange.hxx> +#include <basegfx/point/b2dpoint.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <basegfx/tools/canvastools.hxx> +#include <basegfx/polygon/b2dpolygonclipper.hxx> +#include <basegfx/polygon/b2dpolypolygoncutter.hxx> + +#include <com/sun/star/presentation/XSlideShow.hpp> + +#include <boost/noncopyable.hpp> +#include <boost/bind.hpp> +#include <boost/weak_ptr.hpp> + +#include <vector> +#include <iterator> +#include <algorithm> + +using namespace com::sun::star; + +namespace slideshow { +namespace internal { + +namespace { + +struct StaticUnitRectPoly : public rtl::StaticWithInit<basegfx::B2DPolygon, StaticUnitRectPoly> +{ + basegfx::B2DPolygon operator()() + { + return basegfx::tools::createPolygonFromRect( + basegfx::B2DRectangle( 0.0, 0.0, + 1.0, 1.0 ) ); + } +}; + +/** Sprite entry, to store sprite plus priority + + The operator<() defines a strict weak ordering of sprites, sort + key is the sprite priority. + */ +struct SpriteEntry +{ + SpriteEntry( const cppcanvas::CustomSpriteSharedPtr& rSprite, + double nPrio ) : + mpSprite( rSprite ), + mnPriority( nPrio ) + { + } + + bool operator<(const SpriteEntry& rRHS) const + { + return mnPriority < rRHS.mnPriority; + } + + boost::weak_ptr< cppcanvas::CustomSprite > mpSprite; + double mnPriority; +}; + +typedef std::vector< SpriteEntry > SpriteVector; + + +/** Create a clip polygon for slide views + + @param rClip + Clip to set (can be empty) + + @param rCanvas + Canvas to create the clip polygon for + + @param rUserSize + The size of the view. Note that the returned clip will + <em>always</em> clip to at least the rect defined herein. + + @return the view clip polygon, in view coordinates, which is + guaranteed to at least clip to the view size. + */ +basegfx::B2DPolyPolygon createClipPolygon( const basegfx::B2DPolyPolygon& rClip, + const cppcanvas::CanvasSharedPtr& /*rCanvas*/, + const basegfx::B2DSize& rUserSize ) +{ + // setup canvas clipping + // ===================== + + // AW: Simplified + const basegfx::B2DRange aClipRange(0, 0, rUserSize.getX(), rUserSize.getY()); + + if(rClip.count()) + { + return basegfx::tools::clipPolyPolygonOnRange(rClip, aClipRange, true, false); + } + else + { + return basegfx::B2DPolyPolygon(basegfx::tools::createPolygonFromRect(aClipRange)); + } +} + +/** Prepare given clip polygon to be stored as the current clip + + Note that this is separate from createClipPolygon(), to allow + SlideView implementations to store this intermediate result + (createClipPolygon() has to be called every time the view size + changes) + */ +basegfx::B2DPolyPolygon prepareClip( const basegfx::B2DPolyPolygon& rClip ) +{ + basegfx::B2DPolyPolygon aClip( rClip ); + + // TODO(P2): unnecessary, once XCanvas is correctly handling this + // AW: Should be no longer necessary; tools are now bezier-safe + if( aClip.areControlPointsUsed() ) + aClip = basegfx::tools::adaptiveSubdivideByAngle( aClip ); + + // normalize polygon, preparation for clipping + // in updateCanvas() + aClip = basegfx::tools::correctOrientations(aClip); + aClip = basegfx::tools::solveCrossovers(aClip); + aClip = basegfx::tools::stripNeutralPolygons(aClip); + aClip = basegfx::tools::stripDispensablePolygons(aClip, false); + + return aClip; +} + + +void clearRect( ::cppcanvas::CanvasSharedPtr const& pCanvas, + basegfx::B2IRange const& rArea ) +{ + // convert clip polygon to device coordinate system + ::basegfx::B2DPolyPolygon const* pClipPoly( pCanvas->getClip() ); + if( pClipPoly ) + { + ::basegfx::B2DPolyPolygon aClipPoly( *pClipPoly ); + aClipPoly.transform( pCanvas->getTransformation() ); + pCanvas->setClip( aClipPoly ); + } + + // set transformation to identitiy (->device pixel) + pCanvas->setTransformation( ::basegfx::B2DHomMatrix() ); + + // #i42440# Fill the _full_ background in + // black. Since we had to extend the bitmap by one + // pixel, and the bitmap is initialized white, + // depending on the slide content a one pixel wide + // line will show to the bottom and the right. + const ::basegfx::B2DPolygon aPoly( + ::basegfx::tools::createPolygonFromRect( + basegfx::B2DRange(rArea))); + + ::cppcanvas::PolyPolygonSharedPtr pPolyPoly( + ::cppcanvas::BaseGfxFactory::getInstance().createPolyPolygon( pCanvas, + aPoly ) ); + + if( pPolyPoly ) + { + pPolyPoly->setCompositeOp( cppcanvas::CanvasGraphic::SOURCE ); + pPolyPoly->setRGBAFillColor( 0x00000000U ); + pPolyPoly->draw(); + } + +#if defined(VERBOSE) && defined(DBG_UTIL) + ::cppcanvas::CanvasSharedPtr pCliplessCanvas( pCanvas->clone() ); + pCliplessCanvas->setClip(); + + if( pCanvas->getClip() ) + { + ::cppcanvas::PolyPolygonSharedPtr pPolyPoly2( + ::cppcanvas::BaseGfxFactory::getInstance().createPolyPolygon( pCliplessCanvas, + *(pCanvas->getClip()) )); + if( pPolyPoly2 ) + { + pPolyPoly2->setRGBALineColor( 0x008000FFU ); + pPolyPoly2->draw(); + } + } +#endif +} + +/** Get bounds in pixel + + @param rLayerBounds + Bound rect, in user space coordinates + + @param rTransformation + User space to device pixel transformation + + @return the layer bounds in pixel, extended by one pixel to the + right and bottom + */ +basegfx::B2IRange getLayerBoundsPixel( basegfx::B2DRange const& rLayerBounds, + basegfx::B2DHomMatrix const& rTransformation ) +{ + ::basegfx::B2DRange aTmpRect; + ::canvas::tools::calcTransformedRectBounds( aTmpRect, + rLayerBounds, + rTransformation ); + + if( aTmpRect.isEmpty() ) + return ::basegfx::B2IRange(); + + // #i42440# Returned layer size is one pixel too small, as + // rendering happens one pixel to the right and below the + // actual bound rect. + return ::basegfx::B2IRange( ::basegfx::fround(aTmpRect.getMinX()), + ::basegfx::fround(aTmpRect.getMinY()), + ::basegfx::fround(aTmpRect.getMaxX()) + 1, + ::basegfx::fround(aTmpRect.getMaxY()) + 1 ); +} + + +// ---------------------------------------------------------------- + +/** Container class for sprites issued by a ViewLayer + + This class handles the sprite prioritization issues, that are + needed for layer sprites (e.g. the need to re-prioritize sprites + when the layer changes prio). + */ +class LayerSpriteContainer +{ + /** Max fill level of maSprites, before we try to prune it from + deceased sprites + */ + enum{ SPRITE_ULLAGE=256 }; + + /** All sprites that have been issued by this container (pruned + from time to time, for invalid references). This vector is + kept sorted with increasing sprite priority. + */ + SpriteVector maSprites; + + /// Priority of this layer, relative to other view layers + basegfx::B1DRange maLayerPrioRange; + + double getSpritePriority( std::size_t nSpriteNum ) const + { + // divide the available layer range equally between all + // sprites, assign upper bound of individual sprite range as + // sprite prio (the layer itself gets assigned the lower bound + // of sprite 0's individual range): + // + // | layer 0 | layer 1 | ... + // | sprite 0 | sprite 1 | sprite 0 | sprite 1 | ... + return maLayerPrioRange.getMinimum() + maLayerPrioRange.getRange()*(nSpriteNum+1)/(maSprites.size()+1); + } + + /** Rescan sprite vector, and remove deceased sprites (and reset + sprite prio) + + @param aBegin + Iterator to the first entry to rescan + */ + void updateSprites() + { + SpriteVector aValidSprites; + + // check all sprites for validity and set new priority + SpriteVector::iterator aCurrSprite( maSprites.begin() ); + const SpriteVector::iterator aEnd( maSprites.end() ); + while( aCurrSprite != aEnd ) + { + cppcanvas::CustomSpriteSharedPtr pCurrSprite( aCurrSprite->mpSprite.lock() ); + + if( pCurrSprite ) + { + // only copy still valid sprites over to the refreshed + // sprite vector. + aValidSprites.push_back( *aCurrSprite ); + + pCurrSprite->setPriority( + getSpritePriority( aValidSprites.size()-1 )); + } + + ++aCurrSprite; + } + + // replace sprite list with pruned one + maSprites.swap( aValidSprites ); + } + +public: + LayerSpriteContainer() : + maSprites(), + maLayerPrioRange() + { + } + + basegfx::B1DRange getLayerPriority() const + { + return maLayerPrioRange; + } + + void setLayerPriority( const basegfx::B1DRange& rRange ) + { + if( rRange != maLayerPrioRange ) + { + maLayerPrioRange = rRange; + + // prune and recalc sprite prios + updateSprites(); + } + } + + void addSprite( const cppcanvas::CustomSpriteSharedPtr& pSprite, + double nPriority ) + { + if( !pSprite ) + return; + + SpriteEntry aEntry( pSprite,nPriority ); + + // insert new sprite, such that vector stays sorted + SpriteVector::iterator aInsertPos( + maSprites.insert( + std::lower_bound( maSprites.begin(), + maSprites.end(), + aEntry ), + aEntry )); + + const std::size_t nNumSprites( maSprites.size() ); + if( nNumSprites > SPRITE_ULLAGE || + maSprites.end() - aInsertPos > 1 ) + { + // updateSprites() also updates all sprite prios + updateSprites(); + } + else + { + // added sprite to the end, and not too many sprites in + // vector - perform optimized update (only need to set + // prio). This basically caters for the common case of + // iterated character animations, which generate lots of + // sprites, all added to the end. + pSprite->setPriority( + getSpritePriority( nNumSprites-1 )); + } + } + + void clear() + { + maSprites.clear(); + } +}; + + +// ---------------------------------------------------------------- + + +/** This class provides layers for a slide view + + Layers are used to render animations with the correct z order - + because sprites are always in front of the static canvas + background, shapes that must appear <em<before</em> an animation + must also be displayed as a sprite. + + Each layer has a priority assigned to it (valid range [0,1]), which + also affects all sprites created for this specific layer - i.e. if + the layer priority changes, the sprites change z order together + with their parent. + */ +class SlideViewLayer : public ViewLayer, + private boost::noncopyable +{ + /// Smart container for all sprites issued by this layer + mutable LayerSpriteContainer maSpriteContainer; + + /// Bounds of this layer in user space coordinates + basegfx::B2DRange maLayerBounds; + + /// Bounds of this layer in device pixel + mutable basegfx::B2IRange maLayerBoundsPixel; + + /// Current clip polygon in user coordinates + basegfx::B2DPolyPolygon maClip; + + /// Current size of the view in user coordinates + basegfx::B2DSize maUserSize; + + /// Current overall view transformation + basegfx::B2DHomMatrix maTransformation; + + /// 'parent' canvas, this viewlayer is associated with + const cppcanvas::SpriteCanvasSharedPtr mpSpriteCanvas; + + /** output surface (necessarily a sprite, won't otherwise be able + to display anything <em>before</em> other sprites) + */ + mutable cppcanvas::CustomSpriteSharedPtr mpSprite; + + /// actual output canvas retrieved from a sprite + mutable cppcanvas::CanvasSharedPtr mpOutputCanvas; + + /// ptr back to owning view. needed for isOnView() method + View const* const mpParentView; + +public: + /** Create a new layer + + @param pCanvas + Sprite canvas to create the layer on + + @param rTransform + Initial overall canvas transformation + + @param rLayerBounds + Initial layer bounds, in view coordinate system + */ + SlideViewLayer( const cppcanvas::SpriteCanvasSharedPtr& pCanvas, + const basegfx::B2DHomMatrix& rTransform, + const basegfx::B2DRange& rLayerBounds, + const basegfx::B2DSize& rUserSize, + View const* const pParentView) : + maSpriteContainer(), + maLayerBounds(rLayerBounds), + maLayerBoundsPixel(), + maClip(), + maUserSize(rUserSize), + maTransformation(rTransform), + mpSpriteCanvas(pCanvas), + mpSprite(), + mpOutputCanvas(), + mpParentView(pParentView) + { + } + + void updateView( const basegfx::B2DHomMatrix& rMatrix, + const basegfx::B2DSize& rUserSize ) + { + maTransformation = rMatrix; + maUserSize = rUserSize; + + // limit layer bounds to visible screen + maLayerBounds.intersect( basegfx::B2DRange(0.0, + 0.0, + maUserSize.getX(), + maUserSize.getY()) ); + + basegfx::B2IRange const& rNewLayerPixel( + getLayerBoundsPixel(maLayerBounds, + maTransformation) ); + if( rNewLayerPixel != maLayerBoundsPixel ) + { + // re-gen sprite with new size + mpOutputCanvas.reset(); + mpSprite.reset(); + } + } + +private: + // ViewLayer interface + // ---------------------------------------------- + + virtual cppcanvas::CustomSpriteSharedPtr createSprite( + const ::basegfx::B2DSize& rSpriteSizePixel, + double nPriority ) const + { + cppcanvas::CustomSpriteSharedPtr pSprite( + mpSpriteCanvas->createCustomSprite( rSpriteSizePixel ) ); + + maSpriteContainer.addSprite( pSprite, + nPriority ); + + return pSprite; + } + + virtual void setPriority( const basegfx::B1DRange& rRange ) + { + OSL_ENSURE( !rRange.isEmpty() && + rRange.getMinimum() >= 1.0, + "SlideViewLayer::setPriority(): prio MUST be larger than 1.0 (because " + "the background layer already lies there)" ); + + maSpriteContainer.setLayerPriority( rRange ); + + if( mpSprite ) + mpSprite->setPriority( rRange.getMinimum() ); + } + + virtual basegfx::B2DHomMatrix getTransformation() const + { + // Offset given transformation by left, top border of given + // range (after transformation through given transformation) + basegfx::B2DRectangle aTmpRect; + canvas::tools::calcTransformedRectBounds( aTmpRect, + maLayerBounds, + maTransformation ); + + basegfx::B2DHomMatrix aMatrix( maTransformation ); + + // Add translation according to the origin of aTmpRect. Ignore the + // translation when aTmpRect was not properly initialized. + if ( ! aTmpRect.isEmpty()) + { + aMatrix.translate( -basegfx::fround(aTmpRect.getMinX()), + -basegfx::fround(aTmpRect.getMinY()) ); + } + + return aMatrix; + } + + virtual basegfx::B2DHomMatrix getSpriteTransformation() const + { + return maTransformation; + } + + virtual void clear() const + { + // keep layer clip + clearRect(getCanvas()->clone(), + maLayerBoundsPixel); + } + + virtual void clearAll() const + { + ::cppcanvas::CanvasSharedPtr pCanvas( getCanvas()->clone() ); + + // clear layer clip, to clear whole area + pCanvas->setClip(); + + clearRect(pCanvas, + maLayerBoundsPixel); + } + + virtual bool isOnView(boost::shared_ptr<View> const& rView) const + { + return rView.get() == mpParentView; + } + + virtual cppcanvas::CanvasSharedPtr getCanvas() const + { + if( !mpOutputCanvas ) + { + if( !mpSprite ) + { + maLayerBoundsPixel = getLayerBoundsPixel(maLayerBounds, + maTransformation); + + // HACK: ensure at least 1x1 pixel size. clients might + // need an actual canvas (e.g. for bound rect + // calculations) without rendering anything. Better + // solution: introduce something like a reference + // canvas for ViewLayers, which is always available. + if( maLayerBoundsPixel.isEmpty() ) + maLayerBoundsPixel = basegfx::B2IRange(0,0,1,1); + + const basegfx::B2I64Tuple& rSpriteSize(maLayerBoundsPixel.getRange()); + mpSprite = mpSpriteCanvas->createCustomSprite( + basegfx::B2DVector(sal::static_int_cast<sal_Int32>(rSpriteSize.getX()), + sal::static_int_cast<sal_Int32>(rSpriteSize.getY())) ); + + mpSprite->setPriority( + maSpriteContainer.getLayerPriority().getMinimum() ); + +#if defined(VERBOSE) && defined(DBG_UTIL) + mpSprite->movePixel( + basegfx::B2DPoint(maLayerBoundsPixel.getMinimum()) + + basegfx::B2DPoint(10,10) ); + + mpSprite->setAlpha(0.5); +#else + mpSprite->movePixel( + basegfx::B2DPoint(maLayerBoundsPixel.getMinimum()) ); + + mpSprite->setAlpha(1.0); +#endif + mpSprite->show(); + } + + ENSURE_OR_THROW( mpSprite, + "SlideViewLayer::getCanvas(): no layer sprite" ); + + mpOutputCanvas = mpSprite->getContentCanvas(); + + ENSURE_OR_THROW( mpOutputCanvas, + "SlideViewLayer::getCanvas(): sprite doesn't yield a canvas" ); + + // new canvas retrieved - setup transformation and clip + mpOutputCanvas->setTransformation( getTransformation() ); + mpOutputCanvas->setClip( + createClipPolygon( maClip, + mpOutputCanvas, + maUserSize )); + } + + return mpOutputCanvas; + } + + virtual void setClip( const basegfx::B2DPolyPolygon& rClip ) + { + basegfx::B2DPolyPolygon aNewClip = prepareClip( rClip ); + + if( aNewClip != maClip ) + { + maClip = aNewClip; + + if(mpOutputCanvas ) + mpOutputCanvas->setClip( + createClipPolygon( maClip, + mpOutputCanvas, + maUserSize )); + } + } + + virtual bool resize( const ::basegfx::B2DRange& rArea ) + { + const bool bRet( maLayerBounds != rArea ); + maLayerBounds = rArea; + updateView( maTransformation, + maUserSize ); + + return bRet; + } +}; + + +// --------------------------------------------------------- + +typedef cppu::WeakComponentImplHelper2< + ::com::sun::star::util::XModifyListener, + ::com::sun::star::awt::XPaintListener> SlideViewBase; + +/** SlideView class + + This class implements the View interface, encapsulating + <em>one</em> view a slideshow is displayed on. + */ +class SlideView : private cppu::BaseMutex, + public SlideViewBase, + public UnoView +{ +public: + SlideView( const uno::Reference<presentation::XSlideShowView>& xView, + EventQueue& rEventQueue, + EventMultiplexer& rEventMultiplexer ); + void updateCanvas(); + +private: + // View: + virtual ViewLayerSharedPtr createViewLayer( const basegfx::B2DRange& rLayerBounds ) const; + virtual bool updateScreen() const; + virtual bool paintScreen() const; + virtual void setViewSize( const ::basegfx::B2DSize& ); + virtual void setCursorShape( sal_Int16 nPointerShape ); + + // ViewLayer interface + virtual bool isOnView(boost::shared_ptr<View> const& rView) const; + virtual void clear() const; + virtual void clearAll() const; + virtual cppcanvas::CanvasSharedPtr getCanvas() const; + virtual cppcanvas::CustomSpriteSharedPtr createSprite( const ::basegfx::B2DSize& rSpriteSizePixel, + double nPriority ) const; + virtual void setPriority( const basegfx::B1DRange& rRange ); + virtual ::basegfx::B2DHomMatrix getTransformation() const; + virtual basegfx::B2DHomMatrix getSpriteTransformation() const; + virtual void setClip( const ::basegfx::B2DPolyPolygon& rClip ); + virtual bool resize( const ::basegfx::B2DRange& rArea ); + + // UnoView: + virtual void _dispose(); + virtual uno::Reference<presentation::XSlideShowView> getUnoView()const; + virtual void setIsSoundEnabled (const bool bValue); + virtual bool isSoundEnabled (void) const; + + // XEventListener: + virtual void SAL_CALL disposing( lang::EventObject const& evt ) + throw (uno::RuntimeException); + // XModifyListener: + virtual void SAL_CALL modified( const lang::EventObject& aEvent ) + throw (uno::RuntimeException); + // XPaintListener: + virtual void SAL_CALL windowPaint( const awt::PaintEvent& e ) + throw (uno::RuntimeException); + + // WeakComponentImplHelperBase: + virtual void SAL_CALL disposing(); + + void updateClip(); + +private: + typedef std::vector< boost::weak_ptr<SlideViewLayer> > ViewLayerVector; + + /// Prune viewlayers from deceased ones, optionally update them + void pruneLayers( bool bWithViewLayerUpdate=false ) const; + + /** Max fill level of maViewLayers, before we try to prune it from + deceased layers + */ + enum{ LAYER_ULLAGE=8 }; + + uno::Reference<presentation::XSlideShowView> mxView; + cppcanvas::SpriteCanvasSharedPtr mpCanvas; + + EventMultiplexer& mrEventMultiplexer; + EventQueue& mrEventQueue; + + mutable LayerSpriteContainer maSprites; + mutable ViewLayerVector maViewLayers; + + basegfx::B2DPolyPolygon maClip; + + basegfx::B2DHomMatrix maViewTransform; + basegfx::B2DSize maUserSize; + bool mbIsSoundEnabled; +}; + + +SlideView::SlideView( const uno::Reference<presentation::XSlideShowView>& xView, + EventQueue& rEventQueue, + EventMultiplexer& rEventMultiplexer ) : + SlideViewBase( m_aMutex ), + mxView( xView ), + mpCanvas(), + mrEventMultiplexer( rEventMultiplexer ), + mrEventQueue( rEventQueue ), + maSprites(), + maViewLayers(), + maClip(), + maViewTransform(), + maUserSize( 1.0, 1.0 ), // default size: one-by-one rectangle + mbIsSoundEnabled(true) +{ + // take care not constructing any UNO references to this _inside_ + // ctor, shift that code to createSlideView()! + ENSURE_OR_THROW( mxView.is(), + "SlideView::SlideView(): Invalid view" ); + + mpCanvas = cppcanvas::VCLFactory::getInstance().createSpriteCanvas( + xView->getCanvas() ); + ENSURE_OR_THROW( mpCanvas, + "Could not create cppcanvas" ); + + geometry::AffineMatrix2D aViewTransform( + xView->getTransformation() ); + + if( basegfx::fTools::equalZero( + basegfx::B2DVector(aViewTransform.m00, + aViewTransform.m10).getLength()) || + basegfx::fTools::equalZero( + basegfx::B2DVector(aViewTransform.m01, + aViewTransform.m11).getLength()) ) + { + OSL_ENSURE( false, + "SlideView::SlideView(): Singular matrix!" ); + + canvas::tools::setIdentityAffineMatrix2D(aViewTransform); + } + + basegfx::unotools::homMatrixFromAffineMatrix( + maViewTransform, aViewTransform ); + + // once and forever: set fixed prio to this 'layer' (we're always + // the background layer) + maSprites.setLayerPriority( basegfx::B1DRange(0.0,1.0) ); +} + +void SlideView::disposing() +{ + osl::MutexGuard aGuard( m_aMutex ); + + maViewLayers.clear(); + maSprites.clear(); + mpCanvas.reset(); + + // additionally, also de-register from XSlideShowView + if (mxView.is()) + { + mxView->removeTransformationChangedListener( this ); + mxView->removePaintListener( this ); + mxView.clear(); + } +} + +ViewLayerSharedPtr SlideView::createViewLayer( const basegfx::B2DRange& rLayerBounds ) const +{ + osl::MutexGuard aGuard( m_aMutex ); + + ENSURE_OR_THROW( mpCanvas, + "SlideView::createViewLayer(): Disposed" ); + + const std::size_t nNumLayers( maViewLayers.size() ); + + // avoid filling up layer vector with lots of deceased layer weak + // ptrs + if( nNumLayers > LAYER_ULLAGE ) + pruneLayers(); + + boost::shared_ptr<SlideViewLayer> pViewLayer( new SlideViewLayer(mpCanvas, + getTransformation(), + rLayerBounds, + maUserSize, + this) ); + maViewLayers.push_back( pViewLayer ); + + return pViewLayer; +} + +bool SlideView::updateScreen() const +{ + osl::MutexGuard aGuard( m_aMutex ); + + ENSURE_OR_RETURN( mpCanvas.get(), + "SlideView::updateScreen(): Disposed" ); + + return mpCanvas->updateScreen( false ); +} + +bool SlideView::paintScreen() const +{ + osl::MutexGuard aGuard( m_aMutex ); + + ENSURE_OR_RETURN( mpCanvas.get(), + "SlideView::paintScreen(): Disposed" ); + + return mpCanvas->updateScreen( true ); +} + +void SlideView::clear() const +{ + osl::MutexGuard aGuard( m_aMutex ); + + OSL_ENSURE( mxView.is() && mpCanvas, + "SlideView::clear(): Disposed" ); + if( !mxView.is() || !mpCanvas ) + return; + + // keep layer clip + clearRect(getCanvas()->clone(), + getLayerBoundsPixel( + basegfx::B2DRange(0,0, + maUserSize.getX(), + maUserSize.getY()), + getTransformation())); +} + +void SlideView::clearAll() const +{ + osl::MutexGuard aGuard( m_aMutex ); + + OSL_ENSURE( mxView.is() && mpCanvas, + "SlideView::clear(): Disposed" ); + if( !mxView.is() || !mpCanvas ) + return; + + // clear whole view + mxView->clear(); +} + +void SlideView::setViewSize( const basegfx::B2DSize& rSize ) +{ + osl::MutexGuard aGuard( m_aMutex ); + + maUserSize = rSize; + updateCanvas(); +} + +void SlideView::setCursorShape( sal_Int16 nPointerShape ) +{ + osl::MutexGuard const guard( m_aMutex ); + + if (mxView.is()) + mxView->setMouseCursor( nPointerShape ); +} + +bool SlideView::isOnView(boost::shared_ptr<View> const& rView) const +{ + return rView.get() == this; +} + +cppcanvas::CanvasSharedPtr SlideView::getCanvas() const +{ + osl::MutexGuard aGuard( m_aMutex ); + + ENSURE_OR_THROW( mpCanvas, + "SlideView::getCanvas(): Disposed" ); + + return mpCanvas; +} + +cppcanvas::CustomSpriteSharedPtr SlideView::createSprite( + const basegfx::B2DSize& rSpriteSizePixel, + double nPriority ) const +{ + osl::MutexGuard aGuard( m_aMutex ); + + ENSURE_OR_THROW( mpCanvas, "SlideView::createSprite(): Disposed" ); + + cppcanvas::CustomSpriteSharedPtr pSprite( + mpCanvas->createCustomSprite( rSpriteSizePixel ) ); + + maSprites.addSprite( pSprite, + nPriority ); + + return pSprite; +} + +void SlideView::setPriority( const basegfx::B1DRange& /*rRange*/ ) +{ + osl::MutexGuard aGuard( m_aMutex ); + + OSL_ENSURE( false, + "SlideView::setPriority() is a NOOP for slide view - " + "content will always be shown in the background" ); +} + +basegfx::B2DHomMatrix SlideView::getTransformation() const +{ + osl::MutexGuard aGuard( m_aMutex ); + + basegfx::B2DHomMatrix aMatrix; + aMatrix.scale( 1.0/maUserSize.getX(), 1.0/maUserSize.getY() ); + + return maViewTransform * aMatrix; +} + +basegfx::B2DHomMatrix SlideView::getSpriteTransformation() const +{ + return getTransformation(); +} + +void SlideView::setClip( const basegfx::B2DPolyPolygon& rClip ) +{ + osl::MutexGuard aGuard( m_aMutex ); + + basegfx::B2DPolyPolygon aNewClip = prepareClip( rClip ); + + if( aNewClip != maClip ) + { + maClip = aNewClip; + + updateClip(); + } +} + +bool SlideView::resize( const ::basegfx::B2DRange& /*rArea*/ ) +{ + osl::MutexGuard aGuard( m_aMutex ); + + OSL_ENSURE( false, + "SlideView::resize(): ignored for the View, can't change size " + "effectively, anyway" ); + + return false; +} + +uno::Reference<presentation::XSlideShowView> SlideView::getUnoView() const +{ + osl::MutexGuard aGuard( m_aMutex ); + return mxView; +} + +void SlideView::setIsSoundEnabled (const bool bValue) +{ + mbIsSoundEnabled = bValue; +} + +bool SlideView::isSoundEnabled (void) const +{ + return mbIsSoundEnabled; +} + +void SlideView::_dispose() +{ + dispose(); +} + +// XEventListener +void SlideView::disposing( lang::EventObject const& evt ) + throw (uno::RuntimeException) +{ + (void)evt; + + // no deregistration necessary anymore, XView has left: + osl::MutexGuard const guard( m_aMutex ); + + if (mxView.is()) + { + OSL_ASSERT( evt.Source == mxView ); + mxView.clear(); + } + + dispose(); +} + +// XModifyListener +void SlideView::modified( const lang::EventObject& /*aEvent*/ ) + throw (uno::RuntimeException) +{ + osl::MutexGuard const guard( m_aMutex ); + + OSL_ENSURE( mxView.is(), "SlideView::modified(): " + "Disposed, but event received from XSlideShowView?!"); + + if( !mxView.is() ) + return; + + geometry::AffineMatrix2D aViewTransform( + mxView->getTransformation() ); + + if( basegfx::fTools::equalZero( + basegfx::B2DVector(aViewTransform.m00, + aViewTransform.m10).getLength()) || + basegfx::fTools::equalZero( + basegfx::B2DVector(aViewTransform.m01, + aViewTransform.m11).getLength()) ) + { + OSL_ENSURE( false, + "SlideView::modified(): Singular matrix!" ); + + canvas::tools::setIdentityAffineMatrix2D(aViewTransform); + } + + // view transformation really changed? + basegfx::B2DHomMatrix aNewTransform; + basegfx::unotools::homMatrixFromAffineMatrix( + aNewTransform, + aViewTransform ); + + if( aNewTransform == maViewTransform ) + return; // No change, nothing to do + + maViewTransform = aNewTransform; + + updateCanvas(); + + // notify view change. Don't call EventMultiplexer directly, this + // might not be the main thread! + mrEventQueue.addEvent( + makeEvent( boost::bind( (bool (EventMultiplexer::*)( + const uno::Reference<presentation::XSlideShowView>&)) + &EventMultiplexer::notifyViewChanged, + boost::ref(mrEventMultiplexer), mxView ), + "EventMultiplexer::notifyViewChanged")); +} + +// XPaintListener +void SlideView::windowPaint( const awt::PaintEvent& /*e*/ ) + throw (uno::RuntimeException) +{ + osl::MutexGuard aGuard( m_aMutex ); + + OSL_ENSURE( mxView.is() && mpCanvas, "Disposed, but event received?!" ); + + // notify view clobbering. Don't call EventMultiplexer directly, + // this might not be the main thread! + mrEventQueue.addEvent( + makeEvent( boost::bind( &EventMultiplexer::notifyViewClobbered, + boost::ref(mrEventMultiplexer), mxView ), + "EventMultiplexer::notifyViewClobbered") ); +} + +void SlideView::updateCanvas() +{ + OSL_ENSURE( mpCanvas, + "SlideView::updateCanvasTransform(): Disposed" ); + + if( !mpCanvas || !mxView.is()) + return; + + mpCanvas->clear(); // this is unnecessary, strictly speaking. but + // it makes the SlideView behave exactly like a + // sprite-based SlideViewLayer, because those + // are created from scratch after a resize + clearAll(); + mpCanvas->setTransformation( getTransformation() ); + mpCanvas->setClip( + createClipPolygon( maClip, + mpCanvas, + maUserSize )); + + // forward update to viewlayers + pruneLayers( true ); +} + +void SlideView::updateClip() +{ + OSL_ENSURE( mpCanvas, + "SlideView::updateClip(): Disposed" ); + + if( !mpCanvas ) + return; + + mpCanvas->setClip( + createClipPolygon( maClip, + mpCanvas, + maUserSize )); + + pruneLayers( false ); +} + +void SlideView::pruneLayers( bool bWithViewLayerUpdate ) const +{ + ViewLayerVector aValidLayers; + + const basegfx::B2DHomMatrix& rCurrTransform( + getTransformation() ); + + // check all layers for validity, and retain only the live ones + ViewLayerVector::const_iterator aCurr( maViewLayers.begin() ); + const ViewLayerVector::const_iterator aEnd( maViewLayers.end() ); + while( aCurr != aEnd ) + { + boost::shared_ptr< SlideViewLayer > pCurrLayer( aCurr->lock() ); + + if( pCurrLayer ) + { + aValidLayers.push_back( pCurrLayer ); + + if( bWithViewLayerUpdate ) + pCurrLayer->updateView( rCurrTransform, + maUserSize ); + } + + ++aCurr; + } + + // replace layer list with pruned one + maViewLayers.swap( aValidLayers ); +} + +} // anonymous namespace + +UnoViewSharedPtr createSlideView( uno::Reference< presentation::XSlideShowView> const& xView, + EventQueue& rEventQueue, + EventMultiplexer& rEventMultiplexer ) +{ + boost::shared_ptr<SlideView> const that( + comphelper::make_shared_from_UNO( + new SlideView(xView, + rEventQueue, + rEventMultiplexer))); + + // register listeners with XSlideShowView + xView->addTransformationChangedListener( that.get() ); + xView->addPaintListener( that.get() ); + + // set new transformation + that->updateCanvas(); + + return that; +} + +} // namespace internal +} // namespace slideshow + |