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