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