| /************************************************************** |
| * |
| * 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 "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 <set> |
| #include <boost/bind.hpp> |
| #include <boost/enable_shared_from_this.hpp> |
| |
| namespace sd { namespace slidesorter { namespace view { |
| |
| namespace { |
| |
| class PageObjectRun; |
| |
| class AnimatorAccess |
| { |
| public: |
| virtual void AddRun (const ::boost::shared_ptr<PageObjectRun> pRun) = 0; |
| virtual void RemoveRun (const ::boost::shared_ptr<PageObjectRun> pRun) = 0; |
| virtual model::SlideSorterModel& GetModel (void) const = 0; |
| virtual view::SlideSorterView& GetView (void) const = 0; |
| virtual ::boost::shared_ptr<controller::Animator> 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<PageObjectRun> |
| { |
| 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<Point> maStartOffset; |
| /// Target offset of each item in the run at the end of the current animation. |
| ::std::vector<Point> maEndOffset; |
| /// Time at which the current animation started. |
| double mnStartTime; |
| |
| class Comparator |
| { |
| public: bool operator() ( |
| const ::boost::shared_ptr<PageObjectRun>& rpRunA, |
| const ::boost::shared_ptr<PageObjectRun>& rpRunB) const |
| { |
| return rpRunA->mnRunIndex < rpRunB->mnRunIndex; |
| } |
| }; |
| private: |
| controller::Animator::AnimationId mnAnimationId; |
| AnimatorAccess& mrAnimatorAccess; |
| ::boost::function<double(double)> maAccelerationFunction; |
| |
| Rectangle GetInnerBoundingBox ( |
| const view::Layouter& rLayouter, |
| const sal_Int32 nIndex) const; |
| void RestartAnimation (void); |
| }; |
| typedef ::boost::shared_ptr<PageObjectRun> 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<PageObjectRun> pRun); |
| virtual void RemoveRun (const ::boost::shared_ptr<PageObjectRun> pRun); |
| |
| virtual model::SlideSorterModel& GetModel (void) const { return mrModel; } |
| virtual view::SlideSorterView& GetView (void) const { return mrView; } |
| virtual ::boost::shared_ptr<controller::Animator> GetAnimator (void) { return mpAnimator; } |
| virtual SharedSdWindow GetContentWindow (void) { return mrSlideSorter.GetContentWindow(); } |
| |
| private: |
| model::SlideSorterModel& mrModel; |
| view::SlideSorterView& mrView; |
| SlideSorter& mrSlideSorter; |
| ::boost::shared_ptr<controller::Animator> mpAnimator; |
| typedef ::std::set<SharedPageObjectRun, PageObjectRun::Comparator> 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<sal_Int32>(), |
| ::boost::bind(&PageObjectRun::mnRunIndex, _1), |
| nRunIndex)); |
| } |
| |
| |
| |
| |
| void InsertAnimator::Implementation::AddRun (const ::boost::shared_ptr<PageObjectRun> pRun) |
| { |
| if (pRun) |
| { |
| maRuns.insert(pRun); |
| } |
| else |
| { |
| OSL_ASSERT(pRun); |
| } |
| } |
| |
| |
| |
| |
| |
| void InsertAnimator::Implementation::RemoveRun (const ::boost::shared_ptr<PageObjectRun> 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; nIndex<nRunLength; ++nIndex) |
| { |
| model::SharedPageDescriptor pDescriptor(rModel.GetPageDescriptor(nIndex+mnStartIndex)); |
| if (pDescriptor) |
| maStartOffset[nIndex] = pDescriptor->GetVisualState().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; nIndex<nRunLength; ++nIndex) |
| { |
| model::SharedPageDescriptor pDescriptor(rModel.GetPageDescriptor(nIndex+mnStartIndex)); |
| if (pDescriptor) |
| { |
| if (eMode == controller::Animator::AM_Animated) |
| maStartOffset[nIndex] = pDescriptor->GetVisualState().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 |