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