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