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