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