summaryrefslogtreecommitdiff
path: root/slideshow/source/engine/slideview.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'slideshow/source/engine/slideview.cxx')
-rw-r--r--slideshow/source/engine/slideview.cxx1196
1 files changed, 1196 insertions, 0 deletions
diff --git a/slideshow/source/engine/slideview.cxx b/slideshow/source/engine/slideview.cxx
new file mode 100644
index 000000000000..9573f3c29a3d
--- /dev/null
+++ b/slideshow/source/engine/slideview.cxx
@@ -0,0 +1,1196 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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::createUnitPolygon();
+ }
+};
+
+/** 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_FALSE( mpCanvas.get(),
+ "SlideView::updateScreen(): Disposed" );
+
+ return mpCanvas->updateScreen( false );
+}
+
+bool SlideView::paintScreen() const
+{
+ osl::MutexGuard aGuard( m_aMutex );
+
+ ENSURE_OR_RETURN_FALSE( 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
+