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