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