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