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