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