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