cid#1607171 Data race condition
[LibreOffice.git] / sd / source / ui / slidesorter / view / SlsInsertAnimator.cxx
blobf6a1d680034b562b298bc752d626e2bb9c83cf6c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <view/SlsInsertAnimator.hxx>
21 #include <controller/SlideSorterController.hxx>
22 #include <controller/SlsAnimationFunction.hxx>
23 #include <view/SlideSorterView.hxx>
24 #include <view/SlsLayouter.hxx>
25 #include <model/SlideSorterModel.hxx>
26 #include <SlideSorter.hxx>
27 #include <Window.hxx>
28 #include <osl/diagnose.h>
30 #include <memory>
31 #include <set>
33 namespace sd::slidesorter::view {
35 namespace {
37 class PageObjectRun;
39 class AnimatorAccess
41 public:
42 virtual void AddRun (const std::shared_ptr<PageObjectRun>& rRun) = 0;
43 virtual void RemoveRun (const std::shared_ptr<PageObjectRun>& rRun) = 0;
44 virtual model::SlideSorterModel& GetModel () const = 0;
45 virtual view::SlideSorterView& GetView () const = 0;
46 virtual std::shared_ptr<controller::Animator> GetAnimator () = 0;
47 virtual VclPtr<sd::Window> GetContentWindow () = 0;
49 protected:
50 ~AnimatorAccess() COVERITY_NOEXCEPT_FALSE {}
53 /** Controller of the position offsets of all page objects in one row or one
54 column.
56 class PageObjectRun : public std::enable_shared_from_this<PageObjectRun>
58 public:
59 PageObjectRun (
60 AnimatorAccess& rAnimatorAccess,
61 const sal_Int32 nRunIndex,
62 const sal_Int32 nStartIndex,
63 const sal_Int32 nEndIndex);
65 void operator () (const double nTime);
67 void UpdateOffsets(
68 const InsertPosition& rInsertPosition,
69 const view::Layouter& GetLayouter);
70 void ResetOffsets (const controller::Animator::AnimationMode eMode);
72 /// Index of the row or column that this run represents.
73 sal_Int32 mnRunIndex;
74 /// The index at which to make place for the insertion indicator (-1 for
75 /// no indicator).
76 sal_Int32 mnLocalInsertIndex;
77 /// Index of the first page in the run.
78 sal_Int32 mnStartIndex;
79 /// Index of the last page in the run.
80 sal_Int32 mnEndIndex;
81 /// Offset of each item in the run at the start of the current animation.
82 ::std::vector<Point> maStartOffset;
83 /// Target offset of each item in the run at the end of the current animation.
84 ::std::vector<Point> maEndOffset;
85 /// Time at which the current animation started.
86 double mnStartTime;
88 class Comparator
90 public: bool operator() (
91 const std::shared_ptr<PageObjectRun>& rpRunA,
92 const std::shared_ptr<PageObjectRun>& rpRunB) const
94 return rpRunA->mnRunIndex < rpRunB->mnRunIndex;
97 private:
98 controller::Animator::AnimationId mnAnimationId;
99 AnimatorAccess& mrAnimatorAccess;
100 ::std::function<double (double)> maAccelerationFunction;
102 void RestartAnimation();
104 typedef std::shared_ptr<PageObjectRun> SharedPageObjectRun;
106 Point Blend (const Point& rPointA, const Point& rPointB, const double nT)
108 return Point(
109 sal_Int32(rPointA.X() * (1-nT) + rPointB.X() * nT),
110 sal_Int32(rPointA.Y() * (1-nT) + rPointB.Y() * nT));
113 } // end of anonymous namespace
115 class InsertAnimator::Implementation : public AnimatorAccess
117 public:
118 explicit Implementation (SlideSorter& rSlideSorter);
119 virtual ~Implementation();
121 void SetInsertPosition (
122 const InsertPosition& rInsertPosition,
123 const controller::Animator::AnimationMode eAnimationMode);
125 virtual void AddRun (const std::shared_ptr<PageObjectRun>& rRun) override;
126 virtual void RemoveRun (const std::shared_ptr<PageObjectRun>& rRun) override;
128 virtual model::SlideSorterModel& GetModel() const override { return mrModel; }
129 virtual view::SlideSorterView& GetView() const override { return mrView; }
130 virtual std::shared_ptr<controller::Animator> GetAnimator() override { return mpAnimator; }
131 virtual VclPtr<sd::Window> GetContentWindow() override { return mrSlideSorter.GetContentWindow(); }
133 private:
134 model::SlideSorterModel& mrModel;
135 view::SlideSorterView& mrView;
136 SlideSorter& mrSlideSorter;
137 std::shared_ptr<controller::Animator> mpAnimator;
138 typedef ::std::set<SharedPageObjectRun, PageObjectRun::Comparator> RunContainer;
139 RunContainer maRuns;
140 InsertPosition maInsertPosition;
142 SharedPageObjectRun GetRun (
143 view::Layouter const & rLayouter,
144 const InsertPosition& rInsertPosition);
145 RunContainer::const_iterator FindRun (const sal_Int32 nRunIndex) const;
148 //===== InsertAnimator ========================================================
150 InsertAnimator::InsertAnimator (SlideSorter& rSlideSorter)
151 : mpImplementation(std::make_shared<Implementation>(rSlideSorter))
155 void InsertAnimator::SetInsertPosition (const InsertPosition& rInsertPosition)
157 mpImplementation->SetInsertPosition(rInsertPosition, controller::Animator::AM_Animated);
160 void InsertAnimator::Reset (const controller::Animator::AnimationMode eMode)
162 mpImplementation->SetInsertPosition(InsertPosition(), eMode);
165 //===== InsertAnimator::Implementation ========================================
167 InsertAnimator::Implementation::Implementation (SlideSorter& rSlideSorter)
168 : mrModel(rSlideSorter.GetModel()),
169 mrView(rSlideSorter.GetView()),
170 mrSlideSorter(rSlideSorter),
171 mpAnimator(rSlideSorter.GetController().GetAnimator())
175 InsertAnimator::Implementation::~Implementation()
177 SetInsertPosition(InsertPosition(), controller::Animator::AM_Immediate);
180 void InsertAnimator::Implementation::SetInsertPosition (
181 const InsertPosition& rInsertPosition,
182 const controller::Animator::AnimationMode eMode)
184 if (maInsertPosition == rInsertPosition)
185 return;
187 SharedPageObjectRun pOldRun (GetRun(mrView.GetLayouter(), maInsertPosition));
188 SharedPageObjectRun pCurrentRun (GetRun(mrView.GetLayouter(), rInsertPosition));
189 maInsertPosition = rInsertPosition;
191 // When the new insert position is in a different run then move the page
192 // objects in the old run to their default positions.
193 if (pOldRun != pCurrentRun && pOldRun)
194 pOldRun->ResetOffsets(eMode);
196 if (pCurrentRun)
198 pCurrentRun->UpdateOffsets(rInsertPosition, mrView.GetLayouter());
202 SharedPageObjectRun InsertAnimator::Implementation::GetRun (
203 view::Layouter const & rLayouter,
204 const InsertPosition& rInsertPosition)
206 const sal_Int32 nRow (rInsertPosition.GetRow());
207 if (nRow < 0)
208 return SharedPageObjectRun();
210 RunContainer::const_iterator iRun (maRuns.end());
211 if (rLayouter.GetColumnCount() == 1)
213 // There is only one run that contains all slides.
214 if (maRuns.empty())
215 maRuns.insert(std::make_shared<PageObjectRun>(
216 *this,
219 mrModel.GetPageCount()-1));
220 iRun = maRuns.begin();
222 else
224 iRun = FindRun(nRow);
225 if (iRun == maRuns.end())
227 // Create a new run.
228 const sal_Int32 nStartIndex (rLayouter.GetIndex(nRow, 0));
229 const sal_Int32 nEndIndex (rLayouter.GetIndex(nRow, rLayouter.GetColumnCount()-1));
230 if (nStartIndex <= nEndIndex)
232 iRun = maRuns.insert(std::make_shared<PageObjectRun>(
233 *this,
234 nRow,
235 nStartIndex,
236 nEndIndex)).first;
237 OSL_ASSERT(iRun != maRuns.end());
242 if (iRun != maRuns.end())
243 return *iRun;
244 else
245 return SharedPageObjectRun();
248 InsertAnimator::Implementation::RunContainer::const_iterator
249 InsertAnimator::Implementation::FindRun (const sal_Int32 nRunIndex) const
251 return std::find_if(
252 maRuns.begin(),
253 maRuns.end(),
254 [nRunIndex] (std::shared_ptr<PageObjectRun> const& rRun)
255 { return rRun->mnRunIndex == nRunIndex; });
258 void InsertAnimator::Implementation::AddRun (const std::shared_ptr<PageObjectRun>& rRun)
260 if (rRun)
262 maRuns.insert(rRun);
264 else
266 OSL_ASSERT(rRun);
270 void InsertAnimator::Implementation::RemoveRun (const std::shared_ptr<PageObjectRun>& rRun)
272 if (rRun)
274 // Do not remove runs that show the space for the insertion indicator.
275 if (rRun->mnLocalInsertIndex == -1)
277 InsertAnimator::Implementation::RunContainer::const_iterator iRun (FindRun(rRun->mnRunIndex));
278 if (iRun != maRuns.end())
280 OSL_ASSERT(*iRun == rRun);
281 maRuns.erase(iRun);
285 else
287 OSL_ASSERT(rRun);
291 //===== PageObjectRun =========================================================
293 PageObjectRun::PageObjectRun (
294 AnimatorAccess& rAnimatorAccess,
295 const sal_Int32 nRunIndex,
296 const sal_Int32 nStartIndex,
297 const sal_Int32 nEndIndex)
298 : mnRunIndex(nRunIndex),
299 mnLocalInsertIndex(-1),
300 mnStartIndex(nStartIndex),
301 mnEndIndex(nEndIndex),
302 mnStartTime(-1),
303 mnAnimationId(controller::Animator::NotAnAnimationId),
304 mrAnimatorAccess(rAnimatorAccess),
305 maAccelerationFunction(
306 controller::AnimationParametricFunction(
307 controller::AnimationBezierFunction (0.1,0.7)))
309 maStartOffset.resize(nEndIndex - nStartIndex + 1);
310 maEndOffset.resize(nEndIndex - nStartIndex + 1);
313 void PageObjectRun::UpdateOffsets(
314 const InsertPosition& rInsertPosition,
315 const view::Layouter& rLayouter)
317 const bool bIsVertical (rLayouter.GetColumnCount()==1);
318 const sal_Int32 nLocalInsertIndex(bIsVertical
319 ? rInsertPosition.GetRow()
320 : rInsertPosition.GetColumn());
321 if (nLocalInsertIndex == mnLocalInsertIndex)
322 return;
324 mnLocalInsertIndex = nLocalInsertIndex;
326 model::SlideSorterModel& rModel (mrAnimatorAccess.GetModel());
327 const sal_Int32 nRunLength (mnEndIndex - mnStartIndex + 1);
328 for (sal_Int32 nIndex=0; nIndex<nRunLength; ++nIndex)
330 model::SharedPageDescriptor pDescriptor(rModel.GetPageDescriptor(nIndex+mnStartIndex));
331 if (pDescriptor)
332 maStartOffset[nIndex] = pDescriptor->GetVisualState().GetLocationOffset();
333 maEndOffset[nIndex] = nIndex < mnLocalInsertIndex
334 ? rInsertPosition.GetLeadingOffset()
335 : rInsertPosition.GetTrailingOffset();
336 if (bIsVertical)
337 maEndOffset[nIndex].setX( 0 );
338 else
339 maEndOffset[nIndex].setY( 0 );
341 RestartAnimation();
344 void PageObjectRun::ResetOffsets (const controller::Animator::AnimationMode eMode)
346 mnLocalInsertIndex = -1;
347 const sal_Int32 nRunLength (mnEndIndex - mnStartIndex + 1);
348 model::SlideSorterModel& rModel (mrAnimatorAccess.GetModel());
349 view::SlideSorterView& rView (mrAnimatorAccess.GetView());
350 for (sal_Int32 nIndex=0; nIndex<nRunLength; ++nIndex)
352 model::SharedPageDescriptor pDescriptor(rModel.GetPageDescriptor(nIndex+mnStartIndex));
353 if (pDescriptor)
355 if (eMode == controller::Animator::AM_Animated)
356 maStartOffset[nIndex] = pDescriptor->GetVisualState().GetLocationOffset();
357 else
359 const ::tools::Rectangle aOldBoundingBox (pDescriptor->GetBoundingBox());
360 pDescriptor->GetVisualState().SetLocationOffset(Point(0,0));
361 rView.RequestRepaint(aOldBoundingBox);
362 rView.RequestRepaint(pDescriptor);
365 maEndOffset[nIndex] = Point(0,0);
367 if (eMode == controller::Animator::AM_Animated)
368 RestartAnimation();
369 else
370 mrAnimatorAccess.RemoveRun(shared_from_this());
373 void PageObjectRun::RestartAnimation()
375 // Stop the current animation.
376 if (mnAnimationId != controller::Animator::NotAnAnimationId)
378 mrAnimatorAccess.GetAnimator()->RemoveAnimation(mnAnimationId);
381 // Restart the animation.
382 mrAnimatorAccess.AddRun(shared_from_this());
383 auto sharedThis(shared_from_this());
384 mnAnimationId = mrAnimatorAccess.GetAnimator()->AddAnimation(
385 [this] (double const val) { (*this)(val); },
386 [sharedThis=std::move(sharedThis)] () { sharedThis->mrAnimatorAccess.RemoveRun(sharedThis); }
390 void PageObjectRun::operator () (const double nGlobalTime)
392 if (mnStartTime < 0)
393 mnStartTime = nGlobalTime;
395 double nLocalTime (nGlobalTime - mnStartTime);
396 if (nLocalTime > 1.0)
397 nLocalTime = 1.0;
398 nLocalTime = maAccelerationFunction(nLocalTime);
400 model::SlideSorterModel& rModel (mrAnimatorAccess.GetModel());
401 view::SlideSorterView& rView (mrAnimatorAccess.GetView());
402 for (sal_Int32 nIndex=mnStartIndex; nIndex<=mnEndIndex; ++nIndex)
404 model::SharedPageDescriptor pDescriptor (rModel.GetPageDescriptor(nIndex));
405 if ( ! pDescriptor)
406 continue;
407 const ::tools::Rectangle aOldBoundingBox (pDescriptor->GetBoundingBox());
408 pDescriptor->GetVisualState().SetLocationOffset(
409 Blend(
410 maStartOffset[nIndex-mnStartIndex],
411 maEndOffset[nIndex-mnStartIndex],
412 nLocalTime));
414 // Request a repaint of the old and new bounding box (which largely overlap.)
415 rView.RequestRepaint(aOldBoundingBox);
416 rView.RequestRepaint(pDescriptor);
419 // Call Flush to make
420 // a) animations a bit more smooth and
421 // b) on Mac without the Flush a Reset of the page locations is not properly
422 // visualized when the mouse leaves the window during drag-and-drop.
423 mrAnimatorAccess.GetContentWindow()->GetOutDev()->Flush();
426 } // end of namespace ::sd::slidesorter::view
428 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */