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