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