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