5f1f7b85949671fdd23c5a83252a1e7647bcecbc
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / scrollable / item-view / item-view-impl.cpp
1 //
2 // Copyright (c) 2014 Samsung Electronics Co., Ltd.
3 //
4 // Licensed under the Flora License, Version 1.0 (the License);
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //     http://floralicense.org/license/
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an AS IS BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 // CLASS HEADER
18 #include <dali-toolkit/internal/controls/scrollable/item-view/item-view-impl.h>
19
20 // EXTERNAL INCLUDES
21 #include <algorithm>
22
23 // INTERNAL INCLUDES
24 #include <dali/public-api/events/mouse-wheel-event.h>
25 #include <dali-toolkit/public-api/controls/scrollable/item-view/item-factory.h>
26 #include <dali-toolkit/internal/controls/scrollable/scroll-connector-impl.h>
27
28 using namespace std;
29 using namespace Dali;
30
31 namespace // unnamed namespace
32 {
33
34 //Type registration
35 TypeRegistration mType( typeid(Toolkit::ItemView), typeid(Toolkit::Scrollable), NULL );
36
37 const float DEFAULT_MINIMUM_SWIPE_SPEED = 1.0f;
38 const float DEFAULT_MINIMUM_SWIPE_DISTANCE = 3.0f;
39 const float DEFAULT_MOUSE_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION = 0.1f;
40
41 const int DEFAULT_REFRESH_INTERVAL_MILLISECONDS = 50; // 20 updates per second
42 const int MOUSE_WHEEL_EVENT_FINISHED_TIME_OUT = 500;  // 0.5 second
43
44 const float DEFAULT_ANCHORING_DURATION = 1.0f;  // 1 second
45 const float DEFAULT_COLOR_VISIBILITY_REMOVE_TIME = 0.5f; // 0.5 second
46
47 const float MILLISECONDS_PER_SECONDS = 1000.0f;
48
49 const char* OVERSHOOT_OVERLAY_RIPPLE_IMAGE_PATH = DALI_IMAGE_DIR "overshoot_ripple.png";
50 const Rect<int> OVERSHOOT_BOUNCE_IMAGE_1_PIXEL_AREA( 0, 0, 720, 58 );
51 const Vector4 OVERSHOOT_OVERLAY_NINE_PATCH_BORDER(0.0f, 0.0f, 1.0f, 12.0f);
52 const float MAXIMUM_OVERSHOOT_HEIGHT = 36.0f;  // 36 pixels
53 const float DEFAULT_OVERSHOOT_ANIMATION_DURATION = 0.5f;  // 0.5 second
54 const float DEFAULT_KEYBOARD_FOCUS_SCROLL_DURATION = 0.2f;
55
56 const string LAYOUT_POSITION_PROPERTY_NAME( "item-view-layout-position" );
57 const string POSITION_PROPERTY_NAME( "item-view-position" );
58 const string MINIMUM_LAYOUT_POSITION_PROPERTY_NAME( "item-view-minimum-layout-position" );
59 const string SCROLL_SPEED_PROPERTY_NAME( "item-view-scroll-speed" );
60 const string SCROLL_DIRECTION_PROPERTY_NAME( "item-view-scroll-direction" );
61 const string OVERSHOOT_PROPERTY_NAME( "item-view-overshoot" );
62
63 // Functors which wrap constraint functions with stored item IDs
64
65 struct WrappedVector3Constraint
66 {
67   WrappedVector3Constraint(Toolkit::ItemLayout::Vector3Function wrapMe, unsigned int itemId)
68   : mWrapMe(wrapMe),
69     mItemId(itemId)
70   {
71   }
72
73   Vector3 operator()(const Vector3& current, const PropertyInput& layoutPosition, const PropertyInput& scrollSpeed, const PropertyInput& layoutSize)
74   {
75     float offsetLayoutPosition = layoutPosition.GetFloat() + static_cast<float>(mItemId);
76
77     return mWrapMe(current, offsetLayoutPosition, scrollSpeed.GetFloat(), layoutSize.GetVector3());
78   }
79
80   Toolkit::ItemLayout::Vector3Function mWrapMe;
81   unsigned int mItemId;
82 };
83
84 struct WrappedQuaternionConstraint
85 {
86   WrappedQuaternionConstraint(Toolkit::ItemLayout::QuaternionFunction wrapMe, unsigned int itemId)
87   : mWrapMe(wrapMe),
88     mItemId(itemId)
89   {
90   }
91
92   Quaternion operator()(const Quaternion& current, const PropertyInput& layoutPosition, const PropertyInput& scrollSpeed, const PropertyInput& layoutSize)
93   {
94     float offsetLayoutPosition = layoutPosition.GetFloat() + static_cast<float>(mItemId);
95
96     return mWrapMe(current, offsetLayoutPosition, scrollSpeed.GetFloat(), layoutSize.GetVector3());
97   }
98
99   Toolkit::ItemLayout::QuaternionFunction mWrapMe;
100   unsigned int mItemId;
101 };
102
103 struct WrappedVector4Constraint
104 {
105   WrappedVector4Constraint(Toolkit::ItemLayout::Vector4Function wrapMe, unsigned int itemId)
106   : mWrapMe(wrapMe),
107     mItemId(itemId)
108   {
109   }
110
111   Vector4 operator()(const Vector4& current, const PropertyInput& layoutPosition, const PropertyInput& scrollSpeed, const PropertyInput& layoutSize)
112   {
113     float offsetLayoutPosition = layoutPosition.GetFloat() + static_cast<float>(mItemId);
114
115     return mWrapMe(current, offsetLayoutPosition, scrollSpeed.GetFloat(), layoutSize.GetVector3());
116   }
117
118   Toolkit::ItemLayout::Vector4Function mWrapMe;
119   unsigned int mItemId;
120 };
121
122 struct WrappedBoolConstraint
123 {
124   WrappedBoolConstraint(Toolkit::ItemLayout::BoolFunction wrapMe, unsigned int itemId)
125   : mWrapMe(wrapMe),
126     mItemId(itemId)
127   {
128   }
129
130   bool operator()(const bool& current, const PropertyInput& layoutPosition, const PropertyInput& scrollSpeed, const PropertyInput& layoutSize)
131   {
132     float offsetLayoutPosition = layoutPosition.GetFloat() + static_cast<float>(mItemId);
133
134     return mWrapMe(current, offsetLayoutPosition, scrollSpeed.GetFloat(), layoutSize.GetVector3());
135   }
136
137   Toolkit::ItemLayout::BoolFunction mWrapMe;
138   unsigned int mItemId;
139 };
140
141 /**
142  * Local helper to convert pan distance (in actor coordinates) to the layout-specific scrolling direction
143  */
144 float CalculateScrollDistance(Vector2 panDistance, Toolkit::ItemLayout& layout)
145 {
146   Radian scrollDirection(layout.GetScrollDirection());
147
148   float cosTheta = cosf(scrollDirection);
149   float sinTheta = sinf(scrollDirection);
150
151   return panDistance.x * sinTheta + panDistance.y * cosTheta;
152 }
153
154 // Overshoot overlay constraints
155
156 struct OvershootOverlaySizeConstraint
157 {
158   float operator()(const float& current,
159                      const PropertyInput& parentScrollDirectionProperty,
160                      const PropertyInput& parentOvershootProperty,
161                      const PropertyInput& parentSizeProperty)
162   {
163     const Vector3 parentScrollDirection = parentScrollDirectionProperty.GetVector3();
164     const Vector3 parentSize = parentSizeProperty.GetVector3();
165     const Toolkit::ControlOrientation::Type& parentOrientation = static_cast<Toolkit::ControlOrientation::Type>(parentScrollDirection.z);
166
167     float overlayWidth;
168
169     if(Toolkit::IsVertical(parentOrientation))
170     {
171       overlayWidth = fabsf(parentScrollDirection.y) > Math::MACHINE_EPSILON_1 ? parentSize.x : parentSize.y;
172     }
173     else
174     {
175       overlayWidth = fabsf(parentScrollDirection.x) > Math::MACHINE_EPSILON_1 ? parentSize.y : parentSize.x;
176     }
177
178     return overlayWidth;
179   }
180 };
181
182 struct OvershootOverlayRotationConstraint
183 {
184   Quaternion operator()(const Quaternion& current,
185                         const PropertyInput& parentScrollDirectionProperty,
186                         const PropertyInput& parentOvershootProperty)
187   {
188     const Vector3 parentScrollDirection = parentScrollDirectionProperty.GetVector3();
189     const float parentOvershoot = parentOvershootProperty.GetFloat();
190     const Toolkit::ControlOrientation::Type& parentOrientation = static_cast<Toolkit::ControlOrientation::Type>(parentScrollDirection.z);
191
192     Quaternion rotation;
193
194     if(Toolkit::IsVertical(parentOrientation))
195     {
196       if(fabsf(parentScrollDirection.y) <= Math::MACHINE_EPSILON_1)
197       {
198         if( (parentOrientation == Toolkit::ControlOrientation::Up && parentOvershoot < Math::MACHINE_EPSILON_0)
199             || (parentOrientation == Toolkit::ControlOrientation::Down && parentOvershoot > Math::MACHINE_EPSILON_0) )
200         {
201           rotation = Quaternion(0.5f * Math::PI, Vector3::ZAXIS);
202         }
203         else
204         {
205           rotation = Quaternion(1.5f * Math::PI, Vector3::ZAXIS);
206         }
207       }
208       else if( (parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.y > Math::MACHINE_EPSILON_0)
209             || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.y < Math::MACHINE_EPSILON_0) )
210       {
211         rotation = Quaternion(0.0f, Vector3::ZAXIS);
212       }
213       else
214       {
215         rotation = Quaternion(Math::PI, Vector3::ZAXIS);
216       }
217     }
218     else
219     {
220       if(fabsf(parentScrollDirection.x) <= Math::MACHINE_EPSILON_1)
221       {
222         if( (parentOrientation == Toolkit::ControlOrientation::Left && parentOvershoot > Math::MACHINE_EPSILON_0)
223             ||(parentOrientation == Toolkit::ControlOrientation::Right && parentOvershoot < Math::MACHINE_EPSILON_0) )
224         {
225           rotation = Quaternion(Math::PI, Vector3::ZAXIS);
226         }
227         else
228         {
229           rotation = Quaternion(0.0f, Vector3::ZAXIS);
230         }
231       }
232       else if( (parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.x > Math::MACHINE_EPSILON_0)
233             || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.x < Math::MACHINE_EPSILON_0) )
234       {
235         rotation = Quaternion(1.5f * Math::PI, Vector3::ZAXIS);
236       }
237       else
238       {
239         rotation = Quaternion(0.5f * Math::PI, Vector3::ZAXIS);
240       }
241     }
242
243     return rotation;
244   }
245 };
246
247 struct OvershootOverlayPositionConstraint
248 {
249   Vector3 operator()(const Vector3&    current,
250                      const PropertyInput& parentSizeProperty,
251                      const PropertyInput& parentScrollDirectionProperty,
252                      const PropertyInput& parentOvershootProperty)
253   {
254     const Vector3 parentScrollDirection = parentScrollDirectionProperty.GetVector3();
255     const float parentOvershoot = parentOvershootProperty.GetFloat();
256     const Vector3 parentSize = parentSizeProperty.GetVector3();
257     const Toolkit::ControlOrientation::Type& parentOrientation = static_cast<Toolkit::ControlOrientation::Type>(parentScrollDirection.z);
258
259     Vector3 relativeOffset;
260
261     if(Toolkit::IsVertical(parentOrientation))
262     {
263       if(fabsf(parentScrollDirection.y) <= Math::MACHINE_EPSILON_1)
264       {
265         if( (parentOrientation == Toolkit::ControlOrientation::Up && parentOvershoot < Math::MACHINE_EPSILON_0)
266             || (parentOrientation == Toolkit::ControlOrientation::Down && parentOvershoot > Math::MACHINE_EPSILON_0) )
267         {
268           relativeOffset = Vector3(1.0f, 0.0f, 0.0f);
269         }
270         else
271         {
272           relativeOffset =Vector3(0.0f, 1.0f, 0.0f);
273         }
274       }
275       else if( (parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.y > Math::MACHINE_EPSILON_0)
276             || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.y < Math::MACHINE_EPSILON_0) )
277       {
278         relativeOffset = Vector3(0.0f, 0.0f, 0.0f);
279       }
280       else
281       {
282         relativeOffset = Vector3(1.0f, 1.0f, 0.0f);
283       }
284     }
285     else
286     {
287       if(fabsf(parentScrollDirection.x) <= Math::MACHINE_EPSILON_1)
288       {
289         if( (parentOrientation == Toolkit::ControlOrientation::Left && parentOvershoot < Math::MACHINE_EPSILON_0)
290             || (parentOrientation == Toolkit::ControlOrientation::Right && parentOvershoot > Math::MACHINE_EPSILON_0) )
291         {
292           relativeOffset = Vector3(0.0f, 0.0f, 0.0f);
293         }
294         else
295         {
296           relativeOffset = Vector3(1.0f, 1.0f, 0.0f);
297         }
298       }
299       else if( (parentOvershoot > Math::MACHINE_EPSILON_0 && parentScrollDirection.x > Math::MACHINE_EPSILON_0)
300             || (parentOvershoot < Math::MACHINE_EPSILON_0 && parentScrollDirection.x < Math::MACHINE_EPSILON_0) )
301       {
302         relativeOffset = Vector3(0.0f, 1.0f, 0.0f);
303       }
304       else
305       {
306         relativeOffset = Vector3(1.0f, 0.0f, 0.0f);
307       }
308     }
309
310     return relativeOffset * parentSize;
311
312   }
313 };
314
315 struct OvershootOverlayVisibilityConstraint
316 {
317   bool operator()(const bool& current,
318                   const PropertyInput& parentLayoutScrollableProperty)
319   {
320     const bool parentLayoutScrollable = parentLayoutScrollableProperty.GetBoolean();
321
322     return parentLayoutScrollable;
323   }
324 };
325
326 /**
327  * Relative position Constraint
328  * Generates the relative position value of the item view based on the layout position,
329  * and it's relation to the layout domain. This is a value from 0.0f to 1.0f in each axis.
330  */
331 Vector3 RelativePositionConstraint(const Vector3& current,
332                                    const PropertyInput& scrollPositionProperty,
333                                    const PropertyInput& scrollMinProperty,
334                                    const PropertyInput& scrollMaxProperty,
335                                    const PropertyInput& layoutSizeProperty)
336 {
337   const Vector3& position = Vector3(0.0f, scrollPositionProperty.GetFloat(), 0.0f);
338   const Vector3& min = scrollMinProperty.GetVector3();
339   const Vector3& max = scrollMaxProperty.GetVector3();
340
341   Vector3 relativePosition;
342   Vector3 domainSize = max - min;
343
344   relativePosition.x = fabsf(domainSize.x) > Math::MACHINE_EPSILON_1 ? ((min.x - position.x) / fabsf(domainSize.x)) : 0.0f;
345   relativePosition.y = fabsf(domainSize.y) > Math::MACHINE_EPSILON_1 ? ((min.y - position.y) / fabsf(domainSize.y)) : 0.0f;
346
347   return relativePosition;
348 }
349
350 } // unnamed namespace
351
352 namespace Dali
353 {
354
355 namespace Toolkit
356 {
357
358 namespace Internal
359 {
360
361 namespace // unnamed namespace
362 {
363
364 bool FindById( const ItemContainer& items, ItemId id )
365 {
366   for( ConstItemIter iter = items.begin(); items.end() != iter; ++iter )
367   {
368     if( iter->first == id )
369     {
370       return true;
371     }
372   }
373
374   return false;
375 }
376
377 } // unnamed namespace
378
379 Dali::Toolkit::ItemView ItemView::New(ItemFactory& factory)
380 {
381   // Create the implementation
382   ItemViewPtr itemView(new ItemView(factory));
383
384   // Pass ownership to CustomActor via derived handle
385   Dali::Toolkit::ItemView handle(*itemView);
386
387   // Second-phase init of the implementation
388   // This can only be done after the CustomActor connection has been made...
389   itemView->Initialize();
390
391   return handle;
392 }
393
394 ItemView::ItemView(ItemFactory& factory)
395 : Scrollable(),
396   mItemFactory(factory),
397   mActiveLayout(NULL),
398   mDefaultAlphaFunction(Dali::Constraint::DEFAULT_ALPHA_FUNCTION),
399   mAnimatingOvershootOn(false),
400   mAnimateOvershootOff(false),
401   mAnchoringEnabled(true),
402   mAnchoringDuration(DEFAULT_ANCHORING_DURATION),
403   mRefreshIntervalMilliseconds(DEFAULT_REFRESH_INTERVAL_MILLISECONDS),
404   mRefreshOrderHint(true/*Refresh item 0 first*/),
405   mMinimumSwipeSpeed(DEFAULT_MINIMUM_SWIPE_SPEED),
406   mMinimumSwipeDistance(DEFAULT_MINIMUM_SWIPE_DISTANCE),
407   mScrollDistance(0.0f),
408   mScrollSpeed(0.0f),
409   mTotalPanDisplacement(Vector2::ZERO),
410   mScrollOvershoot(0.0f),
411   mIsFlicking(false),
412   mGestureState(Gesture::Clear)
413 {
414   SetRequiresMouseWheelEvents(true);
415   SetKeyboardNavigationSupport(true);
416 }
417
418 void ItemView::OnInitialize()
419 {
420   SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
421
422   RegisterCommonProperties();
423
424   Actor self = Self();
425
426   mOvershootEffect = OvershootRippleEffect::New();
427   Image overshootImage = Image::New( OVERSHOOT_OVERLAY_RIPPLE_IMAGE_PATH );
428   mOvershootOverlay = ImageActor::New( overshootImage );
429   mOvershootOverlay.SetParentOrigin(ParentOrigin::TOP_LEFT);
430   mOvershootOverlay.SetAnchorPoint(AnchorPoint::TOP_LEFT);
431   mOvershootOverlay.SetDrawMode(DrawMode::OVERLAY);
432   mOvershootOverlay.SetShaderEffect(mOvershootEffect);
433   mOvershootOverlay.SetPixelArea(OVERSHOOT_BOUNCE_IMAGE_1_PIXEL_AREA);
434   self.Add(mOvershootOverlay);
435
436   mScrollConnector = Dali::Toolkit::ScrollConnector::New();
437   mScrollPositionObject = mScrollConnector.GetScrollPositionObject();
438
439   mPropertyMinimumLayoutPosition = self.RegisterProperty(MINIMUM_LAYOUT_POSITION_PROPERTY_NAME, 0.0f);
440   mPropertyPosition = self.RegisterProperty(POSITION_PROPERTY_NAME, 0.0f);
441   mPropertyScrollSpeed = self.RegisterProperty(SCROLL_SPEED_PROPERTY_NAME, 0.0f);
442
443   ApplyOvershootOverlayConstraints();
444
445   Constraint constraint = Constraint::New<Vector3>(mPropertyRelativePosition,
446                                                    LocalSource(mPropertyPosition),
447                                                    LocalSource(mPropertyPositionMin),
448                                                    LocalSource(mPropertyPositionMax),
449                                                    LocalSource(Actor::SIZE),
450                                                    RelativePositionConstraint);
451   self.ApplyConstraint(constraint);
452
453   Vector2 stageSize = Stage::GetCurrent().GetSize();
454   mMouseWheelScrollDistanceStep = stageSize.y * DEFAULT_MOUSE_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION;
455
456   EnableGestureDetection(Gesture::Type(Gesture::Pan));
457
458   mMouseWheelEventFinishedTimer = Timer::New( MOUSE_WHEEL_EVENT_FINISHED_TIME_OUT );
459   mMouseWheelEventFinishedTimer.TickSignal().Connect( this, &ItemView::OnMouseWheelEventFinished );
460 }
461
462 ItemView::~ItemView()
463 {
464 }
465
466 Dali::Toolkit::ScrollConnector ItemView::GetScrollConnector() const
467 {
468   return mScrollConnector;
469 }
470
471 unsigned int ItemView::GetLayoutCount() const
472 {
473   return mLayouts.size();
474 }
475
476 void ItemView::AddLayout(ItemLayout& layout)
477 {
478   mLayouts.push_back(ItemLayoutPtr(&layout));
479 }
480
481 void ItemView::RemoveLayout(unsigned int layoutIndex)
482 {
483   DALI_ASSERT_ALWAYS(layoutIndex < mLayouts.size());
484
485   if (mActiveLayout == mLayouts[layoutIndex].Get())
486   {
487     mActiveLayout = NULL;
488   }
489
490   mLayouts.erase(mLayouts.begin() + layoutIndex);
491 }
492
493 ItemLayoutPtr ItemView::GetLayout(unsigned int layoutIndex) const
494 {
495   return mLayouts[layoutIndex];
496 }
497
498 ItemLayoutPtr ItemView::GetActiveLayout() const
499 {
500   return ItemLayoutPtr(mActiveLayout);
501 }
502
503 float ItemView::GetCurrentLayoutPosition(unsigned int itemId) const
504 {
505   return mScrollPositionObject.GetProperty<float>( ScrollConnector::SCROLL_POSITION ) + static_cast<float>( itemId );
506 }
507
508 void ItemView::ActivateLayout(unsigned int layoutIndex, const Vector3& targetSize, float durationSeconds)
509 {
510   DALI_ASSERT_ALWAYS(layoutIndex < mLayouts.size());
511
512   Actor self = Self();
513
514   // The ItemView size should match the active layout size
515   self.SetSize(targetSize);
516   mActiveLayoutTargetSize = targetSize;
517
518   // Switch to the new layout
519   mActiveLayout = mLayouts[layoutIndex].Get();
520
521   // Move the items to the new layout positions...
522
523   bool resizeAnimationNeeded(false);
524
525   for (ConstItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
526   {
527     unsigned int itemId = iter->first;
528     Actor actor = iter->second;
529
530     // Remove constraints from previous layout
531     actor.RemoveConstraints();
532
533     Vector3 size;
534     if(mActiveLayout->GetItemSize(itemId, targetSize, size))
535     {
536       if( durationSeconds > 0.0f )
537       {
538         // Use a size animation
539         if (!resizeAnimationNeeded)
540         {
541           resizeAnimationNeeded = true;
542           RemoveAnimation(mResizeAnimation);
543           mResizeAnimation = Animation::New(durationSeconds);
544         }
545
546         // The layout provides its own resize animation
547         mActiveLayout->GetResizeAnimation(mResizeAnimation, actor, size, durationSeconds);
548       }
549       else
550       {
551         // resize immediately
552         actor.SetSize(size);
553       }
554     }
555
556     ApplyConstraints(actor, *mActiveLayout, itemId, durationSeconds);
557   }
558
559   if (resizeAnimationNeeded)
560   {
561     mResizeAnimation.Play();
562   }
563
564   // Refresh the new layout
565   ItemRange range = GetItemRange(*mActiveLayout, targetSize, true/*reserve extra*/);
566   AddActorsWithinRange( range, durationSeconds );
567
568   // Scroll to an appropriate layout position
569
570   bool scrollAnimationNeeded(false);
571   float firstItemScrollPosition(0.0f);
572
573   float current = GetCurrentLayoutPosition(0);
574   float minimum = ClampFirstItemPosition(current, targetSize, *mActiveLayout);
575   self.SetProperty(mPropertyPosition, GetScrollPosition(current, targetSize));
576
577   if (current < minimum)
578   {
579     scrollAnimationNeeded = true;
580     firstItemScrollPosition = minimum;
581   }
582   else if (mAnchoringEnabled)
583   {
584     scrollAnimationNeeded = true;
585     firstItemScrollPosition = mActiveLayout->GetClosestAnchorPosition(current);
586   }
587
588   if (scrollAnimationNeeded)
589   {
590     RemoveAnimation(mScrollAnimation);
591     mScrollAnimation = Animation::New(mAnchoringDuration);
592     mScrollAnimation.AnimateTo( Property( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ), firstItemScrollPosition, AlphaFunctions::EaseOut );
593     mScrollAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(firstItemScrollPosition, targetSize), AlphaFunctions::EaseOut );
594     mScrollAnimation.Play();
595   }
596
597   self.SetProperty(mPropertyMinimumLayoutPosition, mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), targetSize));
598   AnimateScrollOvershoot(0.0f);
599   mScrollOvershoot = 0.0f;
600
601   Radian scrollDirection(mActiveLayout->GetScrollDirection());
602   float orientation = static_cast<float>(mActiveLayout->GetOrientation());
603   self.SetProperty(mPropertyScrollDirection, Vector3(sinf(scrollDirection), cosf(scrollDirection), orientation));
604
605   self.SetProperty(mPropertyScrollSpeed, mScrollSpeed);
606
607   CalculateDomainSize(targetSize);
608 }
609
610 void ItemView::DeactivateCurrentLayout()
611 {
612   if (mActiveLayout)
613   {
614     for (ConstItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
615     {
616       Actor actor = iter->second;
617       actor.RemoveConstraints();
618     }
619
620     mActiveLayout = NULL;
621   }
622
623   CancelRefreshTimer();
624 }
625
626 void ItemView::SetDefaultAlphaFunction(AlphaFunction func)
627 {
628   mDefaultAlphaFunction = func;
629 }
630
631 AlphaFunction ItemView::GetDefaultAlphaFunction() const
632 {
633   return mDefaultAlphaFunction;
634 }
635
636 bool ItemView::OnRefreshTick()
637 {
638   // Short-circuit if there is no active layout
639   if (!mActiveLayout)
640   {
641     return false;
642   }
643
644   ItemRange range = GetItemRange(*mActiveLayout, mActiveLayoutTargetSize, true/*reserve extra*/);
645
646   RemoveActorsOutsideRange( range );
647
648   AddActorsWithinRange( range, 0.0f/*immediate*/ );
649
650   // Keep refreshing whilst the layout is moving
651   return mScrollAnimation || (mGestureState == Gesture::Started || mGestureState == Gesture::Continuing);
652 }
653
654 void ItemView::SetMinimumSwipeSpeed(float speed)
655 {
656   mMinimumSwipeSpeed = speed;
657 }
658
659 float ItemView::GetMinimumSwipeSpeed() const
660 {
661   return mMinimumSwipeSpeed;
662 }
663
664 void ItemView::SetMinimumSwipeDistance(float distance)
665 {
666   mMinimumSwipeDistance = distance;
667 }
668
669 float ItemView::GetMinimumSwipeDistance() const
670 {
671   return mMinimumSwipeDistance;
672 }
673
674 void ItemView::SetMouseWheelScrollDistanceStep(float step)
675 {
676   mMouseWheelScrollDistanceStep = step;
677 }
678
679 float ItemView::GetMouseWheelScrollDistanceStep() const
680 {
681   return mMouseWheelScrollDistanceStep;
682 }
683
684 void ItemView::SetAnchoring(bool enabled)
685 {
686   mAnchoringEnabled = enabled;
687 }
688
689 bool ItemView::GetAnchoring() const
690 {
691   return mAnchoringEnabled;
692 }
693
694 void ItemView::SetAnchoringDuration(float durationSeconds)
695 {
696   mAnchoringDuration = durationSeconds;
697 }
698
699 float ItemView::GetAnchoringDuration() const
700 {
701   return mAnchoringDuration;
702 }
703
704 void ItemView::SetRefreshInterval(unsigned int intervalMilliseconds)
705 {
706   mRefreshIntervalMilliseconds = intervalMilliseconds;
707 }
708
709 unsigned int ItemView::GetRefreshInterval() const
710 {
711   return mRefreshIntervalMilliseconds;
712 }
713
714 Actor ItemView::GetItem(unsigned int itemId) const
715 {
716   Actor actor;
717
718   ConstItemPoolIter iter = mItemPool.find( itemId );
719   if( iter != mItemPool.end() )
720   {
721     actor = iter->second;
722   }
723
724   return actor;
725 }
726
727 unsigned int ItemView::GetItemId( Actor actor ) const
728 {
729   unsigned int itemId( 0 );
730
731   for ( ConstItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter )
732   {
733     if( iter->second == actor )
734     {
735       itemId = iter->first;
736       break;
737     }
738   }
739
740   return itemId;
741 }
742
743 void ItemView::InsertItem( Item newItem, float durationSeconds )
744 {
745   SetupActor( newItem, durationSeconds );
746   Self().Add( newItem.second );
747
748   ItemPoolIter foundIter = mItemPool.find( newItem.first );
749   if( mItemPool.end() != foundIter )
750   {
751     Actor moveMe = foundIter->second;
752     foundIter->second = newItem.second;
753
754     // Move the existing actors to make room
755     for( ItemPoolIter iter = ++foundIter; mItemPool.end() != iter; ++iter )
756     {
757       Actor temp = iter->second;
758       iter->second = moveMe;
759       moveMe = temp;
760
761       iter->second.RemoveConstraints();
762       ApplyConstraints( iter->second, *mActiveLayout, iter->first, durationSeconds );
763     }
764
765     // Create last item
766     ItemId lastId = mItemPool.rbegin()->first;
767     Item lastItem( lastId + 1, moveMe );
768     mItemPool.insert( lastItem );
769
770     lastItem.second.RemoveConstraints();
771     ApplyConstraints( lastItem.second, *mActiveLayout, lastItem.first, durationSeconds );
772   }
773   else
774   {
775     mItemPool.insert( newItem );
776   }
777 }
778
779 void ItemView::InsertItems( const ItemContainer& newItems, float durationSeconds )
780 {
781   // Insert from lowest id to highest
782   set<Item> sortedItems;
783   for( ConstItemIter iter = newItems.begin(); newItems.end() != iter; ++iter )
784   {
785     sortedItems.insert( *iter );
786   }
787
788   for( set<Item>::iterator iter = sortedItems.begin(); sortedItems.end() != iter; ++iter )
789   {
790     Self().Add( iter->second );
791
792     cout << "inserting item: " << iter->first << endl;
793
794     ItemPoolIter foundIter = mItemPool.find( iter->first );
795     if( mItemPool.end() != foundIter )
796     {
797       Actor moveMe = foundIter->second;
798       foundIter->second = iter->second;
799
800       // Move the existing actors to make room
801       for( ItemPoolIter iter = ++foundIter; mItemPool.end() != iter; ++iter )
802       {
803         Actor temp = iter->second;
804         iter->second = moveMe;
805         moveMe = temp;
806       }
807
808       // Create last item
809       ItemId lastId = mItemPool.rbegin()->first;
810       Item lastItem( lastId + 1, moveMe );
811       mItemPool.insert( lastItem );
812     }
813     else
814     {
815       mItemPool.insert( *iter );
816     }
817   }
818
819   // Relayout everything
820   for (ItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
821   {
822     // If newly inserted
823     if( FindById( newItems, iter->first ) )
824     {
825       SetupActor( *iter, durationSeconds );
826     }
827     else
828     {
829       iter->second.RemoveConstraints();
830       ApplyConstraints( iter->second, *mActiveLayout, iter->first, durationSeconds );
831     }
832   }
833 }
834
835 void ItemView::RemoveItem( unsigned int itemId, float durationSeconds )
836 {
837   bool actorRemoved = RemoveActor( itemId );
838   if( actorRemoved )
839   {
840     ReapplyAllConstraints( durationSeconds );
841   }
842 }
843
844 void ItemView::RemoveItems( const ItemIdContainer& itemIds, float durationSeconds )
845 {
846   bool actorRemoved( false );
847
848   // Remove from highest id to lowest
849   set<ItemId> sortedItems;
850   for( ConstItemIdIter iter = itemIds.begin(); itemIds.end() != iter; ++iter )
851   {
852     sortedItems.insert( *iter );
853   }
854
855   for( set<ItemId>::reverse_iterator iter = sortedItems.rbegin(); sortedItems.rend() != iter; ++iter )
856   {
857     if( RemoveActor( *iter ) )
858     {
859       actorRemoved = true;
860     }
861   }
862
863   if( actorRemoved )
864   {
865     ReapplyAllConstraints( durationSeconds );
866   }
867 }
868
869 bool ItemView::RemoveActor(unsigned int itemId)
870 {
871   bool removed( false );
872
873   const ItemPoolIter removeIter = mItemPool.find( itemId );
874
875   if( removeIter != mItemPool.end() )
876   {
877     Self().Remove( removeIter->second );
878     removed = true;
879
880     // Adjust the remaining item IDs, for example if item 2 is removed:
881     //   Initial actors:     After insert:
882     //     ID 1 - ActorA       ID 1 - ActorA
883     //     ID 2 - ActorB       ID 2 - ActorC (previously ID 3)
884     //     ID 3 - ActorC       ID 3 - ActorB (previously ID 4)
885     //     ID 4 - ActorD
886     for (ItemPoolIter iter = removeIter; iter != mItemPool.end(); ++iter)
887     {
888       if( iter->first < mItemPool.rbegin()->first )
889       {
890         iter->second = mItemPool[ iter->first + 1 ];
891       }
892       else
893       {
894         mItemPool.erase( iter );
895         break;
896       }
897     }
898   }
899
900   return removed;
901 }
902
903 void ItemView::ReplaceItem( Item replacementItem, float durationSeconds )
904 {
905   SetupActor( replacementItem, durationSeconds );
906   Self().Add( replacementItem.second );
907
908   const ItemPoolIter iter = mItemPool.find( replacementItem.first );
909   if( mItemPool.end() != iter )
910   {
911     Self().Remove( iter->second );
912     iter->second = replacementItem.second;
913   }
914   else
915   {
916     mItemPool.insert( replacementItem );
917   }
918 }
919
920 void ItemView::ReplaceItems( const ItemContainer& replacementItems, float durationSeconds )
921 {
922   for( ConstItemIter iter = replacementItems.begin(); replacementItems.end() != iter; ++iter )
923   {
924     ReplaceItem( *iter, durationSeconds );
925   }
926 }
927
928 void ItemView::RemoveActorsOutsideRange( ItemRange range )
929 {
930   // Remove unwanted actors from the ItemView & ItemPool
931   for (ItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); )
932   {
933     unsigned int current = iter->first;
934
935     if( ! range.Within( current ) )
936     {
937       Self().Remove( iter->second );
938
939       mItemPool.erase( iter++ ); // erase invalidates the return value of post-increment; iter remains valid
940     }
941     else
942     {
943       ++iter;
944     }
945   }
946 }
947
948 void ItemView::AddActorsWithinRange( ItemRange range, float durationSeconds )
949 {
950   range.end = min(mItemFactory.GetNumberOfItems(), range.end);
951
952   // The order of addition depends on the scroll direction.
953   if (mRefreshOrderHint)
954   {
955     for (unsigned int itemId = range.begin; itemId < range.end; ++itemId)
956     {
957       AddNewActor( itemId, durationSeconds );
958     }
959   }
960   else
961   {
962     for (unsigned int itemId = range.end; itemId > range.begin; --itemId)
963     {
964       AddNewActor( itemId-1, durationSeconds );
965     }
966   }
967 }
968
969 void ItemView::AddNewActor( unsigned int itemId, float durationSeconds )
970 {
971   if( mItemPool.end() == mItemPool.find( itemId ) )
972   {
973     Actor actor = mItemFactory.NewItem( itemId );
974
975     if( actor )
976     {
977       Item newItem( itemId, actor );
978
979       mItemPool.insert( newItem );
980
981       SetupActor( newItem, durationSeconds );
982       Self().Add( actor );
983     }
984   }
985 }
986
987 void ItemView::SetupActor( Item item, float durationSeconds )
988 {
989   item.second.SetParentOrigin( ParentOrigin::CENTER );
990   item.second.SetAnchorPoint( AnchorPoint::CENTER );
991
992   if( mActiveLayout )
993   {
994     Vector3 size;
995     if( mActiveLayout->GetItemSize( item.first, mActiveLayoutTargetSize, size ) )
996     {
997       item.second.SetSize( size );
998     }
999
1000     ApplyConstraints( item.second, *mActiveLayout, item.first, durationSeconds );
1001   }
1002 }
1003
1004 ItemRange ItemView::GetItemRange(ItemLayout& layout, const Vector3& layoutSize, bool reserveExtra)
1005 {
1006   unsigned int itemCount = mItemFactory.GetNumberOfItems();
1007
1008   ItemRange available(0u, itemCount);
1009
1010   ItemRange range = layout.GetItemsWithinArea( GetCurrentLayoutPosition(0), layoutSize );
1011
1012   if (reserveExtra)
1013   {
1014     // Add the reserve items for scrolling
1015     unsigned int extra = layout.GetReserveItemCount(layoutSize);
1016     range.begin = (range.begin >= extra) ? (range.begin - extra) : 0u;
1017     range.end += extra;
1018   }
1019
1020   return range.Intersection(available);
1021 }
1022
1023 bool ItemView::OnTouchEvent(const TouchEvent& event)
1024 {
1025   // Ignore events with multiple-touch points
1026   if (event.GetPointCount() != 1)
1027   {
1028     return false;
1029   }
1030
1031   if (event.GetPoint(0).state == TouchPoint::Down)
1032   {
1033     // Cancel ongoing scrolling etc.
1034     mGestureState = Gesture::Clear;
1035
1036     mScrollDistance = 0.0f;
1037     mScrollSpeed = 0.0f;
1038     Self().SetProperty(mPropertyScrollSpeed, mScrollSpeed);
1039
1040     mScrollOvershoot = 0.0f;
1041     AnimateScrollOvershoot(0.0f);
1042
1043     mScrollCompletedSignalV2.Emit(GetCurrentScrollPosition());
1044
1045     RemoveAnimation(mScrollAnimation);
1046   }
1047
1048   return true; // consume since we're potentially scrolling
1049 }
1050
1051 bool ItemView::OnMouseWheelEvent(const MouseWheelEvent& event)
1052 {
1053   // Respond the mouse wheel event to scroll
1054   if (mActiveLayout)
1055   {
1056     Actor self = Self();
1057     const Vector3 layoutSize = Self().GetCurrentSize();
1058     float layoutPositionDelta = GetCurrentLayoutPosition(0) + (event.z * mMouseWheelScrollDistanceStep * mActiveLayout->GetScrollSpeedFactor());
1059     float firstItemScrollPosition = ClampFirstItemPosition(layoutPositionDelta, layoutSize, *mActiveLayout);
1060
1061     mScrollPositionObject.SetProperty( ScrollConnector::SCROLL_POSITION, firstItemScrollPosition );
1062     self.SetProperty(mPropertyPosition, GetScrollPosition(firstItemScrollPosition, layoutSize));
1063     mScrollStartedSignalV2.Emit(GetCurrentScrollPosition());
1064     StartRefreshTimer();
1065   }
1066
1067   if (mMouseWheelEventFinishedTimer.IsRunning())
1068   {
1069     mMouseWheelEventFinishedTimer.Stop();
1070   }
1071
1072   mMouseWheelEventFinishedTimer.Start();
1073
1074   return true;
1075 }
1076
1077 bool ItemView::OnMouseWheelEventFinished()
1078 {
1079   if (mActiveLayout)
1080   {
1081     RemoveAnimation(mScrollAnimation);
1082
1083     // No more mouse wheel events coming. Do the anchoring if enabled.
1084     mScrollAnimation = DoAnchoring();
1085     if (mScrollAnimation)
1086     {
1087       StartRefreshTimer();
1088
1089       mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished);
1090       mScrollAnimation.Play();
1091     }
1092     else
1093     {
1094       mScrollOvershoot = 0.0f;
1095       AnimateScrollOvershoot(0.0f);
1096
1097       mScrollCompletedSignalV2.Emit(GetCurrentScrollPosition());
1098     }
1099   }
1100
1101   return false;
1102 }
1103
1104 void ItemView::ApplyConstraints(Actor& actor, ItemLayout& layout, unsigned int itemId, float duration)
1105 {
1106   ItemLayout::Vector3Function positionConstraint;
1107   if (layout.GetPositionConstraint(itemId, positionConstraint))
1108   {
1109     WrappedVector3Constraint wrapped(positionConstraint, itemId);
1110
1111     Constraint constraint = Constraint::New<Vector3>( Actor::POSITION,
1112                                                       Source( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ),
1113                                                       ParentSource( mPropertyScrollSpeed ),
1114                                                       ParentSource( Actor::SIZE ),
1115                                                       wrapped );
1116     constraint.SetApplyTime(duration);
1117     constraint.SetAlphaFunction(mDefaultAlphaFunction);
1118
1119     actor.ApplyConstraint(constraint);
1120   }
1121
1122   ItemLayout::QuaternionFunction rotationConstraint;
1123   if (layout.GetRotationConstraint(itemId, rotationConstraint))
1124   {
1125     WrappedQuaternionConstraint wrapped(rotationConstraint, itemId);
1126
1127     Constraint constraint = Constraint::New<Quaternion>( Actor::ROTATION,
1128                                                          Source( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ),
1129                                                          ParentSource( mPropertyScrollSpeed ),
1130                                                          ParentSource( Actor::SIZE ),
1131                                                          wrapped );
1132     constraint.SetApplyTime(duration);
1133     constraint.SetAlphaFunction(mDefaultAlphaFunction);
1134
1135     actor.ApplyConstraint(constraint);
1136   }
1137
1138   ItemLayout::Vector3Function scaleConstraint;
1139   if (layout.GetScaleConstraint(itemId, scaleConstraint))
1140   {
1141     WrappedVector3Constraint wrapped(scaleConstraint, itemId);
1142
1143     Constraint constraint = Constraint::New<Vector3>( Actor::SCALE,
1144                                                       Source( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ),
1145                                                       ParentSource( mPropertyScrollSpeed ),
1146                                                       ParentSource( Actor::SIZE ),
1147                                                       wrapped );
1148     constraint.SetApplyTime(duration);
1149     constraint.SetAlphaFunction(mDefaultAlphaFunction);
1150
1151     actor.ApplyConstraint(constraint);
1152   }
1153
1154   ItemLayout::Vector4Function colorConstraint;
1155   if (layout.GetColorConstraint(itemId, colorConstraint))
1156   {
1157     WrappedVector4Constraint wrapped(colorConstraint, itemId);
1158
1159     Constraint constraint = Constraint::New<Vector4>( Actor::COLOR,
1160                                                       Source( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ),
1161                                                       ParentSource( mPropertyScrollSpeed ),
1162                                                       ParentSource( Actor::SIZE ),
1163                                                       wrapped );
1164     constraint.SetApplyTime(duration);
1165     constraint.SetAlphaFunction(mDefaultAlphaFunction);
1166
1167     // Release color constraints slowly; this allows ItemView to co-exist with ImageActor fade-in
1168     constraint.SetRemoveTime(DEFAULT_COLOR_VISIBILITY_REMOVE_TIME);
1169     constraint.SetRemoveAction(Dali::Constraint::Discard);
1170
1171     actor.ApplyConstraint(constraint);
1172   }
1173
1174   ItemLayout::BoolFunction visibilityConstraint;
1175   if (layout.GetVisibilityConstraint(itemId, visibilityConstraint))
1176   {
1177     WrappedBoolConstraint wrapped(visibilityConstraint, itemId);
1178
1179     Constraint constraint = Constraint::New<bool>( Actor::VISIBLE,
1180                                                    Source( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ),
1181                                                    ParentSource( mPropertyScrollSpeed ),
1182                                                    ParentSource( Actor::SIZE ),
1183                                                    wrapped );
1184     constraint.SetApplyTime(duration);
1185     constraint.SetAlphaFunction(mDefaultAlphaFunction);
1186
1187     // Release visibility constraints the same time as the color constraint
1188     constraint.SetRemoveTime(DEFAULT_COLOR_VISIBILITY_REMOVE_TIME);
1189     constraint.SetRemoveAction(Dali::Constraint::Discard);
1190
1191     actor.ApplyConstraint(constraint);
1192   }
1193 }
1194
1195 void ItemView::ReapplyAllConstraints( float durationSeconds )
1196 {
1197   for (ConstItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
1198   {
1199     unsigned int id = iter->first;
1200     Actor actor = iter->second;
1201
1202     actor.RemoveConstraints();
1203     ApplyConstraints(actor, *mActiveLayout, id, durationSeconds);
1204   }
1205
1206   CalculateDomainSize(Self().GetCurrentSize());
1207 }
1208
1209 float ItemView::ClampFirstItemPosition(float targetPosition, const Vector3& targetSize, ItemLayout& layout)
1210 {
1211   Actor self = Self();
1212   float minLayoutPosition = layout.GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), targetSize);
1213   float clamppedPosition = min(0.0f, max(minLayoutPosition, targetPosition));
1214   mScrollOvershoot = targetPosition - clamppedPosition;
1215   self.SetProperty(mPropertyMinimumLayoutPosition, minLayoutPosition);
1216
1217   return clamppedPosition;
1218 }
1219
1220 void ItemView::OnPan(PanGesture gesture)
1221 {
1222   Actor self = Self();
1223   const Vector3 layoutSize = Self().GetCurrentSize();
1224
1225   RemoveAnimation(mScrollAnimation);
1226
1227   // Short-circuit if there is no active layout
1228   if (!mActiveLayout)
1229   {
1230     mGestureState = Gesture::Clear;
1231     return;
1232   }
1233
1234   mGestureState = gesture.state;
1235
1236   switch (mGestureState)
1237   {
1238     case Gesture::Finished:
1239     {
1240       // Swipe Detection
1241       if (fabsf(mScrollDistance) > mMinimumSwipeDistance &&
1242           mScrollSpeed > mMinimumSwipeSpeed)
1243       {
1244         float direction = (mScrollDistance < 0.0f) ? -1.0f : 1.0f;
1245
1246         mRefreshOrderHint = true;
1247
1248         float currentLayoutPosition = GetCurrentLayoutPosition(0);
1249         float firstItemScrollPosition = ClampFirstItemPosition(currentLayoutPosition + mScrollSpeed * direction,
1250                                                                layoutSize,
1251                                                                *mActiveLayout);
1252
1253         if (mAnchoringEnabled)
1254         {
1255           firstItemScrollPosition = mActiveLayout->GetClosestAnchorPosition(firstItemScrollPosition);
1256         }
1257
1258         RemoveAnimation(mScrollAnimation);
1259
1260         float flickAnimationDuration = mActiveLayout->GetItemFlickAnimationDuration() * max(1.0f, fabsf(firstItemScrollPosition - GetCurrentLayoutPosition(0)));
1261         mScrollAnimation = Animation::New(flickAnimationDuration);
1262         mScrollAnimation.AnimateTo( Property( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ), firstItemScrollPosition, AlphaFunctions::EaseOut );
1263         mScrollAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(firstItemScrollPosition, layoutSize), AlphaFunctions::EaseOut );
1264         mScrollAnimation.AnimateTo( Property(self, mPropertyScrollSpeed), 0.0f, AlphaFunctions::EaseOut );
1265
1266         mIsFlicking = true;
1267         // Check whether it has already scrolled to the end
1268         if(fabs(currentLayoutPosition - firstItemScrollPosition) > Math::MACHINE_EPSILON_0)
1269         {
1270           AnimateScrollOvershoot(0.0f);
1271         }
1272       }
1273
1274       // Anchoring may be triggered when there was no swipe
1275       if (!mScrollAnimation)
1276       {
1277         mScrollAnimation = DoAnchoring();
1278       }
1279
1280       // Reset the overshoot if no scroll animation.
1281       if (!mScrollAnimation)
1282       {
1283         mScrollCompletedSignalV2.Emit(GetCurrentScrollPosition());
1284
1285         AnimateScrollOvershoot(0.0f, false);
1286       }
1287     }
1288     break;
1289
1290     case Gesture::Started: // Fall through
1291     {
1292       mTotalPanDisplacement = Vector2::ZERO;
1293     }
1294
1295     case Gesture::Continuing:
1296     {
1297       mScrollDistance = CalculateScrollDistance(gesture.displacement, *mActiveLayout);
1298       mScrollSpeed = Clamp((gesture.GetSpeed() * mActiveLayout->GetScrollSpeedFactor() * MILLISECONDS_PER_SECONDS), 0.0f, mActiveLayout->GetMaximumSwipeSpeed());
1299
1300       // Refresh order depends on the direction of the scroll; negative is towards the last item.
1301       mRefreshOrderHint = mScrollDistance < 0.0f;
1302
1303       RemoveAnimation(mScrollSpeedAnimation);
1304       mScrollSpeedAnimation = Animation::New(0.3f);
1305       mScrollSpeedAnimation.AnimateTo( Property(self, mPropertyScrollSpeed), mScrollSpeed, AlphaFunctions::Linear );
1306       mScrollSpeedAnimation.Play();
1307
1308       float layoutPositionDelta = GetCurrentLayoutPosition(0) + (mScrollDistance * mActiveLayout->GetScrollSpeedFactor());
1309
1310       float firstItemScrollPosition = ClampFirstItemPosition(layoutPositionDelta, layoutSize, *mActiveLayout);
1311
1312       mScrollPositionObject.SetProperty( ScrollConnector::SCROLL_POSITION, firstItemScrollPosition );
1313       self.SetProperty(mPropertyPosition, GetScrollPosition(firstItemScrollPosition, layoutSize));
1314       mScrollStartedSignalV2.Emit(GetCurrentScrollPosition());
1315
1316       mTotalPanDisplacement += gesture.displacement;
1317       mScrollOvershoot = layoutPositionDelta - firstItemScrollPosition;
1318       if( mScrollOvershoot > Math::MACHINE_EPSILON_1 )
1319       {
1320         AnimateScrollOvershoot(1.0f);
1321       }
1322       else if( mScrollOvershoot < -Math::MACHINE_EPSILON_1 )
1323       {
1324         AnimateScrollOvershoot(-1.0f);
1325       }
1326       else
1327       {
1328         AnimateScrollOvershoot(0.0f);
1329       }
1330
1331       StartRefreshTimer();
1332     }
1333     break;
1334
1335     case Gesture::Cancelled:
1336     {
1337       mScrollAnimation = DoAnchoring();
1338     }
1339     break;
1340
1341     default:
1342       break;
1343   }
1344
1345   if (mScrollAnimation)
1346   {
1347     StartRefreshTimer();
1348
1349     mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished);
1350     mScrollAnimation.Play();
1351   }
1352 }
1353
1354 bool ItemView::OnAccessibilityPan(PanGesture gesture)
1355 {
1356   OnPan(gesture);
1357   return true;
1358 }
1359
1360 Actor ItemView::GetNextKeyboardFocusableActor(Actor actor, Control::KeyboardFocusNavigationDirection direction, bool loopEnabled)
1361 {
1362   Actor nextFocusActor;
1363   if(mActiveLayout)
1364   {
1365     int nextItemID = 0;
1366     if(!actor || actor == this->Self())
1367     {
1368       nextFocusActor = GetItem(nextItemID);
1369     }
1370     else if(actor && actor.GetParent() == this->Self())
1371     {
1372       int itemID = GetItemId(actor);
1373       nextItemID = mActiveLayout->GetNextFocusItemID(itemID, mItemFactory.GetNumberOfItems(), direction, loopEnabled);
1374       nextFocusActor = GetItem(nextItemID);
1375       if(nextFocusActor == actor)
1376       {
1377         // need to pass NULL actor back to focus manager
1378         nextFocusActor.Reset();
1379         return nextFocusActor;
1380       }
1381     }
1382     float layoutPosition = mActiveLayout->GetClosestAnchorPosition( GetCurrentLayoutPosition(0) );
1383     Vector3 layoutSize = Self().GetCurrentSize();
1384     if(!nextFocusActor)
1385     {
1386       // likely the current item is not buffered, so not in our item pool, probably best to get first viewable item
1387       ItemRange viewableItems = mActiveLayout->GetItemsWithinArea(layoutPosition, layoutSize);
1388       nextItemID = viewableItems.begin;
1389       nextFocusActor = GetItem(nextItemID);
1390     }
1391   }
1392   return nextFocusActor;
1393 }
1394
1395 void ItemView::OnKeyboardFocusChangeCommitted(Actor commitedFocusableActor)
1396 {
1397   // only in this function if our chosen focus actor was actually used
1398   if(commitedFocusableActor)
1399   {
1400     int nextItemID = GetItemId(commitedFocusableActor);
1401     float layoutPosition = GetCurrentLayoutPosition(0);
1402     Vector3 layoutSize = Self().GetCurrentSize();
1403     Vector3 focusItemPosition = Vector3::ZERO;
1404     ItemLayout::Vector3Function itemPositionConstraint;
1405     if (mActiveLayout->GetPositionConstraint(nextItemID, itemPositionConstraint))
1406     {
1407       focusItemPosition = itemPositionConstraint(Vector3::ZERO, layoutPosition + nextItemID, 0.0f, layoutSize);
1408     }
1409
1410     float scrollTo = mActiveLayout->GetClosestOnScreenLayoutPosition(nextItemID, layoutPosition, layoutSize);
1411     ScrollTo(Vector3(0.0f, scrollTo, 0.0f), DEFAULT_KEYBOARD_FOCUS_SCROLL_DURATION);
1412   }
1413 }
1414
1415 Animation ItemView::DoAnchoring()
1416 {
1417   Animation anchoringAnimation;
1418   Actor self = Self();
1419
1420   if (mActiveLayout && mAnchoringEnabled)
1421   {
1422     float anchorPosition = mActiveLayout->GetClosestAnchorPosition( GetCurrentLayoutPosition(0) );
1423
1424     anchoringAnimation = Animation::New(mAnchoringDuration);
1425     anchoringAnimation.AnimateTo( Property( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ), anchorPosition, AlphaFunctions::EaseOut );
1426     anchoringAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(anchorPosition, self.GetCurrentSize()), AlphaFunctions::EaseOut );
1427     anchoringAnimation.AnimateTo( Property(self, mPropertyScrollSpeed), 0.0f, AlphaFunctions::EaseOut );
1428     if(!mIsFlicking)
1429     {
1430       AnimateScrollOvershoot(0.0f);
1431     }
1432   }
1433
1434   return anchoringAnimation;
1435 }
1436
1437 void ItemView::OnScrollFinished(Animation& source)
1438 {
1439   Actor self = Self();
1440
1441   RemoveAnimation(mScrollAnimation); // mScrollAnimation is used to query whether we're scrolling
1442
1443   mScrollCompletedSignalV2.Emit(GetCurrentScrollPosition());
1444
1445   if(mIsFlicking && fabsf(mScrollOvershoot) > Math::MACHINE_EPSILON_1)
1446   {
1447     AnimateScrollOvershoot( mScrollOvershoot > 0.0f ? 1.0f : -1.0f, true);
1448   }
1449   else
1450   {
1451     // Reset the overshoot
1452     AnimateScrollOvershoot( 0.0f );
1453   }
1454   mIsFlicking = false;
1455
1456   mScrollOvershoot = 0.0f;
1457 }
1458
1459 void ItemView::OnOvershootOnFinished(Animation& animation)
1460 {
1461   mAnimatingOvershootOn = false;
1462   mScrollOvershootAnimation.FinishedSignal().Disconnect(this, &ItemView::OnOvershootOnFinished);
1463   RemoveAnimation(mScrollOvershootAnimation);
1464   if(mAnimateOvershootOff)
1465   {
1466     AnimateScrollOvershoot(0.0f);
1467   }
1468 }
1469
1470 void ItemView::StartRefreshTimer()
1471 {
1472   if (!mRefreshTimer)
1473   {
1474     mRefreshTimer = Timer::New( mRefreshIntervalMilliseconds );
1475     mRefreshTimer.TickSignal().Connect( this, &ItemView::OnRefreshTick );
1476   }
1477
1478   if (!mRefreshTimer.IsRunning())
1479   {
1480     mRefreshTimer.Start();
1481   }
1482 }
1483
1484 void ItemView::CancelRefreshTimer()
1485 {
1486   if (mRefreshTimer)
1487   {
1488     mRefreshTimer.Stop();
1489   }
1490 }
1491
1492 void ItemView::ScrollToItem(unsigned int itemId, float durationSeconds)
1493 {
1494   Actor self = Self();
1495   const Vector3 layoutSize = Self().GetCurrentSize();
1496   float firstItemScrollPosition = ClampFirstItemPosition(mActiveLayout->GetItemScrollToPosition(itemId), layoutSize, *mActiveLayout);
1497
1498   StartRefreshTimer();
1499
1500   if(durationSeconds > 0.0f)
1501   {
1502     RemoveAnimation(mScrollAnimation);
1503     mScrollAnimation = Animation::New(durationSeconds);
1504     mScrollAnimation.AnimateTo( Property( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ), firstItemScrollPosition, AlphaFunctions::EaseOut );
1505     mScrollAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(firstItemScrollPosition, layoutSize), AlphaFunctions::EaseOut );
1506     mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished);
1507     mScrollAnimation.Play();
1508   }
1509   else
1510   {
1511     mScrollPositionObject.SetProperty( ScrollConnector::SCROLL_POSITION, firstItemScrollPosition );
1512     AnimateScrollOvershoot(0.0f);
1513   }
1514
1515   mScrollStartedSignalV2.Emit(GetCurrentScrollPosition());
1516 }
1517
1518 void ItemView::RemoveAnimation(Animation& animation)
1519 {
1520   if(animation)
1521   {
1522     // Cease animating, and reset handle.
1523     animation.Clear();
1524     animation.Reset();
1525   }
1526 }
1527
1528 void ItemView::CalculateDomainSize(const Vector3& layoutSize)
1529 {
1530   Actor self = Self();
1531
1532   Vector3 firstItemPosition(Vector3::ZERO);
1533   Vector3 lastItemPosition(Vector3::ZERO);
1534
1535   if(mActiveLayout)
1536   {
1537     ItemLayout::Vector3Function firstItemPositionConstraint;
1538     if (mActiveLayout->GetPositionConstraint(0, firstItemPositionConstraint))
1539     {
1540       firstItemPosition = firstItemPositionConstraint(Vector3::ZERO, 0, 0.0f, layoutSize);
1541     }
1542
1543     float minLayoutPosition = mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), layoutSize);
1544     ItemLayout::Vector3Function lastItemPositionConstraint;
1545     if (mActiveLayout->GetPositionConstraint(fabs(minLayoutPosition), lastItemPositionConstraint))
1546     {
1547       lastItemPosition = lastItemPositionConstraint(Vector3::ZERO, fabs(minLayoutPosition), 0.0f, layoutSize);
1548     }
1549
1550     if(IsHorizontal(mActiveLayout->GetOrientation()))
1551     {
1552       self.SetProperty(mPropertyPositionMin, Vector3(0.0f, firstItemPosition.x, 0.0f));
1553       self.SetProperty(mPropertyPositionMax, Vector3(0.0f, lastItemPosition.x, 0.0f));
1554     }
1555     else
1556     {
1557       self.SetProperty(mPropertyPositionMin, Vector3(0.0f, firstItemPosition.y, 0.0f));
1558       self.SetProperty(mPropertyPositionMax, Vector3(0.0f, lastItemPosition.y, 0.0f));
1559     }
1560
1561     bool isLayoutScrollable = IsLayoutScrollable(layoutSize);
1562     self.SetProperty(mPropertyCanScrollVertical, isLayoutScrollable);
1563     self.SetProperty(mPropertyCanScrollHorizontal, false);
1564   }
1565 }
1566
1567 Vector3 ItemView::GetDomainSize() const
1568 {
1569   Actor self = Self();
1570
1571   float minScrollPosition = self.GetProperty<float>(mPropertyPositionMin);
1572   float maxScrollPosition = self.GetProperty<float>(mPropertyPositionMax);
1573
1574   return Vector3(0.0f, fabs(maxScrollPosition - minScrollPosition), 0.0f);
1575 }
1576
1577 bool ItemView::IsLayoutScrollable(const Vector3& layoutSize)
1578 {
1579   Actor self = Self();
1580
1581   float currentLayoutPosition = ClampFirstItemPosition( GetCurrentLayoutPosition(0), layoutSize, *mActiveLayout );
1582   float forwardClampedPosition = ClampFirstItemPosition(currentLayoutPosition + 1.0, layoutSize, *mActiveLayout);
1583   float backwardClampedPosition = ClampFirstItemPosition(currentLayoutPosition - 1.0, layoutSize, *mActiveLayout);
1584
1585   return (fabs(forwardClampedPosition - backwardClampedPosition) > Math::MACHINE_EPSILON_0);
1586 }
1587
1588 float ItemView::GetScrollPosition(float layoutPosition, const Vector3& layoutSize) const
1589 {
1590   Vector3 firstItemPosition(Vector3::ZERO);
1591   ItemLayout::Vector3Function firstItemPositionConstraint;
1592   if (mActiveLayout->GetPositionConstraint(0, firstItemPositionConstraint))
1593   {
1594     firstItemPosition = firstItemPositionConstraint(Vector3::ZERO, layoutPosition, 0.0f, layoutSize);
1595   }
1596
1597   return IsHorizontal(mActiveLayout->GetOrientation()) ? firstItemPosition.x: firstItemPosition.y;
1598 }
1599
1600 Vector3 ItemView::GetCurrentScrollPosition() const
1601 {
1602   float currentLayoutPosition = GetCurrentLayoutPosition(0);
1603   return Vector3(0.0f, GetScrollPosition(currentLayoutPosition, Self().GetCurrentSize()), 0.0f);
1604 }
1605
1606 void ItemView::AddOverlay(Actor actor)
1607 {
1608   Self().Add(actor);
1609 }
1610
1611 void ItemView::RemoveOverlay(Actor actor)
1612 {
1613   Self().Remove(actor);
1614 }
1615
1616 void ItemView::ScrollTo(const Vector3& position, float duration)
1617 {
1618   Actor self = Self();
1619   const Vector3 layoutSize = Self().GetCurrentSize();
1620
1621   float firstItemScrollPosition = ClampFirstItemPosition(position.y, layoutSize, *mActiveLayout);
1622
1623   StartRefreshTimer();
1624
1625   if(duration > 0.0f)
1626   {
1627     RemoveAnimation(mScrollAnimation);
1628     mScrollAnimation = Animation::New(duration);
1629     mScrollAnimation.AnimateTo( Property( mScrollPositionObject, ScrollConnector::SCROLL_POSITION ), firstItemScrollPosition, AlphaFunctions::EaseOut );
1630     mScrollAnimation.AnimateTo( Property(self, mPropertyPosition), GetScrollPosition(firstItemScrollPosition, layoutSize), AlphaFunctions::EaseOut );
1631     mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished);
1632     mScrollAnimation.Play();
1633   }
1634   else
1635   {
1636     mScrollPositionObject.SetProperty( ScrollConnector::SCROLL_POSITION, firstItemScrollPosition );
1637     AnimateScrollOvershoot(0.0f);
1638   }
1639
1640   mScrollStartedSignalV2.Emit(GetCurrentScrollPosition());
1641 }
1642
1643 void ItemView::ApplyOvershootOverlayConstraints()
1644 {
1645   Constraint constraint = Constraint::New<float>( Actor::SIZE_WIDTH,
1646                                                     ParentSource( mPropertyScrollDirection ),
1647                                                     Source( mScrollPositionObject, ScrollConnector::OVERSHOOT ),
1648                                                     ParentSource( Actor::SIZE ),
1649                                                     OvershootOverlaySizeConstraint() );
1650   mOvershootOverlay.ApplyConstraint(constraint);
1651   mOvershootOverlay.SetSize(OVERSHOOT_BOUNCE_IMAGE_1_PIXEL_AREA.width, OVERSHOOT_BOUNCE_IMAGE_1_PIXEL_AREA.height);
1652
1653   constraint = Constraint::New<Quaternion>( Actor::ROTATION,
1654                                             ParentSource( mPropertyScrollDirection ),
1655                                             Source( mScrollPositionObject, ScrollConnector::OVERSHOOT ),
1656                                             OvershootOverlayRotationConstraint() );
1657   mOvershootOverlay.ApplyConstraint(constraint);
1658
1659   constraint = Constraint::New<Vector3>( Actor::POSITION,
1660                                          ParentSource( Actor::SIZE ),
1661                                          ParentSource( mPropertyScrollDirection ),
1662                                          Source( mScrollPositionObject, ScrollConnector::OVERSHOOT ),
1663                                          OvershootOverlayPositionConstraint() );
1664   mOvershootOverlay.ApplyConstraint(constraint);
1665
1666   constraint = Constraint::New<bool>( Actor::VISIBLE,
1667                                       ParentSource( mPropertyCanScrollVertical ),
1668                                       OvershootOverlayVisibilityConstraint() );
1669   mOvershootOverlay.ApplyConstraint(constraint);
1670
1671   int effectOvershootPropertyIndex = mOvershootEffect.GetPropertyIndex(mOvershootEffect.GetOvershootPropertyName());
1672   Actor self = Self();
1673   constraint = Constraint::New<float>( effectOvershootPropertyIndex,
1674                                        Source( mScrollPositionObject, ScrollConnector::OVERSHOOT ),
1675                                        EqualToConstraint() );
1676   mOvershootEffect.ApplyConstraint(constraint);
1677 }
1678
1679 float ItemView::CalculateScrollOvershoot()
1680 {
1681   float overshoot = 0.0f;
1682
1683   if(mActiveLayout)
1684   {
1685     // The overshoot must be calculated from the accumulated pan gesture displacement
1686     // since the pan gesture starts.
1687     Actor self = Self();
1688     float scrollDistance = CalculateScrollDistance(mTotalPanDisplacement, *mActiveLayout) * mActiveLayout->GetScrollSpeedFactor();
1689     float positionDelta = GetCurrentLayoutPosition(0) + scrollDistance;
1690     float minLayoutPosition = mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), Self().GetCurrentSize());
1691     self.SetProperty(mPropertyMinimumLayoutPosition, minLayoutPosition);
1692     float clamppedPosition = min(0.0f, max(minLayoutPosition, positionDelta));
1693     overshoot = positionDelta - clamppedPosition;
1694   }
1695
1696   return overshoot;
1697 }
1698
1699 void ItemView::AnimateScrollOvershoot(float overshootAmount, bool animateBack)
1700 {
1701   bool animatingOn = fabsf(overshootAmount) > Math::MACHINE_EPSILON_1;
1702
1703   // make sure we animate back if needed
1704   mAnimateOvershootOff = animateBack || (!animatingOn && mAnimatingOvershootOn);
1705
1706   if( mAnimatingOvershootOn )
1707   {
1708     // animating on, do not allow animate off
1709     return;
1710   }
1711
1712   Actor self = Self();
1713   float currentOvershoot = mScrollPositionObject.GetProperty<float>(ScrollConnector::OVERSHOOT);
1714   float duration = DEFAULT_OVERSHOOT_ANIMATION_DURATION * (animatingOn ? (1.0f - fabsf(currentOvershoot)) : fabsf(currentOvershoot));
1715
1716   RemoveAnimation(mScrollOvershootAnimation);
1717   mScrollOvershootAnimation = Animation::New(duration);
1718   mScrollOvershootAnimation.FinishedSignal().Connect(this, &ItemView::OnOvershootOnFinished);
1719   mScrollOvershootAnimation.AnimateTo( Property(mScrollPositionObject, ScrollConnector::OVERSHOOT), overshootAmount, TimePeriod(0.0f, duration) );
1720   mScrollOvershootAnimation.Play();
1721
1722   mAnimatingOvershootOn = animatingOn;
1723 }
1724
1725 } // namespace Internal
1726
1727 } // namespace Toolkit
1728
1729 } // namespace Dali