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