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