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