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