/************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright 2000, 2010 Oracle and/or its affiliates. * * OpenOffice.org - a multi-platform office productivity suite * * This file is part of OpenOffice.org. * * OpenOffice.org is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * OpenOffice.org is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with OpenOffice.org. If not, see * * for a copy of the LGPLv3 License. * ************************************************************************/ #include "precompiled_sd.hxx" #include "view/SlsInsertAnimator.hxx" #include "controller/SlideSorterController.hxx" #include "controller/SlsAnimationFunction.hxx" #include "view/SlideSorterView.hxx" #include "view/SlsLayouter.hxx" #include "model/SlideSorterModel.hxx" #include "model/SlsPageEnumerationProvider.hxx" #include #include #include namespace sd { namespace slidesorter { namespace view { namespace { class PageObjectRun; class AnimatorAccess { public: virtual void AddRun (const ::boost::shared_ptr pRun) = 0; virtual void RemoveRun (const ::boost::shared_ptr pRun) = 0; virtual model::SlideSorterModel& GetModel (void) const = 0; virtual view::SlideSorterView& GetView (void) const = 0; virtual ::boost::shared_ptr GetAnimator (void) = 0; virtual SharedSdWindow GetContentWindow (void) = 0; }; /** Controller of the position offsets of all page objects in one row or one column. */ class PageObjectRun : public ::boost::enable_shared_from_this { public: PageObjectRun ( AnimatorAccess& rAnimatorAccess, const sal_Int32 nRunIndex, const sal_Int32 nStartIndex, const sal_Int32 nEndIndex); ~PageObjectRun (void); void operator () (const double nTime); void UpdateOffsets( const InsertPosition& rInsertPosition, const view::Layouter& GetLayouter); void ResetOffsets (const controller::Animator::AnimationMode eMode); /// Index of the row or column that this run represents. sal_Int32 mnRunIndex; /// The index at which to make place for the insertion indicator (-1 for /// no indicator). sal_Int32 mnLocalInsertIndex; /// Index of the first page in the run. sal_Int32 mnStartIndex; /// Index of the last page in the run. sal_Int32 mnEndIndex; /// Offset of each item in the run at the start of the current animation. ::std::vector maStartOffset; /// Target offset of each item in the run at the end of the current animation. ::std::vector maEndOffset; /// Time at which the current animation started. double mnStartTime; class Comparator { public: bool operator() ( const ::boost::shared_ptr& rpRunA, const ::boost::shared_ptr& rpRunB) const { return rpRunA->mnRunIndex < rpRunB->mnRunIndex; } }; private: controller::Animator::AnimationId mnAnimationId; AnimatorAccess& mrAnimatorAccess; ::boost::function maAccelerationFunction; Rectangle GetInnerBoundingBox ( const view::Layouter& rLayouter, const sal_Int32 nIndex) const; void RestartAnimation (void); }; typedef ::boost::shared_ptr SharedPageObjectRun; Point Blend (const Point& rPointA, const Point& rPointB, const double nT) { return Point( sal_Int32(rPointA.X() * (1-nT) + rPointB.X() * nT), sal_Int32(rPointA.Y() * (1-nT) + rPointB.Y() * nT)); } } // end of anonymous namespace class InsertAnimator::Implementation : public AnimatorAccess { public: Implementation (SlideSorter& rSlideSorter); virtual ~Implementation (void); void SetInsertPosition ( const InsertPosition& rInsertPosition, const controller::Animator::AnimationMode eAnimationMode); virtual void AddRun (const ::boost::shared_ptr pRun); virtual void RemoveRun (const ::boost::shared_ptr pRun); virtual model::SlideSorterModel& GetModel (void) const { return mrModel; } virtual view::SlideSorterView& GetView (void) const { return mrView; } virtual ::boost::shared_ptr GetAnimator (void) { return mpAnimator; } virtual SharedSdWindow GetContentWindow (void) { return mrSlideSorter.GetContentWindow(); } private: model::SlideSorterModel& mrModel; view::SlideSorterView& mrView; SlideSorter& mrSlideSorter; ::boost::shared_ptr mpAnimator; typedef ::std::set RunContainer; RunContainer maRuns; InsertPosition maInsertPosition; void StopAnimation (void); SharedPageObjectRun GetRun ( view::Layouter& rLayouter, const InsertPosition& rInsertPosition, const bool bCreate = true); RunContainer::const_iterator FindRun (const sal_Int32 nRunIndex) const; }; //===== InsertAnimator ======================================================== InsertAnimator::InsertAnimator (SlideSorter& rSlideSorter) : mpImplementation(new Implementation(rSlideSorter)) { } void InsertAnimator::SetInsertPosition (const InsertPosition& rInsertPosition) { mpImplementation->SetInsertPosition(rInsertPosition, controller::Animator::AM_Animated); } void InsertAnimator::Reset (const controller::Animator::AnimationMode eMode) { mpImplementation->SetInsertPosition(InsertPosition(), eMode); } //===== InsertAnimator::Implementation ======================================== InsertAnimator::Implementation::Implementation (SlideSorter& rSlideSorter) : mrModel(rSlideSorter.GetModel()), mrView(rSlideSorter.GetView()), mrSlideSorter(rSlideSorter), mpAnimator(rSlideSorter.GetController().GetAnimator()), maRuns(), maInsertPosition() { } InsertAnimator::Implementation::~Implementation (void) { SetInsertPosition(InsertPosition(), controller::Animator::AM_Immediate); } void InsertAnimator::Implementation::SetInsertPosition ( const InsertPosition& rInsertPosition, const controller::Animator::AnimationMode eMode) { if (maInsertPosition == rInsertPosition) return; SharedPageObjectRun pOldRun (GetRun(mrView.GetLayouter(), maInsertPosition)); SharedPageObjectRun pCurrentRun (GetRun(mrView.GetLayouter(), rInsertPosition)); maInsertPosition = rInsertPosition; // When the new insert position is in a different run then move the page // objects in the old run to their default positions. if (pOldRun != pCurrentRun) { if (pOldRun) pOldRun->ResetOffsets(eMode); } if (pCurrentRun) { pCurrentRun->UpdateOffsets(rInsertPosition, mrView.GetLayouter()); } } SharedPageObjectRun InsertAnimator::Implementation::GetRun ( view::Layouter& rLayouter, const InsertPosition& rInsertPosition, const bool bCreate) { const sal_Int32 nRow (rInsertPosition.GetRow()); if (nRow < 0) return SharedPageObjectRun(); RunContainer::const_iterator iRun (maRuns.end()); if (rLayouter.GetColumnCount() == 1) { // There is only one run that contains all slides. if (maRuns.empty() && bCreate) maRuns.insert(SharedPageObjectRun(new PageObjectRun( *this, 0, 0, mrModel.GetPageCount()-1))); iRun = maRuns.begin(); } else { iRun = FindRun(nRow); if (iRun == maRuns.end() && bCreate) { // Create a new run. const sal_Int32 nStartIndex (rLayouter.GetIndex(nRow, 0)); const sal_Int32 nEndIndex (rLayouter.GetIndex(nRow, rLayouter.GetColumnCount()-1)); if (nStartIndex <= nEndIndex) { iRun = maRuns.insert(SharedPageObjectRun(new PageObjectRun( *this, nRow, nStartIndex, nEndIndex))).first; OSL_ASSERT(iRun != maRuns.end()); } } } if (iRun != maRuns.end()) return *iRun; else return SharedPageObjectRun(); } InsertAnimator::Implementation::RunContainer::const_iterator InsertAnimator::Implementation::FindRun (const sal_Int32 nRunIndex) const { return std::find_if( maRuns.begin(), maRuns.end(), ::boost::bind( ::std::equal_to(), ::boost::bind(&PageObjectRun::mnRunIndex, _1), nRunIndex)); } void InsertAnimator::Implementation::AddRun (const ::boost::shared_ptr pRun) { if (pRun) { maRuns.insert(pRun); } else { OSL_ASSERT(pRun); } } void InsertAnimator::Implementation::RemoveRun (const ::boost::shared_ptr pRun) { if (pRun) { // Do not remove runs that show the space for the insertion indicator. if (pRun->mnLocalInsertIndex == -1) { InsertAnimator::Implementation::RunContainer::const_iterator iRun (FindRun(pRun->mnRunIndex)); if (iRun != maRuns.end()) { OSL_ASSERT(*iRun == pRun); maRuns.erase(iRun); } } } else { OSL_ASSERT(pRun); } } //===== PageObjectRun ========================================================= PageObjectRun::PageObjectRun ( AnimatorAccess& rAnimatorAccess, const sal_Int32 nRunIndex, const sal_Int32 nStartIndex, const sal_Int32 nEndIndex) : mnRunIndex(nRunIndex), mnLocalInsertIndex(-1), mnStartIndex(nStartIndex), mnEndIndex(nEndIndex), maStartOffset(), maEndOffset(), mnStartTime(-1), mnAnimationId(controller::Animator::NotAnAnimationId), mrAnimatorAccess(rAnimatorAccess), maAccelerationFunction( controller::AnimationParametricFunction( controller::AnimationBezierFunction (0.1,0.7))) { maStartOffset.resize(nEndIndex - nStartIndex + 1); maEndOffset.resize(nEndIndex - nStartIndex + 1); } PageObjectRun::~PageObjectRun (void) { } Rectangle PageObjectRun::GetInnerBoundingBox ( const view::Layouter& rLayouter, const sal_Int32 nIndex) const { model::SharedPageDescriptor pDescriptor ( mrAnimatorAccess.GetModel().GetPageDescriptor(nIndex)); if (pDescriptor) if (pDescriptor->HasState(model::PageDescriptor::ST_Selected)) return rLayouter.GetPageObjectLayouter()->GetBoundingBox( pDescriptor, PageObjectLayouter::PageObject, PageObjectLayouter::ModelCoordinateSystem); else return rLayouter.GetPageObjectLayouter()->GetBoundingBox( pDescriptor, PageObjectLayouter::Preview, PageObjectLayouter::ModelCoordinateSystem); else return Rectangle(); } void PageObjectRun::UpdateOffsets( const InsertPosition& rInsertPosition, const view::Layouter& rLayouter) { const bool bIsVertical (rLayouter.GetColumnCount()==1); const sal_Int32 nLocalInsertIndex(bIsVertical ? rInsertPosition.GetRow() : rInsertPosition.GetColumn()); if (nLocalInsertIndex != mnLocalInsertIndex) { mnLocalInsertIndex = nLocalInsertIndex; model::SlideSorterModel& rModel (mrAnimatorAccess.GetModel()); const sal_Int32 nRunLength (mnEndIndex - mnStartIndex + 1); for (sal_Int32 nIndex=0; nIndexGetVisualState().GetLocationOffset(); maEndOffset[nIndex] = nIndex < mnLocalInsertIndex ? rInsertPosition.GetLeadingOffset() : rInsertPosition.GetTrailingOffset(); if (bIsVertical) maEndOffset[nIndex].X() = 0; else maEndOffset[nIndex].Y() = 0; } RestartAnimation(); } } void PageObjectRun::ResetOffsets (const controller::Animator::AnimationMode eMode) { mnLocalInsertIndex = -1; const sal_Int32 nRunLength (mnEndIndex - mnStartIndex + 1); model::SlideSorterModel& rModel (mrAnimatorAccess.GetModel()); view::SlideSorterView& rView (mrAnimatorAccess.GetView()); for (sal_Int32 nIndex=0; nIndexGetVisualState().GetLocationOffset(); else { const Rectangle aOldBoundingBox (pDescriptor->GetBoundingBox()); pDescriptor->GetVisualState().SetLocationOffset(Point(0,0)); rView.RequestRepaint(aOldBoundingBox); rView.RequestRepaint(pDescriptor); } } maEndOffset[nIndex] = Point(0,0); } if (eMode == controller::Animator::AM_Animated) RestartAnimation(); else mrAnimatorAccess.RemoveRun(shared_from_this()); } void PageObjectRun::RestartAnimation (void) { // Stop the current animation. if (mnAnimationId != controller::Animator::NotAnAnimationId) { mrAnimatorAccess.GetAnimator()->RemoveAnimation(mnAnimationId); } // Restart the animation. mrAnimatorAccess.AddRun(shared_from_this()); mnAnimationId = mrAnimatorAccess.GetAnimator()->AddAnimation( ::boost::ref(*this), 0, 300, ::boost::bind( &AnimatorAccess::RemoveRun, ::boost::ref(mrAnimatorAccess), shared_from_this())); } void PageObjectRun::operator () (const double nGlobalTime) { if (mnStartTime < 0) mnStartTime = nGlobalTime; double nLocalTime (nGlobalTime - mnStartTime); if (nLocalTime > 1.0) nLocalTime = 1.0; nLocalTime = maAccelerationFunction(nLocalTime); model::SlideSorterModel& rModel (mrAnimatorAccess.GetModel()); view::SlideSorterView& rView (mrAnimatorAccess.GetView()); for (sal_Int32 nIndex=mnStartIndex; nIndex<=mnEndIndex; ++nIndex) { model::SharedPageDescriptor pDescriptor (rModel.GetPageDescriptor(nIndex)); if ( ! pDescriptor) continue; const Rectangle aOldBoundingBox (pDescriptor->GetBoundingBox()); pDescriptor->GetVisualState().SetLocationOffset( Blend( maStartOffset[nIndex-mnStartIndex], maEndOffset[nIndex-mnStartIndex], nLocalTime)); // Request a repaint of the old and new bounding box (which largely overlap.) rView.RequestRepaint(aOldBoundingBox); rView.RequestRepaint(pDescriptor); } // Call Flush to make // a) animations a bit more smooth and // b) on Mac without the Flush a Reset of the page locations is not properly // visualized when the mouse leaves the window during drag-and-drop. mrAnimatorAccess.GetContentWindow()->Flush(); } } } } // end of namespace ::sd::slidesorter::view