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