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