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