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