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