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