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