android: Update app-specific/MIME type icons
[LibreOffice.git] / slideshow / source / engine / slideview.cxx
blob6db623468ac790e3d533ffd0ab69ddb1b3d3a94c
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 <comphelper/diagnose_ex.hxx>
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 <comphelper/make_shared_from_uno.hxx>
33 #include <cppcanvas/spritecanvas.hxx>
34 #include <cppcanvas/customsprite.hxx>
35 #include <cppcanvas/vclfactory.hxx>
36 #include <cppcanvas/basegfxfactory.hxx>
38 #include <basegfx/range/b1drange.hxx>
39 #include <basegfx/range/b2drange.hxx>
40 #include <basegfx/range/b2irange.hxx>
41 #include <basegfx/point/b2dpoint.hxx>
42 #include <basegfx/polygon/b2dpolygon.hxx>
43 #include <basegfx/matrix/b2dhommatrix.hxx>
44 #include <basegfx/polygon/b2dpolygontools.hxx>
45 #include <basegfx/polygon/b2dpolypolygontools.hxx>
46 #include <basegfx/utils/canvastools.hxx>
47 #include <basegfx/polygon/b2dpolygonclipper.hxx>
48 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
50 #include <com/sun/star/awt/XPaintListener.hpp>
51 #include <com/sun/star/presentation/XSlideShowView.hpp>
52 #include <com/sun/star/rendering/CompositeOperation.hpp>
53 #include <com/sun/star/util/XModifyListener.hpp>
55 #include <memory>
56 #include <utility>
57 #include <vector>
58 #include <algorithm>
60 using namespace com::sun::star;
62 namespace slideshow::internal {
64 namespace {
66 /** Sprite entry, to store sprite plus priority
68 The operator<() defines a strict weak ordering of sprites, sort
69 key is the sprite priority.
71 struct SpriteEntry
73 SpriteEntry( const cppcanvas::CustomSpriteSharedPtr& rSprite,
74 double nPrio ) :
75 mpSprite( rSprite ),
76 mnPriority( nPrio )
80 bool operator<(const SpriteEntry& rRHS) const
82 return mnPriority < rRHS.mnPriority;
85 std::weak_ptr< cppcanvas::CustomSprite > mpSprite;
86 double mnPriority;
89 typedef std::vector< SpriteEntry > SpriteVector;
92 /** Create a clip polygon for slide views
94 @param rClip
95 Clip to set (can be empty)
97 @param rCanvas
98 Canvas to create the clip polygon for
100 @param rUserSize
101 The size of the view. Note that the returned clip will
102 <em>always</em> clip to at least the rect defined herein.
104 @return the view clip polygon, in view coordinates, which is
105 guaranteed to at least clip to the view size.
107 basegfx::B2DPolyPolygon createClipPolygon( const basegfx::B2DPolyPolygon& rClip,
108 const cppcanvas::CanvasSharedPtr& /*rCanvas*/,
109 const basegfx::B2DSize& rUserSize )
111 // setup canvas clipping
112 // =====================
114 // AW: Simplified
115 const basegfx::B2DRange aClipRange(0, 0, rUserSize.getWidth(), rUserSize.getHeight());
117 if(rClip.count())
119 return basegfx::utils::clipPolyPolygonOnRange(rClip, aClipRange, true, false);
121 else
123 return basegfx::B2DPolyPolygon(basegfx::utils::createPolygonFromRect(aClipRange));
127 /** Prepare given clip polygon to be stored as the current clip
129 Note that this is separate from createClipPolygon(), to allow
130 SlideView implementations to store this intermediate result
131 (createClipPolygon() has to be called every time the view size
132 changes)
134 basegfx::B2DPolyPolygon prepareClip( const basegfx::B2DPolyPolygon& rClip )
136 basegfx::B2DPolyPolygon aClip( rClip );
138 // normalize polygon, preparation for clipping
139 // in updateCanvas()
140 aClip = basegfx::utils::correctOrientations(aClip);
141 aClip = basegfx::utils::solveCrossovers(aClip);
142 aClip = basegfx::utils::stripNeutralPolygons(aClip);
143 aClip = basegfx::utils::stripDispensablePolygons(aClip);
145 return aClip;
149 void clearRect( ::cppcanvas::CanvasSharedPtr const& pCanvas,
150 basegfx::B2IRange const& rArea )
152 // convert clip polygon to device coordinate system
153 ::basegfx::B2DPolyPolygon const* pClipPoly( pCanvas->getClip() );
154 if( pClipPoly )
156 ::basegfx::B2DPolyPolygon aClipPoly( *pClipPoly );
157 aClipPoly.transform( pCanvas->getTransformation() );
158 pCanvas->setClip( aClipPoly );
161 // set transformation to identity (->device pixel)
162 pCanvas->setTransformation( ::basegfx::B2DHomMatrix() );
164 // #i42440# Fill the _full_ background in
165 // black. Since we had to extend the bitmap by one
166 // pixel, and the bitmap is initialized white,
167 // depending on the slide content a one pixel wide
168 // line will show to the bottom and the right.
169 const ::basegfx::B2DPolygon aPoly(
170 ::basegfx::utils::createPolygonFromRect(
171 basegfx::B2DRange(rArea)));
173 ::cppcanvas::PolyPolygonSharedPtr pPolyPoly(
174 ::cppcanvas::BaseGfxFactory::createPolyPolygon( pCanvas, aPoly ) );
176 if( pPolyPoly )
178 pPolyPoly->setCompositeOp( css::rendering::CompositeOperation::SOURCE );
179 pPolyPoly->setRGBAFillColor( 0xFFFFFF00U );
180 pPolyPoly->draw();
183 #if defined(DBG_UTIL)
184 ::cppcanvas::CanvasSharedPtr pCliplessCanvas( pCanvas->clone() );
185 pCliplessCanvas->setClip();
187 if( pCanvas->getClip() )
189 ::cppcanvas::PolyPolygonSharedPtr pPolyPoly2(
190 ::cppcanvas::BaseGfxFactory::createPolyPolygon( pCliplessCanvas, aPoly ));
191 if( pPolyPoly2 )
193 pPolyPoly2->setRGBALineColor( 0x008000FFU );
194 pPolyPoly2->draw();
197 #endif
200 /** Get bounds in pixel
202 @param rLayerBounds
203 Bound rect, in user space coordinates
205 @param rTransformation
206 User space to device pixel transformation
208 @return the layer bounds in pixel, extended by one pixel to the
209 right and bottom
211 basegfx::B2IRange getLayerBoundsPixel( basegfx::B2DRange const& rLayerBounds,
212 basegfx::B2DHomMatrix const& rTransformation )
214 ::basegfx::B2DRange aTmpRect;
215 ::canvas::tools::calcTransformedRectBounds( aTmpRect,
216 rLayerBounds,
217 rTransformation );
219 if( aTmpRect.isEmpty() )
220 return ::basegfx::B2IRange();
222 // #i42440# Returned layer size is one pixel too small, as
223 // rendering happens one pixel to the right and below the
224 // actual bound rect.
225 return ::basegfx::B2IRange( ::basegfx::fround(aTmpRect.getMinX()),
226 ::basegfx::fround(aTmpRect.getMinY()),
227 ::basegfx::fround(aTmpRect.getMaxX()) + 1,
228 ::basegfx::fround(aTmpRect.getMaxY()) + 1 );
232 /** Container class for sprites issued by a ViewLayer
234 This class handles the sprite prioritization issues, that are
235 needed for layer sprites (e.g. the need to re-prioritize sprites
236 when the layer changes prio).
238 class LayerSpriteContainer
240 /** Max fill level of maSprites, before we try to prune it from
241 deceased sprites
243 enum{ SPRITE_ULLAGE=256 };
245 /** All sprites that have been issued by this container (pruned
246 from time to time, for invalid references). This vector is
247 kept sorted with increasing sprite priority.
249 SpriteVector maSprites;
251 /// Priority of this layer, relative to other view layers
252 basegfx::B1DRange maLayerPrioRange;
254 double getSpritePriority( std::size_t nSpriteNum ) const
256 // divide the available layer range equally between all
257 // sprites, assign upper bound of individual sprite range as
258 // sprite prio (the layer itself gets assigned the lower bound
259 // of sprite 0's individual range):
261 // | layer 0 | layer 1 | ...
262 // | sprite 0 | sprite 1 | sprite 0 | sprite 1 | ...
263 return maLayerPrioRange.getMinimum() + maLayerPrioRange.getRange()*(nSpriteNum+1)/(maSprites.size()+1);
266 /** Rescan sprite vector, and remove deceased sprites (and reset
267 sprite prio)
269 @param aBegin
270 Iterator to the first entry to rescan
272 void updateSprites()
274 SpriteVector aValidSprites;
276 // check all sprites for validity and set new priority
277 for( const auto& rSprite : maSprites )
279 cppcanvas::CustomSpriteSharedPtr pCurrSprite( rSprite.mpSprite.lock() );
281 if( pCurrSprite )
283 // only copy still valid sprites over to the refreshed
284 // sprite vector.
285 aValidSprites.push_back( rSprite );
287 pCurrSprite->setPriority(
288 getSpritePriority( aValidSprites.size()-1 ));
292 // replace sprite list with pruned one
293 maSprites.swap( aValidSprites );
296 public:
297 LayerSpriteContainer() :
298 maSprites(),
299 maLayerPrioRange()
303 const basegfx::B1DRange& getLayerPriority() const
305 return maLayerPrioRange;
308 void setLayerPriority( const basegfx::B1DRange& rRange )
310 if( rRange != maLayerPrioRange )
312 maLayerPrioRange = rRange;
314 // prune and recalc sprite prios
315 updateSprites();
319 void addSprite( const cppcanvas::CustomSpriteSharedPtr& pSprite,
320 double nPriority )
322 if( !pSprite )
323 return;
325 SpriteEntry aEntry( pSprite,nPriority );
327 // insert new sprite, such that vector stays sorted
328 SpriteVector::iterator aInsertPos(
329 maSprites.insert(
330 std::lower_bound( maSprites.begin(),
331 maSprites.end(),
332 aEntry ),
333 aEntry ));
335 const std::size_t nNumSprites( maSprites.size() );
336 if( nNumSprites > SPRITE_ULLAGE ||
337 maSprites.end() - aInsertPos > 1 )
339 // updateSprites() also updates all sprite prios
340 updateSprites();
342 else
344 // added sprite to the end, and not too many sprites in
345 // vector - perform optimized update (only need to set
346 // prio). This basically caters for the common case of
347 // iterated character animations, which generate lots of
348 // sprites, all added to the end.
349 pSprite->setPriority(
350 getSpritePriority( nNumSprites-1 ));
354 void clear()
356 maSprites.clear();
361 /** This class provides layers for a slide view
363 Layers are used to render animations with the correct z order -
364 because sprites are always in front of the static canvas
365 background, shapes that must appear <em<before</em> an animation
366 must also be displayed as a sprite.
368 Each layer has a priority assigned to it (valid range [0,1]), which
369 also affects all sprites created for this specific layer - i.e. if
370 the layer priority changes, the sprites change z order together
371 with their parent.
373 class SlideViewLayer : public ViewLayer
375 /// Smart container for all sprites issued by this layer
376 mutable LayerSpriteContainer maSpriteContainer;
378 /// Bounds of this layer in user space coordinates
379 basegfx::B2DRange maLayerBounds;
381 /// Bounds of this layer in device pixel
382 mutable basegfx::B2IRange maLayerBoundsPixel;
384 /// Current clip polygon in user coordinates
385 basegfx::B2DPolyPolygon maClip;
387 /// Current size of the view in user coordinates
388 basegfx::B2DSize maUserSize;
390 /// Current overall view transformation
391 basegfx::B2DHomMatrix maTransformation;
393 /// 'parent' canvas, this viewlayer is associated with
394 const cppcanvas::SpriteCanvasSharedPtr mpSpriteCanvas;
396 /** output surface (necessarily a sprite, won't otherwise be able
397 to display anything <em>before</em> other sprites)
399 mutable cppcanvas::CustomSpriteSharedPtr mpSprite;
401 /// actual output canvas retrieved from a sprite
402 mutable cppcanvas::CanvasSharedPtr mpOutputCanvas;
404 /// ptr back to owning view. needed for isOnView() method
405 View const* const mpParentView;
407 public:
408 /** Create a new layer
410 @param pCanvas
411 Sprite canvas to create the layer on
413 @param rTransform
414 Initial overall canvas transformation
416 @param rLayerBounds
417 Initial layer bounds, in view coordinate system
419 SlideViewLayer( cppcanvas::SpriteCanvasSharedPtr pCanvas,
420 basegfx::B2DHomMatrix aTransform,
421 const basegfx::B2DRange& rLayerBounds,
422 const basegfx::B2DSize& rUserSize,
423 View const* const pParentView) :
424 maSpriteContainer(),
425 maLayerBounds(rLayerBounds),
426 maLayerBoundsPixel(),
427 maClip(),
428 maUserSize(rUserSize),
429 maTransformation(std::move(aTransform)),
430 mpSpriteCanvas(std::move(pCanvas)),
431 mpSprite(),
432 mpOutputCanvas(),
433 mpParentView(pParentView)
437 SlideViewLayer(const SlideViewLayer&) = delete;
438 SlideViewLayer& operator=(const SlideViewLayer&) = delete;
440 void updateView( const basegfx::B2DHomMatrix& rMatrix,
441 const basegfx::B2DSize& rUserSize )
443 maTransformation = rMatrix;
444 maUserSize = rUserSize;
446 // limit layer bounds to visible screen
447 maLayerBounds.intersect( basegfx::B2DRange(0.0,
448 0.0,
449 maUserSize.getWidth(),
450 maUserSize.getHeight()) );
452 basegfx::B2IRange const& rNewLayerPixel(
453 getLayerBoundsPixel(maLayerBounds,
454 maTransformation) );
455 if( rNewLayerPixel != maLayerBoundsPixel )
457 // re-gen sprite with new size
458 mpOutputCanvas.reset();
459 mpSprite.reset();
463 virtual css::geometry::IntegerSize2D getTranslationOffset() const override
465 basegfx::B2DRectangle aTmpRect;
466 canvas::tools::calcTransformedRectBounds( aTmpRect,
467 maLayerBounds,
468 maTransformation );
469 geometry::IntegerSize2D offset(0, 0);
471 // Add translation according to the origin of aTmpRect. Ignore the
472 // translation when aTmpRect was not properly initialized.
473 if ( ! aTmpRect.isEmpty())
475 offset.Width = basegfx::fround(aTmpRect.getMinX());
476 offset.Height = basegfx::fround(aTmpRect.getMinY());
478 return offset;
481 private:
482 // ViewLayer interface
485 virtual cppcanvas::CustomSpriteSharedPtr createSprite(
486 const ::basegfx::B2DSize& rSpriteSizePixel,
487 double nPriority ) const override
489 cppcanvas::CustomSpriteSharedPtr pSprite(
490 mpSpriteCanvas->createCustomSprite( rSpriteSizePixel ) );
492 maSpriteContainer.addSprite( pSprite,
493 nPriority );
495 return pSprite;
498 virtual void setPriority( const basegfx::B1DRange& rRange ) override
500 OSL_ENSURE( !rRange.isEmpty() &&
501 rRange.getMinimum() >= 1.0,
502 "SlideViewLayer::setPriority(): prio MUST be larger than 1.0 (because "
503 "the background layer already lies there)" );
505 maSpriteContainer.setLayerPriority( rRange );
507 if( mpSprite )
508 mpSprite->setPriority( rRange.getMinimum() );
511 virtual basegfx::B2DHomMatrix getTransformation() const override
513 // Offset given transformation by left, top border of given
514 // range (after transformation through given transformation)
515 basegfx::B2DRectangle aTmpRect;
516 canvas::tools::calcTransformedRectBounds( aTmpRect,
517 maLayerBounds,
518 maTransformation );
520 basegfx::B2DHomMatrix aMatrix( maTransformation );
522 // Add translation according to the origin of aTmpRect. Ignore the
523 // translation when aTmpRect was not properly initialized.
524 if ( ! aTmpRect.isEmpty())
526 aMatrix.translate( -basegfx::fround(aTmpRect.getMinX()),
527 -basegfx::fround(aTmpRect.getMinY()) );
530 return aMatrix;
533 virtual basegfx::B2DHomMatrix getSpriteTransformation() const override
535 return maTransformation;
538 virtual void clear() const override
540 // grab canvas - that also lazy-initializes maLayerBoundsPixel
541 cppcanvas::CanvasSharedPtr pCanvas=getCanvas()->clone();
543 // clear whole canvas
544 const basegfx::B2I64Tuple& rSpriteSize(maLayerBoundsPixel.getRange());
545 clearRect(pCanvas,
546 basegfx::B2IRange(0,0,rSpriteSize.getX(),rSpriteSize.getY()));
549 virtual void clearAll() const override
551 // grab canvas - that also lazy-initializes maLayerBoundsPixel
552 ::cppcanvas::CanvasSharedPtr pCanvas( getCanvas()->clone() );
554 // clear layer clip, to clear whole area
555 pCanvas->setClip();
557 // clear whole canvas
558 const basegfx::B2I64Tuple& rSpriteSize(maLayerBoundsPixel.getRange());
559 clearRect(pCanvas,
560 basegfx::B2IRange(0,0,rSpriteSize.getX(),rSpriteSize.getY()));
563 virtual bool isOnView(ViewSharedPtr const& rView) const override
565 return rView.get() == mpParentView;
568 virtual cppcanvas::CanvasSharedPtr getCanvas() const override
570 if( !mpOutputCanvas )
572 if( !mpSprite )
574 maLayerBoundsPixel = getLayerBoundsPixel(maLayerBounds,
575 maTransformation);
577 // HACK: ensure at least 1x1 pixel size. clients might
578 // need an actual canvas (e.g. for bound rect
579 // calculations) without rendering anything. Better
580 // solution: introduce something like a reference
581 // canvas for ViewLayers, which is always available.
582 if( maLayerBoundsPixel.isEmpty() )
583 maLayerBoundsPixel = basegfx::B2IRange(0,0,1,1);
585 const basegfx::B2I64Tuple& rSpriteSize(maLayerBoundsPixel.getRange());
586 mpSprite = mpSpriteCanvas->createCustomSprite(
587 basegfx::B2DSize(sal::static_int_cast<sal_Int32>(rSpriteSize.getX()),
588 sal::static_int_cast<sal_Int32>(rSpriteSize.getY())) );
590 mpSprite->setPriority(
591 maSpriteContainer.getLayerPriority().getMinimum() );
593 #if defined(DBG_UTIL)
594 mpSprite->movePixel(
595 basegfx::B2DPoint(maLayerBoundsPixel.getMinimum()) +
596 basegfx::B2DPoint(10,10) );
598 mpSprite->setAlpha(0.5);
599 #else
600 mpSprite->movePixel(
601 basegfx::B2DPoint(maLayerBoundsPixel.getMinimum()) );
603 mpSprite->setAlpha(1.0);
604 #endif
605 mpSprite->show();
608 ENSURE_OR_THROW( mpSprite,
609 "SlideViewLayer::getCanvas(): no layer sprite" );
611 mpOutputCanvas = mpSprite->getContentCanvas();
613 ENSURE_OR_THROW( mpOutputCanvas,
614 "SlideViewLayer::getCanvas(): sprite doesn't yield a canvas" );
616 // new canvas retrieved - setup transformation and clip
617 mpOutputCanvas->setTransformation( getTransformation() );
618 mpOutputCanvas->setClip(
619 createClipPolygon( maClip,
620 mpOutputCanvas,
621 maUserSize ));
624 return mpOutputCanvas;
627 virtual void setClip( const basegfx::B2DPolyPolygon& rClip ) override
629 basegfx::B2DPolyPolygon aNewClip = prepareClip( rClip );
631 if( aNewClip != maClip )
633 maClip = aNewClip;
635 if(mpOutputCanvas )
636 mpOutputCanvas->setClip(
637 createClipPolygon( maClip,
638 mpOutputCanvas,
639 maUserSize ));
643 virtual bool resize( const ::basegfx::B2DRange& rArea ) override
645 const bool bRet( maLayerBounds != rArea );
646 maLayerBounds = rArea;
647 updateView( maTransformation,
648 maUserSize );
650 return bRet;
655 typedef cppu::WeakComponentImplHelper<
656 css::util::XModifyListener,
657 css::awt::XPaintListener> SlideViewBase;
659 /** SlideView class
661 This class implements the View interface, encapsulating
662 <em>one</em> view a slideshow is displayed on.
664 class SlideView : private cppu::BaseMutex,
665 public SlideViewBase,
666 public UnoView
668 public:
669 SlideView( const uno::Reference<presentation::XSlideShowView>& xView,
670 EventQueue& rEventQueue,
671 EventMultiplexer& rEventMultiplexer );
672 void updateCanvas();
674 private:
675 // View:
676 virtual ViewLayerSharedPtr createViewLayer( const basegfx::B2DRange& rLayerBounds ) const override;
677 virtual bool updateScreen() const override;
678 virtual bool paintScreen() const override;
679 virtual void setViewSize( const ::basegfx::B2DSize& ) override;
680 virtual void setCursorShape( sal_Int16 nPointerShape ) override;
682 // ViewLayer interface
683 virtual bool isOnView(ViewSharedPtr const& rView) const override;
684 virtual void clear() const override;
685 virtual void clearAll() const override;
686 virtual cppcanvas::CanvasSharedPtr getCanvas() const override;
687 virtual cppcanvas::CustomSpriteSharedPtr createSprite( const ::basegfx::B2DSize& rSpriteSizePixel,
688 double nPriority ) const override;
689 virtual void setPriority( const basegfx::B1DRange& rRange ) override;
690 virtual geometry::IntegerSize2D getTranslationOffset() const override;
691 virtual ::basegfx::B2DHomMatrix getTransformation() const override;
692 virtual basegfx::B2DHomMatrix getSpriteTransformation() const override;
693 virtual void setClip( const ::basegfx::B2DPolyPolygon& rClip ) override;
694 virtual bool resize( const ::basegfx::B2DRange& rArea ) override;
696 // UnoView:
697 virtual void _dispose() override;
698 virtual uno::Reference<presentation::XSlideShowView> getUnoView()const override;
699 virtual void setIsSoundEnabled (const bool bValue) override;
700 virtual bool isSoundEnabled() const override;
702 // XEventListener:
703 virtual void SAL_CALL disposing( lang::EventObject const& evt ) override;
704 // XModifyListener:
705 virtual void SAL_CALL modified( const lang::EventObject& aEvent ) override;
706 // XPaintListener:
707 virtual void SAL_CALL windowPaint( const awt::PaintEvent& e ) override;
709 // WeakComponentImplHelperBase:
710 virtual void SAL_CALL disposing() override;
712 void updateClip();
714 private:
715 typedef std::vector< std::weak_ptr<SlideViewLayer> > ViewLayerVector;
717 /// Prune viewlayers from deceased ones, optionally update them
718 void pruneLayers( bool bWithViewLayerUpdate=false ) const;
720 /** Max fill level of maViewLayers, before we try to prune it from
721 deceased layers
723 enum{ LAYER_ULLAGE=8 };
725 uno::Reference<presentation::XSlideShowView> mxView;
726 cppcanvas::SpriteCanvasSharedPtr mpCanvas;
728 EventMultiplexer& mrEventMultiplexer;
729 EventQueue& mrEventQueue;
731 mutable LayerSpriteContainer maSprites;
732 mutable ViewLayerVector maViewLayers;
734 basegfx::B2DPolyPolygon maClip;
736 basegfx::B2DHomMatrix maViewTransform;
737 basegfx::B2DSize maUserSize;
738 bool mbIsSoundEnabled;
742 SlideView::SlideView( const uno::Reference<presentation::XSlideShowView>& xView,
743 EventQueue& rEventQueue,
744 EventMultiplexer& rEventMultiplexer ) :
745 SlideViewBase( m_aMutex ),
746 mxView( xView ),
747 mpCanvas(),
748 mrEventMultiplexer( rEventMultiplexer ),
749 mrEventQueue( rEventQueue ),
750 maSprites(),
751 maViewLayers(),
752 maClip(),
753 maViewTransform(),
754 maUserSize( 1.0, 1.0 ), // default size: one-by-one rectangle
755 mbIsSoundEnabled(true)
757 // take care not constructing any UNO references to this _inside_
758 // ctor, shift that code to createSlideView()!
759 ENSURE_OR_THROW( mxView.is(),
760 "SlideView::SlideView(): Invalid view" );
762 mpCanvas = cppcanvas::VCLFactory::createSpriteCanvas(
763 xView->getCanvas() );
764 ENSURE_OR_THROW( mpCanvas,
765 "Could not create cppcanvas" );
767 geometry::AffineMatrix2D aViewTransform(
768 xView->getTransformation() );
770 if( basegfx::fTools::equalZero(
771 basegfx::B2DVector(aViewTransform.m00,
772 aViewTransform.m10).getLength()) ||
773 basegfx::fTools::equalZero(
774 basegfx::B2DVector(aViewTransform.m01,
775 aViewTransform.m11).getLength()) )
777 OSL_FAIL( "SlideView::SlideView(): Singular matrix!" );
779 canvas::tools::setIdentityAffineMatrix2D(aViewTransform);
782 basegfx::unotools::homMatrixFromAffineMatrix(
783 maViewTransform, aViewTransform );
785 // once and forever: set fixed prio to this 'layer' (we're always
786 // the background layer)
787 maSprites.setLayerPriority( basegfx::B1DRange(0.0,1.0) );
790 void SlideView::disposing()
792 osl::MutexGuard aGuard( m_aMutex );
794 maViewLayers.clear();
795 maSprites.clear();
796 mpCanvas.reset();
798 // additionally, also de-register from XSlideShowView
799 if (mxView.is())
801 mxView->removeTransformationChangedListener( this );
802 mxView->removePaintListener( this );
803 mxView.clear();
807 ViewLayerSharedPtr SlideView::createViewLayer( const basegfx::B2DRange& rLayerBounds ) const
809 osl::MutexGuard aGuard( m_aMutex );
811 ENSURE_OR_THROW( mpCanvas,
812 "SlideView::createViewLayer(): Disposed" );
814 const std::size_t nNumLayers( maViewLayers.size() );
816 // avoid filling up layer vector with lots of deceased layer weak
817 // ptrs
818 if( nNumLayers > LAYER_ULLAGE )
819 pruneLayers();
821 auto xViewLayer = std::make_shared<SlideViewLayer>(mpCanvas,
822 getTransformation(),
823 rLayerBounds,
824 maUserSize,
825 this);
826 maViewLayers.push_back(xViewLayer);
828 return xViewLayer;
831 bool SlideView::updateScreen() const
833 osl::MutexGuard aGuard( m_aMutex );
835 ENSURE_OR_RETURN_FALSE( mpCanvas,
836 "SlideView::updateScreen(): Disposed" );
838 return mpCanvas->updateScreen( false );
841 bool SlideView::paintScreen() const
843 osl::MutexGuard aGuard( m_aMutex );
845 ENSURE_OR_RETURN_FALSE( mpCanvas,
846 "SlideView::paintScreen(): Disposed" );
848 return mpCanvas->updateScreen( true );
851 void SlideView::clear() const
853 osl::MutexGuard aGuard( m_aMutex );
855 OSL_ENSURE( mxView.is() && mpCanvas,
856 "SlideView::clear(): Disposed" );
857 if( !mxView.is() || !mpCanvas )
858 return;
860 // keep layer clip
861 clearRect(getCanvas()->clone(),
862 getLayerBoundsPixel(
863 basegfx::B2DRange(0,0,
864 maUserSize.getWidth(),
865 maUserSize.getHeight()),
866 getTransformation()));
869 void SlideView::clearAll() const
871 osl::MutexGuard aGuard( m_aMutex );
873 OSL_ENSURE( mxView.is() && mpCanvas,
874 "SlideView::clear(): Disposed" );
875 if( !mxView.is() || !mpCanvas )
876 return;
878 mpCanvas->clear(); // this is unnecessary, strictly speaking. but
879 // it makes the SlideView behave exactly like a
880 // sprite-based SlideViewLayer, because those
881 // are created from scratch after a resize
883 // clear whole view
884 mxView->clear();
887 void SlideView::setViewSize( const basegfx::B2DSize& rSize )
889 osl::MutexGuard aGuard( m_aMutex );
891 maUserSize = rSize;
892 updateCanvas();
895 void SlideView::setCursorShape( sal_Int16 nPointerShape )
897 osl::MutexGuard const guard( m_aMutex );
899 if (mxView.is())
900 mxView->setMouseCursor( nPointerShape );
903 bool SlideView::isOnView(ViewSharedPtr const& rView) const
905 return rView.get() == this;
908 cppcanvas::CanvasSharedPtr SlideView::getCanvas() const
910 osl::MutexGuard aGuard( m_aMutex );
912 ENSURE_OR_THROW( mpCanvas,
913 "SlideView::getCanvas(): Disposed" );
915 return mpCanvas;
918 cppcanvas::CustomSpriteSharedPtr SlideView::createSprite(
919 const basegfx::B2DSize& rSpriteSizePixel,
920 double nPriority ) const
922 osl::MutexGuard aGuard( m_aMutex );
924 ENSURE_OR_THROW( mpCanvas, "SlideView::createSprite(): Disposed" );
926 cppcanvas::CustomSpriteSharedPtr pSprite(
927 mpCanvas->createCustomSprite( rSpriteSizePixel ) );
929 maSprites.addSprite( pSprite,
930 nPriority );
932 return pSprite;
935 void SlideView::setPriority( const basegfx::B1DRange& /*rRange*/ )
937 OSL_FAIL( "SlideView::setPriority() is a NOOP for slide view - "
938 "content will always be shown in the background" );
941 basegfx::B2DHomMatrix SlideView::getTransformation() const
943 osl::MutexGuard aGuard( m_aMutex );
945 basegfx::B2DHomMatrix aMatrix;
946 aMatrix.scale( 1.0 / maUserSize.getWidth(), 1.0 / maUserSize.getHeight() );
948 return maViewTransform * aMatrix;
951 geometry::IntegerSize2D SlideView::getTranslationOffset() const
953 return mxView->getTranslationOffset();
956 basegfx::B2DHomMatrix SlideView::getSpriteTransformation() const
958 return getTransformation();
961 void SlideView::setClip( const basegfx::B2DPolyPolygon& rClip )
963 osl::MutexGuard aGuard( m_aMutex );
965 basegfx::B2DPolyPolygon aNewClip = prepareClip( rClip );
967 if( aNewClip != maClip )
969 maClip = aNewClip;
971 updateClip();
975 bool SlideView::resize( const ::basegfx::B2DRange& /*rArea*/ )
977 OSL_FAIL( "SlideView::resize(): ignored for the View, can't change size "
978 "effectively, anyway" );
980 return false;
983 uno::Reference<presentation::XSlideShowView> SlideView::getUnoView() const
985 osl::MutexGuard aGuard( m_aMutex );
986 return mxView;
989 void SlideView::setIsSoundEnabled (const bool bValue)
991 mbIsSoundEnabled = bValue;
994 bool SlideView::isSoundEnabled() const
996 return mbIsSoundEnabled;
999 void SlideView::_dispose()
1001 dispose();
1004 // XEventListener
1005 void SlideView::disposing( lang::EventObject const& evt )
1007 // no deregistration necessary anymore, XView has left:
1008 osl::MutexGuard const guard( m_aMutex );
1010 if (mxView.is())
1012 OSL_ASSERT( evt.Source == mxView );
1013 mxView.clear();
1016 dispose();
1019 // silly wrapper to check that event handlers don't touch dead SlideView
1020 struct WeakRefWrapper
1022 SlideView & m_rObj;
1023 uno::WeakReference<uno::XInterface> const m_wObj;
1024 std::function<void (SlideView&)> const m_func;
1026 WeakRefWrapper(SlideView & rObj, std::function<void (SlideView&)> func)
1027 : m_rObj(rObj)
1028 , m_wObj(rObj.getXWeak())
1029 , m_func(std::move(func))
1033 void operator()()
1035 uno::Reference<uno::XInterface> const xObj(m_wObj);
1036 if (xObj.is())
1038 m_func(m_rObj);
1043 // XModifyListener
1044 void SlideView::modified( const lang::EventObject& /*aEvent*/ )
1046 osl::MutexGuard const guard( m_aMutex );
1048 OSL_ENSURE( mxView.is(), "SlideView::modified(): "
1049 "Disposed, but event received from XSlideShowView?!");
1051 if( !mxView.is() )
1052 return;
1054 geometry::AffineMatrix2D aViewTransform(
1055 mxView->getTransformation() );
1057 if( basegfx::fTools::equalZero(
1058 basegfx::B2DVector(aViewTransform.m00,
1059 aViewTransform.m10).getLength()) ||
1060 basegfx::fTools::equalZero(
1061 basegfx::B2DVector(aViewTransform.m01,
1062 aViewTransform.m11).getLength()) )
1064 OSL_FAIL( "SlideView::modified(): Singular matrix!" );
1066 canvas::tools::setIdentityAffineMatrix2D(aViewTransform);
1069 // view transformation really changed?
1070 basegfx::B2DHomMatrix aNewTransform;
1071 basegfx::unotools::homMatrixFromAffineMatrix(
1072 aNewTransform,
1073 aViewTransform );
1075 if( aNewTransform == maViewTransform )
1076 return; // No change, nothing to do
1078 maViewTransform = aNewTransform;
1080 updateCanvas();
1082 // notify view change. Don't call EventMultiplexer directly, this
1083 // might not be the main thread!
1084 mrEventQueue.addEvent(
1085 makeEvent( WeakRefWrapper(*this,
1086 [] (SlideView & rThis) { rThis.mrEventMultiplexer.notifyViewChanged(rThis.mxView); }),
1087 "EventMultiplexer::notifyViewChanged"));
1090 // XPaintListener
1091 void SlideView::windowPaint( const awt::PaintEvent& /*e*/ )
1093 osl::MutexGuard aGuard( m_aMutex );
1095 OSL_ENSURE( mxView.is() && mpCanvas, "Disposed, but event received?!" );
1097 // notify view clobbering. Don't call EventMultiplexer directly,
1098 // this might not be the main thread!
1099 mrEventQueue.addEvent(
1100 makeEvent( WeakRefWrapper(*this,
1101 [] (SlideView & rThis) { rThis.mrEventMultiplexer.notifyViewClobbered(rThis.mxView); }),
1102 "EventMultiplexer::notifyViewClobbered") );
1105 void SlideView::updateCanvas()
1107 OSL_ENSURE( mpCanvas,
1108 "SlideView::updateCanvasTransform(): Disposed" );
1110 if( !mpCanvas || !mxView.is())
1111 return;
1113 clearAll();
1114 mpCanvas->setTransformation( getTransformation() );
1115 mpCanvas->setClip(
1116 createClipPolygon( maClip,
1117 mpCanvas,
1118 maUserSize ));
1120 // forward update to viewlayers
1121 pruneLayers( true );
1124 void SlideView::updateClip()
1126 OSL_ENSURE( mpCanvas,
1127 "SlideView::updateClip(): Disposed" );
1129 if( !mpCanvas )
1130 return;
1132 mpCanvas->setClip(
1133 createClipPolygon( maClip,
1134 mpCanvas,
1135 maUserSize ));
1137 pruneLayers();
1140 void SlideView::pruneLayers( bool bWithViewLayerUpdate ) const
1142 ViewLayerVector aValidLayers;
1144 const basegfx::B2DHomMatrix& rCurrTransform(
1145 getTransformation() );
1147 // check all layers for validity, and retain only the live ones
1148 for( const auto& rView : maViewLayers )
1150 std::shared_ptr< SlideViewLayer > xCurrLayer( rView.lock() );
1152 if ( xCurrLayer )
1154 aValidLayers.push_back( xCurrLayer );
1156 if( bWithViewLayerUpdate )
1157 xCurrLayer->updateView( rCurrTransform,
1158 maUserSize );
1162 // replace layer list with pruned one
1163 maViewLayers.swap( aValidLayers );
1166 } // anonymous namespace
1168 UnoViewSharedPtr createSlideView( uno::Reference< presentation::XSlideShowView> const& xView,
1169 EventQueue& rEventQueue,
1170 EventMultiplexer& rEventMultiplexer )
1172 std::shared_ptr<SlideView> const that(
1173 comphelper::make_shared_from_UNO(
1174 new SlideView(xView,
1175 rEventQueue,
1176 rEventMultiplexer)));
1178 // register listeners with XSlideShowView
1179 xView->addTransformationChangedListener( that.get() );
1180 xView->addPaintListener( that.get() );
1182 // set new transformation
1183 that->updateCanvas();
1185 return that;
1188 } // namespace slideshow
1190 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */