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