/************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ #include "precompiled_sd.hxx" #include "controller/SlsAnimator.hxx" #include "view/SlideSorterView.hxx" #include "View.hxx" #include namespace sd { namespace slidesorter { namespace controller { namespace { static const sal_Int32 gnResolution = 25; } /** Handle one animation function by using a timer for frequent calls to the animations operator(). */ class Animator::Animation { public: Animation ( const Animator::AnimationFunctor& rAnimation, const double nStartOffset, const double nDuration, const double nGlobalTime, const Animator::AnimationId nAnimationId, const Animator::FinishFunctor& rFinishFunctor); ~Animation (void); /** Run next animation step. If animation has reached its end it is expired. */ bool Run (const double nGlobalTime); /** Typically called when an animation has finished, but also from Animator::Disposed(). The finish functor is called and the animation is marked as expired to prevent another run. */ void Expire (void); bool IsExpired (void); Animator::AnimationFunctor maAnimation; Animator::FinishFunctor maFinishFunctor; const Animator::AnimationId mnAnimationId; const double mnDuration; const double mnEnd; const double mnGlobalTimeAtStart; bool mbIsExpired; }; Animator::Animator (SlideSorter& rSlideSorter) : mrSlideSorter(rSlideSorter), maTimer(), mbIsDisposed(false), maAnimations(), maElapsedTime(), mpDrawLock(), mnNextAnimationId(0) { maTimer.SetTimeout(gnResolution); maTimer.SetTimeoutHdl(LINK(this,Animator,TimeoutHandler)); } Animator::~Animator (void) { if ( ! mbIsDisposed) { OSL_ASSERT(mbIsDisposed); Dispose(); } } void Animator::Dispose (void) { mbIsDisposed = true; AnimationList aCopy (maAnimations); AnimationList::const_iterator iAnimation; for (iAnimation=aCopy.begin(); iAnimation!=aCopy.end(); ++iAnimation) (*iAnimation)->Expire(); maTimer.Stop(); if (mpDrawLock) { mpDrawLock->Dispose(); mpDrawLock.reset(); } } Animator::AnimationId Animator::AddAnimation ( const AnimationFunctor& rAnimation, const sal_Int32 nStartOffset, const sal_Int32 nDuration, const FinishFunctor& rFinishFunctor) { // When the animator is already disposed then ignore this call // silently (well, we show an assertion, but do not throw an exception.) OSL_ASSERT( ! mbIsDisposed); if (mbIsDisposed) return -1; boost::shared_ptr pAnimation ( new Animation( rAnimation, nStartOffset / 1000.0, nDuration / 1000.0, maElapsedTime.getElapsedTime(), ++mnNextAnimationId, rFinishFunctor)); maAnimations.push_back(pAnimation); RequestNextFrame(); return pAnimation->mnAnimationId; } Animator::AnimationId Animator::AddInfiniteAnimation ( const AnimationFunctor& rAnimation, const double nDelta) { (void)nDelta; // When the animator is already disposed then ignore this call // silently (well, we show an assertion, but do not throw an exception.) OSL_ASSERT( ! mbIsDisposed); if (mbIsDisposed) return -1; boost::shared_ptr pAnimation ( new Animation( rAnimation, 0, -1, maElapsedTime.getElapsedTime(), mnNextAnimationId++, FinishFunctor())); maAnimations.push_back(pAnimation); RequestNextFrame(); return pAnimation->mnAnimationId; } void Animator::RemoveAnimation (const Animator::AnimationId nId) { OSL_ASSERT( ! mbIsDisposed); const AnimationList::iterator iAnimation (::std::find_if( maAnimations.begin(), maAnimations.end(), ::boost::bind( ::std::equal_to(), nId, ::boost::bind(&Animation::mnAnimationId, _1)))); if (iAnimation != maAnimations.end()) { OSL_ASSERT((*iAnimation)->mnAnimationId == nId); (*iAnimation)->Expire(); maAnimations.erase(iAnimation); } if (maAnimations.empty()) { // Reset the animation id when we can. mnNextAnimationId = 0; // No more animations => we do not have to suppress painting // anymore. mpDrawLock.reset(); } } void Animator::RemoveAllAnimations (void) { ::std::for_each( maAnimations.begin(), maAnimations.end(), ::boost::bind( &Animation::Expire, _1)); maAnimations.clear(); mnNextAnimationId = 0; // No more animations => we do not have to suppress painting // anymore. mpDrawLock.reset(); } bool Animator::ProcessAnimations (const double nTime) { bool bExpired (false); OSL_ASSERT( ! mbIsDisposed); if (mbIsDisposed) return bExpired; AnimationList aCopy (maAnimations); AnimationList::const_iterator iAnimation; for (iAnimation=aCopy.begin(); iAnimation!=aCopy.end(); ++iAnimation) { bExpired |= (*iAnimation)->Run(nTime); } return bExpired; } void Animator::CleanUpAnimationList (void) { OSL_ASSERT( ! mbIsDisposed); if (mbIsDisposed) return; AnimationList aActiveAnimations; AnimationList::const_iterator iAnimation; for (iAnimation=maAnimations.begin(); iAnimation!=maAnimations.end(); ++iAnimation) { if ( ! (*iAnimation)->IsExpired()) aActiveAnimations.push_back(*iAnimation); } maAnimations.swap(aActiveAnimations); } void Animator::RequestNextFrame (const double nFrameStart) { (void)nFrameStart; if ( ! maTimer.IsActive()) { // Prevent redraws except for the ones in TimeoutHandler. While the // Animator is active it will schedule repaints regularly. Repaints // in between would only lead to visual artifacts. mpDrawLock.reset(new view::SlideSorterView::DrawLock(mrSlideSorter)); maTimer.Start(); } } IMPL_LINK(Animator, TimeoutHandler, Timer*, EMPTYARG) { if (mbIsDisposed) return 0; if (ProcessAnimations(maElapsedTime.getElapsedTime())) CleanUpAnimationList(); // Unlock the draw lock. This should lead to a repaint. mpDrawLock.reset(); if (!maAnimations.empty()) RequestNextFrame(); return 0; } //===== Animator::Animation =================================================== Animator::Animation::Animation ( const Animator::AnimationFunctor& rAnimation, const double nStartOffset, const double nDuration, const double nGlobalTime, const Animator::AnimationId nId, const Animator::FinishFunctor& rFinishFunctor) : maAnimation(rAnimation), maFinishFunctor(rFinishFunctor), mnAnimationId(nId), mnDuration(nDuration), mnEnd(nGlobalTime + nDuration + nStartOffset), mnGlobalTimeAtStart(nGlobalTime + nStartOffset), mbIsExpired(false) { Run(nGlobalTime); } Animator::Animation::~Animation (void) { } bool Animator::Animation::Run (const double nGlobalTime) { if ( ! mbIsExpired) { if (mnDuration > 0) { if (nGlobalTime >= mnEnd) { maAnimation(1.0); Expire(); } else if (nGlobalTime >= mnGlobalTimeAtStart) { maAnimation((nGlobalTime - mnGlobalTimeAtStart) / mnDuration); } } else if (mnDuration < 0) { // Animations without end have to be expired by their owner. maAnimation(nGlobalTime); } } return mbIsExpired; } void Animator::Animation::Expire (void) { if ( ! mbIsExpired) { mbIsExpired = true; if (maFinishFunctor) maFinishFunctor(); } } bool Animator::Animation::IsExpired (void) { return mbIsExpired; } } } } // end of namespace ::sd::slidesorter::controller