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