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