Merge "Adding a TextSelectionToolbar Utc test" into devel/master
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / decorator / text-decorator.cpp
1 /*
2  * Copyright (c) 2015 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/text/decorator/text-decorator.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/integration-api/debug.h>
23
24 #include <dali/public-api/adaptor-framework/timer.h>
25 #include <dali/public-api/actors/layer.h>
26 #include <dali/public-api/common/stage.h>
27 #include <dali/public-api/events/touch-data.h>
28 #include <dali/public-api/events/pan-gesture.h>
29 #include <dali/public-api/images/resource-image.h>
30 #include <dali/public-api/object/property-notification.h>
31
32 #include <dali/devel-api/rendering/geometry.h>
33 #include <dali/devel-api/rendering/renderer.h>
34
35 // INTERNAL INCLUDES
36 #include <dali-toolkit/public-api/controls/image-view/image-view.h>
37 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
38 #include <dali-toolkit/internal/controls/image-view/image-view-impl.h>
39
40 #ifdef DEBUG_ENABLED
41 #define DECORATOR_DEBUG
42
43 #endif
44
45 #define MAKE_SHADER(A)#A
46
47 namespace
48 {
49 const char* VERTEX_SHADER = MAKE_SHADER(
50 attribute mediump vec2    aPosition;
51 uniform   mediump mat4    uMvpMatrix;
52 uniform   mediump vec3    uSize;
53
54 void main()
55 {
56   mediump vec4 position = vec4( aPosition, 0.0, 1.0 );
57   position.xyz *= uSize;
58   gl_Position = uMvpMatrix * position;
59 }
60 );
61
62 const char* FRAGMENT_SHADER = MAKE_SHADER(
63 uniform      lowp vec4 uColor;
64
65 void main()
66 {
67   gl_FragColor = uColor;
68 }
69 );
70 }
71
72 namespace Dali
73 {
74 namespace Internal
75 {
76 namespace
77 {
78 #ifdef DECORATOR_DEBUG
79 Integration::Log::Filter* gLogFilter( Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_TEXT_DECORATOR") );
80 #endif
81 }
82 }
83 }
84
85
86 // Local Data
87 namespace
88 {
89 const Dali::Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.25f, 1.5f, 1.0f );
90 const Dali::Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.25f, 1.5f, 1.0f );
91
92 const Dali::Vector4 LIGHT_BLUE( 0.75f, 0.96f, 1.f, 1.f ); // The text highlight color. TODO: due some problems, maybe with the blending function in the text clipping, the color is fully opaque.
93
94 const Dali::Vector4 HANDLE_COLOR( 0.0f, (183.0f / 255.0f), (229.0f / 255.0f), 1.0f  );
95
96 const unsigned int CURSOR_BLINK_INTERVAL = 500u; // Cursor blink interval
97 const float TO_MILLISECONDS = 1000.f;
98 const float TO_SECONDS = 1.f / TO_MILLISECONDS;
99
100 const unsigned int SCROLL_TICK_INTERVAL = 50u;
101
102 const float SCROLL_THRESHOLD = 10.f;
103 const float SCROLL_SPEED = 300.f;
104 const float SCROLL_DISTANCE = SCROLL_SPEED * SCROLL_TICK_INTERVAL * TO_SECONDS;
105
106 const float CURSOR_WIDTH = 1.f;
107
108 /**
109  * structure to hold coordinates of each quad, which will make up the mesh.
110  */
111 struct QuadCoordinates
112 {
113   /**
114    * Default constructor
115    */
116   QuadCoordinates()
117   {
118   }
119
120   /**
121    * Constructor
122    * @param[in] x1 left co-ordinate
123    * @param[in] y1 top co-ordinate
124    * @param[in] x2 right co-ordinate
125    * @param[in] y2 bottom co-ordinate
126    */
127   QuadCoordinates(float x1, float y1, float x2, float y2)
128   : min(x1, y1),
129     max(x2, y2)
130   {
131   }
132
133   Dali::Vector2 min;                          ///< top-left (minimum) position of quad
134   Dali::Vector2 max;                          ///< bottom-right (maximum) position of quad
135 };
136
137 typedef std::vector<QuadCoordinates> QuadContainer;
138
139 /**
140  * @brief Takes a bounding rectangle in the local coordinates of an actor and returns the world coordinates Bounding Box.
141  * @param[in] boundingRectangle local bounding
142  * @param[out] Vector4 World coordinate bounding Box.
143  */
144 void LocalToWorldCoordinatesBoundingBox( const Dali::Rect<int>& boundingRectangle, Dali::Vector4& boundingBox )
145 {
146   // Convert to world coordinates and store as a Vector4 to be compatible with Property Notifications.
147   Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
148
149   const float originX = boundingRectangle.x - 0.5f * stageSize.width;
150   const float originY = boundingRectangle.y - 0.5f * stageSize.height;
151
152   boundingBox = Dali::Vector4( originX,
153                                originY,
154                                originX + boundingRectangle.width,
155                                originY + boundingRectangle.height );
156 }
157
158 void WorldToLocalCoordinatesBoundingBox( const Dali::Vector4& boundingBox, Dali::Rect<int>& boundingRectangle )
159 {
160   // Convert to local coordinates and store as a Dali::Rect.
161   Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
162
163   boundingRectangle.x = boundingBox.x + 0.5f * stageSize.width;
164   boundingRectangle.y = boundingBox.y + 0.5f * stageSize.height;
165   boundingRectangle.width = boundingBox.z - boundingBox.x;
166   boundingRectangle.height = boundingBox.w - boundingBox.y;
167 }
168
169 } // end of namespace
170
171 namespace Dali
172 {
173
174 namespace Toolkit
175 {
176
177 namespace Text
178 {
179
180 struct Decorator::Impl : public ConnectionTracker
181 {
182   enum ScrollDirection
183   {
184     SCROLL_NONE,
185     SCROLL_RIGHT,
186     SCROLL_LEFT,
187     SCROLL_TOP,
188     SCROLL_BOTTOM
189   };
190
191   struct CursorImpl
192   {
193     CursorImpl()
194     : color( Dali::Color::BLACK ),
195       position(),
196       cursorHeight( 0.0f ),
197       lineHeight( 0.0f )
198     {
199     }
200
201     Vector4 color;
202     Vector2 position;
203     float cursorHeight;
204     float lineHeight;
205   };
206
207   struct HandleImpl
208   {
209     HandleImpl()
210     : position(),
211       size(),
212       lineHeight( 0.0f ),
213       grabDisplacementX( 0.f ),
214       grabDisplacementY( 0.f ),
215       active( false ),
216       visible( false ),
217       pressed( false ),
218       verticallyFlippedPreferred( false ),
219       horizontallyFlipped( false ),
220       verticallyFlipped( false )
221     {
222     }
223
224     ImageView actor;
225     Actor grabArea;
226     ImageView markerActor;
227
228     Vector2 position;
229     Size    size;
230     float   lineHeight;              ///< Not the handle height
231     float   grabDisplacementX;
232     float   grabDisplacementY;
233     bool    active  : 1;
234     bool    visible : 1;
235     bool    pressed : 1;
236     bool    verticallyFlippedPreferred : 1; ///< Whether the handle is preferred to be vertically flipped.
237     bool    horizontallyFlipped        : 1; ///< Whether the handle has been horizontally flipped.
238     bool    verticallyFlipped          : 1; ///< Whether the handle has been vertically flipped.
239   };
240
241   struct PopupImpl
242   {
243     PopupImpl()
244     : position()
245     {
246     }
247
248     TextSelectionPopup actor;
249     Vector3 position;
250   };
251
252   Impl( ControllerInterface& controller,
253         TextSelectionPopupCallbackInterface& callbackInterface )
254   : mController( controller ),
255     mEnabledPopupButtons( TextSelectionPopup::NONE ),
256     mTextSelectionPopupCallbackInterface( callbackInterface ),
257     mHandleColor( HANDLE_COLOR ),
258     mBoundingBox(),
259     mHighlightColor( LIGHT_BLUE ),
260     mHighlightPosition( Vector2::ZERO ),
261     mActiveCursor( ACTIVE_CURSOR_NONE ),
262     mCursorBlinkInterval( CURSOR_BLINK_INTERVAL ),
263     mCursorBlinkDuration( 0.0f ),
264     mCursorWidth( CURSOR_WIDTH ),
265     mHandleScrolling( HANDLE_TYPE_COUNT ),
266     mScrollDirection( SCROLL_NONE ),
267     mScrollThreshold( SCROLL_THRESHOLD ),
268     mScrollSpeed( SCROLL_SPEED ),
269     mScrollDistance( SCROLL_DISTANCE ),
270     mTextDepth( 0u ),
271     mActiveCopyPastePopup( false ),
272     mPopupSetNewPosition( true ),
273     mCursorBlinkStatus( true ),
274     mDelayCursorBlink( false ),
275     mPrimaryCursorVisible( false ),
276     mSecondaryCursorVisible( false ),
277     mFlipSelectionHandlesOnCross( false ),
278     mFlipLeftSelectionHandleDirection( false ),
279     mFlipRightSelectionHandleDirection( false ),
280     mHandlePanning( false ),
281     mHandleCurrentCrossed( false ),
282     mHandlePreviousCrossed( false ),
283     mNotifyEndOfScroll( false )
284   {
285     mQuadVertexFormat[ "aPosition" ] = Property::VECTOR2;
286     mHighlightShader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
287     SetupGestures();
288   }
289
290   /**
291    * Relayout of the decorations owned by the decorator.
292    * @param[in] size The Size of the UI control the decorator is adding it's decorations to.
293    */
294   void Relayout( const Vector2& size )
295   {
296     mControlSize = size;
297
298     // TODO - Remove this if nothing is active
299     CreateActiveLayer();
300
301     // Show or hide the cursors
302     CreateCursors();
303
304     if( mPrimaryCursor )
305     {
306       const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
307       mPrimaryCursorVisible = ( cursor.position.x + mCursorWidth <= mControlSize.width ) && ( cursor.position.x >= 0.f );
308       if( mPrimaryCursorVisible )
309       {
310         mPrimaryCursor.SetPosition( cursor.position.x,
311                                     cursor.position.y );
312         mPrimaryCursor.SetSize( Size( mCursorWidth, cursor.cursorHeight ) );
313       }
314       mPrimaryCursor.SetVisible( mPrimaryCursorVisible && mCursorBlinkStatus );
315     }
316     if( mSecondaryCursor )
317     {
318       const CursorImpl& cursor = mCursor[SECONDARY_CURSOR];
319       mSecondaryCursorVisible = ( cursor.position.x + mCursorWidth <= mControlSize.width ) && ( cursor.position.x >= 0.f );
320       if( mSecondaryCursorVisible )
321       {
322         mSecondaryCursor.SetPosition( cursor.position.x,
323                                       cursor.position.y );
324         mSecondaryCursor.SetSize( Size( mCursorWidth, cursor.cursorHeight ) );
325       }
326       mSecondaryCursor.SetVisible( mSecondaryCursorVisible && mCursorBlinkStatus );
327     }
328
329     // Show or hide the grab handle
330     HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
331     bool newGrabHandlePosition = false;
332     if( grabHandle.active )
333     {
334       const bool isVisible = ( grabHandle.position.x + floor( 0.5f * mCursorWidth ) <= mControlSize.width ) && ( grabHandle.position.x >= 0.f );
335
336       if( isVisible )
337       {
338         CreateGrabHandle();
339
340         // Sets the grab handle position and calculate if it needs to be vertically flipped if it exceeds the boundary box.
341         SetGrabHandlePosition();
342
343         // Sets the grab handle image according if it's pressed, flipped, etc.
344         SetHandleImage( GRAB_HANDLE );
345
346         newGrabHandlePosition = true;
347       }
348
349       if( grabHandle.actor )
350       {
351         grabHandle.actor.SetVisible( isVisible );
352       }
353     }
354     else if( grabHandle.actor )
355     {
356       grabHandle.actor.Unparent();
357     }
358
359     // Show or hide the selection handles/highlight
360     HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
361     HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
362     bool newPrimaryHandlePosition = false;
363     bool newSecondaryHandlePosition = false;
364     if( primary.active || secondary.active )
365     {
366       const bool isPrimaryVisible = ( primary.position.x <= mControlSize.width ) && ( primary.position.x >= 0.f );
367       const bool isSecondaryVisible = ( secondary.position.x <= mControlSize.width ) && ( secondary.position.x >= 0.f );
368
369       if( isPrimaryVisible || isSecondaryVisible )
370       {
371         CreateSelectionHandles();
372
373         if( isPrimaryVisible )
374         {
375           SetSelectionHandlePosition( LEFT_SELECTION_HANDLE );
376
377           // Sets the primary handle image according if it's pressed, flipped, etc.
378           SetHandleImage( LEFT_SELECTION_HANDLE );
379
380           SetSelectionHandleMarkerSize( primary );
381
382           newPrimaryHandlePosition = true;
383         }
384
385         if( isSecondaryVisible )
386         {
387           SetSelectionHandlePosition( RIGHT_SELECTION_HANDLE );
388
389           // Sets the secondary handle image according if it's pressed, flipped, etc.
390           SetHandleImage( RIGHT_SELECTION_HANDLE );
391
392           SetSelectionHandleMarkerSize( secondary );
393
394           newSecondaryHandlePosition = true;
395         }
396       }
397
398       if( primary.actor )
399       {
400         primary.actor.SetVisible( isPrimaryVisible );
401       }
402       if( secondary.actor )
403       {
404         secondary.actor.SetVisible( isSecondaryVisible );
405       }
406
407       CreateHighlight();
408       UpdateHighlight();
409     }
410     else
411     {
412       if( primary.actor )
413       {
414         primary.actor.Unparent();
415       }
416       if( secondary.actor )
417       {
418         secondary.actor.Unparent();
419       }
420       if( mHighlightActor )
421       {
422         mHighlightActor.Unparent();
423       }
424     }
425
426     if( newGrabHandlePosition    ||
427         newPrimaryHandlePosition ||
428         newSecondaryHandlePosition )
429     {
430       // Setup property notifications to find whether the handles leave the boundaries of the current display.
431       SetupActiveLayerPropertyNotifications();
432     }
433
434     if( mActiveCopyPastePopup )
435     {
436       ShowPopup();
437       mPopupSetNewPosition = true;
438     }
439     else
440     {
441       if( mCopyPastePopup.actor )
442       {
443         mCopyPastePopup.actor.HidePopup();
444         mPopupSetNewPosition = true;
445       }
446     }
447   }
448
449   void UpdatePositions( const Vector2& scrollOffset )
450   {
451     mCursor[PRIMARY_CURSOR].position += scrollOffset;
452     mCursor[SECONDARY_CURSOR].position += scrollOffset;
453     mHandle[ GRAB_HANDLE ].position += scrollOffset;
454     mHandle[ LEFT_SELECTION_HANDLE ].position += scrollOffset;
455     mHandle[ RIGHT_SELECTION_HANDLE ].position += scrollOffset;
456     mHighlightPosition += scrollOffset;
457   }
458
459   void ShowPopup()
460   {
461     if ( !mCopyPastePopup.actor )
462     {
463       return;
464     }
465
466     if( !mCopyPastePopup.actor.GetParent() )
467     {
468       mActiveLayer.Add( mCopyPastePopup.actor );
469     }
470
471     mCopyPastePopup.actor.RaiseAbove( mActiveLayer );
472     mCopyPastePopup.actor.ShowPopup();
473   }
474
475   void DeterminePositionPopup()
476   {
477     if( !mActiveCopyPastePopup )
478     {
479       return;
480     }
481
482     // Retrieves the popup's size after relayout.
483     const Vector3 popupSize = Vector3( mCopyPastePopup.actor.GetRelayoutSize( Dimension::WIDTH ), mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT ), 0.0f );
484
485     if( mPopupSetNewPosition )
486     {
487       const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
488       const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
489       const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
490
491       if( primaryHandle.active || secondaryHandle.active )
492       {
493         // Calculates the popup's position if selection handles are active.
494         const float minHandleXPosition = std::min( primaryHandle.position.x, secondaryHandle.position.x );
495         const float maxHandleXPosition = std::max( primaryHandle.position.x, secondaryHandle.position.x );
496         const float maxHandleHeight = std::max( primaryHandle.size.height, secondaryHandle.size.height );
497
498         mCopyPastePopup.position.x = minHandleXPosition + ( ( maxHandleXPosition - minHandleXPosition ) * 0.5f );
499         mCopyPastePopup.position.y = -0.5f * popupSize.height - maxHandleHeight + std::min( primaryHandle.position.y, secondaryHandle.position.y );
500       }
501       else
502       {
503         // Calculates the popup's position if the grab handle is active.
504         const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
505         if( grabHandle.verticallyFlipped )
506         {
507           mCopyPastePopup.position = Vector3( cursor.position.x, -0.5f * popupSize.height - grabHandle.size.height + cursor.position.y, 0.0f );
508         }
509         else
510         {
511           mCopyPastePopup.position = Vector3( cursor.position.x, -0.5f * popupSize.height + cursor.position.y, 0.0f );
512         }
513       }
514     }
515
516     // Checks if there is enough space above the text control. If not it places the popup under it.
517     GetConstrainedPopupPosition( mCopyPastePopup.position, popupSize * AnchorPoint::CENTER, mActiveLayer, mBoundingBox );
518
519     SetUpPopupPositionNotifications();
520
521     mCopyPastePopup.actor.SetPosition( mCopyPastePopup.position );
522     mPopupSetNewPosition = false;
523   }
524
525   void PopupRelayoutComplete( Actor actor )
526   {
527     // Size negotiation for CopyPastePopup complete so can get the size and constrain position within bounding box.
528
529     DeterminePositionPopup();
530   }
531
532   void CreateCursor( Control& cursor, const Vector4& color )
533   {
534     cursor = Control::New();
535     cursor.SetBackgroundColor( color );
536     cursor.SetParentOrigin( ParentOrigin::TOP_LEFT );
537     cursor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
538   }
539
540   // Add or Remove cursor(s) from parent
541   void CreateCursors()
542   {
543     if( mActiveCursor == ACTIVE_CURSOR_NONE )
544     {
545       if( mPrimaryCursor )
546       {
547         mPrimaryCursor.Unparent();
548       }
549       if( mSecondaryCursor )
550       {
551         mSecondaryCursor.Unparent();
552       }
553     }
554     else
555     {
556       // Create Primary and or Secondary Cursor(s) if active and add to parent
557       if ( mActiveCursor == ACTIVE_CURSOR_PRIMARY ||
558            mActiveCursor == ACTIVE_CURSOR_BOTH )
559       {
560         if ( !mPrimaryCursor )
561         {
562           CreateCursor( mPrimaryCursor, mCursor[PRIMARY_CURSOR].color );
563 #ifdef DECORATOR_DEBUG
564           mPrimaryCursor.SetName( "PrimaryCursorActor" );
565 #endif
566         }
567
568         if( !mPrimaryCursor.GetParent() )
569         {
570           mActiveLayer.Add( mPrimaryCursor );
571         }
572       }
573
574       if ( mActiveCursor == ACTIVE_CURSOR_BOTH )
575       {
576         if ( !mSecondaryCursor )
577         {
578           CreateCursor( mSecondaryCursor, mCursor[SECONDARY_CURSOR].color );
579 #ifdef DECORATOR_DEBUG
580           mSecondaryCursor.SetName( "SecondaryCursorActor" );
581 #endif
582         }
583
584         if( !mSecondaryCursor.GetParent() )
585         {
586           mActiveLayer.Add( mSecondaryCursor );
587         }
588       }
589       else
590       {
591         if( mSecondaryCursor )
592         {
593           mSecondaryCursor.Unparent();
594         }
595       }
596     }
597   }
598
599   bool OnCursorBlinkTimerTick()
600   {
601     if( !mDelayCursorBlink )
602     {
603       // Cursor blinking
604       if ( mPrimaryCursor )
605       {
606         mPrimaryCursor.SetVisible( mPrimaryCursorVisible && mCursorBlinkStatus );
607       }
608       if ( mSecondaryCursor )
609       {
610         mSecondaryCursor.SetVisible( mSecondaryCursorVisible && mCursorBlinkStatus );
611       }
612
613       mCursorBlinkStatus = !mCursorBlinkStatus;
614     }
615     else
616     {
617       // Resume blinking
618       mDelayCursorBlink = false;
619     }
620
621     return true;
622   }
623
624   void SetupGestures()
625   {
626     mTapDetector = TapGestureDetector::New();
627     mTapDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnTap );
628
629     mPanGestureDetector = PanGestureDetector::New();
630     mPanGestureDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnPan );
631   }
632
633   void CreateActiveLayer()
634   {
635     if( !mActiveLayer )
636     {
637       mActiveLayer = Layer::New();
638 #ifdef DECORATOR_DEBUG
639       mActiveLayer.SetName ( "ActiveLayerActor" );
640 #endif
641
642       mActiveLayer.SetParentOrigin( ParentOrigin::CENTER );
643       mActiveLayer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
644
645       // Add the active layer telling the controller it doesn't need clipping.
646       mController.AddDecoration( mActiveLayer, false );
647     }
648
649     mActiveLayer.RaiseToTop();
650   }
651
652   void SetSelectionHandleMarkerSize( HandleImpl& handle )
653   {
654     if( handle.markerActor )
655     {
656       handle.markerActor.SetSize( 0, handle.lineHeight );
657     }
658   }
659
660   void CreateGrabHandle()
661   {
662     HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
663     if( !grabHandle.actor )
664     {
665       if( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] )
666       {
667         grabHandle.actor = ImageView::New( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] );
668         GetImpl( grabHandle.actor).SetDepthIndex( DepthIndex::DECORATION );
669         grabHandle.actor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
670
671         // Area that Grab handle responds to, larger than actual handle so easier to move
672 #ifdef DECORATOR_DEBUG
673         grabHandle.actor.SetName( "GrabHandleActor" );
674         if ( Dali::Internal::gLogFilter->IsEnabledFor( Debug::Verbose ) )
675         {
676           grabHandle.grabArea = Control::New();
677           Toolkit::Control control = Toolkit::Control::DownCast( grabHandle.grabArea );
678           control.SetBackgroundColor( Vector4( 1.0f, 1.0f, 1.0f, 0.5f ) );
679           grabHandle.grabArea.SetName( "GrabArea" );
680         }
681         else
682         {
683           grabHandle.grabArea = Actor::New();
684           grabHandle.grabArea.SetName( "GrabArea" );
685         }
686 #else
687         grabHandle.grabArea = Actor::New();
688 #endif
689
690         grabHandle.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
691         grabHandle.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
692         grabHandle.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
693         grabHandle.grabArea.SetSizeModeFactor( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE );
694         grabHandle.actor.Add( grabHandle.grabArea );
695         grabHandle.actor.SetColor( mHandleColor );
696
697         grabHandle.grabArea.TouchSignal().Connect( this, &Decorator::Impl::OnGrabHandleTouched );
698         mTapDetector.Attach( grabHandle.grabArea );
699         mPanGestureDetector.Attach( grabHandle.grabArea );
700
701         mActiveLayer.Add( grabHandle.actor );
702       }
703     }
704
705     if( grabHandle.actor && !grabHandle.actor.GetParent() )
706     {
707       mActiveLayer.Add( grabHandle.actor );
708     }
709   }
710
711   void CreateHandleMarker( HandleImpl& handle, Image& image, HandleType handleType )
712   {
713     if( image )
714     {
715       handle.markerActor = ImageView::New( image );
716       handle.markerActor.SetColor( mHandleColor );
717       handle.actor.Add( handle.markerActor );
718
719       handle.markerActor.SetResizePolicy ( ResizePolicy::FIXED, Dimension::HEIGHT );
720
721       if( LEFT_SELECTION_HANDLE == handleType )
722       {
723         handle.markerActor.SetAnchorPoint( AnchorPoint::BOTTOM_RIGHT );
724         handle.markerActor.SetParentOrigin( ParentOrigin::TOP_RIGHT );
725       }
726       else if( RIGHT_SELECTION_HANDLE == handleType )
727       {
728         handle.markerActor.SetAnchorPoint( AnchorPoint::BOTTOM_LEFT );
729         handle.markerActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
730       }
731     }
732   }
733
734   void CreateSelectionHandles()
735   {
736     HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
737     if( !primary.actor )
738     {
739       if( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] )
740       {
741         primary.actor = ImageView::New( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
742 #ifdef DECORATOR_DEBUG
743         primary.actor.SetName("SelectionHandleOne");
744 #endif
745         primary.actor.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
746         GetImpl( primary.actor ).SetDepthIndex( DepthIndex::DECORATION );
747         primary.actor.SetColor( mHandleColor );
748
749         primary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
750 #ifdef DECORATOR_DEBUG
751         primary.grabArea.SetName("SelectionHandleOneGrabArea");
752 #endif
753         primary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
754         primary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
755         primary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
756         primary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
757
758         mTapDetector.Attach( primary.grabArea );
759         mPanGestureDetector.Attach( primary.grabArea );
760         primary.grabArea.TouchSignal().Connect( this, &Decorator::Impl::OnHandleOneTouched );
761
762         primary.actor.Add( primary.grabArea );
763
764         CreateHandleMarker( primary, mHandleImages[LEFT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], LEFT_SELECTION_HANDLE );
765       }
766     }
767
768     if( primary.actor && !primary.actor.GetParent() )
769     {
770       mActiveLayer.Add( primary.actor );
771     }
772
773     HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
774     if( !secondary.actor )
775     {
776       if( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] )
777       {
778         secondary.actor = ImageView::New( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
779 #ifdef DECORATOR_DEBUG
780         secondary.actor.SetName("SelectionHandleTwo");
781 #endif
782         secondary.actor.SetAnchorPoint( AnchorPoint::TOP_LEFT ); // Change to BOTTOM_LEFT if Look'n'Feel requires handle above text.
783         GetImpl( secondary.actor ).SetDepthIndex( DepthIndex::DECORATION );
784         secondary.actor.SetColor( mHandleColor );
785
786         secondary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
787 #ifdef DECORATOR_DEBUG
788         secondary.grabArea.SetName("SelectionHandleTwoGrabArea");
789 #endif
790         secondary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
791         secondary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
792         secondary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
793         secondary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
794
795         mTapDetector.Attach( secondary.grabArea );
796         mPanGestureDetector.Attach( secondary.grabArea );
797         secondary.grabArea.TouchSignal().Connect( this, &Decorator::Impl::OnHandleTwoTouched );
798
799         secondary.actor.Add( secondary.grabArea );
800
801         CreateHandleMarker( secondary, mHandleImages[RIGHT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], RIGHT_SELECTION_HANDLE  );
802       }
803     }
804
805     if( secondary.actor && !secondary.actor.GetParent() )
806     {
807       mActiveLayer.Add( secondary.actor );
808     }
809   }
810
811   void CalculateHandleWorldCoordinates( HandleImpl& handle, Vector2& position )
812   {
813     // Gets the world position of the active layer. The active layer is where the handles are added.
814     const Vector3 parentWorldPosition = mActiveLayer.GetCurrentWorldPosition();
815
816     // The grab handle position in world coords.
817     // The active layer's world position is the center of the active layer. The origin of the
818     // coord system of the handles is the top left of the active layer.
819     position.x = parentWorldPosition.x - 0.5f * mControlSize.width + handle.position.x;
820     position.y = parentWorldPosition.y - 0.5f * mControlSize.height + handle.position.y;
821   }
822
823   void SetGrabHandlePosition()
824   {
825     // Reference to the grab handle.
826     HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
827
828     // Transforms the handle position into world coordinates.
829     // @note This is not the same value as grabHandle.actor.GetCurrentWorldPosition()
830     // as it's transforming the handle's position set by the text-controller and not
831     // the final position set to the actor. Another difference is the GetCurrentWorldPosition()
832     // retrieves the position of the center of the actor but the handle's position set
833     // by the text controller is not the center of the actor.
834     Vector2 grabHandleWorldPosition;
835     CalculateHandleWorldCoordinates( grabHandle, grabHandleWorldPosition );
836
837     // Check if the grab handle exceeds the boundaries of the decoration box.
838     // At the moment only the height is checked for the grab handle.
839
840     grabHandle.verticallyFlipped = ( grabHandle.verticallyFlippedPreferred &&
841                                      ( ( grabHandleWorldPosition.y - grabHandle.size.height ) > mBoundingBox.y ) ) ||
842                                    ( grabHandleWorldPosition.y + grabHandle.lineHeight + grabHandle.size.height > mBoundingBox.w );
843
844     // The grab handle 'y' position in local coords.
845     // If the grab handle exceeds the bottom of the decoration box,
846     // set the 'y' position to the top of the line.
847     // The SetGrabHandleImage() method will change the orientation.
848     const float yLocalPosition = grabHandle.verticallyFlipped ? grabHandle.position.y : grabHandle.position.y + grabHandle.lineHeight;
849
850     if( grabHandle.actor )
851     {
852       grabHandle.actor.SetPosition( grabHandle.position.x + floor( 0.5f * mCursorWidth ),
853                                     yLocalPosition ); // TODO : Fix for multiline.
854     }
855   }
856
857   void SetSelectionHandlePosition( HandleType type )
858   {
859     const bool isPrimaryHandle = LEFT_SELECTION_HANDLE == type;
860
861     // Reference to the selection handle.
862     HandleImpl& handle = mHandle[type];
863
864     // Transforms the handle position into world coordinates.
865     // @note This is not the same value as handle.actor.GetCurrentWorldPosition()
866     // as it's transforming the handle's position set by the text-controller and not
867     // the final position set to the actor. Another difference is the GetCurrentWorldPosition()
868     // retrieves the position of the center of the actor but the handle's position set
869     // by the text controller is not the center of the actor.
870     Vector2 handleWorldPosition;
871     CalculateHandleWorldCoordinates( handle, handleWorldPosition );
872
873     // Whether to flip the handle (horizontally).
874     bool flipHandle = isPrimaryHandle ? mFlipLeftSelectionHandleDirection : mFlipRightSelectionHandleDirection;
875
876     // Whether to flip the handles if they are crossed.
877     bool crossFlip = false;
878     if( mFlipSelectionHandlesOnCross || !mHandlePanning )
879     {
880       crossFlip = mHandleCurrentCrossed;
881     }
882
883     // Does not flip if both conditions are true (double flip)
884     flipHandle = flipHandle != ( crossFlip || mHandlePreviousCrossed );
885
886     // Will flip the handles vertically if the user prefers it.
887     bool verticallyFlippedPreferred = handle.verticallyFlippedPreferred;
888
889     if( crossFlip || mHandlePreviousCrossed )
890     {
891       if( isPrimaryHandle )
892       {
893         verticallyFlippedPreferred = mHandle[RIGHT_SELECTION_HANDLE].verticallyFlippedPreferred;
894       }
895       else
896       {
897         verticallyFlippedPreferred = mHandle[LEFT_SELECTION_HANDLE].verticallyFlippedPreferred;
898       }
899     }
900
901     // Check if the selection handle exceeds the boundaries of the decoration box.
902     const bool exceedsLeftEdge = ( isPrimaryHandle ? !flipHandle : flipHandle ) && ( handleWorldPosition.x - handle.size.width < mBoundingBox.x );
903     const bool exceedsRightEdge = ( isPrimaryHandle ? flipHandle : !flipHandle ) && ( handleWorldPosition.x + handle.size.width > mBoundingBox.z );
904
905     // Does not flip if both conditions are true (double flip)
906     flipHandle = flipHandle != ( exceedsLeftEdge || exceedsRightEdge );
907
908     if( flipHandle )
909     {
910       if( handle.actor && !handle.horizontallyFlipped )
911       {
912         // Change the anchor point to flip the image.
913         handle.actor.SetAnchorPoint( isPrimaryHandle ? AnchorPoint::TOP_LEFT : AnchorPoint::TOP_RIGHT );
914
915         handle.horizontallyFlipped = true;
916       }
917     }
918     else
919     {
920       if( handle.actor && handle.horizontallyFlipped )
921       {
922         // Reset the anchor point.
923         handle.actor.SetAnchorPoint( isPrimaryHandle ? AnchorPoint::TOP_RIGHT : AnchorPoint::TOP_LEFT );
924
925         handle.horizontallyFlipped = false;
926       }
927     }
928
929     // Whether to flip the handle vertically.
930     handle.verticallyFlipped = ( verticallyFlippedPreferred &&
931                                  ( ( handleWorldPosition.y - handle.size.height ) > mBoundingBox.y ) ) ||
932                                ( handleWorldPosition.y + handle.lineHeight + handle.size.height > mBoundingBox.w );
933
934     // The primary selection handle 'y' position in local coords.
935     // If the handle exceeds the bottom of the decoration box,
936     // set the 'y' position to the top of the line.
937     // The SetHandleImage() method will change the orientation.
938     const float yLocalPosition = handle.verticallyFlipped ? handle.position.y : handle.position.y + handle.lineHeight;
939
940     if( handle.actor )
941     {
942       handle.actor.SetPosition( handle.position.x,
943                                 yLocalPosition ); // TODO : Fix for multiline.
944     }
945   }
946
947   void SetHandleImage( HandleType type )
948   {
949     HandleImpl& handle = mHandle[type];
950
951     HandleType markerType = HANDLE_TYPE_COUNT;
952     // If the selection handle is flipped it chooses the image of the other selection handle. Does nothing for the grab handle.
953     if( LEFT_SELECTION_HANDLE == type )
954     {
955       type = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE;
956       markerType = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE_MARKER : LEFT_SELECTION_HANDLE_MARKER;
957     }
958     else if( RIGHT_SELECTION_HANDLE == type )
959     {
960       type = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE : RIGHT_SELECTION_HANDLE;
961       markerType = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE_MARKER : RIGHT_SELECTION_HANDLE_MARKER;
962     }
963
964     // Chooses between the released or pressed image. It checks whether the pressed image exists.
965     if( handle.actor )
966     {
967       const HandleImageType imageType = ( handle.pressed ? ( mHandleImages[type][HANDLE_IMAGE_PRESSED] ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
968
969       handle.actor.SetImage( mHandleImages[type][imageType] );
970     }
971
972     if( HANDLE_TYPE_COUNT != markerType )
973     {
974       if( handle.markerActor )
975       {
976         const HandleImageType markerImageType = ( handle.pressed ? ( mHandleImages[markerType][HANDLE_IMAGE_PRESSED] ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
977         handle.markerActor.SetImage( mHandleImages[markerType][markerImageType] );
978       }
979     }
980
981     // Whether to flip the handle vertically.
982     if( handle.actor )
983     {
984       handle.actor.SetOrientation( handle.verticallyFlipped ? ANGLE_180 : ANGLE_0, Vector3::XAXIS );
985     }
986   }
987
988   void CreateHighlight()
989   {
990     if( !mHighlightActor )
991     {
992       mHighlightActor = Actor::New();
993
994 #ifdef DECORATOR_DEBUG
995       mHighlightActor.SetName( "HighlightActor" );
996 #endif
997       mHighlightActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
998       mHighlightActor.SetSize( 1.0f, 1.0f );
999       mHighlightActor.SetColor( mHighlightColor );
1000       mHighlightActor.SetColorMode( USE_OWN_COLOR );
1001     }
1002
1003     // Add the highlight box telling the controller it needs clipping.
1004     mController.AddDecoration( mHighlightActor, true );
1005   }
1006
1007   void UpdateHighlight()
1008   {
1009     if ( mHighlightActor )
1010     {
1011       if( !mHighlightQuadList.empty() )
1012       {
1013         Vector< Vector2 > vertices;
1014         Vector< unsigned short> indices;
1015         Vector2 vertex;
1016
1017         std::vector<QuadCoordinates>::iterator iter = mHighlightQuadList.begin();
1018         std::vector<QuadCoordinates>::iterator endIter = mHighlightQuadList.end();
1019
1020         for( std::size_t v = 0; iter != endIter; ++iter,v+=4 )
1021         {
1022           QuadCoordinates& quad = *iter;
1023
1024           // top-left (v+0)
1025           vertex.x = quad.min.x;
1026           vertex.y = quad.min.y;
1027           vertices.PushBack( vertex );
1028
1029           // top-right (v+1)
1030           vertex.x = quad.max.x;
1031           vertex.y = quad.min.y;
1032           vertices.PushBack( vertex );
1033
1034           // bottom-left (v+2)
1035           vertex.x = quad.min.x;
1036           vertex.y = quad.max.y;
1037           vertices.PushBack( vertex );
1038
1039           // bottom-right (v+3)
1040           vertex.x = quad.max.x;
1041           vertex.y = quad.max.y;
1042           vertices.PushBack( vertex );
1043
1044           // triangle A (3, 1, 0)
1045           indices.PushBack( v + 3 );
1046           indices.PushBack( v + 1 );
1047           indices.PushBack( v );
1048
1049           // triangle B (0, 2, 3)
1050           indices.PushBack( v );
1051           indices.PushBack( v + 2 );
1052           indices.PushBack( v + 3 );
1053         }
1054
1055         if( ! mQuadVertices )
1056         {
1057           mQuadVertices = PropertyBuffer::New( mQuadVertexFormat );
1058         }
1059
1060         mQuadVertices.SetData( &vertices[ 0 ], vertices.Size() );
1061
1062         if( !mQuadGeometry )
1063         {
1064           mQuadGeometry = Geometry::New();
1065           mQuadGeometry.AddVertexBuffer( mQuadVertices );
1066         }
1067         mQuadGeometry.SetIndexBuffer( &indices[ 0 ], indices.Size() );
1068
1069         if( !mHighlightRenderer )
1070         {
1071           mHighlightRenderer = Dali::Renderer::New( mQuadGeometry, mHighlightShader );
1072           mHighlightActor.AddRenderer( mHighlightRenderer );
1073         }
1074       }
1075
1076       mHighlightActor.SetPosition( mHighlightPosition.x,
1077                                    mHighlightPosition.y );
1078
1079       mHighlightQuadList.clear();
1080
1081       if( mHighlightRenderer )
1082       {
1083         mHighlightRenderer.SetProperty( Renderer::Property::DEPTH_INDEX, mTextDepth - 2 ); // text is rendered at mTextDepth and text's shadow at mTextDepth -1u.
1084       }
1085     }
1086   }
1087
1088   void OnTap( Actor actor, const TapGesture& tap )
1089   {
1090     if( actor == mHandle[GRAB_HANDLE].actor )
1091     {
1092       // TODO
1093     }
1094   }
1095
1096   void DoPan( HandleImpl& handle, HandleType type, const PanGesture& gesture )
1097   {
1098     if( Gesture::Started == gesture.state )
1099     {
1100       handle.grabDisplacementX = handle.grabDisplacementY = 0.f;
1101     }
1102
1103     handle.grabDisplacementX += gesture.displacement.x;
1104     handle.grabDisplacementY += ( handle.verticallyFlipped ? -gesture.displacement.y : gesture.displacement.y );
1105
1106     const float x = handle.position.x + handle.grabDisplacementX;
1107     const float y = handle.position.y + handle.lineHeight*0.5f + handle.grabDisplacementY;
1108
1109     if( Gesture::Started    == gesture.state ||
1110         Gesture::Continuing == gesture.state )
1111     {
1112       Vector2 targetSize;
1113       mController.GetTargetSize( targetSize );
1114
1115       if( x < mScrollThreshold )
1116       {
1117         mScrollDirection = SCROLL_RIGHT;
1118         mHandleScrolling = type;
1119         StartScrollTimer();
1120       }
1121       else if( x > targetSize.width - mScrollThreshold )
1122       {
1123         mScrollDirection = SCROLL_LEFT;
1124         mHandleScrolling = type;
1125         StartScrollTimer();
1126       }
1127       else
1128       {
1129         mHandleScrolling = HANDLE_TYPE_COUNT;
1130         StopScrollTimer();
1131         mController.DecorationEvent( type, HANDLE_PRESSED, x, y );
1132       }
1133
1134       mHandlePanning = true;
1135     }
1136     else if( Gesture::Finished  == gesture.state ||
1137              Gesture::Cancelled == gesture.state )
1138     {
1139       if( mScrollTimer &&
1140           ( mScrollTimer.IsRunning() || mNotifyEndOfScroll ) )
1141       {
1142         mNotifyEndOfScroll = false;
1143         mHandleScrolling = HANDLE_TYPE_COUNT;
1144         StopScrollTimer();
1145         mController.DecorationEvent( type, HANDLE_STOP_SCROLLING, x, y );
1146       }
1147       else
1148       {
1149         mController.DecorationEvent( type, HANDLE_RELEASED, x, y );
1150       }
1151
1152       if( handle.actor )
1153       {
1154         handle.actor.SetImage( mHandleImages[type][HANDLE_IMAGE_RELEASED] );
1155       }
1156       handle.pressed = false;
1157
1158       mHandlePanning = false;
1159     }
1160   }
1161
1162   void OnPan( Actor actor, const PanGesture& gesture )
1163   {
1164     HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1165     HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1166     HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1167
1168     if( actor == grabHandle.grabArea )
1169     {
1170       DoPan( grabHandle, GRAB_HANDLE, gesture );
1171     }
1172     else if( actor == primarySelectionHandle.grabArea )
1173     {
1174       DoPan( primarySelectionHandle, LEFT_SELECTION_HANDLE, gesture );
1175     }
1176     else if( actor == secondarySelectionHandle.grabArea )
1177     {
1178       DoPan( secondarySelectionHandle, RIGHT_SELECTION_HANDLE, gesture );
1179     }
1180   }
1181
1182   bool OnGrabHandleTouched( Actor actor, const TouchData& touch )
1183   {
1184     // Switch between pressed/release grab-handle images
1185     if( touch.GetPointCount() > 0 &&
1186         mHandle[GRAB_HANDLE].actor )
1187     {
1188       const PointState::Type state = touch.GetState( 0 );
1189
1190       if( PointState::DOWN == state )
1191       {
1192         mHandle[GRAB_HANDLE].pressed = true;
1193       }
1194       else if( ( PointState::UP == state ) ||
1195                ( PointState::INTERRUPTED == state ) )
1196       {
1197         mHandle[GRAB_HANDLE].pressed = false;
1198       }
1199
1200       SetHandleImage( GRAB_HANDLE );
1201     }
1202
1203     // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1204     return true;
1205   }
1206
1207   bool OnHandleOneTouched( Actor actor, const TouchData& touch )
1208   {
1209     // Switch between pressed/release selection handle images
1210     if( touch.GetPointCount() > 0 &&
1211         mHandle[LEFT_SELECTION_HANDLE].actor )
1212     {
1213       const PointState::Type state = touch.GetState( 0 );
1214
1215       if( PointState::DOWN == state )
1216       {
1217         mHandle[LEFT_SELECTION_HANDLE].pressed = true;
1218       }
1219       else if( ( PointState::UP == state ) ||
1220                ( PointState::INTERRUPTED == state ) )
1221       {
1222         mHandle[LEFT_SELECTION_HANDLE].pressed = false;
1223         mHandlePreviousCrossed = mHandleCurrentCrossed;
1224         mHandlePanning = false;
1225       }
1226
1227       SetHandleImage( LEFT_SELECTION_HANDLE );
1228     }
1229
1230     // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1231     return true;
1232   }
1233
1234   bool OnHandleTwoTouched( Actor actor, const TouchData& touch )
1235   {
1236     // Switch between pressed/release selection handle images
1237     if( touch.GetPointCount() > 0 &&
1238         mHandle[RIGHT_SELECTION_HANDLE].actor )
1239     {
1240       const PointState::Type state = touch.GetState( 0 );
1241
1242       if( PointState::DOWN == state )
1243       {
1244         mHandle[RIGHT_SELECTION_HANDLE].pressed = true;
1245       }
1246       else if( ( PointState::UP == state ) ||
1247                ( PointState::INTERRUPTED == state ) )
1248       {
1249         mHandle[RIGHT_SELECTION_HANDLE].pressed = false;
1250         mHandlePreviousCrossed = mHandleCurrentCrossed;
1251         mHandlePanning = false;
1252       }
1253
1254       SetHandleImage( RIGHT_SELECTION_HANDLE );
1255     }
1256
1257     // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1258     return true;
1259   }
1260
1261   void HandleResetPosition( PropertyNotification& source )
1262   {
1263     const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1264
1265     if( grabHandle.active )
1266     {
1267       // Sets the grab handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1268       SetGrabHandlePosition();
1269
1270       // Sets the grab handle image according if it's pressed, flipped, etc.
1271       SetHandleImage( GRAB_HANDLE );
1272     }
1273     else
1274     {
1275       // Sets the primary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1276       SetSelectionHandlePosition( LEFT_SELECTION_HANDLE );
1277
1278       // Sets the primary handle image according if it's pressed, flipped, etc.
1279       SetHandleImage( LEFT_SELECTION_HANDLE );
1280
1281       // Sets the secondary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1282       SetSelectionHandlePosition( RIGHT_SELECTION_HANDLE );
1283
1284       // Sets the secondary handle image according if it's pressed, flipped, etc.
1285       SetHandleImage( RIGHT_SELECTION_HANDLE );
1286     }
1287   }
1288
1289   void SetupActiveLayerPropertyNotifications()
1290   {
1291     if( !mActiveLayer )
1292     {
1293       return;
1294     }
1295
1296     // Vertical notifications.
1297
1298     // Disconnect any previous connected callback.
1299     if( mVerticalLessThanNotification )
1300     {
1301       mVerticalLessThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1302       mActiveLayer.RemovePropertyNotification( mVerticalLessThanNotification );
1303     }
1304
1305     if( mVerticalGreaterThanNotification )
1306     {
1307       mVerticalGreaterThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1308       mActiveLayer.RemovePropertyNotification( mVerticalGreaterThanNotification );
1309     }
1310
1311     const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1312     const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1313     const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1314
1315     if( grabHandle.active )
1316     {
1317       if( grabHandle.verticallyFlipped )
1318       {
1319         // The grab handle is vertically flipped. Never is going to exceed the bottom edje of the display.
1320         mVerticalGreaterThanNotification.Reset();
1321
1322         // The vertical distance from the center of the active layer to the top edje of the display.
1323         const float topHeight = 0.5f * mControlSize.height - grabHandle.position.y + grabHandle.size.height;
1324
1325         mVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1326                                                                               LessThanCondition( mBoundingBox.y + topHeight ) );
1327
1328         // Notifies the change from false to true and from true to false.
1329         mVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1330
1331         // Connects the signals with the callbacks.
1332         mVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1333       }
1334       else
1335       {
1336         // The grab handle is not vertically flipped. Never is going to exceed the top edje of the display.
1337         mVerticalLessThanNotification.Reset();
1338
1339         // The vertical distance from the center of the active layer to the bottom edje of the display.
1340         const float bottomHeight = -0.5f * mControlSize.height + grabHandle.position.y + grabHandle.lineHeight + grabHandle.size.height;
1341
1342         mVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1343                                                                                  GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1344
1345         // Notifies the change from false to true and from true to false.
1346         mVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1347
1348         // Connects the signals with the callbacks.
1349         mVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1350       }
1351     }
1352     else // The selection handles are active
1353     {
1354       if( primaryHandle.verticallyFlipped && secondaryHandle.verticallyFlipped )
1355       {
1356         // Both selection handles are vertically flipped. Never are going to exceed the bottom edje of the display.
1357         mVerticalGreaterThanNotification.Reset();
1358
1359         // The vertical distance from the center of the active layer to the top edje of the display.
1360         const float topHeight = 0.5f * mControlSize.height + std::max( -primaryHandle.position.y + primaryHandle.size.height, -secondaryHandle.position.y + secondaryHandle.size.height );
1361
1362         mVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1363                                                                               LessThanCondition( mBoundingBox.y + topHeight ) );
1364
1365         // Notifies the change from false to true and from true to false.
1366         mVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1367
1368         // Connects the signals with the callbacks.
1369         mVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1370       }
1371       else if( !primaryHandle.verticallyFlipped && !secondaryHandle.verticallyFlipped )
1372       {
1373         // Both selection handles aren't vertically flipped. Never are going to exceed the top edje of the display.
1374         mVerticalLessThanNotification.Reset();
1375
1376         // The vertical distance from the center of the active layer to the bottom edje of the display.
1377         const float bottomHeight = -0.5f * mControlSize.height + std::max( primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height,
1378                                                                            secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height );
1379
1380         mVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1381                                                                                  GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1382
1383         // Notifies the change from false to true and from true to false.
1384         mVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1385
1386         // Connects the signals with the callbacks.
1387         mVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1388       }
1389       else
1390       {
1391         // Only one of the selection handles is vertically flipped. Both vertical notifications are needed.
1392
1393         // The vertical distance from the center of the active layer to the top edje of the display.
1394         const float topHeight = 0.5f * mControlSize.height + ( primaryHandle.verticallyFlipped                              ?
1395                                                                -primaryHandle.position.y + primaryHandle.size.height        :
1396                                                                -secondaryHandle.position.y + secondaryHandle.size.height );
1397
1398         mVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1399                                                                               LessThanCondition( mBoundingBox.y + topHeight ) );
1400
1401         // Notifies the change from false to true and from true to false.
1402         mVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1403
1404         // Connects the signals with the callbacks.
1405         mVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1406
1407         // The vertical distance from the center of the active layer to the bottom edje of the display.
1408         const float bottomHeight = -0.5f * mControlSize.height + ( primaryHandle.verticallyFlipped                                                       ?
1409                                                                    secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height :
1410                                                                    primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height );
1411
1412         mVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1413                                                                                  GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1414
1415         // Notifies the change from false to true and from true to false.
1416         mVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1417
1418         // Connects the signals with the callbacks.
1419         mVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1420       }
1421     }
1422
1423     // Horizontal notifications.
1424
1425     // Disconnect any previous connected callback.
1426     if( mHorizontalLessThanNotification )
1427     {
1428       mHorizontalLessThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1429       mActiveLayer.RemovePropertyNotification( mHorizontalLessThanNotification );
1430     }
1431
1432     if( mHorizontalGreaterThanNotification )
1433     {
1434       mHorizontalGreaterThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1435       mActiveLayer.RemovePropertyNotification( mHorizontalGreaterThanNotification );
1436     }
1437
1438     if( primaryHandle.active || secondaryHandle.active )
1439     {
1440       // The horizontal distance from the center of the active layer to the left edje of the display.
1441       const float leftWidth = 0.5f * mControlSize.width + std::max( -primaryHandle.position.x + primaryHandle.size.width,
1442                                                                     -secondaryHandle.position.x + secondaryHandle.size.width );
1443
1444       mHorizontalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_X,
1445                                                                               LessThanCondition( mBoundingBox.x + leftWidth ) );
1446
1447       // Notifies the change from false to true and from true to false.
1448       mHorizontalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1449
1450       // Connects the signals with the callbacks.
1451       mHorizontalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1452
1453       // The horizontal distance from the center of the active layer to the right edje of the display.
1454       const float rightWidth = -0.5f * mControlSize.width + std::max( primaryHandle.position.x + primaryHandle.size.width,
1455                                                                       secondaryHandle.position.x + secondaryHandle.size.width );
1456
1457       mHorizontalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_X,
1458                                                                                  GreaterThanCondition( mBoundingBox.z - rightWidth ) );
1459
1460       // Notifies the change from false to true and from true to false.
1461       mHorizontalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1462
1463       // Connects the signals with the callbacks.
1464       mHorizontalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1465     }
1466   }
1467
1468   // Popup
1469
1470   float AlternatePopUpPositionRelativeToCursor()
1471   {
1472     float alternativePosition = 0.0f;
1473
1474     const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1475
1476     const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1477     const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1478     const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1479     const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
1480
1481     if( primaryHandle.active || secondaryHandle.active )
1482     {
1483       const float maxHandleHeight = std::max( primaryHandle.size.height, secondaryHandle.size.height );
1484       alternativePosition = 0.5f * popupHeight + cursor.lineHeight + maxHandleHeight + std::min( primaryHandle.position.y, secondaryHandle.position.y );
1485     }
1486     else
1487     {
1488       alternativePosition = 0.5f * popupHeight + cursor.lineHeight + grabHandle.size.height + cursor.position.y;
1489     }
1490
1491     return alternativePosition;
1492   }
1493
1494   void PopUpLeavesVerticalBoundary( PropertyNotification& source )
1495   {
1496     float alternativeYPosition = 0.0f;
1497     // todo use AlternatePopUpPositionRelativeToSelectionHandles() if text is highlighted
1498     // if can't be positioned above, then position below row.
1499     alternativeYPosition = AlternatePopUpPositionRelativeToCursor();
1500
1501     mCopyPastePopup.actor.SetY( alternativeYPosition );
1502   }
1503
1504   void SetUpPopupPositionNotifications()
1505   {
1506     // Note Property notifications ignore any set anchor point so conditions must allow for this.  Default is Top Left.
1507
1508     // Exceeding vertical boundary
1509
1510     const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1511
1512     PropertyNotification verticalExceedNotification = mCopyPastePopup.actor.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1513                                                                                                      OutsideCondition( mBoundingBox.y + popupHeight * 0.5f,
1514                                                                                                                        mBoundingBox.w - popupHeight * 0.5f ) );
1515
1516     verticalExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesVerticalBoundary );
1517   }
1518
1519   void GetConstrainedPopupPosition( Vector3& requiredPopupPosition, const Vector3& popupDistanceFromAnchorPoint, Actor parent, const Vector4& boundingRectangleWorld )
1520   {
1521     DALI_ASSERT_DEBUG ( "Popup parent not on stage" && parent.OnStage() )
1522
1523     // Parent must already by added to Stage for these Get calls to work
1524     const Vector3 parentWorldPositionLeftAnchor = parent.GetCurrentWorldPosition() - parent.GetCurrentSize() * parent.GetCurrentAnchorPoint();
1525     const Vector3 popupWorldPosition = parentWorldPositionLeftAnchor + requiredPopupPosition;  // Parent World position plus popup local position gives World Position
1526
1527     // Calculate distance to move popup (in local space) so fits within the boundary
1528     float xOffSetToKeepWithinBounds = 0.0f;
1529     if( popupWorldPosition.x - popupDistanceFromAnchorPoint.x < boundingRectangleWorld.x )
1530     {
1531       xOffSetToKeepWithinBounds = boundingRectangleWorld.x - ( popupWorldPosition.x - popupDistanceFromAnchorPoint.x );
1532     }
1533     else if( popupWorldPosition.x +  popupDistanceFromAnchorPoint.x > boundingRectangleWorld.z )
1534     {
1535       xOffSetToKeepWithinBounds = boundingRectangleWorld.z - ( popupWorldPosition.x +  popupDistanceFromAnchorPoint.x );
1536     }
1537
1538     // Ensure initial display of Popup is in alternative position if can not fit above. As Property notification will be a frame behind.
1539     if( popupWorldPosition.y - popupDistanceFromAnchorPoint.y < boundingRectangleWorld.y )
1540     {
1541       requiredPopupPosition.y = AlternatePopUpPositionRelativeToCursor();
1542     }
1543
1544     requiredPopupPosition.x = requiredPopupPosition.x + xOffSetToKeepWithinBounds;
1545
1546     // Prevent pixel mis-alignment by rounding down.
1547     requiredPopupPosition.x = floor( requiredPopupPosition.x );
1548     requiredPopupPosition.y = floor( requiredPopupPosition.y );
1549   }
1550
1551   void SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image )
1552   {
1553     HandleImpl& handle = mHandle[handleType];
1554     handle.size = Size( image.GetWidth(), image.GetHeight() );
1555
1556     mHandleImages[handleType][handleImageType] = image;
1557   }
1558
1559   void SetScrollThreshold( float threshold )
1560   {
1561     mScrollThreshold = threshold;
1562   }
1563
1564   float GetScrollThreshold() const
1565   {
1566     return mScrollThreshold;
1567   }
1568
1569   void SetScrollSpeed( float speed )
1570   {
1571     mScrollSpeed = speed;
1572     mScrollDistance = speed * SCROLL_TICK_INTERVAL * TO_SECONDS;
1573   }
1574
1575   float GetScrollSpeed() const
1576   {
1577     return mScrollSpeed;
1578   }
1579
1580   void NotifyEndOfScroll()
1581   {
1582     StopScrollTimer();
1583
1584     if( mScrollTimer )
1585     {
1586       mNotifyEndOfScroll = true;
1587     }
1588   }
1589
1590   /**
1591    * Creates and starts a timer to scroll the text when handles are close to the edges of the text.
1592    *
1593    * It only starts the timer if it's already created.
1594    */
1595   void StartScrollTimer()
1596   {
1597     if( !mScrollTimer )
1598     {
1599       mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1600       mScrollTimer.TickSignal().Connect( this, &Decorator::Impl::OnScrollTimerTick );
1601     }
1602
1603     if( !mScrollTimer.IsRunning() )
1604     {
1605       mScrollTimer.Start();
1606     }
1607   }
1608
1609   /**
1610    * Stops the timer used to scroll the text.
1611    */
1612   void StopScrollTimer()
1613   {
1614     if( mScrollTimer )
1615     {
1616       mScrollTimer.Stop();
1617     }
1618   }
1619
1620   /**
1621    * Callback called by the timer used to scroll the text.
1622    *
1623    * It calculates and sets a new scroll position.
1624    */
1625   bool OnScrollTimerTick()
1626   {
1627     if( HANDLE_TYPE_COUNT != mHandleScrolling )
1628     {
1629       mController.DecorationEvent( mHandleScrolling,
1630                                    HANDLE_SCROLLING,
1631                                    mScrollDirection == SCROLL_RIGHT ? mScrollDistance : -mScrollDistance,
1632                                    0.f );
1633     }
1634
1635     return true;
1636   }
1637
1638   ControllerInterface& mController;
1639
1640   TapGestureDetector  mTapDetector;
1641   PanGestureDetector  mPanGestureDetector;
1642   Timer               mCursorBlinkTimer;          ///< Timer to signal cursor to blink
1643   Timer               mScrollTimer;               ///< Timer used to scroll the text when the grab handle is moved close to the edges.
1644
1645   Layer                mActiveLayer;                       ///< Layer for active handles and alike that ensures they are above all else.
1646   PropertyNotification mVerticalLessThanNotification;      ///< Notifies when the 'y' coord of the active layer is less than a given value.
1647   PropertyNotification mVerticalGreaterThanNotification;   ///< Notifies when the 'y' coord of the active layer is grater than a given value.
1648   PropertyNotification mHorizontalLessThanNotification;    ///< Notifies when the 'x' coord of the active layer is less than a given value.
1649   PropertyNotification mHorizontalGreaterThanNotification; ///< Notifies when the 'x' coord of the active layer is grater than a given value.
1650   Control              mPrimaryCursor;
1651   Control              mSecondaryCursor;
1652
1653   Actor               mHighlightActor;            ///< Actor to display highlight
1654   Renderer            mHighlightRenderer;
1655   Shader              mHighlightShader;           ///< Shader used for highlight
1656   Property::Map       mQuadVertexFormat;
1657   PopupImpl           mCopyPastePopup;
1658   TextSelectionPopup::Buttons mEnabledPopupButtons; /// Bit mask of currently enabled Popup buttons
1659   TextSelectionPopupCallbackInterface& mTextSelectionPopupCallbackInterface;
1660
1661   Image               mHandleImages[HANDLE_TYPE_COUNT][HANDLE_IMAGE_TYPE_COUNT];
1662   Vector4             mHandleColor;
1663
1664   CursorImpl          mCursor[CURSOR_COUNT];
1665   HandleImpl          mHandle[HANDLE_TYPE_COUNT];
1666
1667   PropertyBuffer      mQuadVertices;
1668   Geometry            mQuadGeometry;
1669   QuadContainer       mHighlightQuadList;         ///< Sub-selections that combine to create the complete selection highlight
1670
1671   Vector4             mBoundingBox;               ///< The bounding box in world coords.
1672   Vector4             mHighlightColor;            ///< Color of the highlight
1673   Vector2             mHighlightPosition;         ///< The position of the highlight actor.
1674   Vector2             mControlSize;               ///< The control's size. Set by the Relayout.
1675
1676   unsigned int        mActiveCursor;
1677   unsigned int        mCursorBlinkInterval;
1678   float               mCursorBlinkDuration;
1679   float               mCursorWidth;             ///< The width of the cursors in pixels.
1680   HandleType          mHandleScrolling;         ///< The handle which is scrolling.
1681   ScrollDirection     mScrollDirection;         ///< The direction of the scroll.
1682   float               mScrollThreshold;         ///< Defines a square area inside the control, close to the edge. A cursor entering this area will trigger scroll events.
1683   float               mScrollSpeed;             ///< The scroll speed in pixels per second.
1684   float               mScrollDistance;          ///< Distance the text scrolls during a scroll interval.
1685   int                 mTextDepth;               ///< The depth used to render the text.
1686
1687   bool                mActiveCopyPastePopup              : 1;
1688   bool                mPopupSetNewPosition               : 1;
1689   bool                mCursorBlinkStatus                 : 1; ///< Flag to switch between blink on and blink off.
1690   bool                mDelayCursorBlink                  : 1; ///< Used to avoid cursor blinking when entering text.
1691   bool                mPrimaryCursorVisible              : 1; ///< Whether the primary cursor is visible.
1692   bool                mSecondaryCursorVisible            : 1; ///< Whether the secondary cursor is visible.
1693   bool                mFlipSelectionHandlesOnCross       : 1; ///< Whether to flip the selection handles as soon as they cross.
1694   bool                mFlipLeftSelectionHandleDirection  : 1; ///< Whether to flip the left selection handle image because of the character's direction.
1695   bool                mFlipRightSelectionHandleDirection : 1; ///< Whether to flip the right selection handle image because of the character's direction.
1696   bool                mHandlePanning                     : 1; ///< Whether any of the handles is moving.
1697   bool                mHandleCurrentCrossed              : 1; ///< Whether the handles are crossed.
1698   bool                mHandlePreviousCrossed             : 1; ///< Whether the handles where crossed at the last handle touch up.
1699   bool                mNotifyEndOfScroll                 : 1; ///< Whether to notify the end of the scroll.
1700 };
1701
1702 DecoratorPtr Decorator::New( ControllerInterface& controller,
1703                              TextSelectionPopupCallbackInterface& callbackInterface )
1704 {
1705   return DecoratorPtr( new Decorator( controller,
1706                                       callbackInterface ) );
1707 }
1708
1709 void Decorator::SetBoundingBox( const Rect<int>& boundingBox )
1710 {
1711   LocalToWorldCoordinatesBoundingBox( boundingBox, mImpl->mBoundingBox );
1712 }
1713
1714 void Decorator::GetBoundingBox( Rect<int>& boundingBox ) const
1715 {
1716   WorldToLocalCoordinatesBoundingBox( mImpl->mBoundingBox, boundingBox );
1717 }
1718
1719 void Decorator::Relayout( const Vector2& size )
1720 {
1721   mImpl->Relayout( size );
1722 }
1723
1724 void Decorator::UpdatePositions( const Vector2& scrollOffset )
1725 {
1726   mImpl->UpdatePositions( scrollOffset );
1727 }
1728
1729 /** Cursor **/
1730
1731 void Decorator::SetActiveCursor( ActiveCursor activeCursor )
1732 {
1733   mImpl->mActiveCursor = activeCursor;
1734 }
1735
1736 unsigned int Decorator::GetActiveCursor() const
1737 {
1738   return mImpl->mActiveCursor;
1739 }
1740
1741 void Decorator::SetPosition( Cursor cursor, float x, float y, float cursorHeight, float lineHeight )
1742 {
1743   mImpl->mCursor[cursor].position.x = x;
1744   mImpl->mCursor[cursor].position.y = y;
1745   mImpl->mCursor[cursor].cursorHeight = cursorHeight;
1746   mImpl->mCursor[cursor].lineHeight = lineHeight;
1747 }
1748
1749 void Decorator::GetPosition( Cursor cursor, float& x, float& y, float& cursorHeight, float& lineHeight ) const
1750 {
1751   x = mImpl->mCursor[cursor].position.x;
1752   y = mImpl->mCursor[cursor].position.y;
1753   cursorHeight = mImpl->mCursor[cursor].cursorHeight;
1754   lineHeight = mImpl->mCursor[cursor].lineHeight;
1755 }
1756
1757 const Vector2& Decorator::GetPosition( Cursor cursor ) const
1758 {
1759   return mImpl->mCursor[cursor].position;
1760 }
1761
1762 void Decorator::SetCursorColor( Cursor cursor, const Dali::Vector4& color )
1763 {
1764   mImpl->mCursor[cursor].color = color;
1765 }
1766
1767 const Dali::Vector4& Decorator::GetColor( Cursor cursor ) const
1768 {
1769   return mImpl->mCursor[cursor].color;
1770 }
1771
1772 void Decorator::StartCursorBlink()
1773 {
1774   if ( !mImpl->mCursorBlinkTimer )
1775   {
1776     mImpl->mCursorBlinkTimer = Timer::New( mImpl->mCursorBlinkInterval );
1777     mImpl->mCursorBlinkTimer.TickSignal().Connect( mImpl, &Decorator::Impl::OnCursorBlinkTimerTick );
1778   }
1779
1780   if ( !mImpl->mCursorBlinkTimer.IsRunning() )
1781   {
1782     mImpl->mCursorBlinkTimer.Start();
1783   }
1784 }
1785
1786 void Decorator::StopCursorBlink()
1787 {
1788   if ( mImpl->mCursorBlinkTimer )
1789   {
1790     mImpl->mCursorBlinkTimer.Stop();
1791   }
1792
1793   mImpl->mCursorBlinkStatus = true; // Keep cursor permanently shown
1794 }
1795
1796 void Decorator::DelayCursorBlink()
1797 {
1798   mImpl->mCursorBlinkStatus = true; // Show cursor for a bit longer
1799   mImpl->mDelayCursorBlink = true;
1800 }
1801
1802 void Decorator::SetCursorBlinkInterval( float seconds )
1803 {
1804   mImpl->mCursorBlinkInterval = static_cast<unsigned int>( seconds * TO_MILLISECONDS ); // Convert to milliseconds
1805 }
1806
1807 float Decorator::GetCursorBlinkInterval() const
1808 {
1809   return static_cast<float>( mImpl->mCursorBlinkInterval ) * TO_SECONDS;
1810 }
1811
1812 void Decorator::SetCursorBlinkDuration( float seconds )
1813 {
1814   mImpl->mCursorBlinkDuration = seconds;
1815 }
1816
1817 float Decorator::GetCursorBlinkDuration() const
1818 {
1819   return mImpl->mCursorBlinkDuration;
1820 }
1821
1822 void Decorator::SetCursorWidth( int width )
1823 {
1824   mImpl->mCursorWidth = static_cast<float>( width );
1825 }
1826
1827 int Decorator::GetCursorWidth() const
1828 {
1829   return static_cast<int>( mImpl->mCursorWidth );
1830 }
1831
1832 /** Handles **/
1833
1834 void Decorator::SetHandleActive( HandleType handleType, bool active )
1835 {
1836   mImpl->mHandle[handleType].active = active;
1837
1838   if( !active )
1839   {
1840     if( ( LEFT_SELECTION_HANDLE == handleType ) || ( RIGHT_SELECTION_HANDLE == handleType ) )
1841     {
1842       mImpl->mHandlePreviousCrossed = false;
1843     }
1844
1845     // TODO: this is a work-around.
1846     // The problem is the handle actor does not receive the touch event with the Interrupt
1847     // state when the power button is pressed and the application goes to background.
1848     mImpl->mHandle[handleType].pressed = false;
1849     Image imageReleased = mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED];
1850     ImageView imageView = mImpl->mHandle[handleType].actor;
1851     if( imageReleased && imageView )
1852     {
1853       imageView.SetImage( imageReleased );
1854     }
1855   }
1856
1857 }
1858
1859 bool Decorator::IsHandleActive( HandleType handleType ) const
1860 {
1861   return mImpl->mHandle[handleType].active ;
1862 }
1863
1864 void Decorator::SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image )
1865 {
1866   mImpl->SetHandleImage( handleType, handleImageType, image );
1867 }
1868
1869 Dali::Image Decorator::GetHandleImage( HandleType handleType, HandleImageType handleImageType ) const
1870 {
1871   return mImpl->mHandleImages[handleType][handleImageType];
1872 }
1873
1874 void Decorator::SetHandleColor( const Vector4& color )
1875 {
1876   mImpl->mHandleColor = color;
1877 }
1878
1879 const Vector4& Decorator::GetHandleColor() const
1880 {
1881   return mImpl->mHandleColor;
1882 }
1883
1884 void Decorator::SetPosition( HandleType handleType, float x, float y, float height )
1885 {
1886   // Adjust handle's displacement
1887   Impl::HandleImpl& handle = mImpl->mHandle[handleType];
1888
1889   handle.grabDisplacementX -= x - handle.position.x;
1890   handle.grabDisplacementY -= y - handle.position.y;
1891
1892   handle.position.x = x;
1893   handle.position.y = y;
1894   handle.lineHeight = height;
1895 }
1896
1897 void Decorator::GetPosition( HandleType handleType, float& x, float& y, float& height ) const
1898 {
1899   Impl::HandleImpl& handle = mImpl->mHandle[handleType];
1900
1901   x = handle.position.x;
1902   y = handle.position.y;
1903   height = handle.lineHeight;
1904 }
1905
1906 const Vector2& Decorator::GetPosition( HandleType handleType ) const
1907 {
1908   return mImpl->mHandle[handleType].position;
1909 }
1910
1911 void Decorator::FlipHandleVertically( HandleType handleType, bool flip )
1912 {
1913   mImpl->mHandle[handleType].verticallyFlippedPreferred = flip;
1914 }
1915
1916 bool Decorator::IsHandleVerticallyFlipped( HandleType handleType ) const
1917 {
1918   return mImpl->mHandle[handleType].verticallyFlippedPreferred;
1919 }
1920
1921 void Decorator::FlipSelectionHandlesOnCrossEnabled( bool enable )
1922 {
1923   mImpl->mFlipSelectionHandlesOnCross = enable;
1924 }
1925
1926 void Decorator::SetSelectionHandleFlipState( bool indicesSwapped, bool left, bool right )
1927 {
1928   mImpl->mHandleCurrentCrossed = indicesSwapped;
1929   mImpl->mFlipLeftSelectionHandleDirection = left;
1930   mImpl->mFlipRightSelectionHandleDirection = right;
1931 }
1932
1933 void Decorator::AddHighlight( float x1, float y1, float x2, float y2 )
1934 {
1935   mImpl->mHighlightQuadList.push_back( QuadCoordinates(x1, y1, x2, y2) );
1936 }
1937
1938 void Decorator::ClearHighlights()
1939 {
1940   mImpl->mHighlightQuadList.clear();
1941   mImpl->mHighlightPosition = Vector2::ZERO;
1942 }
1943
1944 void Decorator::SetHighlightColor( const Vector4& color )
1945 {
1946   mImpl->mHighlightColor = color;
1947 }
1948
1949 const Vector4& Decorator::GetHighlightColor() const
1950 {
1951   return mImpl->mHighlightColor;
1952 }
1953
1954 void Decorator::SetTextDepth( int textDepth )
1955 {
1956   mImpl->mTextDepth = textDepth;
1957 }
1958
1959 void Decorator::SetPopupActive( bool active )
1960 {
1961   mImpl->mActiveCopyPastePopup = active;
1962 }
1963
1964 bool Decorator::IsPopupActive() const
1965 {
1966   return mImpl->mActiveCopyPastePopup ;
1967 }
1968
1969 void Decorator::SetEnabledPopupButtons( TextSelectionPopup::Buttons& enabledButtonsBitMask )
1970 {
1971   mImpl->mEnabledPopupButtons = enabledButtonsBitMask;
1972
1973   if ( !mImpl->mCopyPastePopup.actor )
1974   {
1975     mImpl->mCopyPastePopup.actor = TextSelectionPopup::New( &mImpl->mTextSelectionPopupCallbackInterface );
1976 #ifdef DECORATOR_DEBUG
1977     mImpl->mCopyPastePopup.actor.SetName("mCopyPastePopup");
1978 #endif
1979     mImpl->mCopyPastePopup.actor.SetAnchorPoint( AnchorPoint::CENTER );
1980     mImpl->mCopyPastePopup.actor.OnRelayoutSignal().Connect( mImpl,  &Decorator::Impl::PopupRelayoutComplete  ); // Position popup after size negotiation
1981   }
1982
1983   mImpl->mCopyPastePopup.actor.EnableButtons( mImpl->mEnabledPopupButtons );
1984 }
1985
1986 TextSelectionPopup::Buttons& Decorator::GetEnabledPopupButtons()
1987 {
1988   return mImpl->mEnabledPopupButtons;
1989 }
1990
1991 /** Scroll **/
1992
1993 void Decorator::SetScrollThreshold( float threshold )
1994 {
1995   mImpl->SetScrollThreshold( threshold );
1996 }
1997
1998 float Decorator::GetScrollThreshold() const
1999 {
2000   return mImpl->GetScrollThreshold();
2001 }
2002
2003 void Decorator::SetScrollSpeed( float speed )
2004 {
2005   mImpl->SetScrollSpeed( speed );
2006 }
2007
2008 float Decorator::GetScrollSpeed() const
2009 {
2010   return mImpl->GetScrollSpeed();
2011 }
2012
2013 void Decorator::NotifyEndOfScroll()
2014 {
2015   mImpl->NotifyEndOfScroll();
2016 }
2017
2018 Decorator::~Decorator()
2019 {
2020   delete mImpl;
2021 }
2022
2023 Decorator::Decorator( ControllerInterface& controller,
2024                       TextSelectionPopupCallbackInterface& callbackInterface )
2025 : mImpl( NULL )
2026 {
2027   mImpl = new Decorator::Impl( controller, callbackInterface );
2028 }
2029
2030 } // namespace Text
2031
2032 } // namespace Toolkit
2033
2034 } // namespace Dali