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