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