1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 #include "precompiled_slideshow.hxx"
28 
29 #include <canvas/debug.hxx>
30 #include <tools/diagnose_ex.h>
31 #include <canvas/canvastools.hxx>
32 
33 #include "eventqueue.hxx"
34 #include "eventmultiplexer.hxx"
35 #include "slideview.hxx"
36 #include "delayevent.hxx"
37 #include "unoview.hxx"
38 
39 #include <rtl/instance.hxx>
40 #include <cppuhelper/basemutex.hxx>
41 #include <cppuhelper/compbase2.hxx>
42 #include <cppuhelper/implementationentry.hxx>
43 #include <cppuhelper/interfacecontainer.h>
44 #include <comphelper/make_shared_from_uno.hxx>
45 
46 #include <cppcanvas/spritecanvas.hxx>
47 #include <cppcanvas/customsprite.hxx>
48 #include <cppcanvas/vclfactory.hxx>
49 #include <cppcanvas/basegfxfactory.hxx>
50 
51 #include <tools/debug.hxx>
52 
53 #include <basegfx/range/b1drange.hxx>
54 #include <basegfx/range/b2drange.hxx>
55 #include <basegfx/range/b2irange.hxx>
56 #include <basegfx/point/b2dpoint.hxx>
57 #include <basegfx/polygon/b2dpolygon.hxx>
58 #include <basegfx/matrix/b2dhommatrix.hxx>
59 #include <basegfx/polygon/b2dpolygontools.hxx>
60 #include <basegfx/polygon/b2dpolypolygontools.hxx>
61 #include <basegfx/tools/canvastools.hxx>
62 #include <basegfx/polygon/b2dpolygonclipper.hxx>
63 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
64 
65 #include <com/sun/star/presentation/XSlideShow.hpp>
66 
67 #include <boost/noncopyable.hpp>
68 #include <boost/bind.hpp>
69 #include <boost/weak_ptr.hpp>
70 
71 #include <vector>
72 #include <iterator>
73 #include <algorithm>
74 
75 using namespace com::sun::star;
76 
77 namespace slideshow {
78 namespace internal {
79 
80 namespace {
81 
82 struct StaticUnitRectPoly : public rtl::StaticWithInit<basegfx::B2DPolygon, StaticUnitRectPoly>
83 {
84     basegfx::B2DPolygon operator()()
85     {
86         return basegfx::tools::createUnitPolygon();
87     }
88 };
89 
90 /** Sprite entry, to store sprite plus priority
91 
92     The operator<() defines a strict weak ordering of sprites, sort
93     key is the sprite priority.
94  */
95 struct SpriteEntry
96 {
97     SpriteEntry( const cppcanvas::CustomSpriteSharedPtr& rSprite,
98                  double                                  nPrio ) :
99         mpSprite( rSprite ),
100         mnPriority( nPrio )
101     {
102     }
103 
104     bool operator<(const SpriteEntry& rRHS) const
105     {
106         return mnPriority < rRHS.mnPriority;
107     }
108 
109     boost::weak_ptr< cppcanvas::CustomSprite > mpSprite;
110     double                                     mnPriority;
111 };
112 
113 typedef std::vector< SpriteEntry > SpriteVector;
114 
115 
116 /** Create a clip polygon for slide views
117 
118     @param rClip
119     Clip to set (can be empty)
120 
121     @param rCanvas
122     Canvas to create the clip polygon for
123 
124     @param rUserSize
125     The size of the view. Note that the returned clip will
126     <em>always</em> clip to at least the rect defined herein.
127 
128     @return the view clip polygon, in view coordinates, which is
129     guaranteed to at least clip to the view size.
130  */
131 basegfx::B2DPolyPolygon createClipPolygon( const basegfx::B2DPolyPolygon&    rClip,
132                                            const cppcanvas::CanvasSharedPtr& /*rCanvas*/,
133                                            const basegfx::B2DSize&           rUserSize )
134 {
135     // setup canvas clipping
136     // =====================
137 
138     // AW: Simplified
139 	const basegfx::B2DRange aClipRange(0, 0, rUserSize.getX(), rUserSize.getY());
140 
141     if(rClip.count())
142     {
143     	return basegfx::tools::clipPolyPolygonOnRange(rClip, aClipRange, true, false);
144     }
145     else
146     {
147         return basegfx::B2DPolyPolygon(basegfx::tools::createPolygonFromRect(aClipRange));
148     }
149 }
150 
151 /** Prepare given clip polygon to be stored as the current clip
152 
153     Note that this is separate from createClipPolygon(), to allow
154     SlideView implementations to store this intermediate result
155     (createClipPolygon() has to be called every time the view size
156     changes)
157  */
158 basegfx::B2DPolyPolygon prepareClip( const basegfx::B2DPolyPolygon& rClip )
159 {
160     basegfx::B2DPolyPolygon aClip( rClip );
161 
162     // TODO(P2): unnecessary, once XCanvas is correctly handling this
163     // AW: Should be no longer necessary; tools are now bezier-safe
164     if( aClip.areControlPointsUsed() )
165         aClip = basegfx::tools::adaptiveSubdivideByAngle( aClip );
166 
167     // normalize polygon, preparation for clipping
168     // in updateCanvas()
169     aClip = basegfx::tools::correctOrientations(aClip);
170     aClip = basegfx::tools::solveCrossovers(aClip);
171     aClip = basegfx::tools::stripNeutralPolygons(aClip);
172     aClip = basegfx::tools::stripDispensablePolygons(aClip, false);
173 
174     return aClip;
175 }
176 
177 
178 void clearRect( ::cppcanvas::CanvasSharedPtr const& pCanvas,
179                 basegfx::B2IRange const&            rArea )
180 {
181     // convert clip polygon to device coordinate system
182     ::basegfx::B2DPolyPolygon const* pClipPoly( pCanvas->getClip() );
183     if( pClipPoly )
184     {
185         ::basegfx::B2DPolyPolygon aClipPoly( *pClipPoly );
186         aClipPoly.transform( pCanvas->getTransformation() );
187         pCanvas->setClip( aClipPoly );
188     }
189 
190     // set transformation to identitiy (->device pixel)
191     pCanvas->setTransformation( ::basegfx::B2DHomMatrix() );
192 
193     // #i42440# Fill the _full_ background in
194     // black. Since we had to extend the bitmap by one
195     // pixel, and the bitmap is initialized white,
196     // depending on the slide content a one pixel wide
197     // line will show to the bottom and the right.
198     const ::basegfx::B2DPolygon aPoly(
199         ::basegfx::tools::createPolygonFromRect(
200             basegfx::B2DRange(rArea)));
201 
202     ::cppcanvas::PolyPolygonSharedPtr pPolyPoly(
203         ::cppcanvas::BaseGfxFactory::getInstance().createPolyPolygon( pCanvas,
204                                                                       aPoly ) );
205 
206     if( pPolyPoly )
207     {
208         pPolyPoly->setCompositeOp( cppcanvas::CanvasGraphic::SOURCE );
209         pPolyPoly->setRGBAFillColor( 0x00000000U );
210         pPolyPoly->draw();
211     }
212 
213 #if defined(VERBOSE) && defined(DBG_UTIL)
214     ::cppcanvas::CanvasSharedPtr pCliplessCanvas( pCanvas->clone() );
215     pCliplessCanvas->setClip();
216 
217     if( pCanvas->getClip() )
218     {
219         ::cppcanvas::PolyPolygonSharedPtr pPolyPoly2(
220             ::cppcanvas::BaseGfxFactory::getInstance().createPolyPolygon( pCliplessCanvas,
221                                                                           *(pCanvas->getClip()) ));
222         if( pPolyPoly2 )
223         {
224             pPolyPoly2->setRGBALineColor( 0x008000FFU );
225             pPolyPoly2->draw();
226         }
227     }
228 #endif
229 }
230 
231 /** Get bounds in pixel
232 
233     @param rLayerBounds
234     Bound rect, in user space coordinates
235 
236     @param rTransformation
237     User space to device pixel transformation
238 
239     @return the layer bounds in pixel, extended by one pixel to the
240     right and bottom
241  */
242 basegfx::B2IRange getLayerBoundsPixel( basegfx::B2DRange const&     rLayerBounds,
243                                        basegfx::B2DHomMatrix const& rTransformation )
244 {
245     ::basegfx::B2DRange aTmpRect;
246     ::canvas::tools::calcTransformedRectBounds( aTmpRect,
247                                                 rLayerBounds,
248                                                 rTransformation );
249 
250     if( aTmpRect.isEmpty() )
251         return ::basegfx::B2IRange();
252 
253     // #i42440# Returned layer size is one pixel too small, as
254     // rendering happens one pixel to the right and below the
255     // actual bound rect.
256     return ::basegfx::B2IRange( ::basegfx::fround(aTmpRect.getMinX()),
257                                 ::basegfx::fround(aTmpRect.getMinY()),
258                                 ::basegfx::fround(aTmpRect.getMaxX()) + 1,
259                                 ::basegfx::fround(aTmpRect.getMaxY()) + 1 );
260 }
261 
262 
263 // ----------------------------------------------------------------
264 
265 /** Container class for sprites issued by a ViewLayer
266 
267     This class handles the sprite prioritization issues, that are
268     needed for layer sprites (e.g. the need to re-prioritize sprites
269     when the layer changes prio).
270  */
271 class LayerSpriteContainer
272 {
273     /** Max fill level of maSprites, before we try to prune it from
274         deceased sprites
275     */
276     enum{ SPRITE_ULLAGE=256 };
277 
278     /** All sprites that have been issued by this container (pruned
279         from time to time, for invalid references). This vector is
280         kept sorted with increasing sprite priority.
281     */
282     SpriteVector       maSprites;
283 
284     /// Priority of this layer, relative to other view layers
285     basegfx::B1DRange  maLayerPrioRange;
286 
287     double getSpritePriority( std::size_t nSpriteNum ) const
288     {
289         // divide the available layer range equally between all
290         // sprites, assign upper bound of individual sprite range as
291         // sprite prio (the layer itself gets assigned the lower bound
292         // of sprite 0's individual range):
293         //
294         // | layer 0                    | layer 1                    | ...
295         // |    sprite 0 |    sprite 1  |    sprite 0 |    sprite 1  | ...
296         return maLayerPrioRange.getMinimum() + maLayerPrioRange.getRange()*(nSpriteNum+1)/(maSprites.size()+1);
297     }
298 
299     /** Rescan sprite vector, and remove deceased sprites (and reset
300         sprite prio)
301 
302         @param aBegin
303         Iterator to the first entry to rescan
304      */
305     void updateSprites()
306     {
307         SpriteVector aValidSprites;
308 
309         // check all sprites for validity and set new priority
310         SpriteVector::iterator       aCurrSprite( maSprites.begin() );
311         const SpriteVector::iterator aEnd( maSprites.end() );
312         while( aCurrSprite != aEnd )
313         {
314             cppcanvas::CustomSpriteSharedPtr pCurrSprite( aCurrSprite->mpSprite.lock() );
315 
316             if( pCurrSprite )
317             {
318                 // only copy still valid sprites over to the refreshed
319                 // sprite vector.
320                 aValidSprites.push_back( *aCurrSprite );
321 
322                 pCurrSprite->setPriority(
323                     getSpritePriority( aValidSprites.size()-1 ));
324             }
325 
326             ++aCurrSprite;
327         }
328 
329         // replace sprite list with pruned one
330         maSprites.swap( aValidSprites );
331     }
332 
333 public:
334     LayerSpriteContainer() :
335         maSprites(),
336         maLayerPrioRange()
337     {
338     }
339 
340     basegfx::B1DRange getLayerPriority() const
341     {
342         return maLayerPrioRange;
343     }
344 
345     void setLayerPriority( const basegfx::B1DRange& rRange )
346     {
347         if( rRange != maLayerPrioRange )
348         {
349             maLayerPrioRange = rRange;
350 
351             // prune and recalc sprite prios
352             updateSprites();
353         }
354     }
355 
356     void addSprite( const cppcanvas::CustomSpriteSharedPtr& pSprite,
357                     double                                  nPriority )
358     {
359         if( !pSprite )
360             return;
361 
362         SpriteEntry aEntry( pSprite,nPriority );
363 
364         // insert new sprite, such that vector stays sorted
365         SpriteVector::iterator aInsertPos(
366             maSprites.insert(
367                 std::lower_bound( maSprites.begin(),
368                                   maSprites.end(),
369                                   aEntry ),
370                 aEntry ));
371 
372         const std::size_t nNumSprites( maSprites.size() );
373         if( nNumSprites > SPRITE_ULLAGE ||
374             maSprites.end() - aInsertPos > 1 )
375         {
376             // updateSprites() also updates all sprite prios
377             updateSprites();
378         }
379         else
380         {
381             // added sprite to the end, and not too many sprites in
382             // vector - perform optimized update (only need to set
383             // prio). This basically caters for the common case of
384             // iterated character animations, which generate lots of
385             // sprites, all added to the end.
386             pSprite->setPriority(
387                 getSpritePriority( nNumSprites-1 ));
388         }
389     }
390 
391     void clear()
392     {
393         maSprites.clear();
394     }
395 };
396 
397 
398 // ----------------------------------------------------------------
399 
400 
401 /** This class provides layers for a slide view
402 
403     Layers are used to render animations with the correct z order -
404     because sprites are always in front of the static canvas
405     background, shapes that must appear <em<before</em> an animation
406     must also be displayed as a sprite.
407 
408     Each layer has a priority assigned to it (valid range [0,1]), which
409     also affects all sprites created for this specific layer - i.e. if
410     the layer priority changes, the sprites change z order together
411     with their parent.
412  */
413 class SlideViewLayer : public ViewLayer,
414                        private boost::noncopyable
415 {
416     /// Smart container for all sprites issued by this layer
417     mutable LayerSpriteContainer             maSpriteContainer;
418 
419     /// Bounds of this layer in user space coordinates
420     basegfx::B2DRange                        maLayerBounds;
421 
422     /// Bounds of this layer in device pixel
423     mutable basegfx::B2IRange                maLayerBoundsPixel;
424 
425     /// Current clip polygon in user coordinates
426     basegfx::B2DPolyPolygon                  maClip;
427 
428     /// Current size of the view in user coordinates
429     basegfx::B2DSize                         maUserSize;
430 
431     /// Current overall view transformation
432     basegfx::B2DHomMatrix                    maTransformation;
433 
434     /// 'parent' canvas, this viewlayer is associated with
435     const cppcanvas::SpriteCanvasSharedPtr   mpSpriteCanvas;
436 
437     /** output surface (necessarily a sprite, won't otherwise be able
438         to display anything <em>before</em> other sprites)
439     */
440     mutable cppcanvas::CustomSpriteSharedPtr mpSprite;
441 
442     /// actual output canvas retrieved from a sprite
443     mutable cppcanvas::CanvasSharedPtr       mpOutputCanvas;
444 
445     /// ptr back to owning view. needed for isOnView() method
446     View const* const                        mpParentView;
447 
448 public:
449     /** Create a new layer
450 
451         @param pCanvas
452         Sprite canvas to create the layer on
453 
454         @param rTransform
455         Initial overall canvas transformation
456 
457         @param rLayerBounds
458         Initial layer bounds, in view coordinate system
459      */
460     SlideViewLayer( const cppcanvas::SpriteCanvasSharedPtr& pCanvas,
461                     const basegfx::B2DHomMatrix&            rTransform,
462                     const basegfx::B2DRange&                rLayerBounds,
463                     const basegfx::B2DSize&                 rUserSize,
464                     View const* const                       pParentView) :
465         maSpriteContainer(),
466         maLayerBounds(rLayerBounds),
467         maLayerBoundsPixel(),
468         maClip(),
469         maUserSize(rUserSize),
470         maTransformation(rTransform),
471         mpSpriteCanvas(pCanvas),
472         mpSprite(),
473         mpOutputCanvas(),
474         mpParentView(pParentView)
475     {
476     }
477 
478     void updateView( const basegfx::B2DHomMatrix& rMatrix,
479                      const basegfx::B2DSize&      rUserSize )
480     {
481         maTransformation = rMatrix;
482         maUserSize = rUserSize;
483 
484         // limit layer bounds to visible screen
485         maLayerBounds.intersect( basegfx::B2DRange(0.0,
486                                                    0.0,
487                                                    maUserSize.getX(),
488                                                    maUserSize.getY()) );
489 
490         basegfx::B2IRange const& rNewLayerPixel(
491             getLayerBoundsPixel(maLayerBounds,
492                                 maTransformation) );
493         if( rNewLayerPixel != maLayerBoundsPixel )
494         {
495             // re-gen sprite with new size
496             mpOutputCanvas.reset();
497             mpSprite.reset();
498         }
499     }
500 
501 private:
502     // ViewLayer interface
503     // ----------------------------------------------
504 
505     virtual cppcanvas::CustomSpriteSharedPtr createSprite(
506         const ::basegfx::B2DSize& rSpriteSizePixel,
507         double                    nPriority ) const
508     {
509         cppcanvas::CustomSpriteSharedPtr pSprite(
510             mpSpriteCanvas->createCustomSprite( rSpriteSizePixel ) );
511 
512         maSpriteContainer.addSprite( pSprite,
513                                      nPriority );
514 
515         return pSprite;
516     }
517 
518     virtual void setPriority( const basegfx::B1DRange& rRange )
519     {
520         OSL_ENSURE( !rRange.isEmpty() &&
521                     rRange.getMinimum() >= 1.0,
522                     "SlideViewLayer::setPriority(): prio MUST be larger than 1.0 (because "
523                     "the background layer already lies there)" );
524 
525         maSpriteContainer.setLayerPriority( rRange );
526 
527         if( mpSprite )
528             mpSprite->setPriority( rRange.getMinimum() );
529     }
530 
531     virtual basegfx::B2DHomMatrix getTransformation() const
532     {
533         // Offset given transformation by left, top border of given
534         // range (after transformation through given transformation)
535         basegfx::B2DRectangle aTmpRect;
536         canvas::tools::calcTransformedRectBounds( aTmpRect,
537                                                   maLayerBounds,
538                                                   maTransformation );
539 
540         basegfx::B2DHomMatrix aMatrix( maTransformation );
541 
542         // Add translation according to the origin of aTmpRect.  Ignore the
543         // translation when aTmpRect was not properly initialized.
544         if ( ! aTmpRect.isEmpty())
545         {
546             aMatrix.translate( -basegfx::fround(aTmpRect.getMinX()),
547                                -basegfx::fround(aTmpRect.getMinY()) );
548         }
549 
550         return aMatrix;
551     }
552 
553     virtual basegfx::B2DHomMatrix getSpriteTransformation() const
554     {
555         return maTransformation;
556     }
557 
558     virtual void clear() const
559     {
560         // keep layer clip
561         clearRect(getCanvas()->clone(),
562                   maLayerBoundsPixel);
563     }
564 
565     virtual void clearAll() const
566     {
567         ::cppcanvas::CanvasSharedPtr pCanvas( getCanvas()->clone() );
568 
569         // clear layer clip, to clear whole area
570         pCanvas->setClip();
571 
572         clearRect(pCanvas,
573                   maLayerBoundsPixel);
574     }
575 
576     virtual bool isOnView(boost::shared_ptr<View> const& rView) const
577     {
578         return rView.get() == mpParentView;
579     }
580 
581     virtual cppcanvas::CanvasSharedPtr getCanvas() const
582     {
583         if( !mpOutputCanvas )
584         {
585             if( !mpSprite )
586             {
587                 maLayerBoundsPixel = getLayerBoundsPixel(maLayerBounds,
588                                                          maTransformation);
589 
590                 // HACK: ensure at least 1x1 pixel size. clients might
591                 // need an actual canvas (e.g. for bound rect
592                 // calculations) without rendering anything. Better
593                 // solution: introduce something like a reference
594                 // canvas for ViewLayers, which is always available.
595                 if( maLayerBoundsPixel.isEmpty() )
596                     maLayerBoundsPixel = basegfx::B2IRange(0,0,1,1);
597 
598                 const basegfx::B2I64Tuple& rSpriteSize(maLayerBoundsPixel.getRange());
599                 mpSprite = mpSpriteCanvas->createCustomSprite(
600                     basegfx::B2DVector(sal::static_int_cast<sal_Int32>(rSpriteSize.getX()),
601                                        sal::static_int_cast<sal_Int32>(rSpriteSize.getY())) );
602 
603                 mpSprite->setPriority(
604                     maSpriteContainer.getLayerPriority().getMinimum() );
605 
606 #if defined(VERBOSE) && defined(DBG_UTIL)
607                 mpSprite->movePixel(
608                     basegfx::B2DPoint(maLayerBoundsPixel.getMinimum()) +
609                     basegfx::B2DPoint(10,10) );
610 
611                 mpSprite->setAlpha(0.5);
612 #else
613                 mpSprite->movePixel(
614                     basegfx::B2DPoint(maLayerBoundsPixel.getMinimum()) );
615 
616                 mpSprite->setAlpha(1.0);
617 #endif
618                 mpSprite->show();
619             }
620 
621             ENSURE_OR_THROW( mpSprite,
622                               "SlideViewLayer::getCanvas(): no layer sprite" );
623 
624             mpOutputCanvas = mpSprite->getContentCanvas();
625 
626             ENSURE_OR_THROW( mpOutputCanvas,
627                               "SlideViewLayer::getCanvas(): sprite doesn't yield a canvas" );
628 
629             // new canvas retrieved - setup transformation and clip
630             mpOutputCanvas->setTransformation( getTransformation() );
631             mpOutputCanvas->setClip(
632                 createClipPolygon( maClip,
633                                    mpOutputCanvas,
634                                    maUserSize ));
635         }
636 
637         return mpOutputCanvas;
638     }
639 
640     virtual void setClip( const basegfx::B2DPolyPolygon& rClip )
641     {
642         basegfx::B2DPolyPolygon aNewClip = prepareClip( rClip );
643 
644         if( aNewClip != maClip )
645         {
646             maClip = aNewClip;
647 
648             if(mpOutputCanvas )
649                 mpOutputCanvas->setClip(
650                     createClipPolygon( maClip,
651                                        mpOutputCanvas,
652                                        maUserSize ));
653         }
654     }
655 
656     virtual bool resize( const ::basegfx::B2DRange& rArea )
657     {
658         const bool bRet( maLayerBounds != rArea );
659         maLayerBounds = rArea;
660         updateView( maTransformation,
661                     maUserSize );
662 
663         return bRet;
664     }
665 };
666 
667 
668 // ---------------------------------------------------------
669 
670 typedef cppu::WeakComponentImplHelper2<
671     ::com::sun::star::util::XModifyListener,
672       ::com::sun::star::awt::XPaintListener> SlideViewBase;
673 
674 /** SlideView class
675 
676     This class implements the View interface, encapsulating
677     <em>one</em> view a slideshow is displayed on.
678  */
679 class SlideView : private cppu::BaseMutex,
680                   public SlideViewBase,
681                   public UnoView
682 {
683 public:
684     SlideView( const uno::Reference<presentation::XSlideShowView>& xView,
685                EventQueue&                                         rEventQueue,
686                EventMultiplexer&                                   rEventMultiplexer );
687     void updateCanvas();
688 
689 private:
690     // View:
691     virtual ViewLayerSharedPtr createViewLayer( const basegfx::B2DRange& rLayerBounds ) const;
692     virtual bool updateScreen() const;
693     virtual bool paintScreen() const;
694     virtual void setViewSize( const ::basegfx::B2DSize& );
695     virtual void setCursorShape( sal_Int16 nPointerShape );
696 
697     // ViewLayer interface
698     virtual bool isOnView(boost::shared_ptr<View> const& rView) const;
699     virtual void clear() const;
700     virtual void clearAll() const;
701     virtual cppcanvas::CanvasSharedPtr getCanvas() const;
702     virtual cppcanvas::CustomSpriteSharedPtr createSprite( const ::basegfx::B2DSize& rSpriteSizePixel,
703                                                            double                    nPriority ) const;
704     virtual void setPriority( const basegfx::B1DRange& rRange );
705     virtual ::basegfx::B2DHomMatrix getTransformation() const;
706     virtual basegfx::B2DHomMatrix getSpriteTransformation() const;
707     virtual void setClip( const ::basegfx::B2DPolyPolygon& rClip );
708     virtual bool resize( const ::basegfx::B2DRange& rArea );
709 
710     // UnoView:
711     virtual void _dispose();
712     virtual uno::Reference<presentation::XSlideShowView> getUnoView()const;
713     virtual void setIsSoundEnabled (const bool bValue);
714     virtual bool isSoundEnabled (void) const;
715 
716     // XEventListener:
717     virtual void SAL_CALL disposing( lang::EventObject const& evt )
718         throw (uno::RuntimeException);
719     // XModifyListener:
720     virtual void SAL_CALL modified( const lang::EventObject& aEvent )
721         throw (uno::RuntimeException);
722     // XPaintListener:
723     virtual void SAL_CALL windowPaint( const awt::PaintEvent& e )
724         throw (uno::RuntimeException);
725 
726     // WeakComponentImplHelperBase:
727     virtual void SAL_CALL disposing();
728 
729     void updateClip();
730 
731 private:
732     typedef std::vector< boost::weak_ptr<SlideViewLayer> > ViewLayerVector;
733 
734     /// Prune viewlayers from deceased ones, optionally update them
735     void pruneLayers( bool bWithViewLayerUpdate=false ) const;
736 
737     /** Max fill level of maViewLayers, before we try to prune it from
738         deceased layers
739     */
740     enum{ LAYER_ULLAGE=8 };
741 
742     uno::Reference<presentation::XSlideShowView>              mxView;
743     cppcanvas::SpriteCanvasSharedPtr                          mpCanvas;
744 
745     EventMultiplexer&                                         mrEventMultiplexer;
746     EventQueue&                                               mrEventQueue;
747 
748     mutable LayerSpriteContainer                              maSprites;
749     mutable ViewLayerVector                                   maViewLayers;
750 
751     basegfx::B2DPolyPolygon                                   maClip;
752 
753     basegfx::B2DHomMatrix                                     maViewTransform;
754     basegfx::B2DSize                                          maUserSize;
755     bool mbIsSoundEnabled;
756 };
757 
758 
759 SlideView::SlideView( const uno::Reference<presentation::XSlideShowView>& xView,
760                       EventQueue&                                         rEventQueue,
761                       EventMultiplexer&                                   rEventMultiplexer ) :
762     SlideViewBase( m_aMutex ),
763     mxView( xView ),
764     mpCanvas(),
765     mrEventMultiplexer( rEventMultiplexer ),
766     mrEventQueue( rEventQueue ),
767     maSprites(),
768     maViewLayers(),
769     maClip(),
770     maViewTransform(),
771     maUserSize( 1.0, 1.0 ), // default size: one-by-one rectangle
772     mbIsSoundEnabled(true)
773 {
774     // take care not constructing any UNO references to this _inside_
775     // ctor, shift that code to createSlideView()!
776     ENSURE_OR_THROW( mxView.is(),
777                       "SlideView::SlideView(): Invalid view" );
778 
779     mpCanvas = cppcanvas::VCLFactory::getInstance().createSpriteCanvas(
780         xView->getCanvas() );
781     ENSURE_OR_THROW( mpCanvas,
782                       "Could not create cppcanvas" );
783 
784     geometry::AffineMatrix2D aViewTransform(
785         xView->getTransformation() );
786 
787     if( basegfx::fTools::equalZero(
788             basegfx::B2DVector(aViewTransform.m00,
789                                aViewTransform.m10).getLength()) ||
790         basegfx::fTools::equalZero(
791             basegfx::B2DVector(aViewTransform.m01,
792                                aViewTransform.m11).getLength()) )
793     {
794         OSL_ENSURE( false,
795                     "SlideView::SlideView(): Singular matrix!" );
796 
797         canvas::tools::setIdentityAffineMatrix2D(aViewTransform);
798     }
799 
800     basegfx::unotools::homMatrixFromAffineMatrix(
801         maViewTransform, aViewTransform );
802 
803     // once and forever: set fixed prio to this 'layer' (we're always
804     // the background layer)
805     maSprites.setLayerPriority( basegfx::B1DRange(0.0,1.0) );
806 }
807 
808 void SlideView::disposing()
809 {
810     osl::MutexGuard aGuard( m_aMutex );
811 
812     maViewLayers.clear();
813     maSprites.clear();
814     mpCanvas.reset();
815 
816     // additionally, also de-register from XSlideShowView
817     if (mxView.is())
818     {
819         mxView->removeTransformationChangedListener( this );
820         mxView->removePaintListener( this );
821         mxView.clear();
822     }
823 }
824 
825 ViewLayerSharedPtr SlideView::createViewLayer( const basegfx::B2DRange& rLayerBounds ) const
826 {
827     osl::MutexGuard aGuard( m_aMutex );
828 
829     ENSURE_OR_THROW( mpCanvas,
830                       "SlideView::createViewLayer(): Disposed" );
831 
832     const std::size_t nNumLayers( maViewLayers.size() );
833 
834     // avoid filling up layer vector with lots of deceased layer weak
835     // ptrs
836     if( nNumLayers > LAYER_ULLAGE )
837         pruneLayers();
838 
839     boost::shared_ptr<SlideViewLayer> pViewLayer( new SlideViewLayer(mpCanvas,
840                                                                      getTransformation(),
841                                                                      rLayerBounds,
842                                                                      maUserSize,
843                                                                      this) );
844     maViewLayers.push_back( pViewLayer );
845 
846     return pViewLayer;
847 }
848 
849 bool SlideView::updateScreen() const
850 {
851     osl::MutexGuard aGuard( m_aMutex );
852 
853     ENSURE_OR_RETURN_FALSE( mpCanvas.get(),
854                        "SlideView::updateScreen(): Disposed" );
855 
856     return mpCanvas->updateScreen( false );
857 }
858 
859 bool SlideView::paintScreen() const
860 {
861     osl::MutexGuard aGuard( m_aMutex );
862 
863     ENSURE_OR_RETURN_FALSE( mpCanvas.get(),
864                        "SlideView::paintScreen(): Disposed" );
865 
866     return mpCanvas->updateScreen( true );
867 }
868 
869 void SlideView::clear() const
870 {
871     osl::MutexGuard aGuard( m_aMutex );
872 
873     OSL_ENSURE( mxView.is() && mpCanvas,
874                 "SlideView::clear(): Disposed" );
875     if( !mxView.is() || !mpCanvas )
876         return;
877 
878     // keep layer clip
879     clearRect(getCanvas()->clone(),
880               getLayerBoundsPixel(
881                   basegfx::B2DRange(0,0,
882                                     maUserSize.getX(),
883                                     maUserSize.getY()),
884                   getTransformation()));
885 }
886 
887 void SlideView::clearAll() const
888 {
889     osl::MutexGuard aGuard( m_aMutex );
890 
891     OSL_ENSURE( mxView.is() && mpCanvas,
892                 "SlideView::clear(): Disposed" );
893     if( !mxView.is() || !mpCanvas )
894         return;
895 
896     // clear whole view
897     mxView->clear();
898 }
899 
900 void SlideView::setViewSize( const basegfx::B2DSize& rSize )
901 {
902     osl::MutexGuard aGuard( m_aMutex );
903 
904     maUserSize = rSize;
905     updateCanvas();
906 }
907 
908 void SlideView::setCursorShape( sal_Int16 nPointerShape )
909 {
910     osl::MutexGuard const guard( m_aMutex );
911 
912     if (mxView.is())
913         mxView->setMouseCursor( nPointerShape );
914 }
915 
916 bool SlideView::isOnView(boost::shared_ptr<View> const& rView) const
917 {
918     return rView.get() == this;
919 }
920 
921 cppcanvas::CanvasSharedPtr SlideView::getCanvas() const
922 {
923     osl::MutexGuard aGuard( m_aMutex );
924 
925     ENSURE_OR_THROW( mpCanvas,
926                       "SlideView::getCanvas(): Disposed" );
927 
928     return mpCanvas;
929 }
930 
931 cppcanvas::CustomSpriteSharedPtr SlideView::createSprite(
932     const basegfx::B2DSize& rSpriteSizePixel,
933     double                  nPriority ) const
934 {
935     osl::MutexGuard aGuard( m_aMutex );
936 
937     ENSURE_OR_THROW( mpCanvas, "SlideView::createSprite(): Disposed" );
938 
939     cppcanvas::CustomSpriteSharedPtr pSprite(
940         mpCanvas->createCustomSprite( rSpriteSizePixel ) );
941 
942     maSprites.addSprite( pSprite,
943                          nPriority );
944 
945     return pSprite;
946 }
947 
948 void SlideView::setPriority( const basegfx::B1DRange& /*rRange*/ )
949 {
950     osl::MutexGuard aGuard( m_aMutex );
951 
952     OSL_ENSURE( false,
953                 "SlideView::setPriority() is a NOOP for slide view - "
954                 "content will always be shown in the background" );
955 }
956 
957 basegfx::B2DHomMatrix SlideView::getTransformation() const
958 {
959     osl::MutexGuard aGuard( m_aMutex );
960 
961     basegfx::B2DHomMatrix aMatrix;
962     aMatrix.scale( 1.0/maUserSize.getX(), 1.0/maUserSize.getY() );
963 
964     return maViewTransform * aMatrix;
965 }
966 
967 basegfx::B2DHomMatrix SlideView::getSpriteTransformation() const
968 {
969     return getTransformation();
970 }
971 
972 void SlideView::setClip( const basegfx::B2DPolyPolygon& rClip )
973 {
974     osl::MutexGuard aGuard( m_aMutex );
975 
976     basegfx::B2DPolyPolygon aNewClip = prepareClip( rClip );
977 
978     if( aNewClip != maClip )
979     {
980         maClip = aNewClip;
981 
982         updateClip();
983     }
984 }
985 
986 bool SlideView::resize( const ::basegfx::B2DRange& /*rArea*/ )
987 {
988     osl::MutexGuard aGuard( m_aMutex );
989 
990     OSL_ENSURE( false,
991                 "SlideView::resize(): ignored for the View, can't change size "
992                 "effectively, anyway" );
993 
994     return false;
995 }
996 
997 uno::Reference<presentation::XSlideShowView> SlideView::getUnoView() const
998 {
999     osl::MutexGuard aGuard( m_aMutex );
1000     return mxView;
1001 }
1002 
1003 void SlideView::setIsSoundEnabled (const bool bValue)
1004 {
1005     mbIsSoundEnabled = bValue;
1006 }
1007 
1008 bool SlideView::isSoundEnabled (void) const
1009 {
1010     return mbIsSoundEnabled;
1011 }
1012 
1013 void SlideView::_dispose()
1014 {
1015     dispose();
1016 }
1017 
1018 // XEventListener
1019 void SlideView::disposing( lang::EventObject const& evt )
1020     throw (uno::RuntimeException)
1021 {
1022     (void)evt;
1023 
1024     // no deregistration necessary anymore, XView has left:
1025     osl::MutexGuard const guard( m_aMutex );
1026 
1027     if (mxView.is())
1028     {
1029         OSL_ASSERT( evt.Source == mxView );
1030         mxView.clear();
1031     }
1032 
1033     dispose();
1034 }
1035 
1036 // XModifyListener
1037 void SlideView::modified( const lang::EventObject& /*aEvent*/ )
1038     throw (uno::RuntimeException)
1039 {
1040     osl::MutexGuard const guard( m_aMutex );
1041 
1042     OSL_ENSURE( mxView.is(), "SlideView::modified(): "
1043                 "Disposed, but event received from XSlideShowView?!");
1044 
1045     if( !mxView.is() )
1046         return;
1047 
1048     geometry::AffineMatrix2D aViewTransform(
1049         mxView->getTransformation() );
1050 
1051     if( basegfx::fTools::equalZero(
1052             basegfx::B2DVector(aViewTransform.m00,
1053                                aViewTransform.m10).getLength()) ||
1054         basegfx::fTools::equalZero(
1055             basegfx::B2DVector(aViewTransform.m01,
1056                                aViewTransform.m11).getLength()) )
1057     {
1058         OSL_ENSURE( false,
1059                     "SlideView::modified(): Singular matrix!" );
1060 
1061         canvas::tools::setIdentityAffineMatrix2D(aViewTransform);
1062     }
1063 
1064     // view transformation really changed?
1065     basegfx::B2DHomMatrix aNewTransform;
1066     basegfx::unotools::homMatrixFromAffineMatrix(
1067         aNewTransform,
1068         aViewTransform );
1069 
1070     if( aNewTransform == maViewTransform )
1071         return; // No change, nothing to do
1072 
1073     maViewTransform = aNewTransform;
1074 
1075     updateCanvas();
1076 
1077     // notify view change. Don't call EventMultiplexer directly, this
1078     // might not be the main thread!
1079     mrEventQueue.addEvent(
1080         makeEvent( boost::bind( (bool (EventMultiplexer::*)(
1081                                      const uno::Reference<presentation::XSlideShowView>&))
1082                                 &EventMultiplexer::notifyViewChanged,
1083                                 boost::ref(mrEventMultiplexer), mxView ),
1084                    "EventMultiplexer::notifyViewChanged"));
1085 }
1086 
1087 // XPaintListener
1088 void SlideView::windowPaint( const awt::PaintEvent& /*e*/ )
1089     throw (uno::RuntimeException)
1090 {
1091     osl::MutexGuard aGuard( m_aMutex );
1092 
1093     OSL_ENSURE( mxView.is() && mpCanvas, "Disposed, but event received?!" );
1094 
1095     // notify view clobbering. Don't call EventMultiplexer directly,
1096     // this might not be the main thread!
1097     mrEventQueue.addEvent(
1098         makeEvent( boost::bind( &EventMultiplexer::notifyViewClobbered,
1099                                 boost::ref(mrEventMultiplexer), mxView ),
1100                    "EventMultiplexer::notifyViewClobbered") );
1101 }
1102 
1103 void SlideView::updateCanvas()
1104 {
1105     OSL_ENSURE( mpCanvas,
1106                 "SlideView::updateCanvasTransform(): Disposed" );
1107 
1108     if( !mpCanvas || !mxView.is())
1109         return;
1110 
1111     mpCanvas->clear(); // this is unnecessary, strictly speaking. but
1112                        // it makes the SlideView behave exactly like a
1113                        // sprite-based SlideViewLayer, because those
1114                        // are created from scratch after a resize
1115     clearAll();
1116     mpCanvas->setTransformation( getTransformation() );
1117     mpCanvas->setClip(
1118         createClipPolygon( maClip,
1119                            mpCanvas,
1120                            maUserSize ));
1121 
1122     // forward update to viewlayers
1123     pruneLayers( true );
1124 }
1125 
1126 void SlideView::updateClip()
1127 {
1128     OSL_ENSURE( mpCanvas,
1129                 "SlideView::updateClip(): Disposed" );
1130 
1131     if( !mpCanvas )
1132         return;
1133 
1134     mpCanvas->setClip(
1135         createClipPolygon( maClip,
1136                            mpCanvas,
1137                            maUserSize ));
1138 
1139     pruneLayers( false );
1140 }
1141 
1142 void SlideView::pruneLayers( bool bWithViewLayerUpdate ) const
1143 {
1144     ViewLayerVector aValidLayers;
1145 
1146     const basegfx::B2DHomMatrix& rCurrTransform(
1147         getTransformation() );
1148 
1149     // check all layers for validity, and retain only the live ones
1150     ViewLayerVector::const_iterator       aCurr( maViewLayers.begin() );
1151     const ViewLayerVector::const_iterator aEnd( maViewLayers.end() );
1152     while( aCurr != aEnd )
1153     {
1154         boost::shared_ptr< SlideViewLayer > pCurrLayer( aCurr->lock() );
1155 
1156         if( pCurrLayer )
1157         {
1158             aValidLayers.push_back( pCurrLayer );
1159 
1160             if( bWithViewLayerUpdate )
1161                 pCurrLayer->updateView( rCurrTransform,
1162                                         maUserSize );
1163         }
1164 
1165         ++aCurr;
1166     }
1167 
1168     // replace layer list with pruned one
1169     maViewLayers.swap( aValidLayers );
1170 }
1171 
1172 } // anonymous namespace
1173 
1174 UnoViewSharedPtr createSlideView( uno::Reference< presentation::XSlideShowView> const& xView,
1175                                   EventQueue&                                          rEventQueue,
1176                                   EventMultiplexer&                                    rEventMultiplexer )
1177 {
1178     boost::shared_ptr<SlideView> const that(
1179         comphelper::make_shared_from_UNO(
1180             new SlideView(xView,
1181                           rEventQueue,
1182                           rEventMultiplexer)));
1183 
1184     // register listeners with XSlideShowView
1185     xView->addTransformationChangedListener( that.get() );
1186     xView->addPaintListener( that.get() );
1187 
1188     // set new transformation
1189     that->updateCanvas();
1190 
1191     return that;
1192 }
1193 
1194 } // namespace internal
1195 } // namespace slideshow
1196 
1197