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 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_slideshow.hxx"
30 
31 #include <canvas/debug.hxx>
32 #include <tools/diagnose_ex.h>
33 #include <canvas/elapsedtime.hxx>
34 #include <basegfx/polygon/b2dpolygontools.hxx>
35 
36 #include <comphelper/anytostring.hxx>
37 #include <cppuhelper/exc_hlp.hxx>
38 
39 #include <rtl/math.hxx>
40 #include <vcl/metric.hxx>
41 #include <vcl/salbtype.hxx>
42 #include <vcl/canvastools.hxx>
43 #include <vcl/metaact.hxx>
44 #include <com/sun/star/beans/XPropertySet.hpp>
45 #include <com/sun/star/drawing/TextAnimationKind.hpp>
46 #include <com/sun/star/drawing/TextAnimationDirection.hpp>
47 #include <com/sun/star/drawing/TextHorizontalAdjust.hpp>
48 #include <com/sun/star/drawing/TextVerticalAdjust.hpp>
49 #include <com/sun/star/drawing/HomogenMatrix3.hpp>
50 #include <com/sun/star/awt/Rectangle.hpp>
51 
52 #include "activity.hxx"
53 #include "wakeupevent.hxx"
54 #include "eventqueue.hxx"
55 #include "drawshapesubsetting.hxx"
56 #include "drawshape.hxx"
57 #include "shapesubset.hxx"
58 #include "shapeattributelayerholder.hxx"
59 #include "slideshowcontext.hxx"
60 #include "tools.hxx"
61 #include "gdimtftools.hxx"
62 #include "eventmultiplexer.hxx"
63 #include "intrinsicanimationactivity.hxx"
64 #include "intrinsicanimationeventhandler.hxx"
65 
66 #include <boost/weak_ptr.hpp>
67 #include <boost/enable_shared_from_this.hpp>
68 #include <boost/noncopyable.hpp>
69 #include <vector>
70 
71 using namespace com::sun::star;
72 using namespace ::slideshow::internal;
73 
74 namespace {
75 
76 class ScrollTextAnimNode
77 {
78     sal_uInt32  mnDuration; // single duration
79     sal_uInt32  mnRepeat; // 0 -> endless
80     double      mfStart;
81     double      mfStop;
82     sal_uInt32  mnFrequency; // in ms
83     // forth and back change at mnRepeat%2:
84     bool        mbAlternate;
85 
86 public:
87     ScrollTextAnimNode(
88         sal_uInt32 nDuration, sal_uInt32 nRepeat, double fStart, double fStop,
89         sal_uInt32 nFrequency, bool bAlternate)
90         :   mnDuration(nDuration),
91             mnRepeat(nRepeat),
92             mfStart(fStart),
93             mfStop(fStop),
94             mnFrequency(nFrequency),
95             mbAlternate(bAlternate)
96         {}
97 
98     sal_uInt32 GetDuration() const { return mnDuration; }
99     sal_uInt32 GetRepeat() const { return mnRepeat; }
100     sal_uInt32 GetFullTime() const { return mnDuration * mnRepeat; }
101     double GetStart() const { return mfStart; }
102     double GetStop() const { return mfStop; }
103     sal_uInt32 GetFrequency() const { return mnFrequency; }
104     bool DoAlternate() const { return mbAlternate; }
105 
106     double GetStateAtRelativeTime(sal_uInt32 nRelativeTime) const;
107 };
108 
109 double ScrollTextAnimNode::GetStateAtRelativeTime(
110     sal_uInt32 nRelativeTime) const
111 {
112     // #151174# Avoid division by zero.
113     if( mnDuration == 0 )
114         return mfStop;
115 
116     if(mnRepeat)
117     {
118         // ending
119         const sal_uInt32 nRepeatCount(nRelativeTime / mnDuration);
120         sal_uInt32 nFrameTime(nRelativeTime - (nRepeatCount * mnDuration));
121 
122         if(DoAlternate() && (nRepeatCount + 1L) % 2L)
123             nFrameTime = mnDuration - nFrameTime;
124 
125         return mfStart + ((mfStop - mfStart) *
126                           (double(nFrameTime) / mnDuration));
127     }
128     else
129     {
130         // endless
131         sal_uInt32 nFrameTime(nRelativeTime % mnDuration);
132 
133         if(DoAlternate())
134         {
135             const sal_uInt32 nRepeatCount(nRelativeTime / mnDuration);
136 
137             if((nRepeatCount + 1L) % 2L)
138                 nFrameTime = mnDuration - nFrameTime;
139         }
140 
141         return mfStart + ((mfStop - mfStart) * (double(nFrameTime) / mnDuration));
142     }
143 }
144 
145 class ActivityImpl : public Activity,
146                      public boost::enable_shared_from_this<ActivityImpl>,
147                      private boost::noncopyable
148 {
149 public:
150     virtual ~ActivityImpl();
151 
152     ActivityImpl(
153         SlideShowContext const& rContext,
154         boost::shared_ptr<WakeupEvent> const& pWakeupEvent,
155         boost::shared_ptr<DrawShape> const& pDrawShape );
156 
157     bool enableAnimations();
158 
159     // Disposable:
160     virtual void dispose();
161     // Activity:
162     virtual double calcTimeLag() const;
163     virtual bool perform();
164     virtual bool isActive() const;
165     virtual void dequeued();
166     virtual void end();
167 
168 private:
169     void updateShapeAttributes( double fTime,
170                                 basegfx::B2DRectangle const& parentBounds );
171 
172     // Access to VisibleWhenSTarted flags
173     sal_Bool IsVisibleWhenStarted() const { return mbVisibleWhenStarted; }
174     sal_Bool IsVisibleWhenStopped() const { return mbVisibleWhenStopped; }
175 
176     // scroll horizontal? if sal_False, scroll is vertical.
177     bool ScrollHorizontal() const {
178         return (drawing::TextAnimationDirection_LEFT == meDirection ||
179                 drawing::TextAnimationDirection_RIGHT == meDirection);
180     }
181 
182     // Access to StepWidth in logical units
183     sal_uInt32 GetStepWidthLogic() const;
184 
185     // is the animation direction opposite?
186     bool DoScrollForward() const {
187         return (drawing::TextAnimationDirection_RIGHT == meDirection ||
188                 drawing::TextAnimationDirection_DOWN == meDirection);
189     }
190 
191     // do alternate text directions?
192     bool DoAlternate() const { return mbAlternate; }
193 
194     // do scroll in?
195     bool DoScrollIn() const { return mbScrollIn; }
196 
197     // Scroll helper methods
198     void ImpForceScrollTextAnimNodes();
199     ScrollTextAnimNode* ImpGetScrollTextAnimNode(
200         sal_uInt32 nTime, sal_uInt32& rRelativeTime );
201     sal_uInt32 ImpRegisterAgainScrollTextMixerState(
202         sal_uInt32 nTime);
203 
204     // calculate the MixerState value for given time
205     double GetMixerState(sal_uInt32 nTime);
206 
207     ////////////////////////////////////////////////////////////////////
208 
209     SlideShowContext                            maContext;
210     boost::shared_ptr<WakeupEvent>              mpWakeupEvent;
211     boost::weak_ptr<DrawShape>                  mpParentDrawShape;
212     DrawShapeSharedPtr                          mpDrawShape;
213     ShapeAttributeLayerHolder                   maShapeAttrLayer;
214     GDIMetaFileSharedPtr                        mpMetaFile;
215     IntrinsicAnimationEventHandlerSharedPtr     mpListener;
216     canvas::tools::ElapsedTime                  maTimer;
217     double                                      mfRotationAngle;
218     bool                                        mbIsShapeAnimated;
219     bool                                        mbIsDisposed;
220     bool                                        mbIsActive;
221     drawing::TextAnimationKind                  meAnimKind;
222 
223     // The blink frequency in ms
224     sal_uInt32                                  mnFrequency;
225 
226     // The repeat count, init to 0L which means endless
227     sal_uInt32                                  mnRepeat;
228 
229     // Flag to decide if text will be shown when animation has ended
230     bool                                        mbVisibleWhenStopped;
231     bool                                        mbVisibleWhenStarted;
232 
233     // Flag decides if TextScroll alternates. Default is sal_False.
234     bool                                        mbAlternate;
235 
236     // Flag to remember if this is a simple scrollin text
237     bool                                        mbScrollIn;
238 
239     // start time for this animation
240     sal_uInt32                                  mnStartTime;
241 
242     // The AnimationDirection
243     drawing::TextAnimationDirection             meDirection;
244 
245     // Get width per Step. Negative means pixel, positive logical units
246     sal_Int32                                   mnStepWidth;
247 
248     // The single anim steps
249     std::vector< ScrollTextAnimNode >           maVector;
250 
251     // the scroll rectangle
252     Rectangle                                   maScrollRectangleLogic;
253 
254     // the paint rectangle
255     Rectangle                                   maPaintRectangleLogic;
256 };
257 
258 //////////////////////////////////////////////////////////////////////
259 
260 class IntrinsicAnimationListener : public IntrinsicAnimationEventHandler,
261                                    private boost::noncopyable
262 {
263 public:
264     explicit IntrinsicAnimationListener( ActivityImpl& rActivity ) :
265         mrActivity( rActivity )
266     {}
267 
268 private:
269 
270     virtual bool enableAnimations() { return mrActivity.enableAnimations(); }
271     virtual bool disableAnimations() { mrActivity.end(); return true; }
272 
273     ActivityImpl& mrActivity;
274 };
275 
276 //////////////////////////////////////////////////////////////////////
277 
278 double ActivityImpl::GetMixerState( sal_uInt32 nTime )
279 {
280     if( meAnimKind == drawing::TextAnimationKind_BLINK )
281     {
282         // from AInfoBlinkText:
283         double fRetval(0.0);
284         sal_Bool bDone(sal_False);
285         const sal_uInt32 nLoopTime(2 * mnFrequency);
286 
287         if(mnRepeat)
288         {
289             const sal_uInt32 nEndTime(mnRepeat * nLoopTime);
290 
291             if(nTime >= nEndTime)
292             {
293                 if(mbVisibleWhenStopped)
294                     fRetval = 0.0;
295                 else
296                     fRetval = 1.0;
297 
298                 bDone = sal_True;
299             }
300         }
301 
302         if(!bDone)
303         {
304             sal_uInt32 nTimeInLoop(nTime % nLoopTime);
305             fRetval = double(nTimeInLoop) / nLoopTime;
306         }
307 
308         return fRetval;
309     }
310     else
311     {
312         // from AInfoScrollText:
313         double fRetval(0.0);
314         ImpForceScrollTextAnimNodes();
315 
316         if(!maVector.empty())
317         {
318             sal_uInt32 nRelativeTime;
319             ScrollTextAnimNode* pNode =
320                 ImpGetScrollTextAnimNode(nTime, nRelativeTime);
321 
322             if(pNode)
323             {
324                 // use node
325                 fRetval = pNode->GetStateAtRelativeTime(nRelativeTime);
326             }
327             else
328             {
329                 // end of animation, take last entry's end
330                 fRetval = maVector[maVector.size() - 1L].GetStop();
331             }
332         }
333 
334         return fRetval;
335     }
336 }
337 
338 // Access to StepWidth in logical units
339 sal_uInt32 ActivityImpl::GetStepWidthLogic() const
340 {
341     // #i69847# Assuming higher DPI
342     sal_uInt32 const PIXEL_TO_LOGIC = 30;
343 
344     sal_uInt32 nRetval(0L);
345 
346     if(mnStepWidth < 0L)
347     {
348         // is in pixels, convert to logical units
349         nRetval = (-mnStepWidth * PIXEL_TO_LOGIC);
350     }
351     else if(mnStepWidth > 0L)
352     {
353         // is in logical units
354         nRetval = mnStepWidth;
355     }
356 
357     if(0L == nRetval)
358     {
359         // step 1 pixel, canned value
360 
361         // #128389# with very high DPIs like in PDF export, this can
362         // still get zero.  for that cases, set a default, too (taken
363         // from ainfoscrolltext.cxx)
364         nRetval = 100L;
365     }
366 
367     return nRetval;
368 }
369 
370 void ActivityImpl::ImpForceScrollTextAnimNodes()
371 {
372     if(maVector.empty())
373     {
374         // prepare values
375         sal_uInt32 nLoopTime;
376         double fZeroLogic, fOneLogic, fInitLogic, fDistanceLogic;
377         double fZeroLogicAlternate = 0.0, fOneLogicAlternate = 0.0;
378         double fZeroRelative, fOneRelative, fInitRelative,fDistanceRelative;
379 
380         if(ScrollHorizontal())
381         {
382             if(DoAlternate())
383             {
384                 if(maPaintRectangleLogic.GetWidth() >
385                    maScrollRectangleLogic.GetWidth())
386                 {
387                     fZeroLogicAlternate = maScrollRectangleLogic.Right() - maPaintRectangleLogic.GetWidth();
388                     fOneLogicAlternate = maScrollRectangleLogic.Left();
389                 }
390                 else
391                 {
392                     fZeroLogicAlternate = maScrollRectangleLogic.Left();
393                     fOneLogicAlternate = maScrollRectangleLogic.Right() - maPaintRectangleLogic.GetWidth();
394                 }
395             }
396 
397             fZeroLogic = maScrollRectangleLogic.Left() - maPaintRectangleLogic.GetWidth();
398             fOneLogic = maScrollRectangleLogic.Right();
399             fInitLogic = maPaintRectangleLogic.Left();
400         }
401         else
402         {
403             if(DoAlternate())
404             {
405                 if(maPaintRectangleLogic.GetHeight() > maScrollRectangleLogic.GetHeight())
406                 {
407                     fZeroLogicAlternate = maScrollRectangleLogic.Bottom() - maPaintRectangleLogic.GetHeight();
408                     fOneLogicAlternate = maScrollRectangleLogic.Top();
409                 }
410                 else
411                 {
412                     fZeroLogicAlternate = maScrollRectangleLogic.Top();
413                     fOneLogicAlternate = maScrollRectangleLogic.Bottom() - maPaintRectangleLogic.GetHeight();
414                 }
415             }
416 
417             fZeroLogic = maScrollRectangleLogic.Top() - maPaintRectangleLogic.GetHeight();
418             fOneLogic = maScrollRectangleLogic.Bottom();
419             fInitLogic = maPaintRectangleLogic.Top();
420         }
421 
422         fDistanceLogic = fOneLogic - fZeroLogic;
423         fInitRelative = (fInitLogic - fZeroLogic) / fDistanceLogic;
424 
425         if(DoAlternate())
426         {
427             fZeroRelative =
428                 (fZeroLogicAlternate - fZeroLogic) / fDistanceLogic;
429             fOneRelative =
430                 (fOneLogicAlternate - fZeroLogic) / fDistanceLogic;
431             fDistanceRelative = fOneRelative - fZeroRelative;
432         }
433         else
434         {
435             fZeroRelative = 0.0;
436             fOneRelative = 1.0;
437             fDistanceRelative = 1.0;
438         }
439 
440         if(mnStartTime)
441         {
442             // Start time loop
443             ScrollTextAnimNode aStartNode(
444                 mnStartTime, 1L, 0.0, 0.0, mnStartTime, false);
445             maVector.push_back(aStartNode);
446         }
447 
448         if(IsVisibleWhenStarted())
449         {
450             double fRelativeStartValue, fRelativeEndValue,fRelativeDistance;
451 
452             if(DoScrollForward())
453             {
454                 fRelativeStartValue = fInitRelative;
455                 fRelativeEndValue = fOneRelative;
456                 fRelativeDistance = fRelativeEndValue - fRelativeStartValue;
457             }
458             else
459             {
460                 fRelativeStartValue = fInitRelative;
461                 fRelativeEndValue = fZeroRelative;
462                 fRelativeDistance = fRelativeStartValue - fRelativeEndValue;
463             }
464 
465             const double fNumberSteps =
466                 (fRelativeDistance * fDistanceLogic) / GetStepWidthLogic();
467             nLoopTime = FRound(fNumberSteps * mnFrequency);
468 
469             // init loop
470             ScrollTextAnimNode aInitNode(
471                 nLoopTime, 1L,
472                 fRelativeStartValue, fRelativeEndValue,
473                 mnFrequency, false);
474             maVector.push_back(aInitNode);
475         }
476 
477         // prepare main loop values
478         {
479             double fRelativeStartValue, fRelativeEndValue, fRelativeDistance;
480 
481             if(DoScrollForward())
482             {
483                 fRelativeStartValue = fZeroRelative;
484                 fRelativeEndValue = fOneRelative;
485                 fRelativeDistance = fRelativeEndValue - fRelativeStartValue;
486             }
487             else
488             {
489                 fRelativeStartValue = fOneRelative;
490                 fRelativeEndValue = fZeroRelative;
491                 fRelativeDistance = fRelativeStartValue - fRelativeEndValue;
492             }
493 
494             const double fNumberSteps =
495                 (fRelativeDistance * fDistanceLogic) / GetStepWidthLogic();
496             nLoopTime = FRound(fNumberSteps * mnFrequency);
497 
498             if(0L == mnRepeat)
499             {
500                 if(!DoScrollIn())
501                 {
502                     // endless main loop
503                     ScrollTextAnimNode aMainNode(
504                         nLoopTime, 0L,
505                         fRelativeStartValue, fRelativeEndValue,
506                         mnFrequency, DoAlternate());
507                     maVector.push_back(aMainNode);
508                 }
509             }
510             else
511             {
512                 sal_uInt32 nNumRepeat(mnRepeat);
513 
514                 if(DoAlternate() && (nNumRepeat + 1L) % 2L)
515                     nNumRepeat += 1L;
516 
517                 // ending main loop
518                 ScrollTextAnimNode aMainNode(
519                     nLoopTime, nNumRepeat,
520                     fRelativeStartValue, fRelativeEndValue,
521                     mnFrequency, DoAlternate());
522                 maVector.push_back(aMainNode);
523             }
524         }
525 
526         if(IsVisibleWhenStopped())
527         {
528             double fRelativeStartValue, fRelativeEndValue, fRelativeDistance;
529 
530             if(DoScrollForward())
531             {
532                 fRelativeStartValue = fZeroRelative;
533                 fRelativeEndValue = fInitRelative;
534                 fRelativeDistance = fRelativeEndValue - fRelativeStartValue;
535             }
536             else
537             {
538                 fRelativeStartValue = fOneRelative;
539                 fRelativeEndValue = fInitRelative;
540                 fRelativeDistance = fRelativeStartValue - fRelativeEndValue;
541             }
542 
543             const double fNumberSteps =
544                 (fRelativeDistance * fDistanceLogic) / GetStepWidthLogic();
545             nLoopTime = FRound(fNumberSteps * mnFrequency);
546 
547             // exit loop
548             ScrollTextAnimNode aExitNode(
549                 nLoopTime, 1L,
550                 fRelativeStartValue, fRelativeEndValue, mnFrequency, false);
551             maVector.push_back(aExitNode);
552         }
553     }
554 }
555 
556 ScrollTextAnimNode* ActivityImpl::ImpGetScrollTextAnimNode(
557     sal_uInt32 nTime, sal_uInt32& rRelativeTime )
558 {
559     ScrollTextAnimNode* pRetval = 0L;
560     ImpForceScrollTextAnimNodes();
561 
562     if(!maVector.empty())
563     {
564         rRelativeTime = nTime;
565 
566         for(sal_uInt32 a(0L); !pRetval && a < maVector.size(); a++)
567         {
568             ScrollTextAnimNode & rNode = maVector[a];
569             if(!rNode.GetRepeat())
570             {
571                 // endless loop, use it
572                 pRetval = &rNode;
573             }
574             else if(rNode.GetFullTime() > rRelativeTime)
575             {
576                 // ending node
577                 pRetval = &rNode;
578             }
579             else
580             {
581                 // look at next
582                 rRelativeTime -= rNode.GetFullTime();
583             }
584         }
585     }
586 
587     return pRetval;
588 }
589 
590 sal_uInt32 ActivityImpl::ImpRegisterAgainScrollTextMixerState(sal_uInt32 nTime)
591 {
592     sal_uInt32 nRetval(0L);
593     ImpForceScrollTextAnimNodes();
594 
595     if(maVector.size())
596     {
597         sal_uInt32 nRelativeTime;
598         ScrollTextAnimNode* pNode = ImpGetScrollTextAnimNode(nTime, nRelativeTime);
599 
600         if(pNode)
601         {
602             // take register time
603             nRetval = pNode->GetFrequency();
604         }
605     }
606     else
607     {
608         // #i38135# not initialized, return default
609         nRetval = mnFrequency;
610     }
611 
612     return nRetval;
613 }
614 
615 void ActivityImpl::updateShapeAttributes(
616     double fTime, basegfx::B2DRectangle const& parentBounds )
617 {
618     OSL_ASSERT( meAnimKind != drawing::TextAnimationKind_NONE );
619     if( meAnimKind == drawing::TextAnimationKind_NONE )
620         return;
621 
622     double const fMixerState = GetMixerState(
623         static_cast<sal_uInt32>(fTime * 1000.0) );
624 
625     if( meAnimKind == drawing::TextAnimationKind_BLINK )
626     {
627         // show/hide text:
628         maShapeAttrLayer.get()->setVisibility( fMixerState < 0.5 );
629     }
630     else if(mpMetaFile) // scroll mode:
631     {
632         //
633         // keep care: the below code is highly sensible to changes...
634         //
635 
636         // rectangle of the pure text:
637         double const fPaintWidth = maPaintRectangleLogic.GetWidth();
638         double const fPaintHeight = maPaintRectangleLogic.GetHeight();
639         // rectangle where the scrolling takes place (-> clipping):
640         double const fScrollWidth = maScrollRectangleLogic.GetWidth();
641         double const fScrollHeight = maScrollRectangleLogic.GetHeight();
642 
643         basegfx::B2DPoint pos, clipPos;
644 
645         if(ScrollHorizontal())
646         {
647             double const fOneEquiv( fScrollWidth );
648             double const fZeroEquiv( -fPaintWidth );
649 
650             pos.setX( fZeroEquiv + (fMixerState * (fOneEquiv - fZeroEquiv)) );
651 
652             clipPos.setX( -pos.getX() );
653             clipPos.setY( -pos.getY() );
654 
655             // #i69844# Compensation for text-wider-than-shape case
656             if( fPaintWidth > fScrollWidth )
657                 pos.setX( pos.getX() + (fPaintWidth-fScrollWidth) / 2.0 );
658         }
659         else
660         {
661             // scroll vertical:
662             double const fOneEquiv( fScrollHeight );
663             double const fZeroEquiv( -fPaintHeight );
664 
665             pos.setY( fZeroEquiv + (fMixerState * (fOneEquiv - fZeroEquiv)) );
666 
667             clipPos.setX( -pos.getX() );
668             clipPos.setY( -pos.getY() );
669 
670             // #i69844# Compensation for text-higher-than-shape case
671             if( fPaintHeight > fScrollHeight )
672                 pos.setY( pos.getY() + (fPaintHeight-fScrollHeight) / 2.0 );
673         }
674 
675         basegfx::B2DPolygon clipPoly(
676             basegfx::tools::createPolygonFromRect(
677                 basegfx::B2DRectangle( clipPos.getX(),
678                                        clipPos.getY(),
679                                        clipPos.getX() + fScrollWidth,
680                                        clipPos.getY() + fScrollHeight ) ) );
681 
682         if( !::basegfx::fTools::equalZero( mfRotationAngle ))
683         {
684             maShapeAttrLayer.get()->setRotationAngle( mfRotationAngle );
685             double const fRotate = (mfRotationAngle * M_PI / 180.0);
686             basegfx::B2DHomMatrix aTransform;
687             // position:
688             aTransform.rotate( fRotate );
689             pos *= aTransform;
690         }
691 
692         pos += parentBounds.getCenter();
693         maShapeAttrLayer.get()->setPosition( pos );
694         maShapeAttrLayer.get()->setClip( basegfx::B2DPolyPolygon(clipPoly) );
695     }
696 }
697 
698 bool ActivityImpl::perform()
699 {
700     if( !isActive() )
701         return false;
702 
703     ENSURE_OR_RETURN_FALSE(
704         mpDrawShape,
705         "ActivityImpl::perform(): still active, but NULL draw shape" );
706 
707     DrawShapeSharedPtr const pParentDrawShape( mpParentDrawShape );
708     if( !pParentDrawShape )
709         return false; // parent has vanished
710 
711     if( pParentDrawShape->isVisible() )
712     {
713         if( !mbIsShapeAnimated )
714         {
715             mpDrawShape->setVisibility(true); // shape may be initially hidden
716             maContext.mpSubsettableShapeManager->enterAnimationMode( mpDrawShape );
717             maTimer.reset();
718             mbIsShapeAnimated = true;
719         }
720         // update attributes related to current time:
721         basegfx::B2DRectangle const parentBounds(
722             pParentDrawShape->getBounds() );
723 
724         const double nCurrTime( maTimer.getElapsedTime() );
725         updateShapeAttributes( nCurrTime, parentBounds );
726 
727         const sal_uInt32 nFrequency(
728             ImpRegisterAgainScrollTextMixerState(
729                 static_cast<sal_uInt32>(nCurrTime * 1000.0)) );
730 
731         if(nFrequency)
732         {
733             mpWakeupEvent->start();
734             mpWakeupEvent->setNextTimeout(
735                 std::max(0.1,nFrequency/1000.0) );
736             maContext.mrEventQueue.addEvent( mpWakeupEvent );
737 
738             if( mpDrawShape->isContentChanged() )
739                 maContext.mpSubsettableShapeManager->notifyShapeUpdate( mpDrawShape );
740         }
741         // else: finished, not need to wake up again.
742     }
743     else
744     {
745         // busy-wait, until parent shape gets visible
746         mpWakeupEvent->start();
747         mpWakeupEvent->setNextTimeout( 2.0 );
748     }
749 
750     // don't reinsert, WakeupEvent will perform that after the given timeout:
751     return false;
752 }
753 
754 ActivityImpl::ActivityImpl(
755     SlideShowContext const& rContext,
756     boost::shared_ptr<WakeupEvent> const& pWakeupEvent,
757     boost::shared_ptr<DrawShape> const& pParentDrawShape )
758     : maContext(rContext),
759       mpWakeupEvent(pWakeupEvent),
760       mpParentDrawShape(pParentDrawShape),
761       mpListener( new IntrinsicAnimationListener(*this) ),
762       maTimer(rContext.mrEventQueue.getTimer()),
763       mbIsShapeAnimated(false),
764       mbIsDisposed(false),
765       mbIsActive(true),
766       meAnimKind(drawing::TextAnimationKind_NONE),
767       mnStartTime(0L)
768 {
769     // get doctreenode:
770     sal_Int32 const nNodes = pParentDrawShape->getNumberOfTreeNodes(
771         DocTreeNode::NODETYPE_LOGICAL_PARAGRAPH );
772 
773     DocTreeNode scrollTextNode(
774         pParentDrawShape->getTreeNode(
775             0, DocTreeNode::NODETYPE_LOGICAL_PARAGRAPH ));
776     // xxx todo: remove this hack
777     if( nNodes > 1 )
778         scrollTextNode.setEndIndex(
779             pParentDrawShape->getTreeNode(
780                 nNodes - 1,
781                 DocTreeNode::NODETYPE_LOGICAL_PARAGRAPH ).getEndIndex());
782 
783     // TODO(Q3): Doing this manually, instead of using
784     // ShapeSubset. This is because of lifetime issues (ShapeSubset
785     // generates circular references to parent shape)
786     mpDrawShape = boost::dynamic_pointer_cast<DrawShape>(
787         maContext.mpSubsettableShapeManager->getSubsetShape(
788             pParentDrawShape,
789             scrollTextNode ));
790 
791     mpMetaFile = mpDrawShape->forceScrollTextMetaFile();
792 
793     // make scroll text invisible for slide transition bitmaps
794     mpDrawShape->setVisibility(false);
795 
796     basegfx::B2DRectangle aScrollRect, aPaintRect;
797     ENSURE_OR_THROW( getRectanglesFromScrollMtf( aScrollRect,
798                                                   aPaintRect,
799                                                   mpMetaFile ),
800                       "ActivityImpl::ActivityImpl(): Could not extract "
801                       "scroll anim rectangles from mtf" );
802 
803     maScrollRectangleLogic = vcl::unotools::rectangleFromB2DRectangle(
804         aScrollRect );
805     maPaintRectangleLogic = vcl::unotools::rectangleFromB2DRectangle(
806         aPaintRect );
807 
808     maShapeAttrLayer.createAttributeLayer(mpDrawShape);
809 
810     uno::Reference<drawing::XShape> const xShape( mpDrawShape->getXShape() );
811     uno::Reference<beans::XPropertySet> const xProps( xShape, uno::UNO_QUERY_THROW );
812 
813     getPropertyValue( meAnimKind, xProps, OUSTR("TextAnimationKind") );
814     OSL_ASSERT( meAnimKind != drawing::TextAnimationKind_NONE );
815     mbAlternate = (meAnimKind == drawing::TextAnimationKind_ALTERNATE);
816     mbScrollIn = (meAnimKind == drawing::TextAnimationKind_SLIDE);
817 
818     // adopted from in AInfoBlinkText::ImplInit():
819     sal_Int16 nRepeat(0);
820     getPropertyValue( nRepeat, xProps, OUSTR("TextAnimationCount") );
821     mnRepeat = nRepeat;
822 
823     if(mbAlternate)
824     {
825         // force visible when started for scroll-forth-and-back, because
826         // slide has been coming in with visible text in the middle:
827         mbVisibleWhenStarted = true;
828     }
829     else
830     {
831         getPropertyValue( mbVisibleWhenStarted, xProps,
832                           OUSTR("TextAnimationStartInside") );
833     }
834 
835     // set visible when stopped
836     getPropertyValue( mbVisibleWhenStopped, xProps,
837                       OUSTR("TextAnimatiogonStopInside") );
838     // rotation:
839     getPropertyValue( mfRotationAngle, xProps,
840                       OUSTR("RotateAngle") );
841     mfRotationAngle /= -100.0; // (switching direction)
842 
843     // set frequency
844     sal_Int16 nDelay(0);
845     getPropertyValue( nDelay, xProps, OUSTR("TextAnimationDelay") );
846     // set delay if not automatic
847     mnFrequency = (nDelay ? nDelay :
848                    // default:
849                    meAnimKind == drawing::TextAnimationKind_BLINK
850                    ? 250L : 50L );
851 
852     // adopted from in AInfoScrollText::ImplInit():
853 
854     // If it is a simple m_bScrollIn, reset some parameters
855     if( DoScrollIn() )
856     {
857         // most parameters are set correctly from the dialog logic, but
858         // eg VisisbleWhenStopped is grayed out and needs to be corrected here.
859         mbVisibleWhenStopped = true;
860         mbVisibleWhenStarted = false;
861         mnRepeat = 0L;
862     }
863 
864     // Get animation direction
865     getPropertyValue( meDirection, xProps, OUSTR("TextAnimationDirection") );
866 
867     // Get step width. Negative means pixel, positive logical units
868     getPropertyValue( mnStepWidth, xProps, OUSTR("TextAnimationAmount") );
869 
870     maContext.mpSubsettableShapeManager->addIntrinsicAnimationHandler(
871         mpListener );
872 }
873 
874 bool ActivityImpl::enableAnimations()
875 {
876     mbIsActive = true;
877     return maContext.mrActivitiesQueue.addActivity(
878         shared_from_this() );
879 }
880 
881 ActivityImpl::~ActivityImpl()
882 {
883 }
884 
885 void ActivityImpl::dispose()
886 {
887     if( !mbIsDisposed )
888     {
889         end();
890 
891         // only remove subset here, since end() is called on slide end
892         // (and we must not spoil the slide preview bitmap with scroll
893         // text)
894         maShapeAttrLayer.reset();
895         if( mpDrawShape )
896         {
897             // TODO(Q3): Doing this manually, instead of using
898             // ShapeSubset. This is because of lifetime issues
899             // (ShapeSubset generates circular references to parent
900             // shape)
901             DrawShapeSharedPtr pParent( mpParentDrawShape.lock() );
902             if( pParent )
903                 maContext.mpSubsettableShapeManager->revokeSubset(
904                     pParent,
905                     mpDrawShape );
906         }
907 
908         mpMetaFile.reset();
909         mpDrawShape.reset();
910         mpParentDrawShape.reset();
911         mpWakeupEvent.reset();
912         maContext.dispose();
913         mbIsDisposed = true;
914 
915         maContext.mpSubsettableShapeManager->removeIntrinsicAnimationHandler(
916             mpListener );
917     }
918 }
919 
920 double ActivityImpl::calcTimeLag() const
921 {
922     return 0.0;
923 }
924 
925 bool ActivityImpl::isActive() const
926 {
927     return mbIsActive;
928 }
929 
930 void ActivityImpl::dequeued()
931 {
932     // not used here
933 }
934 
935 void ActivityImpl::end()
936 {
937     // not used here
938     mbIsActive = false;
939 
940     if( mbIsShapeAnimated )
941     {
942         maContext.mpSubsettableShapeManager->leaveAnimationMode( mpDrawShape );
943         mbIsShapeAnimated = false;
944     }
945 }
946 
947 } // anon namespace
948 
949 namespace slideshow {
950 namespace internal {
951 
952 boost::shared_ptr<Activity> createDrawingLayerAnimActivity(
953     SlideShowContext const& rContext,
954     boost::shared_ptr<DrawShape> const& pDrawShape )
955 {
956     boost::shared_ptr<Activity> pActivity;
957 
958     try
959     {
960         boost::shared_ptr<WakeupEvent> const pWakeupEvent(
961             new WakeupEvent( rContext.mrEventQueue.getTimer(),
962                              rContext.mrActivitiesQueue ) );
963         pActivity.reset( new ActivityImpl( rContext, pWakeupEvent, pDrawShape ) );
964         pWakeupEvent->setActivity( pActivity );
965     }
966     catch( uno::RuntimeException& )
967     {
968         throw;
969     }
970     catch( uno::Exception& )
971     {
972         // translate any error into empty factory product.
973         OSL_ENSURE( false,
974                     rtl::OUStringToOString(
975                         comphelper::anyToString( cppu::getCaughtException() ),
976                         RTL_TEXTENCODING_UTF8 ).getStr() );
977     }
978 
979     return pActivity;
980 }
981 
982 } // namespace internal
983 } // namespace presentation
984 
985