| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the 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 |
| |