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