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