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