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