Bump version to 6.0-36
[LibreOffice.git] / slideshow / source / engine / slideview.cxx
blobf81f8bd5148232d70c8613c95adb7f6982b4b48b
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <tools/diagnose_ex.h>
21 #include <canvas/canvastools.hxx>
23 #include <eventqueue.hxx>
24 #include <eventmultiplexer.hxx>
25 #include <slideview.hxx>
26 #include <delayevent.hxx>
27 #include <unoview.hxx>
29 #include <cppuhelper/basemutex.hxx>
30 #include <cppuhelper/compbase.hxx>
31 #include <cppuhelper/implementationentry.hxx>
32 #include <cppuhelper/interfacecontainer.h>
33 #include <comphelper/make_shared_from_uno.hxx>
35 #include <cppcanvas/spritecanvas.hxx>
36 #include <cppcanvas/customsprite.hxx>
37 #include <cppcanvas/vclfactory.hxx>
38 #include <cppcanvas/basegfxfactory.hxx>
40 #include <basegfx/range/b1drange.hxx>
41 #include <basegfx/range/b2drange.hxx>
42 #include <basegfx/range/b2irange.hxx>
43 #include <basegfx/point/b2dpoint.hxx>
44 #include <basegfx/polygon/b2dpolygon.hxx>
45 #include <basegfx/matrix/b2dhommatrix.hxx>
46 #include <basegfx/polygon/b2dpolygontools.hxx>
47 #include <basegfx/polygon/b2dpolypolygontools.hxx>
48 #include <basegfx/utils/canvastools.hxx>
49 #include <basegfx/polygon/b2dpolygonclipper.hxx>
50 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
52 #include <com/sun/star/presentation/XSlideShow.hpp>
53 #include <com/sun/star/rendering/CompositeOperation.hpp>
55 #include <memory>
56 #include <vector>
57 #include <iterator>
58 #include <algorithm>
60 using namespace com::sun::star;
62 namespace slideshow {
63 namespace internal {
65 namespace {
67 /** Sprite entry, to store sprite plus priority
69 The operator<() defines a strict weak ordering of sprites, sort
70 key is the sprite priority.
72 struct SpriteEntry
74 SpriteEntry( const cppcanvas::CustomSpriteSharedPtr& rSprite,
75 double nPrio ) :
76 mpSprite( rSprite ),
77 mnPriority( nPrio )
81 bool operator<(const SpriteEntry& rRHS) const
83 return mnPriority < rRHS.mnPriority;
86 std::weak_ptr< cppcanvas::CustomSprite > mpSprite;
87 double mnPriority;
90 typedef std::vector< SpriteEntry > SpriteVector;
93 /** Create a clip polygon for slide views
95 @param rClip
96 Clip to set (can be empty)
98 @param rCanvas
99 Canvas to create the clip polygon for
101 @param rUserSize
102 The size of the view. Note that the returned clip will
103 <em>always</em> clip to at least the rect defined herein.
105 @return the view clip polygon, in view coordinates, which is
106 guaranteed to at least clip to the view size.
108 basegfx::B2DPolyPolygon createClipPolygon( const basegfx::B2DPolyPolygon& rClip,
109 const cppcanvas::CanvasSharedPtr& /*rCanvas*/,
110 const basegfx::B2DSize& rUserSize )
112 // setup canvas clipping
113 // =====================
115 // AW: Simplified
116 const basegfx::B2DRange aClipRange(0, 0, rUserSize.getX(), rUserSize.getY());
118 if(rClip.count())
120 return basegfx::utils::clipPolyPolygonOnRange(rClip, aClipRange, true, false);
122 else
124 return basegfx::B2DPolyPolygon(basegfx::utils::createPolygonFromRect(aClipRange));
128 /** Prepare given clip polygon to be stored as the current clip
130 Note that this is separate from createClipPolygon(), to allow
131 SlideView implementations to store this intermediate result
132 (createClipPolygon() has to be called every time the view size
133 changes)
135 basegfx::B2DPolyPolygon prepareClip( const basegfx::B2DPolyPolygon& rClip )
137 basegfx::B2DPolyPolygon aClip( rClip );
139 // TODO(P2): unnecessary, once XCanvas is correctly handling this
140 // AW: Should be no longer necessary; tools are now bezier-safe
141 if( aClip.areControlPointsUsed() )
142 aClip = basegfx::utils::adaptiveSubdivideByAngle( aClip );
144 // normalize polygon, preparation for clipping
145 // in updateCanvas()
146 aClip = basegfx::utils::correctOrientations(aClip);
147 aClip = basegfx::utils::solveCrossovers(aClip);
148 aClip = basegfx::utils::stripNeutralPolygons(aClip);
149 aClip = basegfx::utils::stripDispensablePolygons(aClip);
151 return aClip;
155 void clearRect( ::cppcanvas::CanvasSharedPtr const& pCanvas,
156 basegfx::B2IRange const& rArea )
158 // convert clip polygon to device coordinate system
159 ::basegfx::B2DPolyPolygon const* pClipPoly( pCanvas->getClip() );
160 if( pClipPoly )
162 ::basegfx::B2DPolyPolygon aClipPoly( *pClipPoly );
163 aClipPoly.transform( pCanvas->getTransformation() );
164 pCanvas->setClip( aClipPoly );
167 // set transformation to identitiy (->device pixel)
168 pCanvas->setTransformation( ::basegfx::B2DHomMatrix() );
170 // #i42440# Fill the _full_ background in
171 // black. Since we had to extend the bitmap by one
172 // pixel, and the bitmap is initialized white,
173 // depending on the slide content a one pixel wide
174 // line will show to the bottom and the right.
175 const ::basegfx::B2DPolygon aPoly(
176 ::basegfx::utils::createPolygonFromRect(
177 basegfx::B2DRange(rArea)));
179 ::cppcanvas::PolyPolygonSharedPtr pPolyPoly(
180 ::cppcanvas::BaseGfxFactory::createPolyPolygon( pCanvas, aPoly ) );
182 if( pPolyPoly )
184 pPolyPoly->setCompositeOp( css::rendering::CompositeOperation::SOURCE );
185 pPolyPoly->setRGBAFillColor( 0xFFFFFF00U );
186 pPolyPoly->draw();
189 #if defined(DBG_UTIL)
190 ::cppcanvas::CanvasSharedPtr pCliplessCanvas( pCanvas->clone() );
191 pCliplessCanvas->setClip();
193 if( pCanvas->getClip() )
195 ::cppcanvas::PolyPolygonSharedPtr pPolyPoly2(
196 ::cppcanvas::BaseGfxFactory::createPolyPolygon( pCliplessCanvas, aPoly ));
197 if( pPolyPoly2 )
199 pPolyPoly2->setRGBALineColor( 0x008000FFU );
200 pPolyPoly2->draw();
203 #endif
206 /** Get bounds in pixel
208 @param rLayerBounds
209 Bound rect, in user space coordinates
211 @param rTransformation
212 User space to device pixel transformation
214 @return the layer bounds in pixel, extended by one pixel to the
215 right and bottom
217 basegfx::B2IRange getLayerBoundsPixel( basegfx::B2DRange const& rLayerBounds,
218 basegfx::B2DHomMatrix const& rTransformation )
220 ::basegfx::B2DRange aTmpRect;
221 ::canvas::tools::calcTransformedRectBounds( aTmpRect,
222 rLayerBounds,
223 rTransformation );
225 if( aTmpRect.isEmpty() )
226 return ::basegfx::B2IRange();
228 // #i42440# Returned layer size is one pixel too small, as
229 // rendering happens one pixel to the right and below the
230 // actual bound rect.
231 return ::basegfx::B2IRange( ::basegfx::fround(aTmpRect.getMinX()),
232 ::basegfx::fround(aTmpRect.getMinY()),
233 ::basegfx::fround(aTmpRect.getMaxX()) + 1,
234 ::basegfx::fround(aTmpRect.getMaxY()) + 1 );
238 /** Container class for sprites issued by a ViewLayer
240 This class handles the sprite prioritization issues, that are
241 needed for layer sprites (e.g. the need to re-prioritize sprites
242 when the layer changes prio).
244 class LayerSpriteContainer
246 /** Max fill level of maSprites, before we try to prune it from
247 deceased sprites
249 enum{ SPRITE_ULLAGE=256 };
251 /** All sprites that have been issued by this container (pruned
252 from time to time, for invalid references). This vector is
253 kept sorted with increasing sprite priority.
255 SpriteVector maSprites;
257 /// Priority of this layer, relative to other view layers
258 basegfx::B1DRange maLayerPrioRange;
260 double getSpritePriority( std::size_t nSpriteNum ) const
262 // divide the available layer range equally between all
263 // sprites, assign upper bound of individual sprite range as
264 // sprite prio (the layer itself gets assigned the lower bound
265 // of sprite 0's individual range):
267 // | layer 0 | layer 1 | ...
268 // | sprite 0 | sprite 1 | sprite 0 | sprite 1 | ...
269 return maLayerPrioRange.getMinimum() + maLayerPrioRange.getRange()*(nSpriteNum+1)/(maSprites.size()+1);
272 /** Rescan sprite vector, and remove deceased sprites (and reset
273 sprite prio)
275 @param aBegin
276 Iterator to the first entry to rescan
278 void updateSprites()
280 SpriteVector aValidSprites;
282 // check all sprites for validity and set new priority
283 for( const auto& rSprite : maSprites )
285 cppcanvas::CustomSpriteSharedPtr pCurrSprite( rSprite.mpSprite.lock() );
287 if( pCurrSprite )
289 // only copy still valid sprites over to the refreshed
290 // sprite vector.
291 aValidSprites.push_back( rSprite );
293 pCurrSprite->setPriority(
294 getSpritePriority( aValidSprites.size()-1 ));
298 // replace sprite list with pruned one
299 maSprites.swap( aValidSprites );
302 public:
303 LayerSpriteContainer() :
304 maSprites(),
305 maLayerPrioRange()
309 const basegfx::B1DRange& getLayerPriority() const
311 return maLayerPrioRange;
314 void setLayerPriority( const basegfx::B1DRange& rRange )
316 if( rRange != maLayerPrioRange )
318 maLayerPrioRange = rRange;
320 // prune and recalc sprite prios
321 updateSprites();
325 void addSprite( const cppcanvas::CustomSpriteSharedPtr& pSprite,
326 double nPriority )
328 if( !pSprite )
329 return;
331 SpriteEntry aEntry( pSprite,nPriority );
333 // insert new sprite, such that vector stays sorted
334 SpriteVector::iterator aInsertPos(
335 maSprites.insert(
336 std::lower_bound( maSprites.begin(),
337 maSprites.end(),
338 aEntry ),
339 aEntry ));
341 const std::size_t nNumSprites( maSprites.size() );
342 if( nNumSprites > SPRITE_ULLAGE ||
343 maSprites.end() - aInsertPos > 1 )
345 // updateSprites() also updates all sprite prios
346 updateSprites();
348 else
350 // added sprite to the end, and not too many sprites in
351 // vector - perform optimized update (only need to set
352 // prio). This basically caters for the common case of
353 // iterated character animations, which generate lots of
354 // sprites, all added to the end.
355 pSprite->setPriority(
356 getSpritePriority( nNumSprites-1 ));
360 void clear()
362 maSprites.clear();
367 /** This class provides layers for a slide view
369 Layers are used to render animations with the correct z order -
370 because sprites are always in front of the static canvas
371 background, shapes that must appear <em<before</em> an animation
372 must also be displayed as a sprite.
374 Each layer has a priority assigned to it (valid range [0,1]), which
375 also affects all sprites created for this specific layer - i.e. if
376 the layer priority changes, the sprites change z order together
377 with their parent.
379 class SlideViewLayer : public ViewLayer
381 /// Smart container for all sprites issued by this layer
382 mutable LayerSpriteContainer maSpriteContainer;
384 /// Bounds of this layer in user space coordinates
385 basegfx::B2DRange maLayerBounds;
387 /// Bounds of this layer in device pixel
388 mutable basegfx::B2IRange maLayerBoundsPixel;
390 /// Current clip polygon in user coordinates
391 basegfx::B2DPolyPolygon maClip;
393 /// Current size of the view in user coordinates
394 basegfx::B2DSize maUserSize;
396 /// Current overall view transformation
397 basegfx::B2DHomMatrix maTransformation;
399 /// 'parent' canvas, this viewlayer is associated with
400 const cppcanvas::SpriteCanvasSharedPtr mpSpriteCanvas;
402 /** output surface (necessarily a sprite, won't otherwise be able
403 to display anything <em>before</em> other sprites)
405 mutable cppcanvas::CustomSpriteSharedPtr mpSprite;
407 /// actual output canvas retrieved from a sprite
408 mutable cppcanvas::CanvasSharedPtr mpOutputCanvas;
410 /// ptr back to owning view. needed for isOnView() method
411 View const* const mpParentView;
413 public:
414 /** Create a new layer
416 @param pCanvas
417 Sprite canvas to create the layer on
419 @param rTransform
420 Initial overall canvas transformation
422 @param rLayerBounds
423 Initial layer bounds, in view coordinate system
425 SlideViewLayer( const cppcanvas::SpriteCanvasSharedPtr& pCanvas,
426 const basegfx::B2DHomMatrix& rTransform,
427 const basegfx::B2DRange& rLayerBounds,
428 const basegfx::B2DSize& rUserSize,
429 View const* const pParentView) :
430 maSpriteContainer(),
431 maLayerBounds(rLayerBounds),
432 maLayerBoundsPixel(),
433 maClip(),
434 maUserSize(rUserSize),
435 maTransformation(rTransform),
436 mpSpriteCanvas(pCanvas),
437 mpSprite(),
438 mpOutputCanvas(),
439 mpParentView(pParentView)
443 SlideViewLayer(const SlideViewLayer&) = delete;
444 SlideViewLayer& operator=(const SlideViewLayer&) = delete;
446 void updateView( const basegfx::B2DHomMatrix& rMatrix,
447 const basegfx::B2DSize& rUserSize )
449 maTransformation = rMatrix;
450 maUserSize = rUserSize;
452 // limit layer bounds to visible screen
453 maLayerBounds.intersect( basegfx::B2DRange(0.0,
454 0.0,
455 maUserSize.getX(),
456 maUserSize.getY()) );
458 basegfx::B2IRange const& rNewLayerPixel(
459 getLayerBoundsPixel(maLayerBounds,
460 maTransformation) );
461 if( rNewLayerPixel != maLayerBoundsPixel )
463 // re-gen sprite with new size
464 mpOutputCanvas.reset();
465 mpSprite.reset();
469 virtual css::geometry::IntegerSize2D getTranslationOffset() const override
471 basegfx::B2DRectangle aTmpRect;
472 canvas::tools::calcTransformedRectBounds( aTmpRect,
473 maLayerBounds,
474 maTransformation );
475 geometry::IntegerSize2D offset(0, 0);
477 // Add translation according to the origin of aTmpRect. Ignore the
478 // translation when aTmpRect was not properly initialized.
479 if ( ! aTmpRect.isEmpty())
481 offset.Width = basegfx::fround(aTmpRect.getMinX());
482 offset.Height = basegfx::fround(aTmpRect.getMinY());
484 return offset;
487 private:
488 // ViewLayer interface
491 virtual cppcanvas::CustomSpriteSharedPtr createSprite(
492 const ::basegfx::B2DSize& rSpriteSizePixel,
493 double nPriority ) const override
495 cppcanvas::CustomSpriteSharedPtr pSprite(
496 mpSpriteCanvas->createCustomSprite( rSpriteSizePixel ) );
498 maSpriteContainer.addSprite( pSprite,
499 nPriority );
501 return pSprite;
504 virtual void setPriority( const basegfx::B1DRange& rRange ) override
506 OSL_ENSURE( !rRange.isEmpty() &&
507 rRange.getMinimum() >= 1.0,
508 "SlideViewLayer::setPriority(): prio MUST be larger than 1.0 (because "
509 "the background layer already lies there)" );
511 maSpriteContainer.setLayerPriority( rRange );
513 if( mpSprite )
514 mpSprite->setPriority( rRange.getMinimum() );
517 virtual basegfx::B2DHomMatrix getTransformation() const override
519 // Offset given transformation by left, top border of given
520 // range (after transformation through given transformation)
521 basegfx::B2DRectangle aTmpRect;
522 canvas::tools::calcTransformedRectBounds( aTmpRect,
523 maLayerBounds,
524 maTransformation );
526 basegfx::B2DHomMatrix aMatrix( maTransformation );
528 // Add translation according to the origin of aTmpRect. Ignore the
529 // translation when aTmpRect was not properly initialized.
530 if ( ! aTmpRect.isEmpty())
532 aMatrix.translate( -basegfx::fround(aTmpRect.getMinX()),
533 -basegfx::fround(aTmpRect.getMinY()) );
536 return aMatrix;
539 virtual basegfx::B2DHomMatrix getSpriteTransformation() const override
541 return maTransformation;
544 virtual void clear() const override
546 // grab canvas - that also lazy-initializes maLayerBoundsPixel
547 cppcanvas::CanvasSharedPtr pCanvas=getCanvas()->clone();
549 // clear whole canvas
550 const basegfx::B2I64Tuple& rSpriteSize(maLayerBoundsPixel.getRange());
551 clearRect(pCanvas,
552 basegfx::B2IRange(0,0,rSpriteSize.getX(),rSpriteSize.getY()));
555 virtual void clearAll() const override
557 // grab canvas - that also lazy-initializes maLayerBoundsPixel
558 ::cppcanvas::CanvasSharedPtr pCanvas( getCanvas()->clone() );
560 // clear layer clip, to clear whole area
561 pCanvas->setClip();
563 // clear whole canvas
564 const basegfx::B2I64Tuple& rSpriteSize(maLayerBoundsPixel.getRange());
565 clearRect(pCanvas,
566 basegfx::B2IRange(0,0,rSpriteSize.getX(),rSpriteSize.getY()));
569 virtual bool isOnView(std::shared_ptr<View> const& rView) const override
571 return rView.get() == mpParentView;
574 virtual cppcanvas::CanvasSharedPtr getCanvas() const override
576 if( !mpOutputCanvas )
578 if( !mpSprite )
580 maLayerBoundsPixel = getLayerBoundsPixel(maLayerBounds,
581 maTransformation);
583 // HACK: ensure at least 1x1 pixel size. clients might
584 // need an actual canvas (e.g. for bound rect
585 // calculations) without rendering anything. Better
586 // solution: introduce something like a reference
587 // canvas for ViewLayers, which is always available.
588 if( maLayerBoundsPixel.isEmpty() )
589 maLayerBoundsPixel = basegfx::B2IRange(0,0,1,1);
591 const basegfx::B2I64Tuple& rSpriteSize(maLayerBoundsPixel.getRange());
592 mpSprite = mpSpriteCanvas->createCustomSprite(
593 basegfx::B2DVector(sal::static_int_cast<sal_Int32>(rSpriteSize.getX()),
594 sal::static_int_cast<sal_Int32>(rSpriteSize.getY())) );
596 mpSprite->setPriority(
597 maSpriteContainer.getLayerPriority().getMinimum() );
599 #if defined(DBG_UTIL)
600 mpSprite->movePixel(
601 basegfx::B2DPoint(maLayerBoundsPixel.getMinimum()) +
602 basegfx::B2DPoint(10,10) );
604 mpSprite->setAlpha(0.5);
605 #else
606 mpSprite->movePixel(
607 basegfx::B2DPoint(maLayerBoundsPixel.getMinimum()) );
609 mpSprite->setAlpha(1.0);
610 #endif
611 mpSprite->show();
614 ENSURE_OR_THROW( mpSprite,
615 "SlideViewLayer::getCanvas(): no layer sprite" );
617 mpOutputCanvas = mpSprite->getContentCanvas();
619 ENSURE_OR_THROW( mpOutputCanvas,
620 "SlideViewLayer::getCanvas(): sprite doesn't yield a canvas" );
622 // new canvas retrieved - setup transformation and clip
623 mpOutputCanvas->setTransformation( getTransformation() );
624 mpOutputCanvas->setClip(
625 createClipPolygon( maClip,
626 mpOutputCanvas,
627 maUserSize ));
630 return mpOutputCanvas;
633 virtual void setClip( const basegfx::B2DPolyPolygon& rClip ) override
635 basegfx::B2DPolyPolygon aNewClip = prepareClip( rClip );
637 if( aNewClip != maClip )
639 maClip = aNewClip;
641 if(mpOutputCanvas )
642 mpOutputCanvas->setClip(
643 createClipPolygon( maClip,
644 mpOutputCanvas,
645 maUserSize ));
649 virtual bool resize( const ::basegfx::B2DRange& rArea ) override
651 const bool bRet( maLayerBounds != rArea );
652 maLayerBounds = rArea;
653 updateView( maTransformation,
654 maUserSize );
656 return bRet;
661 typedef cppu::WeakComponentImplHelper<
662 css::util::XModifyListener,
663 css::awt::XPaintListener> SlideViewBase;
665 /** SlideView class
667 This class implements the View interface, encapsulating
668 <em>one</em> view a slideshow is displayed on.
670 class SlideView : private cppu::BaseMutex,
671 public SlideViewBase,
672 public UnoView
674 public:
675 SlideView( const uno::Reference<presentation::XSlideShowView>& xView,
676 EventQueue& rEventQueue,
677 EventMultiplexer& rEventMultiplexer );
678 void updateCanvas();
680 private:
681 // View:
682 virtual ViewLayerSharedPtr createViewLayer( const basegfx::B2DRange& rLayerBounds ) const override;
683 virtual bool updateScreen() const override;
684 virtual bool paintScreen() const override;
685 virtual void setViewSize( const ::basegfx::B2DSize& ) override;
686 virtual void setCursorShape( sal_Int16 nPointerShape ) override;
688 // ViewLayer interface
689 virtual bool isOnView(std::shared_ptr<View> const& rView) const override;
690 virtual void clear() const override;
691 virtual void clearAll() const override;
692 virtual cppcanvas::CanvasSharedPtr getCanvas() const override;
693 virtual cppcanvas::CustomSpriteSharedPtr createSprite( const ::basegfx::B2DSize& rSpriteSizePixel,
694 double nPriority ) const override;
695 virtual void setPriority( const basegfx::B1DRange& rRange ) override;
696 virtual geometry::IntegerSize2D getTranslationOffset() const override;
697 virtual ::basegfx::B2DHomMatrix getTransformation() const override;
698 virtual basegfx::B2DHomMatrix getSpriteTransformation() const override;
699 virtual void setClip( const ::basegfx::B2DPolyPolygon& rClip ) override;
700 virtual bool resize( const ::basegfx::B2DRange& rArea ) override;
702 // UnoView:
703 virtual void _dispose() override;
704 virtual uno::Reference<presentation::XSlideShowView> getUnoView()const override;
705 virtual void setIsSoundEnabled (const bool bValue) override;
706 virtual bool isSoundEnabled() const override;
708 // XEventListener:
709 virtual void SAL_CALL disposing( lang::EventObject const& evt ) override;
710 // XModifyListener:
711 virtual void SAL_CALL modified( const lang::EventObject& aEvent ) override;
712 // XPaintListener:
713 virtual void SAL_CALL windowPaint( const awt::PaintEvent& e ) override;
715 // WeakComponentImplHelperBase:
716 virtual void SAL_CALL disposing() override;
718 void updateClip();
720 private:
721 typedef std::vector< std::weak_ptr<SlideViewLayer> > ViewLayerVector;
723 /// Prune viewlayers from deceased ones, optionally update them
724 void pruneLayers( bool bWithViewLayerUpdate=false ) const;
726 /** Max fill level of maViewLayers, before we try to prune it from
727 deceased layers
729 enum{ LAYER_ULLAGE=8 };
731 uno::Reference<presentation::XSlideShowView> mxView;
732 cppcanvas::SpriteCanvasSharedPtr mpCanvas;
734 EventMultiplexer& mrEventMultiplexer;
735 EventQueue& mrEventQueue;
737 mutable LayerSpriteContainer maSprites;
738 mutable ViewLayerVector maViewLayers;
740 basegfx::B2DPolyPolygon maClip;
742 basegfx::B2DHomMatrix maViewTransform;
743 basegfx::B2DSize maUserSize;
744 bool mbIsSoundEnabled;
748 SlideView::SlideView( const uno::Reference<presentation::XSlideShowView>& xView,
749 EventQueue& rEventQueue,
750 EventMultiplexer& rEventMultiplexer ) :
751 SlideViewBase( m_aMutex ),
752 mxView( xView ),
753 mpCanvas(),
754 mrEventMultiplexer( rEventMultiplexer ),
755 mrEventQueue( rEventQueue ),
756 maSprites(),
757 maViewLayers(),
758 maClip(),
759 maViewTransform(),
760 maUserSize( 1.0, 1.0 ), // default size: one-by-one rectangle
761 mbIsSoundEnabled(true)
763 // take care not constructing any UNO references to this _inside_
764 // ctor, shift that code to createSlideView()!
765 ENSURE_OR_THROW( mxView.is(),
766 "SlideView::SlideView(): Invalid view" );
768 mpCanvas = cppcanvas::VCLFactory::createSpriteCanvas(
769 xView->getCanvas() );
770 ENSURE_OR_THROW( mpCanvas,
771 "Could not create cppcanvas" );
773 geometry::AffineMatrix2D aViewTransform(
774 xView->getTransformation() );
776 if( basegfx::fTools::equalZero(
777 basegfx::B2DVector(aViewTransform.m00,
778 aViewTransform.m10).getLength()) ||
779 basegfx::fTools::equalZero(
780 basegfx::B2DVector(aViewTransform.m01,
781 aViewTransform.m11).getLength()) )
783 OSL_FAIL( "SlideView::SlideView(): Singular matrix!" );
785 canvas::tools::setIdentityAffineMatrix2D(aViewTransform);
788 basegfx::unotools::homMatrixFromAffineMatrix(
789 maViewTransform, aViewTransform );
791 // once and forever: set fixed prio to this 'layer' (we're always
792 // the background layer)
793 maSprites.setLayerPriority( basegfx::B1DRange(0.0,1.0) );
796 void SlideView::disposing()
798 osl::MutexGuard aGuard( m_aMutex );
800 maViewLayers.clear();
801 maSprites.clear();
802 mpCanvas.reset();
804 // additionally, also de-register from XSlideShowView
805 if (mxView.is())
807 mxView->removeTransformationChangedListener( this );
808 mxView->removePaintListener( this );
809 mxView.clear();
813 ViewLayerSharedPtr SlideView::createViewLayer( const basegfx::B2DRange& rLayerBounds ) const
815 osl::MutexGuard aGuard( m_aMutex );
817 ENSURE_OR_THROW( mpCanvas,
818 "SlideView::createViewLayer(): Disposed" );
820 const std::size_t nNumLayers( maViewLayers.size() );
822 // avoid filling up layer vector with lots of deceased layer weak
823 // ptrs
824 if( nNumLayers > LAYER_ULLAGE )
825 pruneLayers();
827 std::shared_ptr<SlideViewLayer> xViewLayer( new SlideViewLayer(mpCanvas,
828 getTransformation(),
829 rLayerBounds,
830 maUserSize,
831 this) );
832 maViewLayers.push_back(xViewLayer);
834 return xViewLayer;
837 bool SlideView::updateScreen() const
839 osl::MutexGuard aGuard( m_aMutex );
841 ENSURE_OR_RETURN_FALSE( mpCanvas.get(),
842 "SlideView::updateScreen(): Disposed" );
844 return mpCanvas->updateScreen( false );
847 bool SlideView::paintScreen() const
849 osl::MutexGuard aGuard( m_aMutex );
851 ENSURE_OR_RETURN_FALSE( mpCanvas.get(),
852 "SlideView::paintScreen(): Disposed" );
854 return mpCanvas->updateScreen( true );
857 void SlideView::clear() const
859 osl::MutexGuard aGuard( m_aMutex );
861 OSL_ENSURE( mxView.is() && mpCanvas,
862 "SlideView::clear(): Disposed" );
863 if( !mxView.is() || !mpCanvas )
864 return;
866 // keep layer clip
867 clearRect(getCanvas()->clone(),
868 getLayerBoundsPixel(
869 basegfx::B2DRange(0,0,
870 maUserSize.getX(),
871 maUserSize.getY()),
872 getTransformation()));
875 void SlideView::clearAll() const
877 osl::MutexGuard aGuard( m_aMutex );
879 OSL_ENSURE( mxView.is() && mpCanvas,
880 "SlideView::clear(): Disposed" );
881 if( !mxView.is() || !mpCanvas )
882 return;
884 mpCanvas->clear(); // this is unnecessary, strictly speaking. but
885 // it makes the SlideView behave exactly like a
886 // sprite-based SlideViewLayer, because those
887 // are created from scratch after a resize
889 // clear whole view
890 mxView->clear();
893 void SlideView::setViewSize( const basegfx::B2DSize& rSize )
895 osl::MutexGuard aGuard( m_aMutex );
897 maUserSize = rSize;
898 updateCanvas();
901 void SlideView::setCursorShape( sal_Int16 nPointerShape )
903 osl::MutexGuard const guard( m_aMutex );
905 if (mxView.is())
906 mxView->setMouseCursor( nPointerShape );
909 bool SlideView::isOnView(std::shared_ptr<View> const& rView) const
911 return rView.get() == this;
914 cppcanvas::CanvasSharedPtr SlideView::getCanvas() const
916 osl::MutexGuard aGuard( m_aMutex );
918 ENSURE_OR_THROW( mpCanvas,
919 "SlideView::getCanvas(): Disposed" );
921 return mpCanvas;
924 cppcanvas::CustomSpriteSharedPtr SlideView::createSprite(
925 const basegfx::B2DSize& rSpriteSizePixel,
926 double nPriority ) const
928 osl::MutexGuard aGuard( m_aMutex );
930 ENSURE_OR_THROW( mpCanvas, "SlideView::createSprite(): Disposed" );
932 cppcanvas::CustomSpriteSharedPtr pSprite(
933 mpCanvas->createCustomSprite( rSpriteSizePixel ) );
935 maSprites.addSprite( pSprite,
936 nPriority );
938 return pSprite;
941 void SlideView::setPriority( const basegfx::B1DRange& /*rRange*/ )
943 osl::MutexGuard aGuard( m_aMutex );
945 OSL_FAIL( "SlideView::setPriority() is a NOOP for slide view - "
946 "content will always be shown in the background" );
949 basegfx::B2DHomMatrix SlideView::getTransformation() const
951 osl::MutexGuard aGuard( m_aMutex );
953 basegfx::B2DHomMatrix aMatrix;
954 aMatrix.scale( 1.0/maUserSize.getX(), 1.0/maUserSize.getY() );
956 return maViewTransform * aMatrix;
959 geometry::IntegerSize2D SlideView::getTranslationOffset() const
961 return mxView->getTranslationOffset();
964 basegfx::B2DHomMatrix SlideView::getSpriteTransformation() const
966 return getTransformation();
969 void SlideView::setClip( const basegfx::B2DPolyPolygon& rClip )
971 osl::MutexGuard aGuard( m_aMutex );
973 basegfx::B2DPolyPolygon aNewClip = prepareClip( rClip );
975 if( aNewClip != maClip )
977 maClip = aNewClip;
979 updateClip();
983 bool SlideView::resize( const ::basegfx::B2DRange& /*rArea*/ )
985 osl::MutexGuard aGuard( m_aMutex );
987 OSL_FAIL( "SlideView::resize(): ignored for the View, can't change size "
988 "effectively, anyway" );
990 return false;
993 uno::Reference<presentation::XSlideShowView> SlideView::getUnoView() const
995 osl::MutexGuard aGuard( m_aMutex );
996 return mxView;
999 void SlideView::setIsSoundEnabled (const bool bValue)
1001 mbIsSoundEnabled = bValue;
1004 bool SlideView::isSoundEnabled() const
1006 return mbIsSoundEnabled;
1009 void SlideView::_dispose()
1011 dispose();
1014 // XEventListener
1015 void SlideView::disposing( lang::EventObject const& evt )
1017 // no deregistration necessary anymore, XView has left:
1018 osl::MutexGuard const guard( m_aMutex );
1020 if (mxView.is())
1022 OSL_ASSERT( evt.Source == mxView );
1023 mxView.clear();
1026 dispose();
1029 // silly wrapper to check that event handlers don't touch dead SlideView
1030 struct WeakRefWrapper
1032 SlideView & m_rObj;
1033 uno::WeakReference<uno::XInterface> const m_wObj;
1034 std::function<void (SlideView&)> const m_func;
1036 WeakRefWrapper(SlideView & rObj, std::function<void (SlideView&)> const& func)
1037 : m_rObj(rObj)
1038 , m_wObj(static_cast<::cppu::OWeakObject*>(&rObj))
1039 , m_func(func)
1043 void operator()()
1045 uno::Reference<uno::XInterface> const xObj(m_wObj);
1046 if (xObj.is())
1048 m_func(m_rObj);
1053 // XModifyListener
1054 void SlideView::modified( const lang::EventObject& /*aEvent*/ )
1056 osl::MutexGuard const guard( m_aMutex );
1058 OSL_ENSURE( mxView.is(), "SlideView::modified(): "
1059 "Disposed, but event received from XSlideShowView?!");
1061 if( !mxView.is() )
1062 return;
1064 geometry::AffineMatrix2D aViewTransform(
1065 mxView->getTransformation() );
1067 if( basegfx::fTools::equalZero(
1068 basegfx::B2DVector(aViewTransform.m00,
1069 aViewTransform.m10).getLength()) ||
1070 basegfx::fTools::equalZero(
1071 basegfx::B2DVector(aViewTransform.m01,
1072 aViewTransform.m11).getLength()) )
1074 OSL_FAIL( "SlideView::modified(): Singular matrix!" );
1076 canvas::tools::setIdentityAffineMatrix2D(aViewTransform);
1079 // view transformation really changed?
1080 basegfx::B2DHomMatrix aNewTransform;
1081 basegfx::unotools::homMatrixFromAffineMatrix(
1082 aNewTransform,
1083 aViewTransform );
1085 if( aNewTransform == maViewTransform )
1086 return; // No change, nothing to do
1088 maViewTransform = aNewTransform;
1090 updateCanvas();
1092 // notify view change. Don't call EventMultiplexer directly, this
1093 // might not be the main thread!
1094 mrEventQueue.addEvent(
1095 makeEvent( WeakRefWrapper(*this,
1096 [] (SlideView & rThis) { rThis.mrEventMultiplexer.notifyViewChanged(rThis.mxView); }),
1097 "EventMultiplexer::notifyViewChanged"));
1100 // XPaintListener
1101 void SlideView::windowPaint( const awt::PaintEvent& /*e*/ )
1103 osl::MutexGuard aGuard( m_aMutex );
1105 OSL_ENSURE( mxView.is() && mpCanvas, "Disposed, but event received?!" );
1107 // notify view clobbering. Don't call EventMultiplexer directly,
1108 // this might not be the main thread!
1109 mrEventQueue.addEvent(
1110 makeEvent( WeakRefWrapper(*this,
1111 [] (SlideView & rThis) { rThis.mrEventMultiplexer.notifyViewClobbered(rThis.mxView); }),
1112 "EventMultiplexer::notifyViewClobbered") );
1115 void SlideView::updateCanvas()
1117 OSL_ENSURE( mpCanvas,
1118 "SlideView::updateCanvasTransform(): Disposed" );
1120 if( !mpCanvas || !mxView.is())
1121 return;
1123 clearAll();
1124 mpCanvas->setTransformation( getTransformation() );
1125 mpCanvas->setClip(
1126 createClipPolygon( maClip,
1127 mpCanvas,
1128 maUserSize ));
1130 // forward update to viewlayers
1131 pruneLayers( true );
1134 void SlideView::updateClip()
1136 OSL_ENSURE( mpCanvas,
1137 "SlideView::updateClip(): Disposed" );
1139 if( !mpCanvas )
1140 return;
1142 mpCanvas->setClip(
1143 createClipPolygon( maClip,
1144 mpCanvas,
1145 maUserSize ));
1147 pruneLayers();
1150 void SlideView::pruneLayers( bool bWithViewLayerUpdate ) const
1152 ViewLayerVector aValidLayers;
1154 const basegfx::B2DHomMatrix& rCurrTransform(
1155 getTransformation() );
1157 // check all layers for validity, and retain only the live ones
1158 for( const auto& rView : maViewLayers )
1160 std::shared_ptr< SlideViewLayer > xCurrLayer( rView.lock() );
1162 if ( xCurrLayer )
1164 aValidLayers.push_back( xCurrLayer );
1166 if( bWithViewLayerUpdate )
1167 xCurrLayer->updateView( rCurrTransform,
1168 maUserSize );
1172 // replace layer list with pruned one
1173 maViewLayers.swap( aValidLayers );
1176 } // anonymous namespace
1178 UnoViewSharedPtr createSlideView( uno::Reference< presentation::XSlideShowView> const& xView,
1179 EventQueue& rEventQueue,
1180 EventMultiplexer& rEventMultiplexer )
1182 std::shared_ptr<SlideView> const that(
1183 comphelper::make_shared_from_UNO(
1184 new SlideView(xView,
1185 rEventQueue,
1186 rEventMultiplexer)));
1188 // register listeners with XSlideShowView
1189 xView->addTransformationChangedListener( that.get() );
1190 xView->addPaintListener( that.get() );
1192 // set new transformation
1193 that->updateCanvas();
1195 return that;
1198 } // namespace internal
1199 } // namespace slideshow
1201 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */