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