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