Fix for text handle positioning.
[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-event.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     SetupTouchEvents();
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 HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
490       const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
491
492       if( primaryHandle.active || secondaryHandle.active )
493       {
494         // Calculates the popup's position if selection handles are active.
495         const float minHandleXPosition = std::min( primaryHandle.position.x, secondaryHandle.position.x );
496         const float maxHandleXPosition = std::max( primaryHandle.position.x, secondaryHandle.position.x );
497         const float maxHandleHeight = std::max( primaryHandle.size.height, secondaryHandle.size.height );
498
499         mCopyPastePopup.position.x = minHandleXPosition + ( ( maxHandleXPosition - minHandleXPosition ) * 0.5f );
500         mCopyPastePopup.position.y = -0.5f * popupSize.height - maxHandleHeight + std::min( primaryHandle.position.y, secondaryHandle.position.y );
501       }
502       else
503       {
504         // Calculates the popup's position if the grab handle is active.
505         mCopyPastePopup.position = Vector3( cursor.position.x, -0.5f * popupSize.height - grabHandle.size.height + cursor.position.y, 0.0f );
506       }
507     }
508
509     // Checks if there is enough space above the text control. If not it places the popup under it.
510     GetConstrainedPopupPosition( mCopyPastePopup.position, popupSize * AnchorPoint::CENTER, mActiveLayer, mBoundingBox );
511
512     SetUpPopupPositionNotifications();
513
514     mCopyPastePopup.actor.SetPosition( mCopyPastePopup.position );
515     mPopupSetNewPosition = false;
516   }
517
518   void PopupRelayoutComplete( Actor actor )
519   {
520     // Size negotiation for CopyPastePopup complete so can get the size and constrain position within bounding box.
521
522     DeterminePositionPopup();
523   }
524
525   void CreateCursor( Control& cursor, const Vector4& color )
526   {
527     cursor = Control::New();
528     cursor.SetBackgroundColor( color );
529     cursor.SetParentOrigin( ParentOrigin::TOP_LEFT );
530     cursor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
531   }
532
533   // Add or Remove cursor(s) from parent
534   void CreateCursors()
535   {
536     if( mActiveCursor == ACTIVE_CURSOR_NONE )
537     {
538       if( mPrimaryCursor )
539       {
540         mPrimaryCursor.Unparent();
541       }
542       if( mSecondaryCursor )
543       {
544         mSecondaryCursor.Unparent();
545       }
546     }
547     else
548     {
549       // Create Primary and or Secondary Cursor(s) if active and add to parent
550       if ( mActiveCursor == ACTIVE_CURSOR_PRIMARY ||
551            mActiveCursor == ACTIVE_CURSOR_BOTH )
552       {
553         if ( !mPrimaryCursor )
554         {
555           CreateCursor( mPrimaryCursor, mCursor[PRIMARY_CURSOR].color );
556 #ifdef DECORATOR_DEBUG
557           mPrimaryCursor.SetName( "PrimaryCursorActor" );
558 #endif
559         }
560
561         if( !mPrimaryCursor.GetParent() )
562         {
563           mActiveLayer.Add( mPrimaryCursor );
564         }
565       }
566
567       if ( mActiveCursor == ACTIVE_CURSOR_BOTH )
568       {
569         if ( !mSecondaryCursor )
570         {
571           CreateCursor( mSecondaryCursor, mCursor[SECONDARY_CURSOR].color );
572 #ifdef DECORATOR_DEBUG
573           mSecondaryCursor.SetName( "SecondaryCursorActor" );
574 #endif
575         }
576
577         if( !mSecondaryCursor.GetParent() )
578         {
579           mActiveLayer.Add( mSecondaryCursor );
580         }
581       }
582       else
583       {
584         if( mSecondaryCursor )
585         {
586           mSecondaryCursor.Unparent();
587         }
588       }
589     }
590   }
591
592   bool OnCursorBlinkTimerTick()
593   {
594     if( !mDelayCursorBlink )
595     {
596       // Cursor blinking
597       if ( mPrimaryCursor )
598       {
599         mPrimaryCursor.SetVisible( mPrimaryCursorVisible && mCursorBlinkStatus );
600       }
601       if ( mSecondaryCursor )
602       {
603         mSecondaryCursor.SetVisible( mSecondaryCursorVisible && mCursorBlinkStatus );
604       }
605
606       mCursorBlinkStatus = !mCursorBlinkStatus;
607     }
608     else
609     {
610       // Resume blinking
611       mDelayCursorBlink = false;
612     }
613
614     return true;
615   }
616
617   void SetupTouchEvents()
618   {
619     mTapDetector = TapGestureDetector::New();
620     mTapDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnTap );
621
622     mPanGestureDetector = PanGestureDetector::New();
623     mPanGestureDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnPan );
624   }
625
626   void CreateActiveLayer()
627   {
628     if( !mActiveLayer )
629     {
630       mActiveLayer = Layer::New();
631 #ifdef DECORATOR_DEBUG
632       mActiveLayer.SetName ( "ActiveLayerActor" );
633 #endif
634
635       mActiveLayer.SetParentOrigin( ParentOrigin::CENTER );
636       mActiveLayer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
637
638       // Add the active layer telling the controller it doesn't need clipping.
639       mController.AddDecoration( mActiveLayer, false );
640     }
641
642     mActiveLayer.RaiseToTop();
643   }
644
645   void SetSelectionHandleMarkerSize( HandleImpl& handle )
646   {
647     if( handle.markerActor )
648     {
649       handle.markerActor.SetSize( 0, handle.lineHeight );
650     }
651   }
652
653   void CreateGrabHandle()
654   {
655     HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
656     if( !grabHandle.actor )
657     {
658       if( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] )
659       {
660         grabHandle.actor = ImageView::New( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] );
661         GetImpl( grabHandle.actor).SetDepthIndex( DepthIndex::DECORATION );
662         grabHandle.actor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
663
664         // Area that Grab handle responds to, larger than actual handle so easier to move
665 #ifdef DECORATOR_DEBUG
666         grabHandle.actor.SetName( "GrabHandleActor" );
667         if ( Dali::Internal::gLogFilter->IsEnabledFor( Debug::Verbose ) )
668         {
669           grabHandle.grabArea = Control::New();
670           Toolkit::Control control = Toolkit::Control::DownCast( grabHandle.grabArea );
671           control.SetBackgroundColor( Vector4( 1.0f, 1.0f, 1.0f, 0.5f ) );
672           grabHandle.grabArea.SetName( "GrabArea" );
673         }
674         else
675         {
676           grabHandle.grabArea = Actor::New();
677           grabHandle.grabArea.SetName( "GrabArea" );
678         }
679 #else
680         grabHandle.grabArea = Actor::New();
681 #endif
682
683         grabHandle.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
684         grabHandle.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
685         grabHandle.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
686         grabHandle.grabArea.SetSizeModeFactor( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE );
687         grabHandle.actor.Add( grabHandle.grabArea );
688         grabHandle.actor.SetColor( mHandleColor );
689
690         grabHandle.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnGrabHandleTouched );
691         mTapDetector.Attach( grabHandle.grabArea );
692         mPanGestureDetector.Attach( grabHandle.grabArea );
693
694         mActiveLayer.Add( grabHandle.actor );
695       }
696     }
697
698     if( grabHandle.actor && !grabHandle.actor.GetParent() )
699     {
700       mActiveLayer.Add( grabHandle.actor );
701     }
702   }
703
704   void CreateHandleMarker( HandleImpl& handle, Image& image, HandleType handleType )
705   {
706     if( image )
707     {
708       handle.markerActor = ImageView::New( image );
709       handle.markerActor.SetColor( mHandleColor );
710       handle.actor.Add( handle.markerActor );
711
712       handle.markerActor.SetResizePolicy ( ResizePolicy::FIXED, Dimension::HEIGHT );
713
714       if( LEFT_SELECTION_HANDLE == handleType )
715       {
716         handle.markerActor.SetAnchorPoint( AnchorPoint::BOTTOM_RIGHT );
717         handle.markerActor.SetParentOrigin( ParentOrigin::TOP_RIGHT );
718       }
719       else if( RIGHT_SELECTION_HANDLE == handleType )
720       {
721         handle.markerActor.SetAnchorPoint( AnchorPoint::BOTTOM_LEFT );
722         handle.markerActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
723       }
724     }
725   }
726
727   void CreateSelectionHandles()
728   {
729     HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
730     if( !primary.actor )
731     {
732       if( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] )
733       {
734         primary.actor = ImageView::New( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
735 #ifdef DECORATOR_DEBUG
736         primary.actor.SetName("SelectionHandleOne");
737 #endif
738         primary.actor.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
739         GetImpl( primary.actor ).SetDepthIndex( DepthIndex::DECORATION );
740         primary.actor.SetColor( mHandleColor );
741
742         primary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
743 #ifdef DECORATOR_DEBUG
744         primary.grabArea.SetName("SelectionHandleOneGrabArea");
745 #endif
746         primary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
747         primary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
748         primary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
749         primary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
750
751         mTapDetector.Attach( primary.grabArea );
752         mPanGestureDetector.Attach( primary.grabArea );
753         primary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleOneTouched );
754
755         primary.actor.Add( primary.grabArea );
756
757         CreateHandleMarker( primary, mHandleImages[LEFT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], LEFT_SELECTION_HANDLE );
758       }
759     }
760
761     if( primary.actor && !primary.actor.GetParent() )
762     {
763       mActiveLayer.Add( primary.actor );
764     }
765
766     HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
767     if( !secondary.actor )
768     {
769       if( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] )
770       {
771         secondary.actor = ImageView::New( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
772 #ifdef DECORATOR_DEBUG
773         secondary.actor.SetName("SelectionHandleTwo");
774 #endif
775         secondary.actor.SetAnchorPoint( AnchorPoint::TOP_LEFT ); // Change to BOTTOM_LEFT if Look'n'Feel requires handle above text.
776         GetImpl( secondary.actor ).SetDepthIndex( DepthIndex::DECORATION );
777         secondary.actor.SetColor( mHandleColor );
778
779         secondary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
780 #ifdef DECORATOR_DEBUG
781         secondary.grabArea.SetName("SelectionHandleTwoGrabArea");
782 #endif
783         secondary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
784         secondary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
785         secondary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
786         secondary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
787
788         mTapDetector.Attach( secondary.grabArea );
789         mPanGestureDetector.Attach( secondary.grabArea );
790         secondary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleTwoTouched );
791
792         secondary.actor.Add( secondary.grabArea );
793
794         CreateHandleMarker( secondary, mHandleImages[RIGHT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], RIGHT_SELECTION_HANDLE  );
795       }
796     }
797
798     if( secondary.actor && !secondary.actor.GetParent() )
799     {
800       mActiveLayer.Add( secondary.actor );
801     }
802   }
803
804   void CalculateHandleWorldCoordinates( HandleImpl& handle, Vector2& position )
805   {
806     // Gets the world position of the active layer. The active layer is where the handles are added.
807     const Vector3 parentWorldPosition = mActiveLayer.GetCurrentWorldPosition();
808
809     // The grab handle position in world coords.
810     // The active layer's world position is the center of the active layer. The origin of the
811     // coord system of the handles is the top left of the active layer.
812     position.x = parentWorldPosition.x - 0.5f * mControlSize.width + handle.position.x;
813     position.y = parentWorldPosition.y - 0.5f * mControlSize.height + handle.position.y;
814   }
815
816   void SetGrabHandlePosition()
817   {
818     // Reference to the grab handle.
819     HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
820
821     // Transforms the handle position into world coordinates.
822     // @note This is not the same value as grabHandle.actor.GetCurrentWorldPosition()
823     // as it's transforming the handle's position set by the text-controller and not
824     // the final position set to the actor. Another difference is the GetCurrentWorldPosition()
825     // retrieves the position of the center of the actor but the handle's position set
826     // by the text controller is not the center of the actor.
827     Vector2 grabHandleWorldPosition;
828     CalculateHandleWorldCoordinates( grabHandle, grabHandleWorldPosition );
829
830     // Check if the grab handle exceeds the boundaries of the decoration box.
831     // At the moment only the height is checked for the grab handle.
832
833     grabHandle.verticallyFlipped = ( grabHandle.verticallyFlippedPreferred &&
834                                      ( ( grabHandleWorldPosition.y - grabHandle.size.height ) > mBoundingBox.y ) ) ||
835                                    ( grabHandleWorldPosition.y + grabHandle.lineHeight + grabHandle.size.height > mBoundingBox.w );
836
837     // The grab handle 'y' position in local coords.
838     // If the grab handle exceeds the bottom of the decoration box,
839     // set the 'y' position to the top of the line.
840     // The SetGrabHandleImage() method will change the orientation.
841     const float yLocalPosition = grabHandle.verticallyFlipped ? grabHandle.position.y : grabHandle.position.y + grabHandle.lineHeight;
842
843     if( grabHandle.actor )
844     {
845       grabHandle.actor.SetPosition( grabHandle.position.x + floor( 0.5f * mCursorWidth ),
846                                     yLocalPosition ); // TODO : Fix for multiline.
847     }
848   }
849
850   void SetSelectionHandlePosition( HandleType type )
851   {
852     const bool isPrimaryHandle = LEFT_SELECTION_HANDLE == type;
853
854     // Reference to the selection handle.
855     HandleImpl& handle = mHandle[type];
856
857     // Transforms the handle position into world coordinates.
858     // @note This is not the same value as handle.actor.GetCurrentWorldPosition()
859     // as it's transforming the handle's position set by the text-controller and not
860     // the final position set to the actor. Another difference is the GetCurrentWorldPosition()
861     // retrieves the position of the center of the actor but the handle's position set
862     // by the text controller is not the center of the actor.
863     Vector2 handleWorldPosition;
864     CalculateHandleWorldCoordinates( handle, handleWorldPosition );
865
866     // Whether to flip the handle (horizontally).
867     bool flipHandle = isPrimaryHandle ? mFlipLeftSelectionHandleDirection : mFlipRightSelectionHandleDirection;
868
869     // Whether to flip the handles if they are crossed.
870     bool crossFlip = false;
871     if( mFlipSelectionHandlesOnCross || !mHandlePanning )
872     {
873       crossFlip = mHandleCurrentCrossed;
874     }
875
876     // Does not flip if both conditions are true (double flip)
877     flipHandle = flipHandle != ( crossFlip || mHandlePreviousCrossed );
878
879     // Will flip the handles vertically if the user prefers it.
880     bool verticallyFlippedPreferred = handle.verticallyFlippedPreferred;
881
882     if( crossFlip || mHandlePreviousCrossed )
883     {
884       if( isPrimaryHandle )
885       {
886         verticallyFlippedPreferred = mHandle[RIGHT_SELECTION_HANDLE].verticallyFlippedPreferred;
887       }
888       else
889       {
890         verticallyFlippedPreferred = mHandle[LEFT_SELECTION_HANDLE].verticallyFlippedPreferred;
891       }
892     }
893
894     // Check if the selection handle exceeds the boundaries of the decoration box.
895     const bool exceedsLeftEdge = ( isPrimaryHandle ? !flipHandle : flipHandle ) && ( handleWorldPosition.x - handle.size.width < mBoundingBox.x );
896     const bool exceedsRightEdge = ( isPrimaryHandle ? flipHandle : !flipHandle ) && ( handleWorldPosition.x + handle.size.width > mBoundingBox.z );
897
898     // Does not flip if both conditions are true (double flip)
899     flipHandle = flipHandle != ( exceedsLeftEdge || exceedsRightEdge );
900
901     if( flipHandle )
902     {
903       if( handle.actor && !handle.horizontallyFlipped )
904       {
905         // Change the anchor point to flip the image.
906         handle.actor.SetAnchorPoint( isPrimaryHandle ? AnchorPoint::TOP_LEFT : AnchorPoint::TOP_RIGHT );
907
908         handle.horizontallyFlipped = true;
909       }
910     }
911     else
912     {
913       if( handle.actor && handle.horizontallyFlipped )
914       {
915         // Reset the anchor point.
916         handle.actor.SetAnchorPoint( isPrimaryHandle ? AnchorPoint::TOP_RIGHT : AnchorPoint::TOP_LEFT );
917
918         handle.horizontallyFlipped = false;
919       }
920     }
921
922     // Whether to flip the handle vertically.
923     handle.verticallyFlipped = ( verticallyFlippedPreferred &&
924                                  ( ( handleWorldPosition.y - handle.size.height ) > mBoundingBox.y ) ) ||
925                                ( handleWorldPosition.y + handle.lineHeight + handle.size.height > mBoundingBox.w );
926
927     // The primary selection handle 'y' position in local coords.
928     // If the handle exceeds the bottom of the decoration box,
929     // set the 'y' position to the top of the line.
930     // The SetHandleImage() method will change the orientation.
931     const float yLocalPosition = handle.verticallyFlipped ? handle.position.y : handle.position.y + handle.lineHeight;
932
933     if( handle.actor )
934     {
935       handle.actor.SetPosition( handle.position.x,
936                                 yLocalPosition ); // TODO : Fix for multiline.
937     }
938   }
939
940   void SetHandleImage( HandleType type )
941   {
942     HandleImpl& handle = mHandle[type];
943
944     HandleType markerType = HANDLE_TYPE_COUNT;
945     // If the selection handle is flipped it chooses the image of the other selection handle. Does nothing for the grab handle.
946     if( LEFT_SELECTION_HANDLE == type )
947     {
948       type = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE;
949       markerType = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE_MARKER : LEFT_SELECTION_HANDLE_MARKER;
950     }
951     else if( RIGHT_SELECTION_HANDLE == type )
952     {
953       type = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE : RIGHT_SELECTION_HANDLE;
954       markerType = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE_MARKER : RIGHT_SELECTION_HANDLE_MARKER;
955     }
956
957     // Chooses between the released or pressed image. It checks whether the pressed image exists.
958     if( handle.actor )
959     {
960       const HandleImageType imageType = ( handle.pressed ? ( mHandleImages[type][HANDLE_IMAGE_PRESSED] ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
961
962       handle.actor.SetImage( mHandleImages[type][imageType] );
963     }
964
965     if( HANDLE_TYPE_COUNT != markerType )
966     {
967       if( handle.markerActor )
968       {
969         const HandleImageType markerImageType = ( handle.pressed ? ( mHandleImages[markerType][HANDLE_IMAGE_PRESSED] ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
970         handle.markerActor.SetImage( mHandleImages[markerType][markerImageType] );
971       }
972     }
973
974     // Whether to flip the handle vertically.
975     if( handle.actor )
976     {
977       handle.actor.SetOrientation( handle.verticallyFlipped ? ANGLE_180 : ANGLE_0, Vector3::XAXIS );
978     }
979   }
980
981   void CreateHighlight()
982   {
983     if( !mHighlightActor )
984     {
985       mHighlightActor = Actor::New();
986
987 #ifdef DECORATOR_DEBUG
988       mHighlightActor.SetName( "HighlightActor" );
989 #endif
990       mHighlightActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
991       mHighlightActor.SetSize( 1.0f, 1.0f );
992       mHighlightActor.SetColor( mHighlightColor );
993       mHighlightActor.SetColorMode( USE_OWN_COLOR );
994     }
995
996     // Add the highlight box telling the controller it needs clipping.
997     mController.AddDecoration( mHighlightActor, true );
998   }
999
1000   void UpdateHighlight()
1001   {
1002     if ( mHighlightActor )
1003     {
1004       if( !mHighlightQuadList.empty() )
1005       {
1006         Vector< Vector2 > vertices;
1007         Vector< unsigned short> indices;
1008         Vector2 vertex;
1009
1010         std::vector<QuadCoordinates>::iterator iter = mHighlightQuadList.begin();
1011         std::vector<QuadCoordinates>::iterator endIter = mHighlightQuadList.end();
1012
1013         for( std::size_t v = 0; iter != endIter; ++iter,v+=4 )
1014         {
1015           QuadCoordinates& quad = *iter;
1016
1017           // top-left (v+0)
1018           vertex.x = quad.min.x;
1019           vertex.y = quad.min.y;
1020           vertices.PushBack( vertex );
1021
1022           // top-right (v+1)
1023           vertex.x = quad.max.x;
1024           vertex.y = quad.min.y;
1025           vertices.PushBack( vertex );
1026
1027           // bottom-left (v+2)
1028           vertex.x = quad.min.x;
1029           vertex.y = quad.max.y;
1030           vertices.PushBack( vertex );
1031
1032           // bottom-right (v+3)
1033           vertex.x = quad.max.x;
1034           vertex.y = quad.max.y;
1035           vertices.PushBack( vertex );
1036
1037           // triangle A (3, 1, 0)
1038           indices.PushBack( v + 3 );
1039           indices.PushBack( v + 1 );
1040           indices.PushBack( v );
1041
1042           // triangle B (0, 2, 3)
1043           indices.PushBack( v );
1044           indices.PushBack( v + 2 );
1045           indices.PushBack( v + 3 );
1046         }
1047
1048         if( ! mQuadVertices )
1049         {
1050           mQuadVertices = PropertyBuffer::New( mQuadVertexFormat );
1051         }
1052
1053         mQuadVertices.SetData( &vertices[ 0 ], vertices.Size() );
1054
1055         if( !mQuadGeometry )
1056         {
1057           mQuadGeometry = Geometry::New();
1058           mQuadGeometry.AddVertexBuffer( mQuadVertices );
1059         }
1060         mQuadGeometry.SetIndexBuffer( &indices[ 0 ], indices.Size() );
1061
1062         if( !mHighlightRenderer )
1063         {
1064           mHighlightRenderer = Dali::Renderer::New( mQuadGeometry, mHighlightShader );
1065           mHighlightActor.AddRenderer( mHighlightRenderer );
1066         }
1067       }
1068
1069       mHighlightActor.SetPosition( mHighlightPosition.x,
1070                                    mHighlightPosition.y );
1071
1072       mHighlightQuadList.clear();
1073
1074       if( mHighlightRenderer )
1075       {
1076         mHighlightRenderer.SetProperty( Renderer::Property::DEPTH_INDEX, mTextDepth - 2 ); // text is rendered at mTextDepth and text's shadow at mTextDepth -1u.
1077       }
1078     }
1079   }
1080
1081   void OnTap( Actor actor, const TapGesture& tap )
1082   {
1083     if( actor == mHandle[GRAB_HANDLE].actor )
1084     {
1085       // TODO
1086     }
1087   }
1088
1089   void DoPan( HandleImpl& handle, HandleType type, const PanGesture& gesture )
1090   {
1091     if( Gesture::Started == gesture.state )
1092     {
1093       handle.grabDisplacementX = handle.grabDisplacementY = 0.f;
1094     }
1095
1096     handle.grabDisplacementX += gesture.displacement.x;
1097     handle.grabDisplacementY += ( handle.verticallyFlipped ? -gesture.displacement.y : gesture.displacement.y );
1098
1099     const float x = handle.position.x + handle.grabDisplacementX;
1100     const float y = handle.position.y + handle.lineHeight*0.5f + handle.grabDisplacementY;
1101
1102     if( Gesture::Started    == gesture.state ||
1103         Gesture::Continuing == gesture.state )
1104     {
1105       Vector2 targetSize;
1106       mController.GetTargetSize( targetSize );
1107
1108       if( x < mScrollThreshold )
1109       {
1110         mScrollDirection = SCROLL_RIGHT;
1111         mHandleScrolling = type;
1112         StartScrollTimer();
1113       }
1114       else if( x > targetSize.width - mScrollThreshold )
1115       {
1116         mScrollDirection = SCROLL_LEFT;
1117         mHandleScrolling = type;
1118         StartScrollTimer();
1119       }
1120       else
1121       {
1122         mHandleScrolling = HANDLE_TYPE_COUNT;
1123         StopScrollTimer();
1124         mController.DecorationEvent( type, HANDLE_PRESSED, x, y );
1125       }
1126
1127       mHandlePanning = true;
1128     }
1129     else if( Gesture::Finished  == gesture.state ||
1130              Gesture::Cancelled == gesture.state )
1131     {
1132       if( mScrollTimer &&
1133           ( mScrollTimer.IsRunning() || mNotifyEndOfScroll ) )
1134       {
1135         mNotifyEndOfScroll = false;
1136         mHandleScrolling = HANDLE_TYPE_COUNT;
1137         StopScrollTimer();
1138         mController.DecorationEvent( type, HANDLE_STOP_SCROLLING, x, y );
1139       }
1140       else
1141       {
1142         mController.DecorationEvent( type, HANDLE_RELEASED, x, y );
1143       }
1144
1145       if( handle.actor )
1146       {
1147         handle.actor.SetImage( mHandleImages[type][HANDLE_IMAGE_RELEASED] );
1148       }
1149       handle.pressed = false;
1150
1151       mHandlePanning = false;
1152     }
1153   }
1154
1155   void OnPan( Actor actor, const PanGesture& gesture )
1156   {
1157     HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1158     HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1159     HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1160
1161     if( actor == grabHandle.grabArea )
1162     {
1163       DoPan( grabHandle, GRAB_HANDLE, gesture );
1164     }
1165     else if( actor == primarySelectionHandle.grabArea )
1166     {
1167       DoPan( primarySelectionHandle, LEFT_SELECTION_HANDLE, gesture );
1168     }
1169     else if( actor == secondarySelectionHandle.grabArea )
1170     {
1171       DoPan( secondarySelectionHandle, RIGHT_SELECTION_HANDLE, gesture );
1172     }
1173   }
1174
1175   bool OnGrabHandleTouched( Actor actor, const TouchEvent& event )
1176   {
1177     // Switch between pressed/release grab-handle images
1178     if( event.GetPointCount() > 0 &&
1179         mHandle[GRAB_HANDLE].actor )
1180     {
1181       const TouchPoint& point = event.GetPoint(0);
1182
1183       if( TouchPoint::Down == point.state )
1184       {
1185         mHandle[GRAB_HANDLE].pressed = true;
1186       }
1187       else if( ( TouchPoint::Up == point.state ) ||
1188                ( TouchPoint::Interrupted == point.state ) )
1189       {
1190         mHandle[GRAB_HANDLE].pressed = false;
1191       }
1192
1193       SetHandleImage( GRAB_HANDLE );
1194     }
1195
1196     // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1197     return true;
1198   }
1199
1200   bool OnHandleOneTouched( Actor actor, const TouchEvent& event )
1201   {
1202     // Switch between pressed/release selection handle images
1203     if( event.GetPointCount() > 0 &&
1204         mHandle[LEFT_SELECTION_HANDLE].actor )
1205     {
1206       const TouchPoint& point = event.GetPoint(0);
1207
1208       if( TouchPoint::Down == point.state )
1209       {
1210         mHandle[LEFT_SELECTION_HANDLE].pressed = true;
1211       }
1212       else if( ( TouchPoint::Up == point.state ) ||
1213                ( TouchPoint::Interrupted == point.state ) )
1214       {
1215         mHandle[LEFT_SELECTION_HANDLE].pressed = false;
1216         mHandlePreviousCrossed = mHandleCurrentCrossed;
1217         mHandlePanning = false;
1218       }
1219
1220       SetHandleImage( LEFT_SELECTION_HANDLE );
1221     }
1222
1223     // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1224     return true;
1225   }
1226
1227   bool OnHandleTwoTouched( Actor actor, const TouchEvent& event )
1228   {
1229     // Switch between pressed/release selection handle images
1230     if( event.GetPointCount() > 0 &&
1231         mHandle[RIGHT_SELECTION_HANDLE].actor )
1232     {
1233       const TouchPoint& point = event.GetPoint(0);
1234
1235       if( TouchPoint::Down == point.state )
1236       {
1237         mHandle[RIGHT_SELECTION_HANDLE].pressed = true;
1238       }
1239       else if( ( TouchPoint::Up == point.state ) ||
1240                ( TouchPoint::Interrupted == point.state ) )
1241       {
1242         mHandle[RIGHT_SELECTION_HANDLE].pressed = false;
1243         mHandlePreviousCrossed = mHandleCurrentCrossed;
1244         mHandlePanning = false;
1245       }
1246
1247       SetHandleImage( RIGHT_SELECTION_HANDLE );
1248     }
1249
1250     // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1251     return true;
1252   }
1253
1254   void HandleResetPosition( PropertyNotification& source )
1255   {
1256     const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1257
1258     if( grabHandle.active )
1259     {
1260       // Sets the grab handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1261       SetGrabHandlePosition();
1262
1263       // Sets the grab handle image according if it's pressed, flipped, etc.
1264       SetHandleImage( GRAB_HANDLE );
1265     }
1266     else
1267     {
1268       // Sets the primary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1269       SetSelectionHandlePosition( LEFT_SELECTION_HANDLE );
1270
1271       // Sets the primary handle image according if it's pressed, flipped, etc.
1272       SetHandleImage( LEFT_SELECTION_HANDLE );
1273
1274       // Sets the secondary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1275       SetSelectionHandlePosition( RIGHT_SELECTION_HANDLE );
1276
1277       // Sets the secondary handle image according if it's pressed, flipped, etc.
1278       SetHandleImage( RIGHT_SELECTION_HANDLE );
1279     }
1280   }
1281
1282   void SetupActiveLayerPropertyNotifications()
1283   {
1284     if( !mActiveLayer )
1285     {
1286       return;
1287     }
1288
1289     // Vertical notifications.
1290
1291     // Disconnect any previous connected callback.
1292     if( mVerticalLessThanNotification )
1293     {
1294       mVerticalLessThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1295       mActiveLayer.RemovePropertyNotification( mVerticalLessThanNotification );
1296     }
1297
1298     if( mVerticalGreaterThanNotification )
1299     {
1300       mVerticalGreaterThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1301       mActiveLayer.RemovePropertyNotification( mVerticalGreaterThanNotification );
1302     }
1303
1304     const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1305     const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1306     const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1307
1308     if( grabHandle.active )
1309     {
1310       if( grabHandle.verticallyFlipped )
1311       {
1312         // The grab handle is vertically flipped. Never is going to exceed the bottom edje of the display.
1313         mVerticalGreaterThanNotification.Reset();
1314
1315         // The vertical distance from the center of the active layer to the top edje of the display.
1316         const float topHeight = 0.5f * mControlSize.height - grabHandle.position.y + grabHandle.size.height;
1317
1318         mVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1319                                                                               LessThanCondition( mBoundingBox.y + topHeight ) );
1320
1321         // Notifies the change from false to true and from true to false.
1322         mVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1323
1324         // Connects the signals with the callbacks.
1325         mVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1326       }
1327       else
1328       {
1329         // The grab handle is not vertically flipped. Never is going to exceed the top edje of the display.
1330         mVerticalLessThanNotification.Reset();
1331
1332         // The vertical distance from the center of the active layer to the bottom edje of the display.
1333         const float bottomHeight = -0.5f * mControlSize.height + grabHandle.position.y + grabHandle.lineHeight + grabHandle.size.height;
1334
1335         mVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1336                                                                                  GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1337
1338         // Notifies the change from false to true and from true to false.
1339         mVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1340
1341         // Connects the signals with the callbacks.
1342         mVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1343       }
1344     }
1345     else // The selection handles are active
1346     {
1347       if( primaryHandle.verticallyFlipped && secondaryHandle.verticallyFlipped )
1348       {
1349         // Both selection handles are vertically flipped. Never are going to exceed the bottom edje of the display.
1350         mVerticalGreaterThanNotification.Reset();
1351
1352         // The vertical distance from the center of the active layer to the top edje of the display.
1353         const float topHeight = 0.5f * mControlSize.height + std::max( -primaryHandle.position.y + primaryHandle.size.height, -secondaryHandle.position.y + secondaryHandle.size.height );
1354
1355         mVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1356                                                                               LessThanCondition( mBoundingBox.y + topHeight ) );
1357
1358         // Notifies the change from false to true and from true to false.
1359         mVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1360
1361         // Connects the signals with the callbacks.
1362         mVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1363       }
1364       else if( !primaryHandle.verticallyFlipped && !secondaryHandle.verticallyFlipped )
1365       {
1366         // Both selection handles aren't vertically flipped. Never are going to exceed the top edje of the display.
1367         mVerticalLessThanNotification.Reset();
1368
1369         // The vertical distance from the center of the active layer to the bottom edje of the display.
1370         const float bottomHeight = -0.5f * mControlSize.height + std::max( primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height,
1371                                                                            secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height );
1372
1373         mVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1374                                                                                  GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1375
1376         // Notifies the change from false to true and from true to false.
1377         mVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1378
1379         // Connects the signals with the callbacks.
1380         mVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1381       }
1382       else
1383       {
1384         // Only one of the selection handles is vertically flipped. Both vertical notifications are needed.
1385
1386         // The vertical distance from the center of the active layer to the top edje of the display.
1387         const float topHeight = 0.5f * mControlSize.height + ( primaryHandle.verticallyFlipped                              ?
1388                                                                -primaryHandle.position.y + primaryHandle.size.height        :
1389                                                                -secondaryHandle.position.y + secondaryHandle.size.height );
1390
1391         mVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1392                                                                               LessThanCondition( mBoundingBox.y + topHeight ) );
1393
1394         // Notifies the change from false to true and from true to false.
1395         mVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1396
1397         // Connects the signals with the callbacks.
1398         mVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1399
1400         // The vertical distance from the center of the active layer to the bottom edje of the display.
1401         const float bottomHeight = -0.5f * mControlSize.height + ( primaryHandle.verticallyFlipped                                                       ?
1402                                                                    secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height :
1403                                                                    primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height );
1404
1405         mVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1406                                                                                  GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1407
1408         // Notifies the change from false to true and from true to false.
1409         mVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1410
1411         // Connects the signals with the callbacks.
1412         mVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1413       }
1414     }
1415
1416     // Horizontal notifications.
1417
1418     // Disconnect any previous connected callback.
1419     if( mHorizontalLessThanNotification )
1420     {
1421       mHorizontalLessThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1422       mActiveLayer.RemovePropertyNotification( mHorizontalLessThanNotification );
1423     }
1424
1425     if( mHorizontalGreaterThanNotification )
1426     {
1427       mHorizontalGreaterThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1428       mActiveLayer.RemovePropertyNotification( mHorizontalGreaterThanNotification );
1429     }
1430
1431     if( primaryHandle.active || secondaryHandle.active )
1432     {
1433       // The horizontal distance from the center of the active layer to the left edje of the display.
1434       const float leftWidth = 0.5f * mControlSize.width + std::max( -primaryHandle.position.x + primaryHandle.size.width,
1435                                                                     -secondaryHandle.position.x + secondaryHandle.size.width );
1436
1437       mHorizontalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_X,
1438                                                                               LessThanCondition( mBoundingBox.x + leftWidth ) );
1439
1440       // Notifies the change from false to true and from true to false.
1441       mHorizontalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1442
1443       // Connects the signals with the callbacks.
1444       mHorizontalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1445
1446       // The horizontal distance from the center of the active layer to the right edje of the display.
1447       const float rightWidth = -0.5f * mControlSize.width + std::max( primaryHandle.position.x + primaryHandle.size.width,
1448                                                                       secondaryHandle.position.x + secondaryHandle.size.width );
1449
1450       mHorizontalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_X,
1451                                                                                  GreaterThanCondition( mBoundingBox.z - rightWidth ) );
1452
1453       // Notifies the change from false to true and from true to false.
1454       mHorizontalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1455
1456       // Connects the signals with the callbacks.
1457       mHorizontalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1458     }
1459   }
1460
1461   // Popup
1462
1463   float AlternatePopUpPositionRelativeToCursor()
1464   {
1465     float alternativePosition = 0.0f;
1466
1467     const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1468
1469     const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1470     const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1471     const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1472     const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
1473
1474     if( primaryHandle.active || secondaryHandle.active )
1475     {
1476       const float maxHandleHeight = std::max( primaryHandle.size.height, secondaryHandle.size.height );
1477       alternativePosition = 0.5f * popupHeight + cursor.lineHeight + maxHandleHeight + std::min( primaryHandle.position.y, secondaryHandle.position.y );
1478     }
1479     else
1480     {
1481       alternativePosition = 0.5f * popupHeight + cursor.lineHeight + grabHandle.size.height + cursor.position.y;
1482     }
1483
1484     return alternativePosition;
1485   }
1486
1487   void PopUpLeavesVerticalBoundary( PropertyNotification& source )
1488   {
1489     float alternativeYPosition = 0.0f;
1490     // todo use AlternatePopUpPositionRelativeToSelectionHandles() if text is highlighted
1491     // if can't be positioned above, then position below row.
1492     alternativeYPosition = AlternatePopUpPositionRelativeToCursor();
1493
1494     mCopyPastePopup.actor.SetY( alternativeYPosition );
1495   }
1496
1497   void SetUpPopupPositionNotifications()
1498   {
1499     // Note Property notifications ignore any set anchor point so conditions must allow for this.  Default is Top Left.
1500
1501     // Exceeding vertical boundary
1502
1503     const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1504
1505     PropertyNotification verticalExceedNotification = mCopyPastePopup.actor.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1506                                                                                                      OutsideCondition( mBoundingBox.y + popupHeight * 0.5f,
1507                                                                                                                        mBoundingBox.w - popupHeight * 0.5f ) );
1508
1509     verticalExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesVerticalBoundary );
1510   }
1511
1512   void GetConstrainedPopupPosition( Vector3& requiredPopupPosition, const Vector3& popupDistanceFromAnchorPoint, Actor parent, const Vector4& boundingRectangleWorld )
1513   {
1514     DALI_ASSERT_DEBUG ( "Popup parent not on stage" && parent.OnStage() )
1515
1516     // Parent must already by added to Stage for these Get calls to work
1517     const Vector3 parentWorldPositionLeftAnchor = parent.GetCurrentWorldPosition() - parent.GetCurrentSize() * parent.GetCurrentAnchorPoint();
1518     const Vector3 popupWorldPosition = parentWorldPositionLeftAnchor + requiredPopupPosition;  // Parent World position plus popup local position gives World Position
1519
1520     // Calculate distance to move popup (in local space) so fits within the boundary
1521     float xOffSetToKeepWithinBounds = 0.0f;
1522     if( popupWorldPosition.x - popupDistanceFromAnchorPoint.x < boundingRectangleWorld.x )
1523     {
1524       xOffSetToKeepWithinBounds = boundingRectangleWorld.x - ( popupWorldPosition.x - popupDistanceFromAnchorPoint.x );
1525     }
1526     else if( popupWorldPosition.x +  popupDistanceFromAnchorPoint.x > boundingRectangleWorld.z )
1527     {
1528       xOffSetToKeepWithinBounds = boundingRectangleWorld.z - ( popupWorldPosition.x +  popupDistanceFromAnchorPoint.x );
1529     }
1530
1531     // Ensure initial display of Popup is in alternative position if can not fit above. As Property notification will be a frame behind.
1532     if( popupWorldPosition.y - popupDistanceFromAnchorPoint.y < boundingRectangleWorld.y )
1533     {
1534       requiredPopupPosition.y = AlternatePopUpPositionRelativeToCursor();
1535     }
1536
1537     requiredPopupPosition.x = requiredPopupPosition.x + xOffSetToKeepWithinBounds;
1538
1539     // Prevent pixel mis-alignment by rounding down.
1540     requiredPopupPosition.x = floor( requiredPopupPosition.x );
1541     requiredPopupPosition.y = floor( requiredPopupPosition.y );
1542   }
1543
1544   void SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image )
1545   {
1546     HandleImpl& handle = mHandle[handleType];
1547     handle.size = Size( image.GetWidth(), image.GetHeight() );
1548
1549     mHandleImages[handleType][handleImageType] = image;
1550   }
1551
1552   void SetScrollThreshold( float threshold )
1553   {
1554     mScrollThreshold = threshold;
1555   }
1556
1557   float GetScrollThreshold() const
1558   {
1559     return mScrollThreshold;
1560   }
1561
1562   void SetScrollSpeed( float speed )
1563   {
1564     mScrollSpeed = speed;
1565     mScrollDistance = speed * SCROLL_TICK_INTERVAL * TO_SECONDS;
1566   }
1567
1568   float GetScrollSpeed() const
1569   {
1570     return mScrollSpeed;
1571   }
1572
1573   void NotifyEndOfScroll()
1574   {
1575     StopScrollTimer();
1576
1577     if( mScrollTimer )
1578     {
1579       mNotifyEndOfScroll = true;
1580     }
1581   }
1582
1583   /**
1584    * Creates and starts a timer to scroll the text when handles are close to the edges of the text.
1585    *
1586    * It only starts the timer if it's already created.
1587    */
1588   void StartScrollTimer()
1589   {
1590     if( !mScrollTimer )
1591     {
1592       mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1593       mScrollTimer.TickSignal().Connect( this, &Decorator::Impl::OnScrollTimerTick );
1594     }
1595
1596     if( !mScrollTimer.IsRunning() )
1597     {
1598       mScrollTimer.Start();
1599     }
1600   }
1601
1602   /**
1603    * Stops the timer used to scroll the text.
1604    */
1605   void StopScrollTimer()
1606   {
1607     if( mScrollTimer )
1608     {
1609       mScrollTimer.Stop();
1610     }
1611   }
1612
1613   /**
1614    * Callback called by the timer used to scroll the text.
1615    *
1616    * It calculates and sets a new scroll position.
1617    */
1618   bool OnScrollTimerTick()
1619   {
1620     if( HANDLE_TYPE_COUNT != mHandleScrolling )
1621     {
1622       mController.DecorationEvent( mHandleScrolling,
1623                                    HANDLE_SCROLLING,
1624                                    mScrollDirection == SCROLL_RIGHT ? mScrollDistance : -mScrollDistance,
1625                                    0.f );
1626     }
1627
1628     return true;
1629   }
1630
1631   ControllerInterface& mController;
1632
1633   TapGestureDetector  mTapDetector;
1634   PanGestureDetector  mPanGestureDetector;
1635   Timer               mCursorBlinkTimer;          ///< Timer to signal cursor to blink
1636   Timer               mScrollTimer;               ///< Timer used to scroll the text when the grab handle is moved close to the edges.
1637
1638   Layer                mActiveLayer;                       ///< Layer for active handles and alike that ensures they are above all else.
1639   PropertyNotification mVerticalLessThanNotification;      ///< Notifies when the 'y' coord of the active layer is less than a given value.
1640   PropertyNotification mVerticalGreaterThanNotification;   ///< Notifies when the 'y' coord of the active layer is grater than a given value.
1641   PropertyNotification mHorizontalLessThanNotification;    ///< Notifies when the 'x' coord of the active layer is less than a given value.
1642   PropertyNotification mHorizontalGreaterThanNotification; ///< Notifies when the 'x' coord of the active layer is grater than a given value.
1643   Control              mPrimaryCursor;
1644   Control              mSecondaryCursor;
1645
1646   Actor               mHighlightActor;            ///< Actor to display highlight
1647   Renderer            mHighlightRenderer;
1648   Shader              mHighlightShader;           ///< Shader used for highlight
1649   Property::Map       mQuadVertexFormat;
1650   PopupImpl           mCopyPastePopup;
1651   TextSelectionPopup::Buttons mEnabledPopupButtons; /// Bit mask of currently enabled Popup buttons
1652   TextSelectionPopupCallbackInterface& mTextSelectionPopupCallbackInterface;
1653
1654   Image               mHandleImages[HANDLE_TYPE_COUNT][HANDLE_IMAGE_TYPE_COUNT];
1655   Vector4             mHandleColor;
1656
1657   CursorImpl          mCursor[CURSOR_COUNT];
1658   HandleImpl          mHandle[HANDLE_TYPE_COUNT];
1659
1660   PropertyBuffer      mQuadVertices;
1661   Geometry            mQuadGeometry;
1662   QuadContainer       mHighlightQuadList;         ///< Sub-selections that combine to create the complete selection highlight
1663
1664   Vector4             mBoundingBox;               ///< The bounding box in world coords.
1665   Vector4             mHighlightColor;            ///< Color of the highlight
1666   Vector2             mHighlightPosition;         ///< The position of the highlight actor.
1667   Vector2             mControlSize;               ///< The control's size. Set by the Relayout.
1668
1669   unsigned int        mActiveCursor;
1670   unsigned int        mCursorBlinkInterval;
1671   float               mCursorBlinkDuration;
1672   float               mCursorWidth;             ///< The width of the cursors in pixels.
1673   HandleType          mHandleScrolling;         ///< The handle which is scrolling.
1674   ScrollDirection     mScrollDirection;         ///< The direction of the scroll.
1675   float               mScrollThreshold;         ///< Defines a square area inside the control, close to the edge. A cursor entering this area will trigger scroll events.
1676   float               mScrollSpeed;             ///< The scroll speed in pixels per second.
1677   float               mScrollDistance;          ///< Distance the text scrolls during a scroll interval.
1678   int                 mTextDepth;               ///< The depth used to render the text.
1679
1680   bool                mActiveCopyPastePopup              : 1;
1681   bool                mPopupSetNewPosition               : 1;
1682   bool                mCursorBlinkStatus                 : 1; ///< Flag to switch between blink on and blink off.
1683   bool                mDelayCursorBlink                  : 1; ///< Used to avoid cursor blinking when entering text.
1684   bool                mPrimaryCursorVisible              : 1; ///< Whether the primary cursor is visible.
1685   bool                mSecondaryCursorVisible            : 1; ///< Whether the secondary cursor is visible.
1686   bool                mFlipSelectionHandlesOnCross       : 1; ///< Whether to flip the selection handles as soon as they cross.
1687   bool                mFlipLeftSelectionHandleDirection  : 1; ///< Whether to flip the left selection handle image because of the character's direction.
1688   bool                mFlipRightSelectionHandleDirection : 1; ///< Whether to flip the right selection handle image because of the character's direction.
1689   bool                mHandlePanning                     : 1; ///< Whether any of the handles is moving.
1690   bool                mHandleCurrentCrossed              : 1; ///< Whether the handles are crossed.
1691   bool                mHandlePreviousCrossed             : 1; ///< Whether the handles where crossed at the last handle touch up.
1692   bool                mNotifyEndOfScroll                 : 1; ///< Whether to notify the end of the scroll.
1693 };
1694
1695 DecoratorPtr Decorator::New( ControllerInterface& controller,
1696                              TextSelectionPopupCallbackInterface& callbackInterface )
1697 {
1698   return DecoratorPtr( new Decorator( controller,
1699                                       callbackInterface ) );
1700 }
1701
1702 void Decorator::SetBoundingBox( const Rect<int>& boundingBox )
1703 {
1704   LocalToWorldCoordinatesBoundingBox( boundingBox, mImpl->mBoundingBox );
1705 }
1706
1707 void Decorator::GetBoundingBox( Rect<int>& boundingBox ) const
1708 {
1709   WorldToLocalCoordinatesBoundingBox( mImpl->mBoundingBox, boundingBox );
1710 }
1711
1712 void Decorator::Relayout( const Vector2& size )
1713 {
1714   mImpl->Relayout( size );
1715 }
1716
1717 void Decorator::UpdatePositions( const Vector2& scrollOffset )
1718 {
1719   mImpl->UpdatePositions( scrollOffset );
1720 }
1721
1722 /** Cursor **/
1723
1724 void Decorator::SetActiveCursor( ActiveCursor activeCursor )
1725 {
1726   mImpl->mActiveCursor = activeCursor;
1727 }
1728
1729 unsigned int Decorator::GetActiveCursor() const
1730 {
1731   return mImpl->mActiveCursor;
1732 }
1733
1734 void Decorator::SetPosition( Cursor cursor, float x, float y, float cursorHeight, float lineHeight )
1735 {
1736   mImpl->mCursor[cursor].position.x = x;
1737   mImpl->mCursor[cursor].position.y = y;
1738   mImpl->mCursor[cursor].cursorHeight = cursorHeight;
1739   mImpl->mCursor[cursor].lineHeight = lineHeight;
1740 }
1741
1742 void Decorator::GetPosition( Cursor cursor, float& x, float& y, float& cursorHeight, float& lineHeight ) const
1743 {
1744   x = mImpl->mCursor[cursor].position.x;
1745   y = mImpl->mCursor[cursor].position.y;
1746   cursorHeight = mImpl->mCursor[cursor].cursorHeight;
1747   lineHeight = mImpl->mCursor[cursor].lineHeight;
1748 }
1749
1750 const Vector2& Decorator::GetPosition( Cursor cursor ) const
1751 {
1752   return mImpl->mCursor[cursor].position;
1753 }
1754
1755 void Decorator::SetCursorColor( Cursor cursor, const Dali::Vector4& color )
1756 {
1757   mImpl->mCursor[cursor].color = color;
1758 }
1759
1760 const Dali::Vector4& Decorator::GetColor( Cursor cursor ) const
1761 {
1762   return mImpl->mCursor[cursor].color;
1763 }
1764
1765 void Decorator::StartCursorBlink()
1766 {
1767   if ( !mImpl->mCursorBlinkTimer )
1768   {
1769     mImpl->mCursorBlinkTimer = Timer::New( mImpl->mCursorBlinkInterval );
1770     mImpl->mCursorBlinkTimer.TickSignal().Connect( mImpl, &Decorator::Impl::OnCursorBlinkTimerTick );
1771   }
1772
1773   if ( !mImpl->mCursorBlinkTimer.IsRunning() )
1774   {
1775     mImpl->mCursorBlinkTimer.Start();
1776   }
1777 }
1778
1779 void Decorator::StopCursorBlink()
1780 {
1781   if ( mImpl->mCursorBlinkTimer )
1782   {
1783     mImpl->mCursorBlinkTimer.Stop();
1784   }
1785
1786   mImpl->mCursorBlinkStatus = true; // Keep cursor permanently shown
1787 }
1788
1789 void Decorator::DelayCursorBlink()
1790 {
1791   mImpl->mCursorBlinkStatus = true; // Show cursor for a bit longer
1792   mImpl->mDelayCursorBlink = true;
1793 }
1794
1795 void Decorator::SetCursorBlinkInterval( float seconds )
1796 {
1797   mImpl->mCursorBlinkInterval = static_cast<unsigned int>( seconds * TO_MILLISECONDS ); // Convert to milliseconds
1798 }
1799
1800 float Decorator::GetCursorBlinkInterval() const
1801 {
1802   return static_cast<float>( mImpl->mCursorBlinkInterval ) * TO_SECONDS;
1803 }
1804
1805 void Decorator::SetCursorBlinkDuration( float seconds )
1806 {
1807   mImpl->mCursorBlinkDuration = seconds;
1808 }
1809
1810 float Decorator::GetCursorBlinkDuration() const
1811 {
1812   return mImpl->mCursorBlinkDuration;
1813 }
1814
1815 void Decorator::SetCursorWidth( int width )
1816 {
1817   mImpl->mCursorWidth = static_cast<float>( width );
1818 }
1819
1820 int Decorator::GetCursorWidth() const
1821 {
1822   return static_cast<int>( mImpl->mCursorWidth );
1823 }
1824
1825 /** Handles **/
1826
1827 void Decorator::SetHandleActive( HandleType handleType, bool active )
1828 {
1829   mImpl->mHandle[handleType].active = active;
1830
1831   if( !active )
1832   {
1833     if( ( LEFT_SELECTION_HANDLE == handleType ) || ( RIGHT_SELECTION_HANDLE == handleType ) )
1834     {
1835       mImpl->mHandlePreviousCrossed = false;
1836     }
1837
1838     // TODO: this is a work-around.
1839     // The problem is the handle actor does not receive the touch event with the Interrupt
1840     // state when the power button is pressed and the application goes to background.
1841     mImpl->mHandle[handleType].pressed = false;
1842     Image imageReleased = mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED];
1843     ImageView imageView = mImpl->mHandle[handleType].actor;
1844     if( imageReleased && imageView )
1845     {
1846       imageView.SetImage( imageReleased );
1847     }
1848   }
1849
1850 }
1851
1852 bool Decorator::IsHandleActive( HandleType handleType ) const
1853 {
1854   return mImpl->mHandle[handleType].active ;
1855 }
1856
1857 void Decorator::SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image )
1858 {
1859   mImpl->SetHandleImage( handleType, handleImageType, image );
1860 }
1861
1862 Dali::Image Decorator::GetHandleImage( HandleType handleType, HandleImageType handleImageType ) const
1863 {
1864   return mImpl->mHandleImages[handleType][handleImageType];
1865 }
1866
1867 void Decorator::SetHandleColor( const Vector4& color )
1868 {
1869   mImpl->mHandleColor = color;
1870 }
1871
1872 const Vector4& Decorator::GetHandleColor() const
1873 {
1874   return mImpl->mHandleColor;
1875 }
1876
1877 void Decorator::SetPosition( HandleType handleType, float x, float y, float height )
1878 {
1879   // Adjust handle's displacement
1880   Impl::HandleImpl& handle = mImpl->mHandle[handleType];
1881
1882   handle.grabDisplacementX -= x - handle.position.x;
1883   handle.grabDisplacementY -= y - handle.position.y;
1884
1885   handle.position.x = x;
1886   handle.position.y = y;
1887   handle.lineHeight = height;
1888 }
1889
1890 void Decorator::GetPosition( HandleType handleType, float& x, float& y, float& height ) const
1891 {
1892   Impl::HandleImpl& handle = mImpl->mHandle[handleType];
1893
1894   x = handle.position.x;
1895   y = handle.position.y;
1896   height = handle.lineHeight;
1897 }
1898
1899 const Vector2& Decorator::GetPosition( HandleType handleType ) const
1900 {
1901   return mImpl->mHandle[handleType].position;
1902 }
1903
1904 void Decorator::FlipHandleVertically( HandleType handleType, bool flip )
1905 {
1906   mImpl->mHandle[handleType].verticallyFlippedPreferred = flip;
1907 }
1908
1909 bool Decorator::IsHandleVerticallyFlipped( HandleType handleType ) const
1910 {
1911   return mImpl->mHandle[handleType].verticallyFlippedPreferred;
1912 }
1913
1914 void Decorator::FlipSelectionHandlesOnCrossEnabled( bool enable )
1915 {
1916   mImpl->mFlipSelectionHandlesOnCross = enable;
1917 }
1918
1919 void Decorator::SetSelectionHandleFlipState( bool indicesSwapped, bool left, bool right )
1920 {
1921   mImpl->mHandleCurrentCrossed = indicesSwapped;
1922   mImpl->mFlipLeftSelectionHandleDirection = left;
1923   mImpl->mFlipRightSelectionHandleDirection = right;
1924 }
1925
1926 void Decorator::AddHighlight( float x1, float y1, float x2, float y2 )
1927 {
1928   mImpl->mHighlightQuadList.push_back( QuadCoordinates(x1, y1, x2, y2) );
1929 }
1930
1931 void Decorator::ClearHighlights()
1932 {
1933   mImpl->mHighlightQuadList.clear();
1934   mImpl->mHighlightPosition = Vector2::ZERO;
1935 }
1936
1937 void Decorator::SetHighlightColor( const Vector4& color )
1938 {
1939   mImpl->mHighlightColor = color;
1940 }
1941
1942 const Vector4& Decorator::GetHighlightColor() const
1943 {
1944   return mImpl->mHighlightColor;
1945 }
1946
1947 void Decorator::SetTextDepth( int textDepth )
1948 {
1949   mImpl->mTextDepth = textDepth;
1950 }
1951
1952 void Decorator::SetPopupActive( bool active )
1953 {
1954   mImpl->mActiveCopyPastePopup = active;
1955 }
1956
1957 bool Decorator::IsPopupActive() const
1958 {
1959   return mImpl->mActiveCopyPastePopup ;
1960 }
1961
1962 void Decorator::SetEnabledPopupButtons( TextSelectionPopup::Buttons& enabledButtonsBitMask )
1963 {
1964   mImpl->mEnabledPopupButtons = enabledButtonsBitMask;
1965
1966   if ( !mImpl->mCopyPastePopup.actor )
1967   {
1968     mImpl->mCopyPastePopup.actor = TextSelectionPopup::New( &mImpl->mTextSelectionPopupCallbackInterface );
1969 #ifdef DECORATOR_DEBUG
1970     mImpl->mCopyPastePopup.actor.SetName("mCopyPastePopup");
1971 #endif
1972     mImpl->mCopyPastePopup.actor.SetAnchorPoint( AnchorPoint::CENTER );
1973     mImpl->mCopyPastePopup.actor.OnRelayoutSignal().Connect( mImpl,  &Decorator::Impl::PopupRelayoutComplete  ); // Position popup after size negotiation
1974   }
1975
1976   mImpl->mCopyPastePopup.actor.EnableButtons( mImpl->mEnabledPopupButtons );
1977 }
1978
1979 TextSelectionPopup::Buttons& Decorator::GetEnabledPopupButtons()
1980 {
1981   return mImpl->mEnabledPopupButtons;
1982 }
1983
1984 /** Scroll **/
1985
1986 void Decorator::SetScrollThreshold( float threshold )
1987 {
1988   mImpl->SetScrollThreshold( threshold );
1989 }
1990
1991 float Decorator::GetScrollThreshold() const
1992 {
1993   return mImpl->GetScrollThreshold();
1994 }
1995
1996 void Decorator::SetScrollSpeed( float speed )
1997 {
1998   mImpl->SetScrollSpeed( speed );
1999 }
2000
2001 float Decorator::GetScrollSpeed() const
2002 {
2003   return mImpl->GetScrollSpeed();
2004 }
2005
2006 void Decorator::NotifyEndOfScroll()
2007 {
2008   mImpl->NotifyEndOfScroll();
2009 }
2010
2011 Decorator::~Decorator()
2012 {
2013   delete mImpl;
2014 }
2015
2016 Decorator::Decorator( ControllerInterface& controller,
2017                       TextSelectionPopupCallbackInterface& callbackInterface )
2018 : mImpl( NULL )
2019 {
2020   mImpl = new Decorator::Impl( controller, callbackInterface );
2021 }
2022
2023 } // namespace Text
2024
2025 } // namespace Toolkit
2026
2027 } // namespace Dali