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