Fixed typo in ImageView::GetNaturalSize().
[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   mActiveLayout(NULL),
293   mAnimatingOvershootOn(false),
294   mAnimateOvershootOff(false),
295   mAnchoringEnabled(false),
296   mAnchoringDuration(DEFAULT_ANCHORING_DURATION),
297   mRefreshIntervalLayoutPositions(0.0f),
298   mRefreshOrderHint(true/*Refresh item 0 first*/),
299   mMinimumSwipeSpeed(DEFAULT_MINIMUM_SWIPE_SPEED),
300   mMinimumSwipeDistance(DEFAULT_MINIMUM_SWIPE_DISTANCE),
301   mWheelScrollDistanceStep(0.0f),
302   mScrollDistance(0.0f),
303   mScrollSpeed(0.0f),
304   mTotalPanDisplacement(Vector2::ZERO),
305   mScrollOvershoot(0.0f),
306   mIsFlicking(false),
307   mGestureState(Gesture::Clear),
308   mAddingItems(false),
309   mRefreshEnabled(true),
310   mItemsParentOrigin( ParentOrigin::CENTER),
311   mItemsAnchorPoint( AnchorPoint::CENTER)
312 {
313 }
314
315 void ItemView::OnInitialize()
316 {
317   Actor self = Self();
318
319   Vector2 stageSize = Stage::GetCurrent().GetSize();
320   mWheelScrollDistanceStep = stageSize.y * DEFAULT_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION;
321
322   EnableGestureDetection(Gesture::Type(Gesture::Pan));
323
324   mWheelEventFinishedTimer = Timer::New( WHEEL_EVENT_FINISHED_TIME_OUT );
325   mWheelEventFinishedTimer.TickSignal().Connect( this, &ItemView::OnWheelEventFinished );
326
327   SetRefreshInterval(DEFAULT_REFRESH_INTERVAL_LAYOUT_POSITIONS);
328 }
329
330 ItemView::~ItemView()
331 {
332 }
333
334 unsigned int ItemView::GetLayoutCount() const
335 {
336   return mLayouts.size();
337 }
338
339 void ItemView::AddLayout(ItemLayout& layout)
340 {
341   mLayouts.push_back(ItemLayoutPtr(&layout));
342 }
343
344 void ItemView::RemoveLayout(unsigned int layoutIndex)
345 {
346   DALI_ASSERT_ALWAYS(layoutIndex < mLayouts.size());
347
348   if (mActiveLayout == mLayouts[layoutIndex].Get())
349   {
350     mActiveLayout = NULL;
351   }
352
353   mLayouts.erase(mLayouts.begin() + layoutIndex);
354 }
355
356 ItemLayoutPtr ItemView::GetLayout(unsigned int layoutIndex) const
357 {
358   return mLayouts[layoutIndex];
359 }
360
361 ItemLayoutPtr ItemView::GetActiveLayout() const
362 {
363   return ItemLayoutPtr(mActiveLayout);
364 }
365
366 float ItemView::GetCurrentLayoutPosition(unsigned int itemId) const
367 {
368   return Self().GetProperty<float>( Toolkit::ItemView::Property::LAYOUT_POSITION ) + static_cast<float>( itemId );
369 }
370
371 void ItemView::ActivateLayout(unsigned int layoutIndex, const Vector3& targetSize, float durationSeconds)
372 {
373   DALI_ASSERT_ALWAYS(layoutIndex < mLayouts.size());
374
375   mRefreshEnabled = false;
376
377   Actor self = Self();
378
379   // The ItemView size should match the active layout size
380   self.SetSize(targetSize);
381   mActiveLayoutTargetSize = targetSize;
382
383   // Switch to the new layout
384   mActiveLayout = mLayouts[layoutIndex].Get();
385
386   // Move the items to the new layout positions...
387
388   for (ConstItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
389   {
390     unsigned int itemId = iter->first;
391     Actor actor = iter->second;
392
393     // Remove constraints from previous layout
394     actor.RemoveConstraints();
395
396     Vector3 size;
397     mActiveLayout->GetItemSize( itemId, targetSize, size );
398     actor.SetSize( size.GetVectorXY() );
399
400     mActiveLayout->ApplyConstraints(actor, itemId, targetSize, Self() );
401   }
402
403   // Refresh the new layout
404   ItemRange range = GetItemRange(*mActiveLayout, targetSize, GetCurrentLayoutPosition(0), false/* don't reserve extra*/);
405   AddActorsWithinRange( range, targetSize );
406
407   // Scroll to an appropriate layout position
408
409   bool scrollAnimationNeeded(false);
410   float firstItemScrollPosition(0.0f);
411
412   float current = GetCurrentLayoutPosition(0);
413   float minimum = ClampFirstItemPosition(current, targetSize, *mActiveLayout);
414
415   if (current < minimum)
416   {
417     scrollAnimationNeeded = true;
418     firstItemScrollPosition = minimum;
419   }
420   else if (mAnchoringEnabled)
421   {
422     scrollAnimationNeeded = true;
423     firstItemScrollPosition = mActiveLayout->GetClosestAnchorPosition(current);
424   }
425
426   if (scrollAnimationNeeded)
427   {
428     RemoveAnimation(mScrollAnimation);
429     mScrollAnimation = Animation::New(durationSeconds);
430     mScrollAnimation.AnimateTo( Property(self, Toolkit::ItemView::Property::LAYOUT_POSITION), firstItemScrollPosition, AlphaFunction::EASE_OUT );
431     mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnLayoutActivationScrollFinished);
432     mScrollAnimation.Play();
433   }
434   else
435   {
436     // Emit the layout activated signal
437     mLayoutActivatedSignal.Emit();
438   }
439
440   AnimateScrollOvershoot(0.0f);
441   mScrollOvershoot = 0.0f;
442
443   Radian scrollDirection(mActiveLayout->GetScrollDirection());
444   self.SetProperty(Toolkit::ItemView::Property::SCROLL_DIRECTION, Vector2(sinf(scrollDirection), cosf(scrollDirection)));
445   self.SetProperty(Toolkit::ItemView::Property::LAYOUT_ORIENTATION, static_cast<int>(mActiveLayout->GetOrientation()));
446   self.SetProperty(Toolkit::ItemView::Property::SCROLL_SPEED, mScrollSpeed);
447
448   CalculateDomainSize(targetSize);
449 }
450
451 void ItemView::DeactivateCurrentLayout()
452 {
453   if (mActiveLayout)
454   {
455     for (ConstItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
456     {
457       Actor actor = iter->second;
458       actor.RemoveConstraints();
459     }
460
461     mActiveLayout = NULL;
462   }
463 }
464
465 void ItemView::OnRefreshNotification(PropertyNotification& source)
466 {
467   // Cancel scroll animation to prevent any fighting of setting the scroll position property by scroll bar during fast scroll.
468   if(!mRefreshEnabled && mScrollAnimation)
469   {
470     RemoveAnimation(mScrollAnimation);
471   }
472
473   // Only cache extra items when it is not a fast scroll
474   DoRefresh(GetCurrentLayoutPosition(0), mRefreshEnabled || mScrollAnimation);
475 }
476
477 void ItemView::Refresh()
478 {
479   for (ItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter )
480   {
481     ReleaseActor( iter->first, iter->second );
482   }
483   mItemPool.clear();
484
485   DoRefresh(GetCurrentLayoutPosition(0), true);
486 }
487
488 void ItemView::DoRefresh(float currentLayoutPosition, bool cacheExtra)
489 {
490   if (mActiveLayout)
491   {
492     ItemRange range = GetItemRange(*mActiveLayout, mActiveLayoutTargetSize, currentLayoutPosition, cacheExtra/*reserve extra*/);
493     RemoveActorsOutsideRange( range );
494     AddActorsWithinRange( range, Self().GetCurrentSize() );
495
496     mScrollUpdatedSignal.Emit( Vector2(0.0f, currentLayoutPosition) );
497   }
498 }
499
500 void ItemView::SetMinimumSwipeSpeed(float speed)
501 {
502   mMinimumSwipeSpeed = speed;
503 }
504
505 float ItemView::GetMinimumSwipeSpeed() const
506 {
507   return mMinimumSwipeSpeed;
508 }
509
510 void ItemView::SetMinimumSwipeDistance(float distance)
511 {
512   mMinimumSwipeDistance = distance;
513 }
514
515 float ItemView::GetMinimumSwipeDistance() const
516 {
517   return mMinimumSwipeDistance;
518 }
519
520 void ItemView::SetWheelScrollDistanceStep(float step)
521 {
522   mWheelScrollDistanceStep = step;
523 }
524
525 float ItemView::GetWheelScrollDistanceStep() const
526 {
527   return mWheelScrollDistanceStep;
528 }
529
530 void ItemView::SetAnchoring(bool enabled)
531 {
532   mAnchoringEnabled = enabled;
533 }
534
535 bool ItemView::GetAnchoring() const
536 {
537   return mAnchoringEnabled;
538 }
539
540 void ItemView::SetAnchoringDuration(float durationSeconds)
541 {
542   mAnchoringDuration = durationSeconds;
543 }
544
545 float ItemView::GetAnchoringDuration() const
546 {
547   return mAnchoringDuration;
548 }
549
550 void ItemView::SetRefreshInterval(float intervalLayoutPositions)
551 {
552   if( !Equals(mRefreshIntervalLayoutPositions, intervalLayoutPositions) )
553   {
554     mRefreshIntervalLayoutPositions = intervalLayoutPositions;
555
556     Actor self = Self();
557     if(mRefreshNotification)
558     {
559       self.RemovePropertyNotification(mRefreshNotification);
560     }
561     mRefreshNotification = self.AddPropertyNotification( Toolkit::ItemView::Property::LAYOUT_POSITION, StepCondition(mRefreshIntervalLayoutPositions, 0.0f) );
562     mRefreshNotification.NotifySignal().Connect( this, &ItemView::OnRefreshNotification );
563   }
564 }
565
566 float ItemView::GetRefreshInterval() const
567 {
568   return mRefreshIntervalLayoutPositions;
569 }
570
571 void ItemView::SetRefreshEnabled(bool enabled)
572 {
573   mRefreshEnabled = enabled;
574 }
575
576 Actor ItemView::GetItem(unsigned int itemId) const
577 {
578   Actor actor;
579
580   ConstItemPoolIter iter = mItemPool.find( itemId );
581   if( iter != mItemPool.end() )
582   {
583     actor = iter->second;
584   }
585
586   return actor;
587 }
588
589 unsigned int ItemView::GetItemId( Actor actor ) const
590 {
591   unsigned int itemId( 0 );
592
593   for ( ConstItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter )
594   {
595     if( iter->second == actor )
596     {
597       itemId = iter->first;
598       break;
599     }
600   }
601
602   return itemId;
603 }
604
605 void ItemView::InsertItem( Item newItem, float durationSeconds )
606 {
607   mAddingItems = true;
608   Vector3 layoutSize = Self().GetCurrentSize();
609
610   Actor displacedActor;
611   ItemPoolIter afterDisplacedIter = mItemPool.end();
612
613   ItemPoolIter foundIter = mItemPool.find( newItem.first );
614   if( mItemPool.end() != foundIter )
615   {
616     SetupActor( newItem, layoutSize );
617     Self().Add( newItem.second );
618
619     displacedActor = foundIter->second;
620     foundIter->second = newItem.second;
621
622     afterDisplacedIter = ++foundIter;
623   }
624   else
625   {
626     // Inserting before the existing item range?
627     ItemPoolIter iter = mItemPool.begin();
628     if( iter != mItemPool.end() &&
629         iter->first > newItem.first )
630     {
631       displacedActor = iter->second;
632       mItemPool.erase( iter++ ); // iter is still valid after the erase
633
634       afterDisplacedIter = iter;
635     }
636   }
637
638   if( displacedActor )
639   {
640     // Move the existing actors to make room
641     for( ItemPoolIter iter = afterDisplacedIter; mItemPool.end() != iter; ++iter )
642     {
643       Actor temp = iter->second;
644       iter->second = displacedActor;
645       displacedActor = temp;
646
647       iter->second.RemoveConstraints();
648       mActiveLayout->ApplyConstraints( iter->second, iter->first, layoutSize, Self() );
649     }
650
651     // Create last item
652     ItemPool::reverse_iterator lastIter = mItemPool.rbegin();
653     if ( lastIter != mItemPool.rend() )
654     {
655       ItemId lastId = lastIter->first;
656       Item lastItem( lastId + 1, displacedActor );
657       mItemPool.insert( lastItem );
658
659       lastItem.second.RemoveConstraints();
660       mActiveLayout->ApplyConstraints( lastItem.second, lastItem.first, layoutSize, Self() );
661     }
662   }
663
664   CalculateDomainSize( layoutSize );
665
666   mAddingItems = false;
667 }
668
669 void ItemView::InsertItems( const ItemContainer& newItems, float durationSeconds )
670 {
671   mAddingItems = true;
672   Vector3 layoutSize = Self().GetCurrentSize();
673
674   // Insert from lowest id to highest
675   std::set<Item> sortedItems;
676   for( ConstItemIter iter = newItems.begin(); newItems.end() != iter; ++iter )
677   {
678     sortedItems.insert( *iter );
679   }
680
681   for( std::set<Item>::iterator iter = sortedItems.begin(); sortedItems.end() != iter; ++iter )
682   {
683     Self().Add( iter->second );
684
685     ItemPoolIter foundIter = mItemPool.find( iter->first );
686     if( mItemPool.end() != foundIter )
687     {
688       Actor moveMe = foundIter->second;
689       foundIter->second = iter->second;
690
691       // Move the existing actors to make room
692       for( ItemPoolIter iter = ++foundIter; mItemPool.end() != iter; ++iter )
693       {
694         Actor temp = iter->second;
695         iter->second = moveMe;
696         moveMe = temp;
697       }
698
699       // Create last item
700       ItemId lastId = mItemPool.rbegin()->first;
701       Item lastItem( lastId + 1, moveMe );
702       mItemPool.insert( lastItem );
703     }
704     else
705     {
706       mItemPool.insert( *iter );
707     }
708   }
709
710   // Relayout everything
711   for (ItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
712   {
713     // If newly inserted
714     if( FindById( newItems, iter->first ) )
715     {
716       SetupActor( *iter, layoutSize );
717     }
718     else
719     {
720       iter->second.RemoveConstraints();
721       mActiveLayout->ApplyConstraints( iter->second, iter->first, layoutSize, Self() );
722     }
723   }
724
725   CalculateDomainSize( layoutSize );
726
727   mAddingItems = false;
728 }
729
730 void ItemView::RemoveItem( unsigned int itemId, float durationSeconds )
731 {
732   bool actorsReordered = RemoveActor( itemId );
733   if( actorsReordered )
734   {
735     ReapplyAllConstraints();
736
737     OnItemsRemoved();
738   }
739 }
740
741 void ItemView::RemoveItems( const ItemIdContainer& itemIds, float durationSeconds )
742 {
743   bool actorsReordered( false );
744
745   // Remove from highest id to lowest
746   set<ItemId> sortedItems;
747   for( ConstItemIdIter iter = itemIds.begin(); itemIds.end() != iter; ++iter )
748   {
749     sortedItems.insert( *iter );
750   }
751
752   for( set<ItemId>::reverse_iterator iter = sortedItems.rbegin(); sortedItems.rend() != iter; ++iter )
753   {
754     if( RemoveActor( *iter ) )
755     {
756       actorsReordered = true;
757     }
758   }
759
760   if( actorsReordered )
761   {
762     ReapplyAllConstraints();
763
764     OnItemsRemoved();
765   }
766 }
767
768 bool ItemView::RemoveActor(unsigned int itemId)
769 {
770   bool reordered( false );
771
772   ItemPoolIter removeIter = mItemPool.find( itemId );
773   if( removeIter != mItemPool.end() )
774   {
775     ReleaseActor(itemId, removeIter->second);
776   }
777   else
778   {
779     // Removing before the existing item range?
780     ItemPoolIter iter = mItemPool.begin();
781     if( iter != mItemPool.end() &&
782         iter->first > itemId )
783     {
784       // In order to decrement the first visible item ID
785       mItemPool.insert( Item(iter->first - 1, Actor()) );
786
787       removeIter = mItemPool.begin();
788     }
789   }
790
791   if( removeIter != mItemPool.end() )
792   {
793     reordered = true;
794
795     // Adjust the remaining item IDs, for example if item 2 is removed:
796     //   Initial actors:     After insert:
797     //     ID 1 - ActorA       ID 1 - ActorA
798     //     ID 2 - ActorB       ID 2 - ActorC (previously ID 3)
799     //     ID 3 - ActorC       ID 3 - ActorB (previously ID 4)
800     //     ID 4 - ActorD
801     for (ItemPoolIter iter = removeIter; iter != mItemPool.end(); ++iter)
802     {
803       if( iter->first < mItemPool.rbegin()->first )
804       {
805         iter->second = mItemPool[ iter->first + 1 ];
806       }
807       else
808       {
809         mItemPool.erase( iter );
810         break;
811       }
812     }
813   }
814
815   return reordered;
816 }
817
818 void ItemView::ReplaceItem( Item replacementItem, float durationSeconds )
819 {
820   mAddingItems = true;
821   Vector3 layoutSize = Self().GetCurrentSize();
822
823   SetupActor( replacementItem, layoutSize );
824   Self().Add( replacementItem.second );
825
826   const ItemPoolIter iter = mItemPool.find( replacementItem.first );
827   if( mItemPool.end() != iter )
828   {
829     ReleaseActor(iter->first, iter->second);
830     iter->second = replacementItem.second;
831   }
832   else
833   {
834     mItemPool.insert( replacementItem );
835   }
836
837   CalculateDomainSize( layoutSize );
838
839   mAddingItems = false;
840 }
841
842 void ItemView::ReplaceItems( const ItemContainer& replacementItems, float durationSeconds )
843 {
844   for( ConstItemIter iter = replacementItems.begin(); replacementItems.end() != iter; ++iter )
845   {
846     ReplaceItem( *iter, durationSeconds );
847   }
848 }
849
850 void ItemView::RemoveActorsOutsideRange( ItemRange range )
851 {
852   // Remove unwanted actors from the ItemView & ItemPool
853   for (ItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); )
854   {
855     unsigned int current = iter->first;
856
857     if( ! range.Within( current ) )
858     {
859       ReleaseActor(iter->first, iter->second);
860
861       mItemPool.erase( iter++ ); // erase invalidates the return value of post-increment; iter remains valid
862     }
863     else
864     {
865       ++iter;
866     }
867   }
868 }
869
870 void ItemView::AddActorsWithinRange( ItemRange range, const Vector3& layoutSize )
871 {
872   range.end = std::min(mItemFactory.GetNumberOfItems(), range.end);
873
874   // The order of addition depends on the scroll direction.
875   if (mRefreshOrderHint)
876   {
877     for (unsigned int itemId = range.begin; itemId < range.end; ++itemId)
878     {
879       AddNewActor( itemId, layoutSize );
880     }
881   }
882   else
883   {
884     for (unsigned int itemId = range.end; itemId > range.begin; --itemId)
885     {
886       AddNewActor( itemId-1, layoutSize );
887     }
888   }
889
890   // Total number of items may change dynamically.
891   // Always recalculate the domain size to reflect that.
892   CalculateDomainSize(Self().GetCurrentSize());
893 }
894
895 void ItemView::AddNewActor( unsigned int itemId, const Vector3& layoutSize )
896 {
897   mAddingItems = true;
898
899   if( mItemPool.end() == mItemPool.find( itemId ) )
900   {
901     Actor actor = mItemFactory.NewItem( itemId );
902
903     if( actor )
904     {
905       Item newItem( itemId, actor );
906
907       mItemPool.insert( newItem );
908
909       SetupActor( newItem, layoutSize );
910       Self().Add( actor );
911     }
912   }
913
914   mAddingItems = false;
915 }
916
917 void ItemView::SetupActor( Item item, const Vector3& layoutSize )
918 {
919   item.second.SetParentOrigin( mItemsParentOrigin );
920   item.second.SetAnchorPoint( mItemsAnchorPoint );
921
922   if( mActiveLayout )
923   {
924     Vector3 size;
925     mActiveLayout->GetItemSize( item.first, mActiveLayoutTargetSize, size );
926     item.second.SetSize( size.GetVectorXY() );
927
928     mActiveLayout->ApplyConstraints( item.second, item.first, layoutSize, Self() );
929   }
930 }
931
932 void ItemView::ReleaseActor( ItemId item, Actor actor )
933 {
934   Self().Remove( actor );
935   mItemFactory.ItemReleased(item, actor);
936 }
937
938 ItemRange ItemView::GetItemRange(ItemLayout& layout, const Vector3& layoutSize, float layoutPosition, bool reserveExtra)
939 {
940   unsigned int itemCount = mItemFactory.GetNumberOfItems();
941
942   ItemRange available(0u, itemCount);
943
944   ItemRange range = layout.GetItemsWithinArea( layoutPosition, layoutSize );
945
946   if (reserveExtra)
947   {
948     // Add the reserve items for scrolling
949     unsigned int extra = layout.GetReserveItemCount(layoutSize);
950     range.begin = (range.begin >= extra) ? (range.begin - extra) : 0u;
951     range.end += extra;
952   }
953
954   return range.Intersection(available);
955 }
956
957 void ItemView::OnChildAdd(Actor& child)
958 {
959   if(!mAddingItems)
960   {
961     // We don't want to do this downcast check for any item added by ItemView itself.
962     Dali::Toolkit::ScrollBar scrollBar = Dali::Toolkit::ScrollBar::DownCast(child);
963     if(scrollBar)
964     {
965       scrollBar.SetScrollPropertySource(Self(),
966                                         Toolkit::ItemView::Property::LAYOUT_POSITION,
967                                         Toolkit::Scrollable::Property::SCROLL_POSITION_MIN_Y,
968                                         Toolkit::Scrollable::Property::SCROLL_POSITION_MAX_Y,
969                                         Toolkit::ItemView::Property::SCROLL_CONTENT_SIZE);
970     }
971   }
972 }
973
974 bool ItemView::OnTouchEvent(const TouchEvent& event)
975 {
976   // Ignore events with multiple-touch points
977   if (event.GetPointCount() != 1)
978   {
979     return false;
980   }
981
982   if (event.GetPoint(0).state == TouchPoint::Down)
983   {
984     // Cancel ongoing scrolling etc.
985     mGestureState = Gesture::Clear;
986
987     mScrollDistance = 0.0f;
988     mScrollSpeed = 0.0f;
989     Self().SetProperty(Toolkit::ItemView::Property::SCROLL_SPEED, mScrollSpeed);
990
991     mScrollOvershoot = 0.0f;
992     AnimateScrollOvershoot(0.0f);
993
994     if(mScrollAnimation)
995     {
996       mScrollCompletedSignal.Emit(GetCurrentScrollPosition());
997     }
998
999     RemoveAnimation(mScrollAnimation);
1000   }
1001
1002   return true; // consume since we're potentially scrolling
1003 }
1004
1005 bool ItemView::OnWheelEvent(const WheelEvent& event)
1006 {
1007   // Respond the wheel event to scroll
1008   if (mActiveLayout)
1009   {
1010     Actor self = Self();
1011     const Vector3 layoutSize = Self().GetCurrentSize();
1012     float layoutPositionDelta = GetCurrentLayoutPosition(0) - (event.z * mWheelScrollDistanceStep * mActiveLayout->GetScrollSpeedFactor());
1013     float firstItemScrollPosition = ClampFirstItemPosition(layoutPositionDelta, layoutSize, *mActiveLayout);
1014
1015     self.SetProperty(Toolkit::ItemView::Property::LAYOUT_POSITION, firstItemScrollPosition );
1016
1017     mScrollStartedSignal.Emit(GetCurrentScrollPosition());
1018     mRefreshEnabled = true;
1019   }
1020
1021   if (mWheelEventFinishedTimer.IsRunning())
1022   {
1023     mWheelEventFinishedTimer.Stop();
1024   }
1025
1026   mWheelEventFinishedTimer.Start();
1027
1028   return true;
1029 }
1030
1031 bool ItemView::OnWheelEventFinished()
1032 {
1033   if (mActiveLayout)
1034   {
1035     RemoveAnimation(mScrollAnimation);
1036
1037     // No more wheel events coming. Do the anchoring if enabled.
1038     mScrollAnimation = DoAnchoring();
1039     if (mScrollAnimation)
1040     {
1041       mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished);
1042       mScrollAnimation.Play();
1043     }
1044     else
1045     {
1046       mScrollOvershoot = 0.0f;
1047       AnimateScrollOvershoot(0.0f);
1048
1049       mScrollCompletedSignal.Emit(GetCurrentScrollPosition());
1050     }
1051   }
1052
1053   return false;
1054 }
1055
1056 void ItemView::ReapplyAllConstraints()
1057 {
1058   Vector3 layoutSize = Self().GetCurrentSize();
1059
1060   for (ConstItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
1061   {
1062     unsigned int id = iter->first;
1063     Actor actor = iter->second;
1064
1065     actor.RemoveConstraints();
1066     mActiveLayout->ApplyConstraints(actor, id, layoutSize, Self());
1067   }
1068 }
1069
1070 void ItemView::OnItemsRemoved()
1071 {
1072   CalculateDomainSize(Self().GetCurrentSize());
1073
1074   // Adjust scroll-position after an item is removed
1075   if( mActiveLayout )
1076   {
1077     float firstItemScrollPosition = ClampFirstItemPosition(GetCurrentLayoutPosition(0), Self().GetCurrentSize(), *mActiveLayout);
1078     Self().SetProperty( Toolkit::ItemView::Property::LAYOUT_POSITION, firstItemScrollPosition );
1079   }
1080 }
1081
1082 float ItemView::ClampFirstItemPosition(float targetPosition, const Vector3& targetSize, ItemLayout& layout)
1083 {
1084   Actor self = Self();
1085   float minLayoutPosition = layout.GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), targetSize);
1086   float clamppedPosition = std::min(0.0f, std::max(minLayoutPosition, targetPosition));
1087   mScrollOvershoot = targetPosition - clamppedPosition;
1088   self.SetProperty(Toolkit::Scrollable::Property::SCROLL_POSITION_MAX, Vector2(0.0f, -minLayoutPosition));
1089
1090   return clamppedPosition;
1091 }
1092
1093 void ItemView::OnPan( const PanGesture& gesture )
1094 {
1095   Actor self = Self();
1096   const Vector3 layoutSize = Self().GetCurrentSize();
1097
1098   RemoveAnimation(mScrollAnimation);
1099
1100   // Short-circuit if there is no active layout
1101   if (!mActiveLayout)
1102   {
1103     mGestureState = Gesture::Clear;
1104     return;
1105   }
1106
1107   mGestureState = gesture.state;
1108
1109   switch (mGestureState)
1110   {
1111     case Gesture::Finished:
1112     {
1113       // Swipe Detection
1114       if (fabsf(mScrollDistance) > mMinimumSwipeDistance &&
1115           mScrollSpeed > mMinimumSwipeSpeed)
1116       {
1117         float direction = (mScrollDistance < 0.0f) ? -1.0f : 1.0f;
1118
1119         mRefreshOrderHint = true;
1120
1121         float currentLayoutPosition = GetCurrentLayoutPosition(0);
1122         float firstItemScrollPosition = ClampFirstItemPosition(currentLayoutPosition + mScrollSpeed * direction,
1123                                                                layoutSize,
1124                                                                *mActiveLayout);
1125
1126         if (mAnchoringEnabled)
1127         {
1128           firstItemScrollPosition = mActiveLayout->GetClosestAnchorPosition(firstItemScrollPosition);
1129         }
1130
1131         RemoveAnimation(mScrollAnimation);
1132
1133         float flickAnimationDuration = Clamp( mActiveLayout->GetItemFlickAnimationDuration() * std::max(1.0f, fabsf(firstItemScrollPosition - GetCurrentLayoutPosition(0)))
1134                                        , DEFAULT_MINIMUM_SWIPE_DURATION, DEFAULT_MAXIMUM_SWIPE_DURATION);
1135
1136         mScrollAnimation = Animation::New(flickAnimationDuration);
1137         mScrollAnimation.AnimateTo( Property(self, Toolkit::ItemView::Property::LAYOUT_POSITION ), firstItemScrollPosition, AlphaFunction::EASE_OUT );
1138         mScrollAnimation.AnimateTo( Property(self, Toolkit::ItemView::Property::SCROLL_SPEED), 0.0f, AlphaFunction::EASE_OUT );
1139
1140         mIsFlicking = true;
1141         // Check whether it has already scrolled to the end
1142         if(fabs(currentLayoutPosition - firstItemScrollPosition) > Math::MACHINE_EPSILON_0)
1143         {
1144           AnimateScrollOvershoot(0.0f);
1145         }
1146       }
1147
1148       // Anchoring may be triggered when there was no swipe
1149       if (!mScrollAnimation)
1150       {
1151         mScrollAnimation = DoAnchoring();
1152       }
1153
1154       // Reset the overshoot if no scroll animation.
1155       if (!mScrollAnimation)
1156       {
1157         mScrollCompletedSignal.Emit(GetCurrentScrollPosition());
1158
1159         AnimateScrollOvershoot(0.0f, false);
1160       }
1161     }
1162     break;
1163
1164     case Gesture::Started: // Fall through
1165     {
1166       mTotalPanDisplacement = Vector2::ZERO;
1167       mScrollStartedSignal.Emit(GetCurrentScrollPosition());
1168       mRefreshEnabled = true;
1169     }
1170
1171     case Gesture::Continuing:
1172     {
1173       mScrollDistance = CalculateScrollDistance(gesture.displacement, *mActiveLayout);
1174       mScrollSpeed = Clamp((gesture.GetSpeed() * gesture.GetSpeed() * mActiveLayout->GetFlickSpeedFactor() * MILLISECONDS_PER_SECONDS), 0.0f, mActiveLayout->GetMaximumSwipeSpeed());
1175
1176       // Refresh order depends on the direction of the scroll; negative is towards the last item.
1177       mRefreshOrderHint = mScrollDistance < 0.0f;
1178
1179       float layoutPositionDelta = GetCurrentLayoutPosition(0) + (mScrollDistance * mActiveLayout->GetScrollSpeedFactor());
1180
1181       float firstItemScrollPosition = ClampFirstItemPosition(layoutPositionDelta, layoutSize, *mActiveLayout);
1182
1183       float currentOvershoot = self.GetProperty<float>(Toolkit::ItemView::Property::OVERSHOOT);
1184
1185       self.SetProperty(Toolkit::ItemView::Property::LAYOUT_POSITION, firstItemScrollPosition );
1186
1187       if( ( firstItemScrollPosition >= 0.0f &&
1188             currentOvershoot < 1.0f ) ||
1189           ( firstItemScrollPosition <= mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), layoutSize) &&
1190             currentOvershoot > -1.0f ) )
1191       {
1192         mTotalPanDisplacement += gesture.displacement;
1193       }
1194
1195       mScrollOvershoot = CalculateScrollOvershoot();
1196       self.SetProperty( Toolkit::ItemView::Property::OVERSHOOT, mScrollOvershoot );
1197     }
1198     break;
1199
1200     case Gesture::Cancelled:
1201     {
1202       mScrollAnimation = DoAnchoring();
1203     }
1204     break;
1205
1206     default:
1207       break;
1208   }
1209
1210   if (mScrollAnimation)
1211   {
1212     mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished);
1213     mScrollAnimation.Play();
1214   }
1215 }
1216
1217 bool ItemView::OnAccessibilityPan(PanGesture gesture)
1218 {
1219   OnPan(gesture);
1220   return true;
1221 }
1222
1223 Actor ItemView::GetNextKeyboardFocusableActor(Actor actor, Toolkit::Control::KeyboardFocus::Direction direction, bool loopEnabled)
1224 {
1225   Actor nextFocusActor;
1226   if(mActiveLayout)
1227   {
1228     int nextItemID = 0;
1229     if(!actor || actor == this->Self())
1230     {
1231       nextFocusActor = GetItem(nextItemID);
1232     }
1233     else if(actor && actor.GetParent() == this->Self())
1234     {
1235       int itemID = GetItemId(actor);
1236       nextItemID = mActiveLayout->GetNextFocusItemID(itemID, mItemFactory.GetNumberOfItems(), direction, loopEnabled);
1237       nextFocusActor = GetItem(nextItemID);
1238       if(nextFocusActor == actor)
1239       {
1240         // need to pass NULL actor back to focus manager
1241         nextFocusActor.Reset();
1242         return nextFocusActor;
1243       }
1244     }
1245     float layoutPosition = mActiveLayout->GetClosestAnchorPosition( GetCurrentLayoutPosition(0) );
1246     Vector3 layoutSize = Self().GetCurrentSize();
1247     if(!nextFocusActor)
1248     {
1249       // likely the current item is not buffered, so not in our item pool, probably best to get first viewable item
1250       ItemRange viewableItems = mActiveLayout->GetItemsWithinArea(layoutPosition, layoutSize);
1251       nextItemID = viewableItems.begin;
1252       nextFocusActor = GetItem(nextItemID);
1253     }
1254   }
1255   return nextFocusActor;
1256 }
1257
1258 void ItemView::OnKeyboardFocusChangeCommitted(Actor commitedFocusableActor)
1259 {
1260   // only in this function if our chosen focus actor was actually used
1261   if(commitedFocusableActor)
1262   {
1263     int nextItemID = GetItemId(commitedFocusableActor);
1264     float layoutPosition = GetCurrentLayoutPosition(0);
1265     Vector3 layoutSize = Self().GetCurrentSize();
1266
1267     float scrollTo = mActiveLayout->GetClosestOnScreenLayoutPosition(nextItemID, layoutPosition, layoutSize);
1268     ScrollTo(Vector2(0.0f, scrollTo), DEFAULT_KEYBOARD_FOCUS_SCROLL_DURATION);
1269   }
1270 }
1271
1272 Animation ItemView::DoAnchoring()
1273 {
1274   Animation anchoringAnimation;
1275   Actor self = Self();
1276
1277   if (mActiveLayout && mAnchoringEnabled)
1278   {
1279     float anchorPosition = mActiveLayout->GetClosestAnchorPosition( GetCurrentLayoutPosition(0) );
1280
1281     anchoringAnimation = Animation::New(mAnchoringDuration);
1282     anchoringAnimation.AnimateTo( Property(self, Toolkit::ItemView::Property::LAYOUT_POSITION), anchorPosition, AlphaFunction::EASE_OUT );
1283     anchoringAnimation.AnimateTo( Property(self, Toolkit::ItemView::Property::SCROLL_SPEED), 0.0f, AlphaFunction::EASE_OUT );
1284     if(!mIsFlicking)
1285     {
1286       AnimateScrollOvershoot(0.0f);
1287     }
1288   }
1289
1290   return anchoringAnimation;
1291 }
1292
1293 void ItemView::OnScrollFinished(Animation& source)
1294 {
1295   Actor self = Self();
1296
1297   RemoveAnimation(mScrollAnimation); // mScrollAnimation is used to query whether we're scrolling
1298
1299   mScrollCompletedSignal.Emit(GetCurrentScrollPosition());
1300
1301   if(mIsFlicking && fabsf(mScrollOvershoot) > Math::MACHINE_EPSILON_1)
1302   {
1303     AnimateScrollOvershoot( mScrollOvershoot > 0.0f ? 1.0f : -1.0f, true);
1304   }
1305   else
1306   {
1307     // Reset the overshoot
1308     AnimateScrollOvershoot( 0.0f );
1309   }
1310   mIsFlicking = false;
1311
1312   mScrollOvershoot = 0.0f;
1313 }
1314
1315 void ItemView::OnLayoutActivationScrollFinished(Animation& source)
1316 {
1317   RemoveAnimation(mScrollAnimation);
1318   mRefreshEnabled = true;
1319   DoRefresh(GetCurrentLayoutPosition(0), true);
1320
1321   // Emit the layout activated signal
1322   mLayoutActivatedSignal.Emit();
1323 }
1324
1325 void ItemView::OnOvershootOnFinished(Animation& animation)
1326 {
1327   mAnimatingOvershootOn = false;
1328   mScrollOvershootAnimation.FinishedSignal().Disconnect(this, &ItemView::OnOvershootOnFinished);
1329   RemoveAnimation(mScrollOvershootAnimation);
1330   if(mAnimateOvershootOff)
1331   {
1332     AnimateScrollOvershoot(0.0f);
1333   }
1334 }
1335
1336 void ItemView::ScrollToItem(unsigned int itemId, float durationSeconds)
1337 {
1338   Actor self = Self();
1339   const Vector3 layoutSize = Self().GetCurrentSize();
1340   float firstItemScrollPosition = ClampFirstItemPosition(mActiveLayout->GetItemScrollToPosition(itemId), layoutSize, *mActiveLayout);
1341
1342   if(durationSeconds > 0.0f)
1343   {
1344     RemoveAnimation(mScrollAnimation);
1345     mScrollAnimation = Animation::New(durationSeconds);
1346     mScrollAnimation.AnimateTo( Property(self, Toolkit::ItemView::Property::LAYOUT_POSITION), firstItemScrollPosition, AlphaFunction::EASE_OUT );
1347     mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished);
1348     mScrollAnimation.Play();
1349   }
1350   else
1351   {
1352     self.SetProperty( Toolkit::ItemView::Property::LAYOUT_POSITION, firstItemScrollPosition );
1353     AnimateScrollOvershoot(0.0f);
1354   }
1355
1356   mScrollStartedSignal.Emit(GetCurrentScrollPosition());
1357   mRefreshEnabled = true;
1358 }
1359
1360 void ItemView::RemoveAnimation(Animation& animation)
1361 {
1362   if(animation)
1363   {
1364     // Cease animating, and reset handle.
1365     animation.Clear();
1366     animation.Reset();
1367   }
1368 }
1369
1370 void ItemView::CalculateDomainSize(const Vector3& layoutSize)
1371 {
1372   Actor self = Self();
1373
1374   Vector3 firstItemPosition(Vector3::ZERO);
1375   Vector3 lastItemPosition(Vector3::ZERO);
1376
1377   if(mActiveLayout)
1378   {
1379     firstItemPosition = mActiveLayout->GetItemPosition( 0,0,layoutSize );
1380
1381     float minLayoutPosition = mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), layoutSize);
1382     lastItemPosition = mActiveLayout->GetItemPosition( fabs(minLayoutPosition),fabs(minLayoutPosition),layoutSize );
1383
1384     float domainSize;
1385
1386     if(IsHorizontal(mActiveLayout->GetOrientation()))
1387     {
1388       domainSize = fabs(firstItemPosition.x - lastItemPosition.x);
1389     }
1390     else
1391     {
1392       domainSize = fabs(firstItemPosition.y - lastItemPosition.y);
1393     }
1394
1395     self.SetProperty(Toolkit::Scrollable::Property::SCROLL_POSITION_MIN, Vector2::ZERO);
1396     self.SetProperty(Toolkit::Scrollable::Property::SCROLL_POSITION_MAX, Vector2(0.0f, -minLayoutPosition));
1397
1398     self.SetProperty(Toolkit::ItemView::Property::SCROLL_CONTENT_SIZE, domainSize);
1399
1400     bool isLayoutScrollable = IsLayoutScrollable(layoutSize);
1401     self.SetProperty(Toolkit::Scrollable::Property::CAN_SCROLL_VERTICAL, isLayoutScrollable);
1402     self.SetProperty(Toolkit::Scrollable::Property::CAN_SCROLL_HORIZONTAL, false);
1403   }
1404 }
1405
1406 Vector2 ItemView::GetDomainSize() const
1407 {
1408   Actor self = Self();
1409
1410   float minScrollPosition = self.GetProperty<float>(Toolkit::Scrollable::Property::SCROLL_POSITION_MIN_Y);
1411   float maxScrollPosition = self.GetProperty<float>(Toolkit::Scrollable::Property::SCROLL_POSITION_MAX_Y);
1412
1413   return Vector2(0.0f, fabs(GetScrollPosition(minScrollPosition, self.GetCurrentSize()) - GetScrollPosition(-maxScrollPosition, self.GetCurrentSize())));
1414 }
1415
1416 bool ItemView::IsLayoutScrollable(const Vector3& layoutSize)
1417 {
1418   Actor self = Self();
1419
1420   float currentLayoutPosition = ClampFirstItemPosition( GetCurrentLayoutPosition(0), layoutSize, *mActiveLayout );
1421   float forwardClampedPosition = ClampFirstItemPosition(currentLayoutPosition + 1.0, layoutSize, *mActiveLayout);
1422   float backwardClampedPosition = ClampFirstItemPosition(currentLayoutPosition - 1.0, layoutSize, *mActiveLayout);
1423
1424   return (fabs(forwardClampedPosition - backwardClampedPosition) > Math::MACHINE_EPSILON_0);
1425 }
1426
1427 float ItemView::GetScrollPosition(float layoutPosition, const Vector3& layoutSize) const
1428 {
1429   Vector3 firstItemPosition( mActiveLayout->GetItemPosition(0, layoutPosition, layoutSize ) );
1430   return IsHorizontal(mActiveLayout->GetOrientation()) ? firstItemPosition.x: firstItemPosition.y;
1431 }
1432
1433 Vector2 ItemView::GetCurrentScrollPosition() const
1434 {
1435   return Vector2(0.0f, GetScrollPosition(GetCurrentLayoutPosition(0), Self().GetCurrentSize()));
1436 }
1437
1438 void ItemView::AddOverlay(Actor actor)
1439 {
1440   actor.SetDrawMode( DrawMode::OVERLAY_2D );
1441   Self().Add(actor);
1442 }
1443
1444 void ItemView::RemoveOverlay(Actor actor)
1445 {
1446   Self().Remove(actor);
1447 }
1448
1449 void ItemView::ScrollTo(const Vector2& position, float duration)
1450 {
1451   Actor self = Self();
1452   const Vector3 layoutSize = Self().GetCurrentSize();
1453
1454   float firstItemScrollPosition = ClampFirstItemPosition(position.y, layoutSize, *mActiveLayout);
1455
1456   if(duration > 0.0f)
1457   {
1458     RemoveAnimation(mScrollAnimation);
1459     mScrollAnimation = Animation::New(duration);
1460     mScrollAnimation.AnimateTo( Property(self, Toolkit::ItemView::Property::LAYOUT_POSITION), firstItemScrollPosition, AlphaFunction::EASE_OUT );
1461     mScrollAnimation.FinishedSignal().Connect(this, &ItemView::OnScrollFinished);
1462     mScrollAnimation.Play();
1463   }
1464   else
1465   {
1466     self.SetProperty( Toolkit::ItemView::Property::LAYOUT_POSITION, firstItemScrollPosition );
1467     AnimateScrollOvershoot(0.0f);
1468   }
1469
1470   mScrollStartedSignal.Emit(GetCurrentScrollPosition());
1471   mRefreshEnabled = true;
1472 }
1473
1474 void ItemView::SetOvershootEffectColor( const Vector4& color )
1475 {
1476   mOvershootEffectColor = color;
1477   if( mOvershootOverlay )
1478   {
1479     mOvershootOverlay.SetColor( color );
1480   }
1481 }
1482
1483 void ItemView::EnableScrollOvershoot( bool enable )
1484 {
1485   Actor self = Self();
1486   if( enable )
1487   {
1488     Property::Index effectOvershootPropertyIndex = Property::INVALID_INDEX;
1489     mOvershootOverlay = CreateBouncingEffectActor( effectOvershootPropertyIndex );
1490     mOvershootOverlay.SetColor(mOvershootEffectColor);
1491     mOvershootOverlay.SetParentOrigin(ParentOrigin::TOP_LEFT);
1492     mOvershootOverlay.SetAnchorPoint(AnchorPoint::TOP_LEFT);
1493     mOvershootOverlay.SetDrawMode( DrawMode::OVERLAY_2D );
1494     self.Add(mOvershootOverlay);
1495
1496     Constraint constraint = Constraint::New<Vector3>( mOvershootOverlay, Actor::Property::SIZE, OvershootOverlaySizeConstraint(mOvershootSize.height) );
1497     constraint.AddSource( ParentSource( Toolkit::ItemView::Property::SCROLL_DIRECTION ) );
1498     constraint.AddSource( ParentSource( Toolkit::ItemView::Property::LAYOUT_ORIENTATION ) );
1499     constraint.AddSource( ParentSource( Actor::Property::SIZE ) );
1500     constraint.Apply();
1501
1502     mOvershootOverlay.SetSize(mOvershootSize.width, mOvershootSize.height);
1503
1504     constraint = Constraint::New<Quaternion>( mOvershootOverlay, Actor::Property::ORIENTATION, OvershootOverlayRotationConstraint );
1505     constraint.AddSource( ParentSource( Toolkit::ItemView::Property::SCROLL_DIRECTION ) );
1506     constraint.AddSource( ParentSource( Toolkit::ItemView::Property::LAYOUT_ORIENTATION ) );
1507     constraint.AddSource( ParentSource( Toolkit::ItemView::Property::OVERSHOOT ) );
1508     constraint.Apply();
1509
1510     constraint = Constraint::New<Vector3>( mOvershootOverlay, Actor::Property::POSITION, OvershootOverlayPositionConstraint );
1511     constraint.AddSource( ParentSource( Actor::Property::SIZE ) );
1512     constraint.AddSource( ParentSource( Toolkit::ItemView::Property::SCROLL_DIRECTION ) );
1513     constraint.AddSource( ParentSource( Toolkit::ItemView::Property::LAYOUT_ORIENTATION ) );
1514     constraint.AddSource( ParentSource( Toolkit::ItemView::Property::OVERSHOOT ) );
1515     constraint.Apply();
1516
1517     constraint = Constraint::New<bool>( mOvershootOverlay, Actor::Property::VISIBLE, OvershootOverlayVisibilityConstraint );
1518     constraint.AddSource( ParentSource( Toolkit::Scrollable::Property::CAN_SCROLL_VERTICAL ) );
1519     constraint.Apply();
1520
1521     constraint = Constraint::New<float>( mOvershootOverlay, effectOvershootPropertyIndex, EqualToConstraint() );
1522     constraint.AddSource( ParentSource( Toolkit::ItemView::Property::OVERSHOOT ) );
1523     constraint.Apply();
1524   }
1525   else
1526   {
1527     if( mOvershootOverlay )
1528     {
1529       self.Remove(mOvershootOverlay);
1530       mOvershootOverlay.Reset();
1531     }
1532   }
1533 }
1534
1535 float ItemView::CalculateScrollOvershoot()
1536 {
1537   float overshoot = 0.0f;
1538
1539   if(mActiveLayout)
1540   {
1541     // The overshoot must be calculated from the accumulated pan gesture displacement
1542     // since the pan gesture starts.
1543     Actor self = Self();
1544     float scrollDistance = CalculateScrollDistance(mTotalPanDisplacement, *mActiveLayout) * mActiveLayout->GetScrollSpeedFactor();
1545     float positionDelta = GetCurrentLayoutPosition(0) + scrollDistance;
1546     float minLayoutPosition = mActiveLayout->GetMinimumLayoutPosition(mItemFactory.GetNumberOfItems(), Self().GetCurrentSize());
1547     self.SetProperty(Toolkit::Scrollable::Property::SCROLL_POSITION_MAX, Vector2(0.0f, -minLayoutPosition));
1548     float clamppedPosition = std::min(0.0f, std::max(minLayoutPosition, positionDelta));
1549     overshoot = positionDelta - clamppedPosition;
1550   }
1551
1552   return overshoot > 0.0f ? std::min(overshoot, 1.0f) : std::max(overshoot, -1.0f);
1553 }
1554
1555 void ItemView::AnimateScrollOvershoot(float overshootAmount, bool animateBack)
1556 {
1557   bool animatingOn = fabsf(overshootAmount) > Math::MACHINE_EPSILON_1;
1558
1559   // make sure we animate back if needed
1560   mAnimateOvershootOff = animateBack || (!animatingOn && mAnimatingOvershootOn);
1561
1562   if( mAnimatingOvershootOn )
1563   {
1564     // animating on, do not allow animate off
1565     return;
1566   }
1567
1568   Actor self = Self();
1569
1570   if(mOvershootAnimationSpeed > Math::MACHINE_EPSILON_0)
1571   {
1572     float currentOvershoot = self.GetProperty<float>(Toolkit::ItemView::Property::OVERSHOOT);
1573     float duration = 0.0f;
1574
1575     if (mOvershootOverlay)
1576     {
1577       duration = mOvershootOverlay.GetCurrentSize().height * (animatingOn ? (1.0f - fabsf(currentOvershoot)) : fabsf(currentOvershoot)) / mOvershootAnimationSpeed;
1578     }
1579
1580     RemoveAnimation(mScrollOvershootAnimation);
1581     mScrollOvershootAnimation = Animation::New(duration);
1582     mScrollOvershootAnimation.FinishedSignal().Connect(this, &ItemView::OnOvershootOnFinished);
1583     mScrollOvershootAnimation.AnimateTo( Property(self, Toolkit::ItemView::Property::OVERSHOOT), overshootAmount, TimePeriod(0.0f, duration) );
1584     mScrollOvershootAnimation.Play();
1585
1586     mAnimatingOvershootOn = animatingOn;
1587   }
1588   else
1589   {
1590     self.SetProperty( Toolkit::ItemView::Property::OVERSHOOT, overshootAmount );
1591   }
1592 }
1593
1594 void ItemView::SetItemsParentOrigin( const Vector3& parentOrigin )
1595 {
1596   if( parentOrigin != mItemsParentOrigin )
1597   {
1598     mItemsParentOrigin = parentOrigin;
1599     for (ItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
1600     {
1601       iter->second.SetParentOrigin(parentOrigin);
1602     }
1603   }
1604 }
1605
1606 Vector3 ItemView::GetItemsParentOrigin() const
1607 {
1608   return mItemsParentOrigin;
1609 }
1610
1611 void ItemView::SetItemsAnchorPoint( const Vector3& anchorPoint )
1612 {
1613   if( anchorPoint != mItemsAnchorPoint )
1614   {
1615     mItemsAnchorPoint = anchorPoint;
1616     for (ItemPoolIter iter = mItemPool.begin(); iter != mItemPool.end(); ++iter)
1617     {
1618       iter->second.SetAnchorPoint(anchorPoint);
1619     }
1620   }
1621 }
1622
1623 Vector3 ItemView::GetItemsAnchorPoint() const
1624 {
1625   return mItemsAnchorPoint;
1626 }
1627
1628 void ItemView::GetItemsRange(ItemRange& range)
1629 {
1630   if( !mItemPool.empty() )
1631   {
1632     range.begin = mItemPool.begin()->first;
1633     range.end = mItemPool.rbegin()->first + 1;
1634   }
1635   else
1636   {
1637     range.begin = 0;
1638     range.end = 0;
1639   }
1640 }
1641
1642 bool ItemView::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
1643 {
1644   Dali::BaseHandle handle( object );
1645
1646   bool connected( true );
1647   Toolkit::ItemView itemView = Toolkit::ItemView::DownCast( handle );
1648
1649   if( 0 == strcmp( signalName.c_str(), LAYOUT_ACTIVATED_SIGNAL ) )
1650   {
1651     itemView.LayoutActivatedSignal().Connect( tracker, functor );
1652   }
1653   else
1654   {
1655     // signalName does not match any signal
1656     connected = false;
1657   }
1658
1659   return connected;
1660 }
1661
1662 } // namespace Internal
1663
1664 } // namespace Toolkit
1665
1666 } // namespace Dali