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 {
operator ()slideshow::internal::__anonc2de6d920111::StaticUnitRectPoly80 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 {
SpriteEntryslideshow::internal::__anonc2de6d920111::SpriteEntry93 SpriteEntry( const cppcanvas::CustomSpriteSharedPtr& rSprite,
94 double nPrio ) :
95 mpSprite( rSprite ),
96 mnPriority( nPrio )
97 {
98 }
99
operator <slideshow::internal::__anonc2de6d920111::SpriteEntry100 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 */
createClipPolygon(const basegfx::B2DPolyPolygon & rClip,const cppcanvas::CanvasSharedPtr &,const basegfx::B2DSize & rUserSize)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 */
prepareClip(const basegfx::B2DPolyPolygon & rClip)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
clearRect(::cppcanvas::CanvasSharedPtr const & pCanvas,basegfx::B2IRange const & rArea)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 */
getLayerBoundsPixel(basegfx::B2DRange const & rLayerBounds,basegfx::B2DHomMatrix const & rTransformation)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
getSpritePriority(std::size_t nSpriteNum) const283 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 */
updateSprites()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:
LayerSpriteContainer()330 LayerSpriteContainer() :
331 maSprites(),
332 maLayerPrioRange()
333 {
334 }
335
getLayerPriority() const336 basegfx::B1DRange getLayerPriority() const
337 {
338 return maLayerPrioRange;
339 }
340
setLayerPriority(const basegfx::B1DRange & rRange)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
addSprite(const cppcanvas::CustomSpriteSharedPtr & pSprite,double nPriority)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
clear()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 */
SlideViewLayer(const cppcanvas::SpriteCanvasSharedPtr & pCanvas,const basegfx::B2DHomMatrix & rTransform,const basegfx::B2DRange & rLayerBounds,const basegfx::B2DSize & rUserSize,View const * const pParentView)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
updateView(const basegfx::B2DHomMatrix & rMatrix,const basegfx::B2DSize & rUserSize)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
createSprite(const::basegfx::B2DSize & rSpriteSizePixel,double nPriority) const501 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
setPriority(const basegfx::B1DRange & rRange)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
getTransformation() const527 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
getSpriteTransformation() const549 virtual basegfx::B2DHomMatrix getSpriteTransformation() const
550 {
551 return maTransformation;
552 }
553
clear() const554 virtual void clear() const
555 {
556 // keep layer clip
557 clearRect(getCanvas()->clone(),
558 maLayerBoundsPixel);
559 }
560
clearAll() const561 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
isOnView(boost::shared_ptr<View> const & rView) const572 virtual bool isOnView(boost::shared_ptr<View> const& rView) const
573 {
574 return rView.get() == mpParentView;
575 }
576
getCanvas() const577 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
setClip(const basegfx::B2DPolyPolygon & rClip)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
resize(const::basegfx::B2DRange & rArea)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
SlideView(const uno::Reference<presentation::XSlideShowView> & xView,EventQueue & rEventQueue,EventMultiplexer & rEventMultiplexer)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
disposing()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
createViewLayer(const basegfx::B2DRange & rLayerBounds) const821 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
updateScreen() const845 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
paintScreen() const855 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
clear() const865 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
clearAll() const883 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
setViewSize(const basegfx::B2DSize & rSize)896 void SlideView::setViewSize( const basegfx::B2DSize& rSize )
897 {
898 osl::MutexGuard aGuard( m_aMutex );
899
900 maUserSize = rSize;
901 updateCanvas();
902 }
903
setCursorShape(sal_Int16 nPointerShape)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
isOnView(boost::shared_ptr<View> const & rView) const912 bool SlideView::isOnView(boost::shared_ptr<View> const& rView) const
913 {
914 return rView.get() == this;
915 }
916
getCanvas() const917 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
createSprite(const basegfx::B2DSize & rSpriteSizePixel,double nPriority) const927 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
setPriority(const basegfx::B1DRange &)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
getTransformation() const953 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
getSpriteTransformation() const963 basegfx::B2DHomMatrix SlideView::getSpriteTransformation() const
964 {
965 return getTransformation();
966 }
967
setClip(const basegfx::B2DPolyPolygon & rClip)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
resize(const::basegfx::B2DRange &)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
getUnoView() const993 uno::Reference<presentation::XSlideShowView> SlideView::getUnoView() const
994 {
995 osl::MutexGuard aGuard( m_aMutex );
996 return mxView;
997 }
998
setIsSoundEnabled(const bool bValue)999 void SlideView::setIsSoundEnabled (const bool bValue)
1000 {
1001 mbIsSoundEnabled = bValue;
1002 }
1003
isSoundEnabled(void) const1004 bool SlideView::isSoundEnabled (void) const
1005 {
1006 return mbIsSoundEnabled;
1007 }
1008
_dispose()1009 void SlideView::_dispose()
1010 {
1011 dispose();
1012 }
1013
1014 // XEventListener
disposing(lang::EventObject const & evt)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
modified(const lang::EventObject &)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
windowPaint(const awt::PaintEvent &)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
updateCanvas()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
updateClip()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
pruneLayers(bool bWithViewLayerUpdate) const1138 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
createSlideView(uno::Reference<presentation::XSlideShowView> const & xView,EventQueue & rEventQueue,EventMultiplexer & rEventMultiplexer)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