Merge branch 'master' into tizen
[platform/core/uifw/dali-toolkit.git] / optional / dali-toolkit / internal / controls / page-turn-view / page-turn-view-impl.cpp
1 //
2 // Copyright (c) 2014 Samsung Electronics Co., Ltd.
3 //
4 // Licensed under the Flora License, Version 1.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://floralicense.org/license/
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 // CLASS HEADER
18 #include <dali-toolkit/internal/controls/page-turn-view/page-turn-view-impl.h>
19
20 // INTERNAL INCLUDES
21 #include <dali-toolkit/public-api/controls/default-controls/solid-color-actor.h>
22
23 using namespace Dali;
24
25 namespace //unnamed namespace
26 {
27 // To register type
28 TypeRegistration mType( typeid(Toolkit::PageTurnView), typeid(Toolkit::Control), NULL );
29
30 // default grid density for page turn effect, 10 pixels by 10 pixels
31 const float DEFAULT_GRID_DENSITY(10.0f);
32
33 // to bent the page, the minimal horizontal pan start position is pageSize.x * MINIMUM_START_POSITION_RATIO
34 const float MINIMUM_START_POSITION_RATIO(0.6f);
35
36 // the maximum vertical displacement of pan gesture, if exceed, will reduce it: pageSize.y * MAXIMUM_VERTICAL_MOVEMENT_RATIO
37 const float MAXIMUM_VERTICAL_MOVEMENT_RATIO(0.15f);
38
39 // when the x component of pan position reaches pageSize.x * PAGE_TURN_OVER_THRESHOLD_RATIO, page starts to turn over
40 const float PAGE_TURN_OVER_THRESHOLD_RATIO(0.5f);
41
42 // duration of animation, shorter for faster speed
43 const float PAGE_SLIDE_BACK_ANIMATION_DURATION(1.0f);
44 const float PAGE_TURN_OVER_ANIMATION_DURATION(1.2f);
45
46 // the major&minor radius (in pixels) to form an ellipse shape
47 // the top-left quarter of this ellipse is used to calculate spine normal for simulating shadow
48 const Vector2 DEFAULT_SPINE_SHADOW_PARAMETER(50.0f, 20.0f);
49
50 // constants for shadow casting
51 const float POINT_LIGHT_HEIGHT_RATIO(2.f);
52 const Vector4 DEFAULT_SHADOW_COLOR = Vector4(0.2f, 0.2f, 0.2f, 0.5f);
53
54 // constraints ////////////////////////////////////////////////////////////////
55 /**
56  * Original Center Constraint
57  *
58  * This constraint adjusts the original center property of the page turn shader effect
59  * based on the X-direction displacement of the pan gesture
60  */
61 struct OriginalCenterConstraint
62 {
63   OriginalCenterConstraint(const Vector2& originalCenter, const Vector2& offset)
64   : mOldCenter( originalCenter )
65   {
66     mNewCenter = originalCenter + offset;
67     mDistance = offset.Length() * 0.5f;
68     mDirection = offset  / mDistance;
69   }
70
71   Vector2 operator()(const Vector2& current, const PropertyInput& panDisplacement)
72   {
73     float displacement = panDisplacement.GetFloat();
74
75     if( displacement < mDistance )
76     {
77       return mOldCenter + mDirection * displacement;
78     }
79     else
80     {
81       return mNewCenter + Vector2(0.25f*(displacement-mDistance), 0.f);
82     }
83   }
84
85   Vector2 mOldCenter;
86   Vector2 mNewCenter;
87   float mDistance;
88   Vector2 mDirection;
89 };
90
91 /**
92  * Rotation Constraint
93  *
94  * This constraint adjusts the rotation property of the page actor
95  * based on the X-direction displacement of the pan gesture
96  */
97 struct RotationConstraint
98 {
99   RotationConstraint( float distance, float pageWidth, bool isTurnBack )
100   : mDistance( distance*0.5f )
101   {
102     mStep = 1.f / pageWidth;
103     mSign = isTurnBack ? -1.0f : 1.0f;
104     mConst = isTurnBack ? -1.0f : 0.f;
105     mRotation = isTurnBack ? Quaternion( -Math::PI, Vector3::YAXIS ) : Quaternion( 0.f, Vector3::YAXIS );
106   }
107
108   Quaternion operator()( const Quaternion& current, const PropertyInput& panDisplacement )
109   {
110     float displacement = panDisplacement.GetFloat();
111     float angle;
112     if( displacement < mDistance)
113     {
114       return mRotation;
115     }
116     else
117     {
118       float coef = std::max(-1.0f, mStep*(mDistance-displacement));
119       angle = Math::PI*( mConst + mSign*coef );
120       return Quaternion( angle, Vector3::YAXIS );
121     }
122   }
123
124   float mDistance;
125   float mStep;
126   float mConst;
127   float mSign;
128   Quaternion mRotation;
129 };
130
131 /**
132  * Current Center Constraint
133  *
134  * This constraint adjusts the current center property of the page turn shader effect
135  * based on the pan position and the original center position
136  */
137 struct CurrentCenterConstraint
138 {
139   CurrentCenterConstraint( float pageWidth)
140   : mPageWidth( pageWidth )
141   {
142     mThres = pageWidth * PAGE_TURN_OVER_THRESHOLD_RATIO * 0.5f;
143   }
144
145   Vector2 operator()( const Vector2& current, const PropertyInput& center, const PropertyInput& originalCenter )
146   {
147     Vector2 centerPosition = center.GetVector2();
148     if( centerPosition.x > 0.f )
149     {
150       return Vector2( mThres+centerPosition.x*0.5f , centerPosition.y);
151     }
152     else
153     {
154       Vector2 centerOrigin = originalCenter.GetVector2();
155       Vector2 direction = centerOrigin - Vector2(mThres, centerPosition.y);
156       float coef = 1.f+(centerPosition.x*2.f / mPageWidth);
157       // Todo: when coef <= 0, the page is flat, slow down the last moment of the page stretch by 10 times to avoid a small bounce
158       if(coef < 0.025f)
159       {
160         coef = (coef+0.225f)/10.0f;
161       }
162       return centerOrigin - direction * coef;
163     }
164   }
165
166   float mPageWidth;
167   float mThres;
168 };
169
170 struct ShadowBlurStrengthConstraint
171 {
172   ShadowBlurStrengthConstraint( float thres )
173   : mThres( thres )
174   {}
175
176   float operator()( const float current,  const PropertyInput& currentCenter, const PropertyInput& originalCenter, const PropertyInput& panDisplacement)
177   {
178     float displacement = panDisplacement.GetFloat();
179     float blurStrength;
180     if( EqualsZero(displacement))
181     {
182       Vector2 cur = currentCenter.GetVector2();
183       Vector2 ori = originalCenter.GetVector2();
184       blurStrength =  5.f*(ori-cur).Length() / mThres;
185     }
186     else
187     {
188       blurStrength =  1.f - (displacement-2.f*mThres)/mThres;
189     }
190
191     blurStrength = blurStrength > 1.f ? 1.f : ( blurStrength < 0.f ? 0.f : blurStrength );
192     return blurStrength;
193   }
194
195   float mThres;
196 };
197
198 bool IsActorHittableFunction( Actor actor, Dali::HitTestAlgorithm::TraverseType type )
199 {
200   bool hittable = false;
201
202   switch (type)
203   {
204     case Dali::HitTestAlgorithm::CHECK_ACTOR:
205     {
206       // Check whether the actor is visible and not fully transparent.
207       Property::Index propertyActorHittable = actor.GetPropertyIndex(Toolkit::PageFactory::ACTOR_HITTABLE);
208       if( actor.IsSensitive()
209        && actor.IsVisible()
210        && actor.GetCurrentWorldColor().a > 0.01f// not FULLY_TRANSPARENT
211        && ( propertyActorHittable != Property::INVALID_INDEX &&
212             actor.GetProperty<bool>( propertyActorHittable ) ) )
213       {
214          hittable = true;
215       }
216       break;
217     }
218     case Dali::HitTestAlgorithm::DESCEND_ACTOR_TREE:
219     {
220       if( actor.IsSensitive() && actor.IsVisible() ) // Actor is visible, if not visible then none of its children are visible.
221       {
222         hittable = true;
223       }
224       break;
225     }
226     default:
227     {
228       break;
229     }
230   }
231
232   return hittable;
233 }
234
235 } //unnamed namespace
236
237 namespace Dali
238 {
239
240 namespace Toolkit
241 {
242
243 namespace Internal
244 {
245
246 // these several constants are also used in the derived classes
247 const int PageTurnView::MAXIMUM_TURNING_NUM = 4;
248 const int PageTurnView::NUMBER_OF_CACHED_PAGES_EACH_SIDE = MAXIMUM_TURNING_NUM + 1;
249 const int PageTurnView::NUMBER_OF_CACHED_PAGES = NUMBER_OF_CACHED_PAGES_EACH_SIDE*2;
250 const float PageTurnView::STATIC_PAGE_INTERVAL_DISTANCE = 1.0f;
251
252 PageTurnView::PageTurnView( PageFactory& pageFactory, const Vector2& pageSize )
253 : Control( true ),
254   mPageFactory( pageFactory ),
255   mPageSize( pageSize ),
256   mIsEditMode( false ),
257   mPanning( false ),
258   mSpineShadowParameter( DEFAULT_SPINE_SHADOW_PARAMETER ),
259   mCurrentPageIndex( 0 ),
260   mIndex( 0 ),
261   mPress( false ),
262   mPageUpdated( true ),
263   mPageTurnStartedSignal(),
264   mPageTurnFinishedSignal(),
265   mPagePanStartedSignal(),
266   mPagePanFinishedSignal()
267 {
268   mPageActors.resize( NUMBER_OF_CACHED_PAGES );
269   mIsAnimating.resize( MAXIMUM_TURNING_NUM );
270   mIsSliding.resize( MAXIMUM_TURNING_NUM );
271   mTurnEffect.resize( MAXIMUM_TURNING_NUM );
272   mPropertyPanDisplacement.resize( MAXIMUM_TURNING_NUM );
273   mPropertyCurrentCenter.resize( MAXIMUM_TURNING_NUM );
274 }
275
276 PageTurnView::~PageTurnView()
277 {
278 }
279
280 void PageTurnView::OnInitialize()
281 {
282    // create the two book spine effect for static images, left and right side pages respectively
283   mSpineEffectFront = PageTurnBookSpineEffect::New();
284   mSpineEffectFront.SetIsBackImageVisible( false );
285   mSpineEffectFront.SetPageWidth( mPageSize.width );
286   mSpineEffectFront.SetShadowWidth( 0.f );
287   mSpineEffectFront.SetSpineShadowParameter( mSpineShadowParameter );
288
289   mSpineEffectBack = PageTurnBookSpineEffect::New();
290   mSpineEffectBack.SetIsBackImageVisible( true );
291   mSpineEffectBack.SetPageWidth( mPageSize.width );
292   mSpineEffectBack.SetShadowWidth( 0.f );
293   mSpineEffectBack.SetSpineShadowParameter( mSpineShadowParameter );
294
295   // create the page turn effect objects
296   for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
297   {
298     mTurnEffect[i] = Toolkit::PageTurnEffect::New( false );
299     mTurnEffect[i].SetProperty( ShaderEffect::GRID_DENSITY, Property::Value( DEFAULT_GRID_DENSITY ) );
300     mTurnEffect[i].SetPageSize( mPageSize );
301     mTurnEffect[i].SetShadowWidth(0.f);
302     mTurnEffect[i].SetSpineShadowParameter( mSpineShadowParameter );
303     mIsAnimating[i] = false;
304     mIsSliding[i] = false;
305     mPropertyPanDisplacement[i] = Self().RegisterProperty("PAN_DISPLACEMENT_PROPERTY_"+i, 0.0f);
306     mPropertyCurrentCenter[i] = Self().RegisterProperty("CURRENT_CENTER_PROPERTY_"+i, Vector2(0.0f,0.0f));
307   }
308
309   mTurningPageLayer = Layer::New();
310   mTurningPageLayer.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
311   // Set control size and the parent origin of turningPageLayer
312   OnPageTurnViewInitialize();
313
314   mRootOnScreen = Actor::New();
315   mRootOnScreen.SetPositionInheritanceMode( USE_PARENT_POSITION );
316   mRootOnScreen.SetSize( mControlSize );
317   Self().Add( mRootOnScreen );
318   mRootOnScreen.Add(mTurningPageLayer);
319
320   mTotalPageCount = static_cast<int>( mPageFactory.GetNumberOfPages() );
321   mNeedOffscreenRendering = mPageFactory.IsOffscreenRenderingNeeded();
322   if( mNeedOffscreenRendering )
323   {
324     SetupRenderTasks();
325   }
326
327   // add pages to the scene, and set depth for the stacked pages
328   for( int i = 0; i < NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
329   {
330     AddPage( i );
331     if(mPageActors[i])
332     {
333       mPageActors[i].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
334     }
335   }
336
337   // enable the pan gesture which is attached to the control
338   EnableGestureDetection(Gesture::Type(Gesture::Pan));
339
340   mPageFactory.PageRefreshSignal().Connect(this, &PageTurnView::RenderPage);
341 }
342
343 void PageTurnView::SetupRenderTasks()
344 {
345   mPageSourceActor.resize( NUMBER_OF_CACHED_PAGES );
346   mOffscreenTask.resize( NUMBER_OF_CACHED_PAGES );
347   mRenderedPage.resize( NUMBER_OF_CACHED_PAGES );
348
349   mCameraActor = CameraActor::New(mControlSize);
350   mCameraActor.SetParentOrigin(ParentOrigin::CENTER);
351   mCameraActor.SetPositionInheritanceMode( DONT_INHERIT_POSITION );
352   mCameraActor.SetInheritScale( false );
353   Self().Add(mCameraActor);
354
355   RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList();
356   for(int i=0; i<NUMBER_OF_CACHED_PAGES; i++)
357   {
358     mPageSourceActor[i] = Actor::New();
359     mPageSourceActor[i].SetParentOrigin(ParentOrigin::CENTER);
360     mPageSourceActor[i].SetColorMode( USE_OWN_COLOR );
361     mPageSourceActor[i].SetPositionInheritanceMode( DONT_INHERIT_POSITION );
362     mPageSourceActor[i].SetInheritScale( false );
363     Self().Add( mPageSourceActor[i] );
364     mPageSourceActor[i].SetSensitive( false );
365
366     mRenderedPage[i] = FrameBufferImage::New( mControlSize.width, mControlSize.height, Pixel::RGB8888, Image::Unused );
367     mOffscreenTask[i] = taskList.CreateTask();
368     mOffscreenTask[i].SetRefreshRate( RenderTask::REFRESH_ONCE );
369     mOffscreenTask[i].SetCameraActor(mCameraActor);
370     mOffscreenTask[i].SetSourceActor( mPageSourceActor[i] );
371     mOffscreenTask[i].SetExclusive(true);
372     mOffscreenTask[i].SetInputEnabled( false );
373     mOffscreenTask[i].SetClearEnabled( true );
374     mOffscreenTask[i].SetClearColor( Vector4(0.f,0.f,0.f,0.f) );
375     mOffscreenTask[i].SetTargetFrameBuffer( mRenderedPage[i] );
376     mOffscreenTask[i].SetScreenToFrameBufferMappingActor( Self() );
377    }
378 }
379
380 void PageTurnView::SetupShadowView()
381 {
382   mShadowView = Toolkit::ShadowView::New( 0.25f, 0.25f );
383   Vector3 origin = mTurningPageLayer.GetCurrentParentOrigin();
384   mShadowView.SetParentOrigin( origin );
385   mShadowView.SetAnchorPoint( origin );
386   mShadowView.SetPointLightFieldOfView( Math::PI / 2.0f);
387   mShadowView.SetShadowColor(DEFAULT_SHADOW_COLOR);
388
389   mShadowLayer = Layer::New();
390   mShadowLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
391   mRootOnScreen.Add(mShadowLayer);
392   mShadowLayer.Raise();
393
394   mShadowPlane = CreateSolidColorActor( Vector4 (0.0f, 0.0f, 0.0f, 0.0f) );
395   mShadowPlane.SetPositionInheritanceMode( USE_PARENT_POSITION_PLUS_LOCAL_POSITION );
396   mShadowPlane.SetSize( mControlSize );
397   mShadowLayer.Add( mShadowPlane );
398   mShadowView.SetShadowPlane( mShadowPlane );
399
400   mPointLight = Actor::New();
401   mPointLight.SetAnchorPoint( origin );
402   mPointLight.SetParentOrigin( origin );
403   mPointLight.SetPosition( 0.f, 0.f, mPageSize.width*POINT_LIGHT_HEIGHT_RATIO );
404   mRootOnScreen.Add( mPointLight );
405   mShadowView.SetPointLight( mPointLight );
406
407   mTurningPageLayer.Add( mShadowView );
408   mShadowView.Activate();
409 }
410
411 void PageTurnView::OnControlStageConnection()
412 {
413   SetupShadowView();
414   mTurningPageLayer.RaiseToTop();
415 }
416
417 void PageTurnView::OnControlStageDisconnection()
418 {
419   if(mShadowView)
420   {
421     Self().Remove(mPointLight);
422     Self().Remove(mShadowLayer);
423     mTurningPageLayer.Remove( mShadowView );
424   }
425
426   // make sure the status of the control is updated correctly when the pan gesture is interrupted
427   if(mPanning)
428   {
429     mPanning = false;
430
431     mRootOnScreen.Add(mPanActor);
432     mIsAnimating[mIndex] = false;
433     mPanActor.RemoveConstraints();
434     mTurnEffect[mIndex].RemoveConstraints();
435     mPageUpdated = true;
436
437     SetSpineEffect( mPanActor, mIsTurnBack[mPanActor] );
438   }
439 }
440
441 void PageTurnView::OnControlSizeSet( const Vector3& size )
442 {
443   // disable the SetSize of the control from the application
444   Self().SetSize( mControlSize );
445 }
446
447 void PageTurnView::SetSpineShadowParameter( const Vector2& spineShadowParameter )
448 {
449   mSpineShadowParameter = spineShadowParameter;
450
451   // set spine shadow parameter to all the shader effects
452   mSpineEffectFront.SetSpineShadowParameter( mSpineShadowParameter );
453   mSpineEffectBack.SetSpineShadowParameter( mSpineShadowParameter );
454   for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
455   {
456     mTurnEffect[i].SetSpineShadowParameter( mSpineShadowParameter );
457   }
458 }
459
460 Vector2 PageTurnView::GetSpineShadowParameter()
461 {
462   return mSpineShadowParameter;
463 }
464
465 void PageTurnView::GoToPage( unsigned int pageId )
466 {
467   int pageIdx = static_cast<int>(pageId);
468   // record the new current page index
469   mCurrentPageIndex = pageIdx;
470
471   // clear the old pages
472   for(int i = 0; i < NUMBER_OF_CACHED_PAGES; i++ )
473   {
474     if( mPageActors[i] )
475     {
476       mPageActors[i].Unparent();
477       mPageActors[i].Reset();
478     }
479   }
480
481   // add the current page and the pages right before and after it
482   for( int i = pageIdx - NUMBER_OF_CACHED_PAGES_EACH_SIDE; i < pageIdx + NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
483   {
484     AddPage( i );
485   }
486   // set ordered depth to the stacked pages
487   OrganizePageDepth();
488 }
489
490 unsigned int PageTurnView::GetCurrentPage()
491 {
492   DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
493   return static_cast< unsigned int >( mCurrentPageIndex );
494 }
495
496 Actor PageTurnView::EnterEditMode()
497 {
498   if( mNeedOffscreenRendering )
499   {
500     DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
501
502     mIsEditMode = true;
503
504     int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
505     mOffscreenTask[index].SetInputEnabled( true );
506     mPageSourceActor[index].SetSensitive( true );
507     mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ALWAYS );
508
509     mRootOnScreen.SetSensitive(false);
510
511     return mPageSourceActor[index].GetChildAt( 0 );
512   }
513   else
514   {
515     return Actor();
516   }
517 }
518
519 void PageTurnView::LeaveEditMode()
520 {
521   if( mNeedOffscreenRendering )
522   {
523     DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
524
525     mIsEditMode = false;
526
527     int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
528     mOffscreenTask[index].SetInputEnabled( false );
529     mPageSourceActor[index].SetSensitive( false );
530     mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
531
532     mRootOnScreen.SetSensitive(true);
533   }
534 }
535
536 Actor PageTurnView::GetHitActor( Vector2& screenCoordinates, Vector2& actorCoordinates )
537 {
538   if( mNeedOffscreenRendering && mCurrentPageIndex < mTotalPageCount)
539   {
540     int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
541
542     Dali::HitTestAlgorithm::Results results;
543     if( !mOffscreenTask[index].GetInputEnabled() )
544     {
545       mOffscreenTask[index].SetInputEnabled( true );
546       mPageSourceActor[index].SetSensitive( true );
547       Dali::HitTestAlgorithm::HitTest( mOffscreenTask[index], screenCoordinates, results, IsActorHittableFunction );
548       mOffscreenTask[index].SetInputEnabled( false );
549       mPageSourceActor[index].SetSensitive( false );
550     }
551     else
552     {
553       Dali::HitTestAlgorithm::HitTest( mOffscreenTask[index], screenCoordinates, results, IsActorHittableFunction );
554     }
555     actorCoordinates = results.actorCoordinates;
556     return results.actor;
557   }
558   else
559   {
560     return Actor();
561   }
562 }
563
564 void PageTurnView::AddPage( int pageIndex )
565 {
566   if(pageIndex > -1  && pageIndex < mTotalPageCount) // whether the page is available from the page factory
567   {
568     int index = pageIndex % NUMBER_OF_CACHED_PAGES;
569     ImageActor newPage;
570     if( mNeedOffscreenRendering )
571     {
572       Actor source = mPageFactory.NewPage( pageIndex );
573       if( mPageSourceActor[index].GetChildCount() > 0 )
574       {
575         mPageSourceActor[index].Remove( mPageSourceActor[index].GetChildAt( 0 ) );
576       }
577       mPageSourceActor[index].Add( source );
578       mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
579       newPage = NewPageFromRenderBuffer( pageIndex );
580     }
581     else
582     {
583       newPage= ImageActor::DownCast( mPageFactory.NewPage( pageIndex ) );
584       DALI_ASSERT_ALWAYS( newPage );
585     }
586     newPage.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
587     newPage.SetParentOrigin( ParentOrigin::CENTER_LEFT );
588     newPage.SetSize( mPageSize );
589     mRootOnScreen.Add( newPage );
590     mPageActors[index] = newPage;
591
592     bool isLeftSide = ( pageIndex < mCurrentPageIndex );
593     mIsTurnBack[ newPage ] = isLeftSide;
594     if( isLeftSide )
595     {
596       // new page is added to the left side, so need to rotate it 180 degrees
597       newPage.RotateBy( Degree(-180.0f ), Vector3::YAXIS );
598     }
599     else
600     {
601       SetShaderEffect( newPage, mSpineEffectFront);
602     }
603
604     // For Portrait, nothing to do
605     // For Landscape, set spineEffectBack to the new effect if it is in the left side, and set properties to the back image actor if it exists
606     OnAddPage( newPage, isLeftSide );
607   }
608 }
609
610 void PageTurnView::RemovePage( int pageIndex )
611 {
612   if( pageIndex > -1 && pageIndex < mTotalPageCount)
613   {
614     int index = pageIndex % NUMBER_OF_CACHED_PAGES;
615     mPageActors[index].Unparent();
616     mIsTurnBack.erase( mPageActors[index] );
617     mPageActors[index].Reset();
618     if( mNeedOffscreenRendering )
619     {
620       mPageSourceActor[index].Remove( mPageSourceActor[index].GetChildAt( 0 ) );
621     }
622   }
623 }
624
625 void PageTurnView::RenderPage( int pageIndex )
626 {
627   if( pageIndex > std::max(-1, mCurrentPageIndex - NUMBER_OF_CACHED_PAGES_EACH_SIDE -1)
628    && pageIndex < std::min(mTotalPageCount, mCurrentPageIndex + NUMBER_OF_CACHED_PAGES_EACH_SIDE))
629   {
630     int index = pageIndex % NUMBER_OF_CACHED_PAGES;
631     mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
632   }
633 }
634
635 void PageTurnView::RefreshAll()
636 {
637   mTotalPageCount = static_cast<int>( mPageFactory.GetNumberOfPages() );
638   if( mTotalPageCount > 0 )
639   {
640     if(mCurrentPageIndex < mTotalPageCount)
641     {
642       GoToPage( mCurrentPageIndex );
643     }
644     else
645     {
646       GoToPage( mCurrentPageIndex-- );
647     }
648   }
649 }
650
651 void PageTurnView::RefreshCurrentPage()
652 {
653   RenderPage( mCurrentPageIndex );
654 }
655
656 void PageTurnView::OnPan( PanGesture gesture )
657 {
658   if( mIsEditMode )
659   {
660     // when interrupted by the call of DisplayCurrentPageSourceActor(),
661     // make sure the panFinished is always called before stopping to responding the gesture
662     // so the status of the control is updated correctly
663     if(mPanning)
664     {
665       mPanning = false;
666       PanFinished( SetPanPosition( gesture.position ), gesture.GetSpeed() );
667     }
668
669     return;
670   }
671   // the pan gesture is attached to control itself instead of each page
672   switch( gesture.state )
673   {
674     case Gesture::Started:
675     {
676       mPanning = true;
677       // to find out whether the undergoing turning page number already reaches the maximum allowed
678       // and get one idle index when it is animatable
679       bool animatable = false;
680       for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
681       {
682         if( !mIsAnimating[mIndex] )
683         {
684           animatable = true;
685           break;
686         }
687         if( mIsSliding[mIndex] )
688         {
689           animatable = false;
690           break;
691         }
692         mIndex++;
693         mIndex = mIndex % MAXIMUM_TURNING_NUM;
694       }
695
696       if( mPageUpdated && animatable )
697       {
698         SetPanActor( gesture.position ); // determine which page actor is panned
699         if(mPanActor && mPanActor.GetParent() != mRootOnScreen) // if the page is added to turning layer,it is undergoing an animation currently
700         {
701           mPanActor.Reset();
702         }
703         PanStarted( SetPanPosition( gesture.position ) );  // pass in the pan position in the local page coordinate
704       }
705       else
706       {
707         mPanActor.Reset();
708       }
709       break;
710     }
711     case Gesture::Continuing:
712     {
713       PanContinuing( SetPanPosition( gesture.position ) ); // pass in the pan position in the local page coordinate
714       break;
715     }
716     case Gesture::Finished:
717     case Gesture::Cancelled:
718     {
719       mPanning = false;
720       PanFinished( SetPanPosition( gesture.position ), gesture.GetSpeed() );
721       break;
722     }
723     case Gesture::Clear:
724     case Gesture::Possible:
725     default:
726     {
727       break;
728     }
729   }
730 }
731
732 void PageTurnView::PanStarted( const Vector2& gesturePosition )
733 {
734   mPressDownPosition = gesturePosition;
735
736   if( !mPanActor )
737   {
738     return;
739   }
740
741   mOriginalCenter = gesturePosition;
742   mTurnEffect[mIndex].SetIsTurningBack( mIsTurnBack[ mPanActor] );
743   mPress = false;
744   mPageUpdated = false;
745
746   // Guard against destruction during signal emission
747   Toolkit::PageTurnView handle( GetOwner() );
748   mPagePanStartedSignal.Emit( handle );
749 }
750
751 void PageTurnView::PanContinuing( const Vector2& gesturePosition )
752 {
753   if( !mPanActor )
754   {
755     return;
756   }
757
758   // Guard against destruction during signal emission
759   Toolkit::PageTurnView handle( GetOwner() );
760
761   if(!mPress)
762   {
763     // when the touch down position is near the spine
764     // or when the panning goes outwards or some other position which would tear the paper in real situation
765     // we change the start position into the current panning position and update the shader parameters
766     if( mOriginalCenter.x <  mPageSize.width*MINIMUM_START_POSITION_RATIO
767         || gesturePosition.x > mOriginalCenter.x-1.0f
768         || ( ( gesturePosition.x/mOriginalCenter.x > gesturePosition.y/mOriginalCenter.y ) &&
769              ( gesturePosition.x/mOriginalCenter.x > (gesturePosition.y-mPageSize.height )/(mOriginalCenter.y-mPageSize.height ) ) ) )
770     {
771       mOriginalCenter = gesturePosition;
772     }
773     else
774     {
775       mDistanceUpCorner = mOriginalCenter.Length();
776       mDistanceBottomCorner = ( mOriginalCenter - Vector2( 0.0f, mPageSize.height ) ).Length();
777       mShadowView.Add( mPanActor );
778       SetShaderEffect( mPanActor, mTurnEffect[mIndex] );
779       mTurnEffect[mIndex].SetOriginalCenter( mOriginalCenter );
780       mCurrentCenter = mOriginalCenter;
781       mTurnEffect[mIndex].SetCurrentCenter( mCurrentCenter );
782       mPanDisplacement = 0.f;
783       mConstraints = true;
784       mPress = true;
785       mIsAnimating[mIndex] = true;
786
787       mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>(mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), !mIsTurnBack[mPanActor] );
788
789       mShadowView.RemoveConstraints();
790       Actor self = Self();
791       self.SetProperty( mPropertyPanDisplacement[mIndex], 0.f );
792       Constraint shadowBlurStrengthConstraint = Constraint::New<float>( mShadowView.GetBlurStrengthPropertyIndex(),
793                                                                         Source(mTurnEffect[mIndex],  mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName())),
794                                                                         Source(mTurnEffect[mIndex],  mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetOriginalCenterPropertyName())),
795                                                                         Source( self, mPropertyPanDisplacement[mIndex] ),
796                                                                         ShadowBlurStrengthConstraint( mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO ) );
797       mShadowView.ApplyConstraint( shadowBlurStrengthConstraint  );
798     }
799   }
800   else
801   {
802     Vector2 currentCenter = gesturePosition;
803
804     // Test whether the new current center would tear the paper from the top pine in real situation
805     // we do not forbid this totally, which would restrict the panning gesture too much
806     // instead, set it to the nearest allowable position
807     float distanceUpCorner = currentCenter.Length();
808     float distanceBottomCorner = ( currentCenter-Vector2( 0.0f, mPageSize.height ) ).Length();
809     if( distanceUpCorner > mDistanceUpCorner )
810     {
811       currentCenter = currentCenter*mDistanceUpCorner/distanceUpCorner;
812     }
813     // would tear the paper from the bottom spine in real situation
814     if( distanceBottomCorner > mDistanceBottomCorner )
815     {
816       currentCenter = ( ( currentCenter - Vector2( 0.0f, mPageSize.height ) )*mDistanceBottomCorner/distanceBottomCorner + Vector2(0.0f,mPageSize.height ) );
817     }
818     // If direction has a very high y component, reduce it.
819     Vector2 curveDirection = currentCenter - mOriginalCenter;
820     if( fabs( curveDirection.y ) > fabs( curveDirection.x ) )
821     {
822       currentCenter.y = mOriginalCenter.y + ( currentCenter.y - mOriginalCenter.y ) * fabs( curveDirection.x/curveDirection.y );
823     }
824     // If the vertical distance is high, reduce it
825     float yShift = currentCenter.y - mOriginalCenter.y;
826     if( fabs( yShift ) > mPageSize.height * MAXIMUM_VERTICAL_MOVEMENT_RATIO )
827     {
828       currentCenter.y = mOriginalCenter.y + yShift*mPageSize.height*MAXIMUM_VERTICAL_MOVEMENT_RATIO/fabs(yShift );
829     }
830
831     // use contraints to control the page shape and rotation when the pan position is near the spine
832     if( currentCenter.x <= mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO && mOriginalCenter.x > mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO )
833     {
834       // set the property values used by the constraints
835       mPanDisplacement = mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO - currentCenter.x;
836       Self().SetProperty( mPropertyPanDisplacement[mIndex], mPanDisplacement );
837       Self().SetProperty( mPropertyCurrentCenter[mIndex], currentCenter );
838
839       // set up the OriginalCenterConstraint and CurrentCebterConstraint to the PageTurnEdffect
840       // also set up the RotationConstraint to the page actor
841       if( mConstraints )
842       {
843         Vector2 corner;
844         // the corner position need to be a little far away from the page edge to ensure the whole page is lift up
845         if( currentCenter.y >= mOriginalCenter.y )
846         {
847           corner = Vector2( 1.1f*mPageSize.width, 0.f );
848         }
849         else
850         {
851           corner = mPageSize*1.1f;
852         }
853
854         Vector2 offset( currentCenter-mOriginalCenter );
855         float k = - ( (mOriginalCenter.x-corner.x)*offset.x + (mOriginalCenter.y-corner.y)*offset.y )
856                    /( offset.x*offset.x + offset.y*offset.y );
857         offset *= k;
858         Actor self = Self();
859         Source source(self, mPropertyPanDisplacement[mIndex]);
860
861         Property::Index shaderOriginalCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetOriginalCenterPropertyName());
862         Constraint originalCenterConstraint = Constraint::New<Vector2>( shaderOriginalCenterPropertyIndex ,
863                                                                         source,
864                                                                         OriginalCenterConstraint( mOriginalCenter, offset ));
865         mTurnEffect[mIndex].ApplyConstraint( originalCenterConstraint );
866
867         Property::Index shaderCurrentCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName());
868         Constraint currentCenterConstraint = Constraint::New<Vector2>( shaderCurrentCenterPropertyIndex,
869                                                                        Source(self, mPropertyCurrentCenter[mIndex]),
870                                                                        Source(mTurnEffect[mIndex], shaderOriginalCenterPropertyIndex),
871                                                                        CurrentCenterConstraint(mPageSize.width));
872         mTurnEffect[mIndex].ApplyConstraint( currentCenterConstraint );
873
874         GetImpl( mTurnEffect[mIndex] ).ApplyInternalConstraint();
875
876         float distance = offset.Length();
877         Constraint rotationConstraint = Constraint::New<Quaternion>( Actor::ROTATION,
878                                                                      Source( self, mPropertyPanDisplacement[mIndex] ),
879                                                                      RotationConstraint(distance, mPageSize.width, mIsTurnBack[mPanActor]));
880         mPanActor.ApplyConstraint( rotationConstraint );
881
882         mConstraints = false;
883       }
884     }
885     else
886     {
887       if(!mConstraints) // remove the constraint is the pan position move back to far away from the spine
888       {
889         mPanActor.RemoveConstraints();
890         mTurnEffect[mIndex].RemoveConstraints();
891         mTurnEffect[mIndex].SetOriginalCenter( mOriginalCenter );
892         mConstraints = true;
893         mPanDisplacement = 0.f;
894       }
895
896       mTurnEffect[mIndex].SetCurrentCenter( currentCenter );
897       mCurrentCenter = currentCenter;
898       GetImpl( mTurnEffect[mIndex] ).ApplyInternalConstraint();
899     }
900   }
901 }
902
903 void PageTurnView::PanFinished( const Vector2& gesturePosition, float gestureSpeed )
904 {
905   // Guard against destruction during signal emission
906   Toolkit::PageTurnView handle( GetOwner() );
907
908   if( !mPanActor )
909   {
910     if(!mIsAnimating[mIndex])
911     {
912       OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
913     }
914     return;
915   }
916
917   mPagePanFinishedSignal.Emit( handle );
918
919   Actor actor = mPanActor;
920   if(mPress)
921   {
922     if(!mConstraints) // if with constraints, the pan finished position is near spine, set up an animation to turn the page over
923     {
924       // update the pages here instead of in the TurnedOver callback function
925       // as new page is allowed to respond to the pan gesture before other pages finishing animation
926       if(mIsTurnBack[actor])
927       {
928         mCurrentPageIndex--;
929         RemovePage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE );
930         AddPage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE );
931       }
932       else
933       {
934         mCurrentPageIndex++;
935         RemovePage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
936         AddPage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
937       }
938       OrganizePageDepth();
939
940       // set up an animation to turn the page over
941       Actor self = Self();
942       float width = mPageSize.width*(1.f+PAGE_TURN_OVER_THRESHOLD_RATIO);
943       Animation animation = Animation::New( std::max(0.1f,PAGE_TURN_OVER_ANIMATION_DURATION * (1.0f - mPanDisplacement / width)) );
944       animation.AnimateTo( Property(self, mPropertyPanDisplacement[mIndex]),
945                            width,AlphaFunctions::EaseOutSine33);
946       animation.AnimateTo( Property(self, mPropertyCurrentCenter[mIndex]),
947                            Vector2(-mPageSize.width, 0.5f*mPageSize.height), AlphaFunctions::EaseOutSine33);
948       mAnimationActorPair[animation] = actor;
949       mAnimationIndexPair[animation] = mIndex;
950       animation.Play();
951       animation.FinishedSignal().Connect( this, &PageTurnView::TurnedOver );
952     }
953     else // the pan finished position is far away from the spine, set up an animation to slide the page back instead of turning over
954     {
955       Animation animation= Animation::New( PAGE_SLIDE_BACK_ANIMATION_DURATION * (mOriginalCenter.x - mCurrentCenter.x) / mPageSize.width / PAGE_TURN_OVER_THRESHOLD_RATIO );
956       animation.AnimateTo( Property( mTurnEffect[mIndex], mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName() ),
957                            mOriginalCenter, AlphaFunctions::Linear );
958       mAnimationActorPair[animation] = actor;
959       mAnimationIndexPair[animation] = mIndex;
960       animation.Play();
961       mIsSliding[mIndex] = true;
962       animation.FinishedSignal().Connect( this, &PageTurnView::SliddenBack );
963
964       mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), mIsTurnBack[mPanActor] );
965     }
966   }
967   else
968   {
969     // In portrait view, an outwards flick should turn the previous page back
970     // In landscape view, nothing to do
971     OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
972   }
973
974   mPageUpdated = true;
975 }
976
977 void PageTurnView::TurnedOver( Animation& animation )
978 {
979   Actor actor = mAnimationActorPair[animation];
980   mIsTurnBack[actor] = !mIsTurnBack[actor];
981   actor.RemoveConstraints();
982   mRootOnScreen.Add(actor);
983   int index = mAnimationIndexPair[animation];
984   mIsAnimating[index] = false;
985   mTurnEffect[index].RemoveConstraints();
986   mAnimationIndexPair.erase( animation );
987   mAnimationActorPair.erase( animation );
988
989   SetSpineEffect( actor, mIsTurnBack[actor] );
990
991   // Guard against destruction during signal emission
992   Toolkit::PageTurnView handle( GetOwner() );
993   mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[actor] ) ? -1 : 0 ) ), mIsTurnBack[actor] );
994 }
995
996 void PageTurnView::SliddenBack( Animation& animation )
997 {
998   Actor actor = mAnimationActorPair[animation];
999   mRootOnScreen.Add(actor);
1000   int index = mAnimationIndexPair[animation];
1001   mIsSliding[index] = false;
1002   mIsAnimating[index] = false;
1003   mAnimationIndexPair.erase( animation );
1004   mAnimationActorPair.erase( animation );
1005
1006   SetSpineEffect( actor, mIsTurnBack[actor] );
1007
1008   // Guard against destruction during signal emission
1009   Toolkit::PageTurnView handle( GetOwner() );
1010   mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), mIsTurnBack[actor] );
1011 }
1012
1013 void PageTurnView::OrganizePageDepth()
1014 {
1015   for( int i=0; i<NUMBER_OF_CACHED_PAGES_EACH_SIDE;i++ )
1016   {
1017     if(mCurrentPageIndex+i < mTotalPageCount)
1018     {
1019       mPageActors[( mCurrentPageIndex+i )%NUMBER_OF_CACHED_PAGES].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
1020     }
1021     if( mCurrentPageIndex >= i + 1 )
1022     {
1023       mPageActors[( mCurrentPageIndex-i-1 )%NUMBER_OF_CACHED_PAGES].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
1024     }
1025   }
1026 }
1027
1028 void PageTurnView::SetShaderEffect( Actor actor, ShaderEffect shaderEffect )
1029 {
1030   actor.SetShaderEffect( shaderEffect );
1031
1032   if( actor.GetChildCount() > 0 )
1033   {
1034     actor.GetChildAt( 0 ).SetShaderEffect(shaderEffect);
1035   }
1036 }
1037
1038 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnStartedSignal()
1039 {
1040   return mPageTurnStartedSignal;
1041 }
1042
1043 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnFinishedSignal()
1044 {
1045   return mPageTurnFinishedSignal;
1046 }
1047
1048 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanStartedSignal()
1049 {
1050   return mPagePanStartedSignal;
1051 }
1052
1053 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanFinishedSignal()
1054 {
1055   return mPagePanFinishedSignal;
1056 }
1057
1058 } // namespace Internal
1059
1060 } // namespace Toolkit
1061
1062 } // namespace Dali