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