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 #include "precompiled_sd.hxx"
25 
26 #include "view/SlsInsertAnimator.hxx"
27 #include "controller/SlideSorterController.hxx"
28 #include "controller/SlsAnimationFunction.hxx"
29 #include "view/SlideSorterView.hxx"
30 #include "view/SlsLayouter.hxx"
31 #include "model/SlideSorterModel.hxx"
32 #include "model/SlsPageEnumerationProvider.hxx"
33 
34 #include <set>
35 #include <boost/bind.hpp>
36 #include <boost/enable_shared_from_this.hpp>
37 
38 namespace sd { namespace slidesorter { namespace view {
39 
40 namespace {
41 
42 class PageObjectRun;
43 
44 class AnimatorAccess
45 {
46 public:
47     virtual void AddRun (const ::boost::shared_ptr<PageObjectRun> pRun) = 0;
48     virtual void RemoveRun (const ::boost::shared_ptr<PageObjectRun> pRun) = 0;
49     virtual model::SlideSorterModel& GetModel (void) const = 0;
50     virtual view::SlideSorterView& GetView (void) const = 0;
51     virtual ::boost::shared_ptr<controller::Animator> GetAnimator (void) = 0;
52     virtual SharedSdWindow GetContentWindow (void) = 0;
53 };
54 
55 
56 /** Controller of the position offsets of all page objects in one row or one
57     column.
58 */
59 class PageObjectRun : public ::boost::enable_shared_from_this<PageObjectRun>
60 {
61 public:
62     PageObjectRun (
63         AnimatorAccess& rAnimatorAccess,
64         const sal_Int32 nRunIndex,
65         const sal_Int32 nStartIndex,
66         const sal_Int32 nEndIndex);
67     ~PageObjectRun (void);
68 
69     void operator () (const double nTime);
70 
71     void UpdateOffsets(
72         const InsertPosition& rInsertPosition,
73         const view::Layouter& GetLayouter);
74     void ResetOffsets (const controller::Animator::AnimationMode eMode);
75 
76     /// Index of the row or column that this run represents.
77     sal_Int32 mnRunIndex;
78     /// The index at which to make place for the insertion indicator (-1 for
79     /// no indicator).
80     sal_Int32 mnLocalInsertIndex;
81     /// Index of the first page in the run.
82     sal_Int32 mnStartIndex;
83     /// Index of the last page in the run.
84     sal_Int32 mnEndIndex;
85     /// Offset of each item in the run at the start of the current animation.
86     ::std::vector<Point> maStartOffset;
87     /// Target offset of each item in the run at the end of the current animation.
88     ::std::vector<Point> maEndOffset;
89     /// Time at which the current animation started.
90     double mnStartTime;
91 
92     class Comparator
93     {
operator ()(const::boost::shared_ptr<PageObjectRun> & rpRunA,const::boost::shared_ptr<PageObjectRun> & rpRunB) const94         public: bool operator() (
95             const ::boost::shared_ptr<PageObjectRun>& rpRunA,
96             const ::boost::shared_ptr<PageObjectRun>& rpRunB) const
97         {
98             return rpRunA->mnRunIndex < rpRunB->mnRunIndex;
99         }
100     };
101 private:
102     controller::Animator::AnimationId mnAnimationId;
103     AnimatorAccess& mrAnimatorAccess;
104     ::boost::function<double(double)> maAccelerationFunction;
105 
106     Rectangle GetInnerBoundingBox (
107         const view::Layouter& rLayouter,
108         const sal_Int32 nIndex) const;
109     void RestartAnimation (void);
110 };
111 typedef ::boost::shared_ptr<PageObjectRun> SharedPageObjectRun;
112 
113 
Blend(const Point & rPointA,const Point & rPointB,const double nT)114 Point Blend (const Point& rPointA, const Point& rPointB, const double nT)
115 {
116     return Point(
117         sal_Int32(rPointA.X() * (1-nT) + rPointB.X() * nT),
118         sal_Int32(rPointA.Y() * (1-nT) + rPointB.Y() * nT));
119 }
120 
121 } // end of anonymous namespace
122 
123 
124 
125 class InsertAnimator::Implementation : public AnimatorAccess
126 {
127 public:
128     Implementation (SlideSorter& rSlideSorter);
129     virtual ~Implementation (void);
130 
131     void SetInsertPosition (
132         const InsertPosition& rInsertPosition,
133         const controller::Animator::AnimationMode eAnimationMode);
134 
135     virtual void AddRun (const ::boost::shared_ptr<PageObjectRun> pRun);
136     virtual void RemoveRun (const ::boost::shared_ptr<PageObjectRun> pRun);
137 
GetModel(void) const138     virtual model::SlideSorterModel& GetModel (void) const { return mrModel; }
GetView(void) const139     virtual view::SlideSorterView& GetView (void) const { return mrView; }
GetAnimator(void)140     virtual ::boost::shared_ptr<controller::Animator> GetAnimator (void) { return mpAnimator; }
GetContentWindow(void)141     virtual SharedSdWindow GetContentWindow (void) { return mrSlideSorter.GetContentWindow(); }
142 
143 private:
144     model::SlideSorterModel& mrModel;
145     view::SlideSorterView& mrView;
146     SlideSorter& mrSlideSorter;
147     ::boost::shared_ptr<controller::Animator> mpAnimator;
148     typedef ::std::set<SharedPageObjectRun, PageObjectRun::Comparator> RunContainer;
149     RunContainer maRuns;
150     InsertPosition maInsertPosition;
151 
152     void StopAnimation (void);
153     SharedPageObjectRun GetRun (
154         view::Layouter& rLayouter,
155         const InsertPosition& rInsertPosition,
156         const bool bCreate = true);
157     RunContainer::const_iterator FindRun (const sal_Int32 nRunIndex) const;
158 };
159 
160 
161 
162 
163 
164 //===== InsertAnimator ========================================================
165 
InsertAnimator(SlideSorter & rSlideSorter)166 InsertAnimator::InsertAnimator (SlideSorter& rSlideSorter)
167     : mpImplementation(new Implementation(rSlideSorter))
168 {
169 }
170 
171 
172 
173 
SetInsertPosition(const InsertPosition & rInsertPosition)174 void InsertAnimator::SetInsertPosition (const InsertPosition& rInsertPosition)
175 {
176     mpImplementation->SetInsertPosition(rInsertPosition, controller::Animator::AM_Animated);
177 }
178 
179 
180 
181 
Reset(const controller::Animator::AnimationMode eMode)182 void InsertAnimator::Reset (const controller::Animator::AnimationMode eMode)
183 {
184     mpImplementation->SetInsertPosition(InsertPosition(), eMode);
185 }
186 
187 
188 
189 
190 //===== InsertAnimator::Implementation ========================================
191 
Implementation(SlideSorter & rSlideSorter)192 InsertAnimator::Implementation::Implementation (SlideSorter& rSlideSorter)
193     : mrModel(rSlideSorter.GetModel()),
194       mrView(rSlideSorter.GetView()),
195       mrSlideSorter(rSlideSorter),
196       mpAnimator(rSlideSorter.GetController().GetAnimator()),
197       maRuns(),
198       maInsertPosition()
199 {
200 }
201 
202 
203 
204 
~Implementation(void)205 InsertAnimator::Implementation::~Implementation (void)
206 {
207     SetInsertPosition(InsertPosition(), controller::Animator::AM_Immediate);
208 }
209 
210 
211 
212 
SetInsertPosition(const InsertPosition & rInsertPosition,const controller::Animator::AnimationMode eMode)213 void InsertAnimator::Implementation::SetInsertPosition (
214     const InsertPosition& rInsertPosition,
215     const controller::Animator::AnimationMode eMode)
216 {
217     if (maInsertPosition == rInsertPosition)
218         return;
219 
220     SharedPageObjectRun pOldRun (GetRun(mrView.GetLayouter(), maInsertPosition));
221     SharedPageObjectRun pCurrentRun (GetRun(mrView.GetLayouter(), rInsertPosition));
222     maInsertPosition = rInsertPosition;
223 
224     // When the new insert position is in a different run then move the page
225     // objects in the old run to their default positions.
226     if (pOldRun != pCurrentRun)
227     {
228         if (pOldRun)
229             pOldRun->ResetOffsets(eMode);
230     }
231 
232     if (pCurrentRun)
233     {
234         pCurrentRun->UpdateOffsets(rInsertPosition, mrView.GetLayouter());
235     }
236 }
237 
238 
239 
240 
GetRun(view::Layouter & rLayouter,const InsertPosition & rInsertPosition,const bool bCreate)241 SharedPageObjectRun InsertAnimator::Implementation::GetRun (
242     view::Layouter& rLayouter,
243     const InsertPosition& rInsertPosition,
244     const bool bCreate)
245 {
246     const sal_Int32 nRow (rInsertPosition.GetRow());
247     if (nRow < 0)
248         return SharedPageObjectRun();
249 
250     RunContainer::const_iterator iRun (maRuns.end());
251     if (rLayouter.GetColumnCount() == 1)
252     {
253         // There is only one run that contains all slides.
254         if (maRuns.empty() && bCreate)
255             maRuns.insert(SharedPageObjectRun(new PageObjectRun(
256                 *this,
257                 0,
258                 0,
259                 mrModel.GetPageCount()-1)));
260         iRun = maRuns.begin();
261     }
262     else
263     {
264         iRun = FindRun(nRow);
265         if (iRun == maRuns.end() && bCreate)
266         {
267             // Create a new run.
268             const sal_Int32 nStartIndex (rLayouter.GetIndex(nRow, 0));
269             const sal_Int32 nEndIndex (rLayouter.GetIndex(nRow, rLayouter.GetColumnCount()-1));
270             if (nStartIndex <= nEndIndex)
271             {
272                 iRun = maRuns.insert(SharedPageObjectRun(new PageObjectRun(
273                     *this,
274                     nRow,
275                     nStartIndex,
276                     nEndIndex))).first;
277                 OSL_ASSERT(iRun != maRuns.end());
278             }
279         }
280     }
281 
282     if (iRun != maRuns.end())
283         return *iRun;
284     else
285         return SharedPageObjectRun();
286 }
287 
288 
289 
290 
291 InsertAnimator::Implementation::RunContainer::const_iterator
FindRun(const sal_Int32 nRunIndex) const292     InsertAnimator::Implementation::FindRun (const sal_Int32 nRunIndex) const
293 {
294     return std::find_if(
295         maRuns.begin(),
296         maRuns.end(),
297         ::boost::bind(
298             ::std::equal_to<sal_Int32>(),
299             ::boost::bind(&PageObjectRun::mnRunIndex, _1),
300             nRunIndex));
301 }
302 
303 
304 
305 
AddRun(const::boost::shared_ptr<PageObjectRun> pRun)306 void InsertAnimator::Implementation::AddRun (const ::boost::shared_ptr<PageObjectRun> pRun)
307 {
308     if (pRun)
309     {
310         maRuns.insert(pRun);
311     }
312     else
313     {
314         OSL_ASSERT(pRun);
315     }
316 }
317 
318 
319 
320 
321 
RemoveRun(const::boost::shared_ptr<PageObjectRun> pRun)322 void InsertAnimator::Implementation::RemoveRun (const ::boost::shared_ptr<PageObjectRun> pRun)
323 {
324     if (pRun)
325     {
326         // Do not remove runs that show the space for the insertion indicator.
327         if (pRun->mnLocalInsertIndex == -1)
328         {
329             InsertAnimator::Implementation::RunContainer::const_iterator iRun (FindRun(pRun->mnRunIndex));
330             if (iRun != maRuns.end())
331             {
332                 OSL_ASSERT(*iRun == pRun);
333                 maRuns.erase(iRun);
334             }
335         }
336     }
337     else
338     {
339         OSL_ASSERT(pRun);
340     }
341 }
342 
343 
344 
345 
346 
347 //===== PageObjectRun =========================================================
348 
PageObjectRun(AnimatorAccess & rAnimatorAccess,const sal_Int32 nRunIndex,const sal_Int32 nStartIndex,const sal_Int32 nEndIndex)349 PageObjectRun::PageObjectRun (
350     AnimatorAccess& rAnimatorAccess,
351     const sal_Int32 nRunIndex,
352     const sal_Int32 nStartIndex,
353     const sal_Int32 nEndIndex)
354     : mnRunIndex(nRunIndex),
355       mnLocalInsertIndex(-1),
356       mnStartIndex(nStartIndex),
357       mnEndIndex(nEndIndex),
358       maStartOffset(),
359       maEndOffset(),
360       mnStartTime(-1),
361       mnAnimationId(controller::Animator::NotAnAnimationId),
362       mrAnimatorAccess(rAnimatorAccess),
363       maAccelerationFunction(
364           controller::AnimationParametricFunction(
365               controller::AnimationBezierFunction (0.1,0.7)))
366 {
367     maStartOffset.resize(nEndIndex - nStartIndex + 1);
368     maEndOffset.resize(nEndIndex - nStartIndex + 1);
369 }
370 
371 
372 
373 
~PageObjectRun(void)374 PageObjectRun::~PageObjectRun (void)
375 {
376 }
377 
378 
379 
380 
GetInnerBoundingBox(const view::Layouter & rLayouter,const sal_Int32 nIndex) const381 Rectangle PageObjectRun::GetInnerBoundingBox (
382     const view::Layouter& rLayouter,
383     const sal_Int32 nIndex) const
384 {
385     model::SharedPageDescriptor pDescriptor (
386         mrAnimatorAccess.GetModel().GetPageDescriptor(nIndex));
387     if (pDescriptor)
388         if (pDescriptor->HasState(model::PageDescriptor::ST_Selected))
389             return rLayouter.GetPageObjectLayouter()->GetBoundingBox(
390                 pDescriptor,
391                 PageObjectLayouter::PageObject,
392                 PageObjectLayouter::ModelCoordinateSystem);
393         else
394             return rLayouter.GetPageObjectLayouter()->GetBoundingBox(
395                 pDescriptor,
396                 PageObjectLayouter::Preview,
397                 PageObjectLayouter::ModelCoordinateSystem);
398     else
399         return Rectangle();
400 }
401 
402 
403 
404 
UpdateOffsets(const InsertPosition & rInsertPosition,const view::Layouter & rLayouter)405 void PageObjectRun::UpdateOffsets(
406     const InsertPosition& rInsertPosition,
407     const view::Layouter& rLayouter)
408 {
409     const bool bIsVertical (rLayouter.GetColumnCount()==1);
410     const sal_Int32 nLocalInsertIndex(bIsVertical
411         ? rInsertPosition.GetRow()
412         : rInsertPosition.GetColumn());
413     if (nLocalInsertIndex != mnLocalInsertIndex)
414     {
415         mnLocalInsertIndex = nLocalInsertIndex;
416 
417         model::SlideSorterModel& rModel (mrAnimatorAccess.GetModel());
418         const sal_Int32 nRunLength (mnEndIndex - mnStartIndex + 1);
419         for (sal_Int32 nIndex=0; nIndex<nRunLength; ++nIndex)
420         {
421             model::SharedPageDescriptor pDescriptor(rModel.GetPageDescriptor(nIndex+mnStartIndex));
422             if (pDescriptor)
423                 maStartOffset[nIndex] = pDescriptor->GetVisualState().GetLocationOffset();
424             maEndOffset[nIndex] = nIndex < mnLocalInsertIndex
425                 ? rInsertPosition.GetLeadingOffset()
426                 : rInsertPosition.GetTrailingOffset();
427             if (bIsVertical)
428                 maEndOffset[nIndex].X() = 0;
429             else
430                 maEndOffset[nIndex].Y() = 0;
431         }
432         RestartAnimation();
433     }
434 }
435 
436 
437 
438 
ResetOffsets(const controller::Animator::AnimationMode eMode)439 void PageObjectRun::ResetOffsets (const controller::Animator::AnimationMode eMode)
440 {
441     mnLocalInsertIndex = -1;
442     const sal_Int32 nRunLength (mnEndIndex - mnStartIndex + 1);
443     model::SlideSorterModel& rModel (mrAnimatorAccess.GetModel());
444     view::SlideSorterView& rView (mrAnimatorAccess.GetView());
445     for (sal_Int32 nIndex=0; nIndex<nRunLength; ++nIndex)
446     {
447         model::SharedPageDescriptor pDescriptor(rModel.GetPageDescriptor(nIndex+mnStartIndex));
448         if (pDescriptor)
449         {
450             if (eMode == controller::Animator::AM_Animated)
451                 maStartOffset[nIndex] = pDescriptor->GetVisualState().GetLocationOffset();
452             else
453             {
454                 const Rectangle aOldBoundingBox (pDescriptor->GetBoundingBox());
455                 pDescriptor->GetVisualState().SetLocationOffset(Point(0,0));
456                 rView.RequestRepaint(aOldBoundingBox);
457                 rView.RequestRepaint(pDescriptor);
458             }
459         }
460         maEndOffset[nIndex] = Point(0,0);
461     }
462     if (eMode == controller::Animator::AM_Animated)
463         RestartAnimation();
464     else
465         mrAnimatorAccess.RemoveRun(shared_from_this());
466 }
467 
468 
469 
470 
RestartAnimation(void)471 void PageObjectRun::RestartAnimation (void)
472 {
473     // Stop the current animation.
474     if (mnAnimationId != controller::Animator::NotAnAnimationId)
475     {
476         mrAnimatorAccess.GetAnimator()->RemoveAnimation(mnAnimationId);
477     }
478 
479     // Restart the animation.
480     mrAnimatorAccess.AddRun(shared_from_this());
481     mnAnimationId = mrAnimatorAccess.GetAnimator()->AddAnimation(
482         ::boost::ref(*this),
483         0,
484         300,
485         ::boost::bind(
486             &AnimatorAccess::RemoveRun,
487             ::boost::ref(mrAnimatorAccess),
488             shared_from_this()));
489 }
490 
491 
492 
493 
operator ()(const double nGlobalTime)494 void PageObjectRun::operator () (const double nGlobalTime)
495 {
496     if (mnStartTime < 0)
497         mnStartTime = nGlobalTime;
498 
499     double nLocalTime (nGlobalTime - mnStartTime);
500     if (nLocalTime > 1.0)
501         nLocalTime = 1.0;
502     nLocalTime = maAccelerationFunction(nLocalTime);
503 
504     model::SlideSorterModel& rModel (mrAnimatorAccess.GetModel());
505     view::SlideSorterView& rView (mrAnimatorAccess.GetView());
506     for (sal_Int32 nIndex=mnStartIndex; nIndex<=mnEndIndex; ++nIndex)
507     {
508         model::SharedPageDescriptor pDescriptor (rModel.GetPageDescriptor(nIndex));
509         if ( ! pDescriptor)
510             continue;
511         const Rectangle aOldBoundingBox (pDescriptor->GetBoundingBox());
512         pDescriptor->GetVisualState().SetLocationOffset(
513             Blend(
514                 maStartOffset[nIndex-mnStartIndex],
515                 maEndOffset[nIndex-mnStartIndex],
516                 nLocalTime));
517 
518         // Request a repaint of the old and new bounding box (which largely overlap.)
519         rView.RequestRepaint(aOldBoundingBox);
520         rView.RequestRepaint(pDescriptor);
521     }
522 
523     // Call Flush to make
524     // a) animations a bit more smooth and
525     // b) on Mac without the Flush a Reset of the page locations is not properly
526     // visualized when the mouse leaves the window during drag-and-drop.
527     mrAnimatorAccess.GetContentWindow()->Flush();
528 }
529 
530 
531 
532 
533 } } } // end of namespace ::sd::slidesorter::view
534