Merge "Changed Motion Blur and MotionStretch Effect to use new custom shaders." into...
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / decorator / text-decorator.cpp
1 /*
2  * Copyright (c) 2015 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/integration-api/debug.h>
23
24 #include <dali/public-api/adaptor-framework/timer.h>
25 #include <dali/public-api/actors/layer.h>
26 #include <dali/public-api/common/stage.h>
27 #include <dali/public-api/events/touch-event.h>
28 #include <dali/public-api/events/pan-gesture.h>
29 #include <dali/public-api/images/resource-image.h>
30 #include <dali/public-api/object/property-notification.h>
31
32 #include <dali/devel-api/rendering/geometry.h>
33 #include <dali/devel-api/rendering/renderer.h>
34
35 // INTERNAL INCLUDES
36 #include <dali-toolkit/public-api/controls/control-depth-index-ranges.h>
37 #include <dali-toolkit/public-api/controls/default-controls/solid-color-actor.h>
38
39 #ifdef DEBUG_ENABLED
40 #define DECORATOR_DEBUG
41
42 #endif
43
44 #define MAKE_SHADER(A)#A
45
46 namespace
47 {
48 const char* VERTEX_SHADER = MAKE_SHADER(
49 attribute mediump vec2    aPosition;
50 uniform   mediump mat4    uMvpMatrix;
51 uniform   mediump vec3    uSize;
52
53 void main()
54 {
55   mediump vec4 position = vec4( aPosition, 0.0, 1.0 );
56   position.xyz *= uSize;
57   gl_Position = uMvpMatrix * position;
58 }
59 );
60
61 const char* FRAGMENT_SHADER = MAKE_SHADER(
62 uniform      lowp vec4 uColor;
63
64 void main()
65 {
66   gl_FragColor = uColor;
67 }
68 );
69 }
70
71 namespace Dali
72 {
73 namespace Internal
74 {
75 namespace
76 {
77 #ifdef DECORATOR_DEBUG
78 Integration::Log::Filter* gLogFilter( Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_TEXT_DECORATOR") );
79 #endif
80 }
81 }
82 }
83
84
85 // Local Data
86 namespace
87 {
88 const Dali::Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.25f, 1.5f, 1.0f );
89 const Dali::Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.25f, 1.5f, 1.0f );
90
91 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.
92
93 const Dali::Vector4 HANDLE_COLOR( 0.0f, (183.0f / 255.0f), (229.0f / 255.0f), 1.0f  );
94
95 const unsigned int CURSOR_BLINK_INTERVAL = 500u; // Cursor blink interval
96 const float TO_MILLISECONDS = 1000.f;
97 const float TO_SECONDS = 1.f / TO_MILLISECONDS;
98
99 const unsigned int SCROLL_TICK_INTERVAL = 50u;
100
101 const float SCROLL_THRESHOLD = 10.f;
102 const float SCROLL_SPEED = 300.f;
103 const float SCROLL_DISTANCE = SCROLL_SPEED * SCROLL_TICK_INTERVAL * TO_SECONDS;
104
105 const float CURSOR_WIDTH = 1.f;
106
107 /**
108  * structure to hold coordinates of each quad, which will make up the mesh.
109  */
110 struct QuadCoordinates
111 {
112   /**
113    * Default constructor
114    */
115   QuadCoordinates()
116   {
117   }
118
119   /**
120    * Constructor
121    * @param[in] x1 left co-ordinate
122    * @param[in] y1 top co-ordinate
123    * @param[in] x2 right co-ordinate
124    * @param[in] y2 bottom co-ordinate
125    */
126   QuadCoordinates(float x1, float y1, float x2, float y2)
127   : min(x1, y1),
128     max(x2, y2)
129   {
130   }
131
132   Dali::Vector2 min;                          ///< top-left (minimum) position of quad
133   Dali::Vector2 max;                          ///< bottom-right (maximum) position of quad
134 };
135
136 typedef std::vector<QuadCoordinates> QuadContainer;
137
138 /**
139  * @brief Takes a bounding rectangle in the local coordinates of an actor and returns the world coordinates Bounding Box.
140  * @param[in] boundingRectangle local bounding
141  * @param[out] Vector4 World coordinate bounding Box.
142  */
143 void LocalToWorldCoordinatesBoundingBox( const Dali::Rect<int>& boundingRectangle, Dali::Vector4& boundingBox )
144 {
145   // Convert to world coordinates and store as a Vector4 to be compatible with Property Notifications.
146   Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
147
148   const float originX = boundingRectangle.x - 0.5f * stageSize.width;
149   const float originY = boundingRectangle.y - 0.5f * stageSize.height;
150
151   boundingBox = Dali::Vector4( originX,
152                                originY,
153                                originX + boundingRectangle.width,
154                                originY + boundingRectangle.height );
155 }
156
157 void WorldToLocalCoordinatesBoundingBox( const Dali::Vector4& boundingBox, Dali::Rect<int>& boundingRectangle )
158 {
159   // Convert to local coordinates and store as a Dali::Rect.
160   Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
161
162   boundingRectangle.x = boundingBox.x + 0.5f * stageSize.width;
163   boundingRectangle.y = boundingBox.y + 0.5f * stageSize.height;
164   boundingRectangle.width = boundingBox.z - boundingBox.x;
165   boundingRectangle.height = boundingBox.w - boundingBox.y;
166 }
167
168 } // end of namespace
169
170 namespace Dali
171 {
172
173 namespace Toolkit
174 {
175
176 namespace Text
177 {
178
179 struct Decorator::Impl : public ConnectionTracker
180 {
181   enum ScrollDirection
182   {
183     SCROLL_NONE,
184     SCROLL_RIGHT,
185     SCROLL_LEFT,
186     SCROLL_TOP,
187     SCROLL_BOTTOM
188   };
189
190   struct CursorImpl
191   {
192     CursorImpl()
193     : color( Dali::Color::BLACK ),
194       position(),
195       cursorHeight( 0.0f ),
196       lineHeight( 0.0f )
197     {
198     }
199
200     Vector4 color;
201     Vector2 position;
202     float cursorHeight;
203     float lineHeight;
204   };
205
206   struct HandleImpl
207   {
208     HandleImpl()
209     : position(),
210       size(),
211       lineHeight( 0.0f ),
212       grabDisplacementX( 0.f ),
213       grabDisplacementY( 0.f ),
214       active( false ),
215       visible( false ),
216       pressed( false ),
217       verticallyFlippedPreferred( false ),
218       horizontallyFlipped( false ),
219       verticallyFlipped( false )
220     {
221     }
222
223     ImageActor actor;
224     Actor grabArea;
225     ImageActor markerActor;
226
227     Vector2 position;
228     Size    size;
229     float   lineHeight;              ///< Not the handle height
230     float   grabDisplacementX;
231     float   grabDisplacementY;
232     bool    active  : 1;
233     bool    visible : 1;
234     bool    pressed : 1;
235     bool    verticallyFlippedPreferred : 1; ///< Whether the handle is preferred to be vertically flipped.
236     bool    horizontallyFlipped        : 1; ///< Whether the handle has been horizontally flipped.
237     bool    verticallyFlipped          : 1; ///< Whether the handle has been vertically flipped.
238   };
239
240   struct PopupImpl
241   {
242     PopupImpl()
243     : position()
244     {
245     }
246
247     TextSelectionPopup actor;
248     Vector3 position;
249   };
250
251   Impl( ControllerInterface& controller,
252         TextSelectionPopupCallbackInterface& callbackInterface )
253   : mController( controller ),
254     mEnabledPopupButtons( TextSelectionPopup::NONE ),
255     mTextSelectionPopupCallbackInterface( callbackInterface ),
256     mHandleColor( HANDLE_COLOR ),
257     mBoundingBox(),
258     mHighlightColor( LIGHT_BLUE ),
259     mHighlightPosition( Vector2::ZERO ),
260     mActiveCursor( ACTIVE_CURSOR_NONE ),
261     mCursorBlinkInterval( CURSOR_BLINK_INTERVAL ),
262     mCursorBlinkDuration( 0.0f ),
263     mCursorWidth( CURSOR_WIDTH ),
264     mHandleScrolling( HANDLE_TYPE_COUNT ),
265     mScrollDirection( SCROLL_NONE ),
266     mScrollThreshold( SCROLL_THRESHOLD ),
267     mScrollSpeed( SCROLL_SPEED ),
268     mScrollDistance( SCROLL_DISTANCE ),
269     mTextDepth( 0u ),
270     mActiveCopyPastePopup( false ),
271     mPopupSetNewPosition( true ),
272     mCursorBlinkStatus( true ),
273     mDelayCursorBlink( false ),
274     mPrimaryCursorVisible( false ),
275     mSecondaryCursorVisible( false ),
276     mFlipSelectionHandlesOnCross( false ),
277     mFlipLeftSelectionHandleDirection( false ),
278     mFlipRightSelectionHandleDirection( false ),
279     mHandlePanning( false ),
280     mHandleCurrentCrossed( false ),
281     mHandlePreviousCrossed( false ),
282     mNotifyEndOfScroll( false )
283   {
284     mQuadVertexFormat[ "aPosition" ] = Property::VECTOR2;
285     mQuadIndexFormat[ "indices" ] = Property::INTEGER;
286     mHighlightMaterial = Material::New( Shader::New( VERTEX_SHADER, FRAGMENT_SHADER ) );
287
288     SetupTouchEvents();
289   }
290
291   /**
292    * Relayout of the decorations owned by the decorator.
293    * @param[in] size The Size of the UI control the decorator is adding it's decorations to.
294    */
295   void Relayout( const Vector2& size )
296   {
297     mControlSize = size;
298
299     // TODO - Remove this if nothing is active
300     CreateActiveLayer();
301
302     // Show or hide the cursors
303     CreateCursors();
304
305     if( mPrimaryCursor )
306     {
307       const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
308       mPrimaryCursorVisible = ( cursor.position.x + mCursorWidth <= mControlSize.width ) && ( cursor.position.x >= 0.f );
309       if( mPrimaryCursorVisible )
310       {
311         mPrimaryCursor.SetPosition( cursor.position.x,
312                                     cursor.position.y );
313         mPrimaryCursor.SetSize( Size( mCursorWidth, cursor.cursorHeight ) );
314       }
315       mPrimaryCursor.SetVisible( mPrimaryCursorVisible && mCursorBlinkStatus );
316     }
317     if( mSecondaryCursor )
318     {
319       const CursorImpl& cursor = mCursor[SECONDARY_CURSOR];
320       mSecondaryCursorVisible = ( cursor.position.x + mCursorWidth <= mControlSize.width ) && ( cursor.position.x >= 0.f );
321       if( mSecondaryCursorVisible )
322       {
323         mSecondaryCursor.SetPosition( cursor.position.x,
324                                       cursor.position.y );
325         mSecondaryCursor.SetSize( Size( mCursorWidth, cursor.cursorHeight ) );
326       }
327       mSecondaryCursor.SetVisible( mSecondaryCursorVisible && mCursorBlinkStatus );
328     }
329
330     // Show or hide the grab handle
331     HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
332     bool newGrabHandlePosition = false;
333     if( grabHandle.active )
334     {
335       const bool isVisible = ( grabHandle.position.x + floor( 0.5f * mCursorWidth ) <= mControlSize.width ) && ( grabHandle.position.x >= 0.f );
336
337       if( isVisible )
338       {
339         CreateGrabHandle();
340
341         // Sets the grab handle position and calculate if it needs to be vertically flipped if it exceeds the boundary box.
342         SetGrabHandlePosition();
343
344         // Sets the grab handle image according if it's pressed, flipped, etc.
345         SetHandleImage( GRAB_HANDLE );
346
347         newGrabHandlePosition = true;
348       }
349
350       if( grabHandle.actor )
351       {
352         grabHandle.actor.SetVisible( isVisible );
353       }
354     }
355     else if( grabHandle.actor )
356     {
357       grabHandle.actor.Unparent();
358     }
359
360     // Show or hide the selection handles/highlight
361     HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
362     HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
363     bool newPrimaryHandlePosition = false;
364     bool newSecondaryHandlePosition = false;
365     if( primary.active || secondary.active )
366     {
367       const bool isPrimaryVisible = ( primary.position.x <= mControlSize.width ) && ( primary.position.x >= 0.f );
368       const bool isSecondaryVisible = ( secondary.position.x <= mControlSize.width ) && ( secondary.position.x >= 0.f );
369
370       if( isPrimaryVisible || isSecondaryVisible )
371       {
372         CreateSelectionHandles();
373
374         if( isPrimaryVisible )
375         {
376           SetSelectionHandlePosition( LEFT_SELECTION_HANDLE );
377
378           // Sets the primary handle image according if it's pressed, flipped, etc.
379           SetHandleImage( LEFT_SELECTION_HANDLE );
380
381           SetSelectionHandleMarkerSize( primary );
382
383           newPrimaryHandlePosition = true;
384         }
385
386         if( isSecondaryVisible )
387         {
388           SetSelectionHandlePosition( RIGHT_SELECTION_HANDLE );
389
390           // Sets the secondary handle image according if it's pressed, flipped, etc.
391           SetHandleImage( RIGHT_SELECTION_HANDLE );
392
393           SetSelectionHandleMarkerSize( secondary );
394
395           newSecondaryHandlePosition = true;
396         }
397       }
398
399       if( primary.actor )
400       {
401         primary.actor.SetVisible( isPrimaryVisible );
402       }
403       if( secondary.actor )
404       {
405         secondary.actor.SetVisible( isSecondaryVisible );
406       }
407
408       CreateHighlight();
409       UpdateHighlight();
410     }
411     else
412     {
413       if( primary.actor )
414       {
415         primary.actor.Unparent();
416       }
417       if( secondary.actor )
418       {
419         secondary.actor.Unparent();
420       }
421       if( mHighlightActor )
422       {
423         mHighlightActor.Unparent();
424       }
425     }
426
427     if( newGrabHandlePosition    ||
428         newPrimaryHandlePosition ||
429         newSecondaryHandlePosition )
430     {
431       // Setup property notifications to find whether the handles leave the boundaries of the current display.
432       SetupActiveLayerPropertyNotifications();
433     }
434
435     if( mActiveCopyPastePopup )
436     {
437       ShowPopup();
438     }
439     else
440     {
441       if( mCopyPastePopup.actor )
442       {
443         mCopyPastePopup.actor.HidePopup();
444         mPopupSetNewPosition = true;
445       }
446     }
447   }
448
449   void UpdatePositions( const Vector2& scrollOffset )
450   {
451     mCursor[PRIMARY_CURSOR].position += scrollOffset;
452     mCursor[SECONDARY_CURSOR].position += scrollOffset;
453     mHandle[ GRAB_HANDLE ].position += scrollOffset;
454     mHandle[ LEFT_SELECTION_HANDLE ].position += scrollOffset;
455     mHandle[ RIGHT_SELECTION_HANDLE ].position += scrollOffset;
456     mHighlightPosition += scrollOffset;
457   }
458
459   void ShowPopup()
460   {
461     if ( !mCopyPastePopup.actor )
462     {
463       return;
464     }
465
466     if( !mCopyPastePopup.actor.GetParent() )
467     {
468       mActiveLayer.Add( mCopyPastePopup.actor );
469     }
470
471     mCopyPastePopup.actor.RaiseAbove( mActiveLayer );
472     mCopyPastePopup.actor.ShowPopup();
473   }
474
475   void DeterminePositionPopup()
476   {
477     if( !mActiveCopyPastePopup )
478     {
479       return;
480     }
481
482     // Retrieves the popup's size after relayout.
483     const Vector3 popupSize = Vector3( mCopyPastePopup.actor.GetRelayoutSize( Dimension::WIDTH ), mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT ), 0.0f );
484
485     if( mPopupSetNewPosition )
486     {
487       const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
488       const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
489       const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
490       const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
491
492       if( primaryHandle.active || secondaryHandle.active )
493       {
494         // Calculates the popup's position if selection handles are active.
495         const float minHandleXPosition = std::min( primaryHandle.position.x, secondaryHandle.position.x );
496         const float maxHandleXPosition = std::max( primaryHandle.position.x, secondaryHandle.position.x );
497         const float maxHandleHeight = std::max( primaryHandle.size.height, secondaryHandle.size.height );
498
499         mCopyPastePopup.position.x = minHandleXPosition + ( ( maxHandleXPosition - minHandleXPosition ) * 0.5f );
500         mCopyPastePopup.position.y = -0.5f * popupSize.height - maxHandleHeight + std::min( primaryHandle.position.y, secondaryHandle.position.y );
501       }
502       else
503       {
504         // Calculates the popup's position if the grab handle is active.
505         mCopyPastePopup.position = Vector3( cursor.position.x, -0.5f * popupSize.height - grabHandle.size.height + cursor.position.y, 0.0f );
506       }
507     }
508
509     // Checks if there is enough space above the text control. If not it places the popup under it.
510     GetConstrainedPopupPosition( mCopyPastePopup.position, popupSize * AnchorPoint::CENTER, mActiveLayer, mBoundingBox );
511
512     SetUpPopupPositionNotifications();
513
514     mCopyPastePopup.actor.SetPosition( mCopyPastePopup.position );
515     mPopupSetNewPosition = false;
516   }
517
518   void PopupRelayoutComplete( Actor actor )
519   {
520     // Size negotiation for CopyPastePopup complete so can get the size and constrain position within bounding box.
521
522     DeterminePositionPopup();
523   }
524
525   void CreateCursor( ImageActor& cursor, const Vector4& color )
526   {
527     cursor = CreateSolidColorActor( color );
528     cursor.SetSortModifier( DECORATION_DEPTH_INDEX );
529     cursor.SetParentOrigin( ParentOrigin::TOP_LEFT ); // Need to set the default parent origin as CreateSolidColorActor() sets a different one.
530     cursor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
531   }
532
533   // Add or Remove cursor(s) from parent
534   void CreateCursors()
535   {
536     if( mActiveCursor == ACTIVE_CURSOR_NONE )
537     {
538       if( mPrimaryCursor )
539       {
540         mPrimaryCursor.Unparent();
541       }
542       if( mSecondaryCursor )
543       {
544         mSecondaryCursor.Unparent();
545       }
546     }
547     else
548     {
549       // Create Primary and or Secondary Cursor(s) if active and add to parent
550       if ( mActiveCursor == ACTIVE_CURSOR_PRIMARY ||
551            mActiveCursor == ACTIVE_CURSOR_BOTH )
552       {
553         if ( !mPrimaryCursor )
554         {
555           CreateCursor( mPrimaryCursor, mCursor[PRIMARY_CURSOR].color );
556 #ifdef DECORATOR_DEBUG
557           mPrimaryCursor.SetName( "PrimaryCursorActor" );
558 #endif
559         }
560
561         if( !mPrimaryCursor.GetParent() )
562         {
563           mActiveLayer.Add( mPrimaryCursor );
564         }
565       }
566
567       if ( mActiveCursor == ACTIVE_CURSOR_BOTH )
568       {
569         if ( !mSecondaryCursor )
570         {
571           CreateCursor( mSecondaryCursor, mCursor[SECONDARY_CURSOR].color );
572 #ifdef DECORATOR_DEBUG
573           mSecondaryCursor.SetName( "SecondaryCursorActor" );
574 #endif
575         }
576
577         if( !mSecondaryCursor.GetParent() )
578         {
579           mActiveLayer.Add( mSecondaryCursor );
580         }
581       }
582       else
583       {
584         if( mSecondaryCursor )
585         {
586           mSecondaryCursor.Unparent();
587         }
588       }
589     }
590   }
591
592   bool OnCursorBlinkTimerTick()
593   {
594     if( !mDelayCursorBlink )
595     {
596       // Cursor blinking
597       if ( mPrimaryCursor )
598       {
599         mPrimaryCursor.SetVisible( mPrimaryCursorVisible && mCursorBlinkStatus );
600       }
601       if ( mSecondaryCursor )
602       {
603         mSecondaryCursor.SetVisible( mSecondaryCursorVisible && mCursorBlinkStatus );
604       }
605
606       mCursorBlinkStatus = !mCursorBlinkStatus;
607     }
608     else
609     {
610       // Resume blinking
611       mDelayCursorBlink = false;
612     }
613
614     return true;
615   }
616
617   void SetupTouchEvents()
618   {
619     mTapDetector = TapGestureDetector::New();
620     mTapDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnTap );
621
622     mPanGestureDetector = PanGestureDetector::New();
623     mPanGestureDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnPan );
624   }
625
626   void CreateActiveLayer()
627   {
628     if( !mActiveLayer )
629     {
630       mActiveLayer = Layer::New();
631 #ifdef DECORATOR_DEBUG
632       mActiveLayer.SetName ( "ActiveLayerActor" );
633 #endif
634
635       mActiveLayer.SetParentOrigin( ParentOrigin::CENTER );
636       mActiveLayer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
637       mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
638
639       // Add the active layer telling the controller it doesn't need clipping.
640       mController.AddDecoration( mActiveLayer, false );
641     }
642
643     mActiveLayer.RaiseToTop();
644   }
645
646   void SetSelectionHandleMarkerSize( HandleImpl& handle )
647   {
648     if( handle.markerActor )
649     {
650       handle.markerActor.SetSize( 0, handle.lineHeight );
651     }
652   }
653
654   void CreateGrabHandle()
655   {
656     HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
657     if( !grabHandle.actor )
658     {
659       grabHandle.actor = ImageActor::New( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] );
660       grabHandle.actor.SetSortModifier( DECORATION_DEPTH_INDEX );
661       grabHandle.actor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
662       // Area that Grab handle responds to, larger than actual handle so easier to move
663 #ifdef DECORATOR_DEBUG
664       grabHandle.actor.SetName( "GrabHandleActor" );
665       if ( Dali::Internal::gLogFilter->IsEnabledFor( Debug::Verbose ) )
666       {
667         grabHandle.grabArea = Toolkit::CreateSolidColorActor( Vector4(0.0f, 0.0f, 0.0f, 0.0f), true, Color::RED, 1 );
668         grabHandle.grabArea.SetName( "GrabArea" );
669       }
670       else
671       {
672         grabHandle.grabArea = Actor::New();
673         grabHandle.grabArea.SetName( "GrabArea" );
674       }
675 #else
676       grabHandle.grabArea = Actor::New();
677 #endif
678
679       grabHandle.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
680       grabHandle.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
681       grabHandle.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
682       grabHandle.grabArea.SetSizeModeFactor( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE );
683       grabHandle.actor.Add( grabHandle.grabArea );
684       grabHandle.actor.SetColor( mHandleColor );
685
686       grabHandle.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnGrabHandleTouched );
687       mTapDetector.Attach( grabHandle.grabArea );
688       mPanGestureDetector.Attach( grabHandle.grabArea );
689
690       mActiveLayer.Add( grabHandle.actor );
691     }
692
693     if( !grabHandle.actor.GetParent() )
694     {
695       mActiveLayer.Add( grabHandle.actor );
696     }
697   }
698
699   void CreateHandleMarker( HandleImpl& handle, Image& image, HandleType handleType )
700   {
701     if( image )
702     {
703       handle.markerActor = ImageActor::New( image );
704       handle.markerActor.SetColor( mHandleColor );
705       handle.actor.Add( handle.markerActor );
706
707       handle.markerActor.SetResizePolicy ( ResizePolicy::FIXED, Dimension::HEIGHT );
708
709       if( LEFT_SELECTION_HANDLE == handleType )
710       {
711         handle.markerActor.SetAnchorPoint( AnchorPoint::BOTTOM_RIGHT );
712         handle.markerActor.SetParentOrigin( ParentOrigin::TOP_RIGHT );
713       }
714       else if( RIGHT_SELECTION_HANDLE == handleType )
715       {
716         handle.markerActor.SetAnchorPoint( AnchorPoint::BOTTOM_LEFT );
717         handle.markerActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
718       }
719     }
720   }
721
722   void CreateSelectionHandles()
723   {
724     HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
725     if( !primary.actor )
726     {
727       primary.actor = ImageActor::New( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
728 #ifdef DECORATOR_DEBUG
729       primary.actor.SetName("SelectionHandleOne");
730 #endif
731       primary.actor.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
732       primary.actor.SetSortModifier( DECORATION_DEPTH_INDEX );
733       primary.actor.SetColor( mHandleColor );
734
735       primary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
736 #ifdef DECORATOR_DEBUG
737       primary.grabArea.SetName("SelectionHandleOneGrabArea");
738 #endif
739       primary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
740       primary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
741       primary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
742       primary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
743
744       mTapDetector.Attach( primary.grabArea );
745       mPanGestureDetector.Attach( primary.grabArea );
746       primary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleOneTouched );
747
748       primary.actor.Add( primary.grabArea );
749
750       CreateHandleMarker( primary, mHandleImages[LEFT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], LEFT_SELECTION_HANDLE );
751     }
752
753     if( !primary.actor.GetParent() )
754     {
755       mActiveLayer.Add( primary.actor );
756     }
757
758     HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
759     if( !secondary.actor )
760     {
761       secondary.actor = ImageActor::New( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
762 #ifdef DECORATOR_DEBUG
763       secondary.actor.SetName("SelectionHandleTwo");
764 #endif
765       secondary.actor.SetAnchorPoint( AnchorPoint::TOP_LEFT ); // Change to BOTTOM_LEFT if Look'n'Feel requires handle above text.
766       secondary.actor.SetSortModifier( DECORATION_DEPTH_INDEX );
767       secondary.actor.SetColor( mHandleColor );
768
769       secondary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
770 #ifdef DECORATOR_DEBUG
771       secondary.grabArea.SetName("SelectionHandleTwoGrabArea");
772 #endif
773       secondary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
774       secondary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
775       secondary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
776       secondary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
777
778       mTapDetector.Attach( secondary.grabArea );
779       mPanGestureDetector.Attach( secondary.grabArea );
780       secondary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleTwoTouched );
781
782       secondary.actor.Add( secondary.grabArea );
783
784       CreateHandleMarker( secondary, mHandleImages[RIGHT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], RIGHT_SELECTION_HANDLE  );
785     }
786
787     if( !secondary.actor.GetParent() )
788     {
789       mActiveLayer.Add( secondary.actor );
790     }
791   }
792
793   void CalculateHandleWorldCoordinates( HandleImpl& handle, Vector2& position )
794   {
795     // Gets the world position of the active layer. The active layer is where the handles are added.
796     const Vector3 parentWorldPosition = mActiveLayer.GetCurrentWorldPosition();
797
798     // The grab handle position in world coords.
799     // The active layer's world position is the center of the active layer. The origin of the
800     // coord system of the handles is the top left of the active layer.
801     position.x = parentWorldPosition.x - 0.5f * mControlSize.width + handle.position.x;
802     position.y = parentWorldPosition.y - 0.5f * mControlSize.height + handle.position.y;
803   }
804
805   void SetGrabHandlePosition()
806   {
807     // Reference to the grab handle.
808     HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
809
810     // Transforms the handle position into world coordinates.
811     // @note This is not the same value as grabHandle.actor.GetCurrentWorldPosition()
812     // as it's transforming the handle's position set by the text-controller and not
813     // the final position set to the actor. Another difference is the GetCurrentWorldPosition()
814     // retrieves the position of the center of the actor but the handle's position set
815     // by the text controller is not the center of the actor.
816     Vector2 grabHandleWorldPosition;
817     CalculateHandleWorldCoordinates( grabHandle, grabHandleWorldPosition );
818
819     // Check if the grab handle exceeds the boundaries of the decoration box.
820     // At the moment only the height is checked for the grab handle.
821
822     grabHandle.verticallyFlipped = ( grabHandle.verticallyFlippedPreferred &&
823                                      ( ( grabHandleWorldPosition.y - grabHandle.size.height ) > mBoundingBox.y ) ) ||
824                                    ( grabHandleWorldPosition.y + grabHandle.lineHeight + grabHandle.size.height > mBoundingBox.w );
825
826     // The grab handle 'y' position in local coords.
827     // If the grab handle exceeds the bottom of the decoration box,
828     // set the 'y' position to the top of the line.
829     // The SetGrabHandleImage() method will change the orientation.
830     const float yLocalPosition = grabHandle.verticallyFlipped ? grabHandle.position.y : grabHandle.position.y + grabHandle.lineHeight;
831
832     grabHandle.actor.SetPosition( grabHandle.position.x + floor( 0.5f * mCursorWidth ),
833                                   yLocalPosition ); // TODO : Fix for multiline.
834   }
835
836   void SetSelectionHandlePosition( HandleType type )
837   {
838     const bool isPrimaryHandle = LEFT_SELECTION_HANDLE == type;
839
840     // Reference to the selection handle.
841     HandleImpl& handle = mHandle[type];
842
843     // Transforms the handle position into world coordinates.
844     // @note This is not the same value as handle.actor.GetCurrentWorldPosition()
845     // as it's transforming the handle's position set by the text-controller and not
846     // the final position set to the actor. Another difference is the GetCurrentWorldPosition()
847     // retrieves the position of the center of the actor but the handle's position set
848     // by the text controller is not the center of the actor.
849     Vector2 handleWorldPosition;
850     CalculateHandleWorldCoordinates( handle, handleWorldPosition );
851
852     // Whether to flip the handle (horizontally).
853     bool flipHandle = isPrimaryHandle ? mFlipLeftSelectionHandleDirection : mFlipRightSelectionHandleDirection;
854
855     // Whether to flip the handles if they are crossed.
856     bool crossFlip = false;
857     if( mFlipSelectionHandlesOnCross || !mHandlePanning )
858     {
859       crossFlip = mHandleCurrentCrossed;
860     }
861
862     // Does not flip if both conditions are true (double flip)
863     flipHandle = flipHandle != ( crossFlip || mHandlePreviousCrossed );
864
865     // Will flip the handles vertically if the user prefers it.
866     bool verticallyFlippedPreferred = handle.verticallyFlippedPreferred;
867
868     if( crossFlip || mHandlePreviousCrossed )
869     {
870       if( isPrimaryHandle )
871       {
872         verticallyFlippedPreferred = mHandle[RIGHT_SELECTION_HANDLE].verticallyFlippedPreferred;
873       }
874       else
875       {
876         verticallyFlippedPreferred = mHandle[LEFT_SELECTION_HANDLE].verticallyFlippedPreferred;
877       }
878     }
879
880     // Check if the selection handle exceeds the boundaries of the decoration box.
881     const bool exceedsLeftEdge = ( isPrimaryHandle ? !flipHandle : flipHandle ) && ( handleWorldPosition.x - handle.size.width < mBoundingBox.x );
882     const bool exceedsRightEdge = ( isPrimaryHandle ? flipHandle : !flipHandle ) && ( handleWorldPosition.x + handle.size.width > mBoundingBox.z );
883
884     // Does not flip if both conditions are true (double flip)
885     flipHandle = flipHandle != ( exceedsLeftEdge || exceedsRightEdge );
886
887     if( flipHandle )
888     {
889       if( !handle.horizontallyFlipped )
890       {
891         // Change the anchor point to flip the image.
892         handle.actor.SetAnchorPoint( isPrimaryHandle ? AnchorPoint::TOP_LEFT : AnchorPoint::TOP_RIGHT );
893
894         handle.horizontallyFlipped = true;
895       }
896     }
897     else
898     {
899       if( handle.horizontallyFlipped )
900       {
901         // Reset the anchor point.
902         handle.actor.SetAnchorPoint( isPrimaryHandle ? AnchorPoint::TOP_RIGHT : AnchorPoint::TOP_LEFT );
903
904         handle.horizontallyFlipped = false;
905       }
906     }
907
908     // Whether to flip the handle vertically.
909     handle.verticallyFlipped = ( verticallyFlippedPreferred &&
910                                  ( ( handleWorldPosition.y - handle.size.height ) > mBoundingBox.y ) ) ||
911                                ( handleWorldPosition.y + handle.lineHeight + handle.size.height > mBoundingBox.w );
912
913     // The primary selection handle 'y' position in local coords.
914     // If the handle exceeds the bottom of the decoration box,
915     // set the 'y' position to the top of the line.
916     // The SetHandleImage() method will change the orientation.
917     const float yLocalPosition = handle.verticallyFlipped ? handle.position.y : handle.position.y + handle.lineHeight;
918
919     handle.actor.SetPosition( handle.position.x,
920                               yLocalPosition ); // TODO : Fix for multiline.
921   }
922
923   void SetHandleImage( HandleType type )
924   {
925     HandleImpl& handle = mHandle[type];
926
927     HandleType markerType = HANDLE_TYPE_COUNT;
928     // If the selection handle is flipped it chooses the image of the other selection handle. Does nothing for the grab handle.
929     if( LEFT_SELECTION_HANDLE == type )
930     {
931       type = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE;
932       markerType = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE_MARKER : LEFT_SELECTION_HANDLE_MARKER;
933     }
934     else if( RIGHT_SELECTION_HANDLE == type )
935     {
936       type = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE : RIGHT_SELECTION_HANDLE;
937       markerType = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE_MARKER : RIGHT_SELECTION_HANDLE_MARKER;
938     }
939
940     // Chooses between the released or pressed image. It checks whether the pressed image exists.
941     if( handle.actor )
942     {
943       const HandleImageType imageType = ( handle.pressed ? ( mHandleImages[type][HANDLE_IMAGE_PRESSED] ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
944
945       handle.actor.SetImage( mHandleImages[type][imageType] );
946     }
947
948     if( HANDLE_TYPE_COUNT != markerType )
949     {
950       if( handle.markerActor )
951       {
952         const HandleImageType markerImageType = ( handle.pressed ? ( mHandleImages[markerType][HANDLE_IMAGE_PRESSED] ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
953         handle.markerActor.SetImage( mHandleImages[markerType][markerImageType] );
954       }
955     }
956
957     // Whether to flip the handle vertically.
958     if( handle.actor )
959     {
960       handle.actor.SetOrientation( handle.verticallyFlipped ? ANGLE_180 : ANGLE_0, Vector3::XAXIS );
961     }
962   }
963
964   void CreateHighlight()
965   {
966     if( !mHighlightActor )
967     {
968       mHighlightActor = Actor::New();
969
970 #ifdef DECORATOR_DEBUG
971       mHighlightActor.SetName( "HighlightActor" );
972 #endif
973       mHighlightActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
974       mHighlightActor.SetSize( 1.0f, 1.0f );
975       mHighlightActor.SetColor( mHighlightColor );
976       mHighlightActor.SetColorMode( USE_OWN_COLOR );
977     }
978
979     // Add the highlight box telling the controller it needs clipping.
980     mController.AddDecoration( mHighlightActor, true );
981   }
982
983   void UpdateHighlight()
984   {
985     if ( mHighlightActor )
986     {
987       if( !mHighlightQuadList.empty() )
988       {
989         Vector< Vector2 > vertices;
990         Vector< unsigned int> indices;
991         Vector2 vertex;
992
993         std::vector<QuadCoordinates>::iterator iter = mHighlightQuadList.begin();
994         std::vector<QuadCoordinates>::iterator endIter = mHighlightQuadList.end();
995
996         for( std::size_t v = 0; iter != endIter; ++iter,v+=4 )
997         {
998           QuadCoordinates& quad = *iter;
999
1000           // top-left (v+0)
1001           vertex.x = quad.min.x;
1002           vertex.y = quad.min.y;
1003           vertices.PushBack( vertex );
1004
1005           // top-right (v+1)
1006           vertex.x = quad.max.x;
1007           vertex.y = quad.min.y;
1008           vertices.PushBack( vertex );
1009
1010           // bottom-left (v+2)
1011           vertex.x = quad.min.x;
1012           vertex.y = quad.max.y;
1013           vertices.PushBack( vertex );
1014
1015           // bottom-right (v+3)
1016           vertex.x = quad.max.x;
1017           vertex.y = quad.max.y;
1018           vertices.PushBack( vertex );
1019
1020           // triangle A (3, 1, 0)
1021           indices.PushBack( v + 3 );
1022           indices.PushBack( v + 1 );
1023           indices.PushBack( v );
1024
1025           // triangle B (0, 2, 3)
1026           indices.PushBack( v );
1027           indices.PushBack( v + 2 );
1028           indices.PushBack( v + 3 );
1029         }
1030
1031         if( mQuadVertices )
1032         {
1033           mQuadVertices.SetSize( vertices.Size() );
1034         }
1035         else
1036         {
1037           mQuadVertices = PropertyBuffer::New( mQuadVertexFormat, vertices.Size() );
1038         }
1039
1040         if( mQuadIndices )
1041         {
1042           mQuadIndices.SetSize( indices.Size() );
1043         }
1044         else
1045         {
1046           mQuadIndices = PropertyBuffer::New( mQuadIndexFormat, indices.Size() );
1047         }
1048
1049         mQuadVertices.SetData( &vertices[ 0 ] );
1050         mQuadIndices.SetData( &indices[ 0 ] );
1051
1052         if( !mQuadGeometry )
1053         {
1054           mQuadGeometry = Geometry::New();
1055           mQuadGeometry.AddVertexBuffer( mQuadVertices );
1056         }
1057         mQuadGeometry.SetIndexBuffer( mQuadIndices );
1058
1059         if( !mHighlightRenderer )
1060         {
1061           mHighlightRenderer = Dali::Renderer::New( mQuadGeometry, mHighlightMaterial );
1062           mHighlightActor.AddRenderer( mHighlightRenderer );
1063         }
1064       }
1065
1066       mHighlightActor.SetPosition( mHighlightPosition.x,
1067                                    mHighlightPosition.y );
1068
1069       mHighlightQuadList.clear();
1070
1071       if( mHighlightRenderer )
1072       {
1073         mHighlightRenderer.SetDepthIndex( mTextDepth - 2u ); // text is rendered at mTextDepth and text's shadow at mTextDepth -1u.
1074       }
1075     }
1076   }
1077
1078   void OnTap( Actor actor, const TapGesture& tap )
1079   {
1080     if( actor == mHandle[GRAB_HANDLE].actor )
1081     {
1082       // TODO
1083     }
1084   }
1085
1086   void DoPan( HandleImpl& handle, HandleType type, const PanGesture& gesture )
1087   {
1088     if( Gesture::Started == gesture.state )
1089     {
1090       handle.grabDisplacementX = handle.grabDisplacementY = 0;
1091     }
1092
1093     handle.grabDisplacementX += gesture.displacement.x;
1094     handle.grabDisplacementY += gesture.displacement.y;
1095
1096     const float x = handle.position.x + handle.grabDisplacementX;
1097     const float y = handle.position.y + handle.lineHeight*0.5f + handle.grabDisplacementY;
1098
1099     if( Gesture::Started    == gesture.state ||
1100         Gesture::Continuing == gesture.state )
1101     {
1102       Vector2 targetSize;
1103       mController.GetTargetSize( targetSize );
1104
1105       if( x < mScrollThreshold )
1106       {
1107         mScrollDirection = SCROLL_RIGHT;
1108         mHandleScrolling = type;
1109         StartScrollTimer();
1110       }
1111       else if( x > targetSize.width - mScrollThreshold )
1112       {
1113         mScrollDirection = SCROLL_LEFT;
1114         mHandleScrolling = type;
1115         StartScrollTimer();
1116       }
1117       else
1118       {
1119         mHandleScrolling = HANDLE_TYPE_COUNT;
1120         StopScrollTimer();
1121         mController.DecorationEvent( type, HANDLE_PRESSED, x, y );
1122       }
1123
1124       mHandlePanning = true;
1125     }
1126     else if( Gesture::Finished  == gesture.state ||
1127              Gesture::Cancelled == gesture.state )
1128     {
1129       if( mScrollTimer &&
1130           ( mScrollTimer.IsRunning() || mNotifyEndOfScroll ) )
1131       {
1132         mNotifyEndOfScroll = false;
1133         mHandleScrolling = HANDLE_TYPE_COUNT;
1134         StopScrollTimer();
1135         mController.DecorationEvent( type, HANDLE_STOP_SCROLLING, x, y );
1136       }
1137       else
1138       {
1139         mController.DecorationEvent( type, HANDLE_RELEASED, x, y );
1140       }
1141
1142       handle.actor.SetImage( mHandleImages[type][HANDLE_IMAGE_RELEASED] );
1143       handle.pressed = false;
1144
1145       mHandlePanning = false;
1146     }
1147   }
1148
1149   void OnPan( Actor actor, const PanGesture& gesture )
1150   {
1151     HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1152     HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1153     HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1154
1155     if( actor == grabHandle.grabArea )
1156     {
1157       DoPan( grabHandle, GRAB_HANDLE, gesture );
1158     }
1159     else if( actor == primarySelectionHandle.grabArea )
1160     {
1161       DoPan( primarySelectionHandle, LEFT_SELECTION_HANDLE, gesture );
1162     }
1163     else if( actor == secondarySelectionHandle.grabArea )
1164     {
1165       DoPan( secondarySelectionHandle, RIGHT_SELECTION_HANDLE, gesture );
1166     }
1167   }
1168
1169   bool OnGrabHandleTouched( Actor actor, const TouchEvent& event )
1170   {
1171     // Switch between pressed/release grab-handle images
1172     if( event.GetPointCount() > 0 &&
1173         mHandle[GRAB_HANDLE].actor )
1174     {
1175       const TouchPoint& point = event.GetPoint(0);
1176
1177       if( TouchPoint::Down == point.state )
1178       {
1179         mHandle[GRAB_HANDLE].pressed = true;
1180       }
1181       else if( ( TouchPoint::Up == point.state ) ||
1182                ( TouchPoint::Interrupted == point.state ) )
1183       {
1184         mHandle[GRAB_HANDLE].pressed = false;
1185       }
1186
1187       SetHandleImage( GRAB_HANDLE );
1188     }
1189
1190     // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1191     return true;
1192   }
1193
1194   bool OnHandleOneTouched( Actor actor, const TouchEvent& event )
1195   {
1196     // Switch between pressed/release selection handle images
1197     if( event.GetPointCount() > 0 &&
1198         mHandle[LEFT_SELECTION_HANDLE].actor )
1199     {
1200       const TouchPoint& point = event.GetPoint(0);
1201
1202       if( TouchPoint::Down == point.state )
1203       {
1204         mHandle[LEFT_SELECTION_HANDLE].pressed = true;
1205       }
1206       else if( ( TouchPoint::Up == point.state ) ||
1207                ( TouchPoint::Interrupted == point.state ) )
1208       {
1209         mHandle[LEFT_SELECTION_HANDLE].pressed = false;
1210         mHandlePreviousCrossed = mHandleCurrentCrossed;
1211         mHandlePanning = false;
1212       }
1213
1214       SetHandleImage( LEFT_SELECTION_HANDLE );
1215     }
1216
1217     // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1218     return true;
1219   }
1220
1221   bool OnHandleTwoTouched( Actor actor, const TouchEvent& event )
1222   {
1223     // Switch between pressed/release selection handle images
1224     if( event.GetPointCount() > 0 &&
1225         mHandle[RIGHT_SELECTION_HANDLE].actor )
1226     {
1227       const TouchPoint& point = event.GetPoint(0);
1228
1229       if( TouchPoint::Down == point.state )
1230       {
1231         mHandle[RIGHT_SELECTION_HANDLE].pressed = true;
1232       }
1233       else if( ( TouchPoint::Up == point.state ) ||
1234                ( TouchPoint::Interrupted == point.state ) )
1235       {
1236         mHandle[RIGHT_SELECTION_HANDLE].pressed = false;
1237         mHandlePreviousCrossed = mHandleCurrentCrossed;
1238         mHandlePanning = false;
1239       }
1240
1241       SetHandleImage( RIGHT_SELECTION_HANDLE );
1242     }
1243
1244     // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1245     return true;
1246   }
1247
1248   void HandleResetPosition( PropertyNotification& source )
1249   {
1250     const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1251
1252     if( grabHandle.active )
1253     {
1254       // Sets the grab handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1255       SetGrabHandlePosition();
1256
1257       // Sets the grab handle image according if it's pressed, flipped, etc.
1258       SetHandleImage( GRAB_HANDLE );
1259     }
1260     else
1261     {
1262       // Sets the primary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1263       SetSelectionHandlePosition( LEFT_SELECTION_HANDLE );
1264
1265       // Sets the primary handle image according if it's pressed, flipped, etc.
1266       SetHandleImage( LEFT_SELECTION_HANDLE );
1267
1268       // Sets the secondary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1269       SetSelectionHandlePosition( RIGHT_SELECTION_HANDLE );
1270
1271       // Sets the secondary handle image according if it's pressed, flipped, etc.
1272       SetHandleImage( RIGHT_SELECTION_HANDLE );
1273     }
1274   }
1275
1276   void SetupActiveLayerPropertyNotifications()
1277   {
1278     if( !mActiveLayer )
1279     {
1280       return;
1281     }
1282
1283     // Vertical notifications.
1284
1285     // Disconnect any previous connected callback.
1286     if( mVerticalLessThanNotification )
1287     {
1288       mVerticalLessThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1289       mActiveLayer.RemovePropertyNotification( mVerticalLessThanNotification );
1290     }
1291
1292     if( mVerticalGreaterThanNotification )
1293     {
1294       mVerticalGreaterThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1295       mActiveLayer.RemovePropertyNotification( mVerticalGreaterThanNotification );
1296     }
1297
1298     const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1299     const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1300     const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1301
1302     if( grabHandle.active )
1303     {
1304       if( grabHandle.verticallyFlipped )
1305       {
1306         // The grab handle is vertically flipped. Never is going to exceed the bottom edje of the display.
1307         mVerticalGreaterThanNotification.Reset();
1308
1309         // The vertical distance from the center of the active layer to the top edje of the display.
1310         const float topHeight = 0.5f * mControlSize.height - grabHandle.position.y + grabHandle.size.height;
1311
1312         mVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1313                                                                               LessThanCondition( mBoundingBox.y + topHeight ) );
1314
1315         // Notifies the change from false to true and from true to false.
1316         mVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1317
1318         // Connects the signals with the callbacks.
1319         mVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1320       }
1321       else
1322       {
1323         // The grab handle is not vertically flipped. Never is going to exceed the top edje of the display.
1324         mVerticalLessThanNotification.Reset();
1325
1326         // The vertical distance from the center of the active layer to the bottom edje of the display.
1327         const float bottomHeight = -0.5f * mControlSize.height + grabHandle.position.y + grabHandle.lineHeight + grabHandle.size.height;
1328
1329         mVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1330                                                                                  GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1331
1332         // Notifies the change from false to true and from true to false.
1333         mVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1334
1335         // Connects the signals with the callbacks.
1336         mVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1337       }
1338     }
1339     else // The selection handles are active
1340     {
1341       if( primaryHandle.verticallyFlipped && secondaryHandle.verticallyFlipped )
1342       {
1343         // Both selection handles are vertically flipped. Never are going to exceed the bottom edje of the display.
1344         mVerticalGreaterThanNotification.Reset();
1345
1346         // The vertical distance from the center of the active layer to the top edje of the display.
1347         const float topHeight = 0.5f * mControlSize.height + std::max( -primaryHandle.position.y + primaryHandle.size.height, -secondaryHandle.position.y + secondaryHandle.size.height );
1348
1349         mVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1350                                                                               LessThanCondition( mBoundingBox.y + topHeight ) );
1351
1352         // Notifies the change from false to true and from true to false.
1353         mVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1354
1355         // Connects the signals with the callbacks.
1356         mVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1357       }
1358       else if( !primaryHandle.verticallyFlipped && !secondaryHandle.verticallyFlipped )
1359       {
1360         // Both selection handles aren't vertically flipped. Never are going to exceed the top edje of the display.
1361         mVerticalLessThanNotification.Reset();
1362
1363         // The vertical distance from the center of the active layer to the bottom edje of the display.
1364         const float bottomHeight = -0.5f * mControlSize.height + std::max( primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height,
1365                                                                            secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height );
1366
1367         mVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1368                                                                                  GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1369
1370         // Notifies the change from false to true and from true to false.
1371         mVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1372
1373         // Connects the signals with the callbacks.
1374         mVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1375       }
1376       else
1377       {
1378         // Only one of the selection handles is vertically flipped. Both vertical notifications are needed.
1379
1380         // The vertical distance from the center of the active layer to the top edje of the display.
1381         const float topHeight = 0.5f * mControlSize.height + ( primaryHandle.verticallyFlipped                              ?
1382                                                                -primaryHandle.position.y + primaryHandle.size.height        :
1383                                                                -secondaryHandle.position.y + secondaryHandle.size.height );
1384
1385         mVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1386                                                                               LessThanCondition( mBoundingBox.y + topHeight ) );
1387
1388         // Notifies the change from false to true and from true to false.
1389         mVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1390
1391         // Connects the signals with the callbacks.
1392         mVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1393
1394         // The vertical distance from the center of the active layer to the bottom edje of the display.
1395         const float bottomHeight = -0.5f * mControlSize.height + ( primaryHandle.verticallyFlipped                                                       ?
1396                                                                    secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height :
1397                                                                    primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height );
1398
1399         mVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1400                                                                                  GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1401
1402         // Notifies the change from false to true and from true to false.
1403         mVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1404
1405         // Connects the signals with the callbacks.
1406         mVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1407       }
1408     }
1409
1410     // Horizontal notifications.
1411
1412     // Disconnect any previous connected callback.
1413     if( mHorizontalLessThanNotification )
1414     {
1415       mHorizontalLessThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1416       mActiveLayer.RemovePropertyNotification( mHorizontalLessThanNotification );
1417     }
1418
1419     if( mHorizontalGreaterThanNotification )
1420     {
1421       mHorizontalGreaterThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1422       mActiveLayer.RemovePropertyNotification( mHorizontalGreaterThanNotification );
1423     }
1424
1425     if( primaryHandle.active || secondaryHandle.active )
1426     {
1427       // The horizontal distance from the center of the active layer to the left edje of the display.
1428       const float leftWidth = 0.5f * mControlSize.width + std::max( -primaryHandle.position.x + primaryHandle.size.width,
1429                                                                     -secondaryHandle.position.x + secondaryHandle.size.width );
1430
1431       mHorizontalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_X,
1432                                                                               LessThanCondition( mBoundingBox.x + leftWidth ) );
1433
1434       // Notifies the change from false to true and from true to false.
1435       mHorizontalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1436
1437       // Connects the signals with the callbacks.
1438       mHorizontalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1439
1440       // The horizontal distance from the center of the active layer to the right edje of the display.
1441       const float rightWidth = -0.5f * mControlSize.width + std::max( primaryHandle.position.x + primaryHandle.size.width,
1442                                                                       secondaryHandle.position.x + secondaryHandle.size.width );
1443
1444       mHorizontalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_X,
1445                                                                                  GreaterThanCondition( mBoundingBox.z - rightWidth ) );
1446
1447       // Notifies the change from false to true and from true to false.
1448       mHorizontalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1449
1450       // Connects the signals with the callbacks.
1451       mHorizontalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1452     }
1453   }
1454
1455   // Popup
1456
1457   float AlternatePopUpPositionRelativeToCursor()
1458   {
1459     float alternativePosition = 0.0f;
1460
1461     const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1462
1463     const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1464     const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1465     const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1466     const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
1467
1468     if( primaryHandle.active || secondaryHandle.active )
1469     {
1470       const float maxHandleHeight = std::max( primaryHandle.size.height, secondaryHandle.size.height );
1471       alternativePosition = 0.5f * popupHeight + cursor.lineHeight + maxHandleHeight + std::min( primaryHandle.position.y, secondaryHandle.position.y );
1472     }
1473     else
1474     {
1475       alternativePosition = 0.5f * popupHeight + cursor.lineHeight + grabHandle.size.height + cursor.position.y;
1476     }
1477
1478     return alternativePosition;
1479   }
1480
1481   void PopUpLeavesVerticalBoundary( PropertyNotification& source )
1482   {
1483     float alternativeYPosition = 0.0f;
1484     // todo use AlternatePopUpPositionRelativeToSelectionHandles() if text is highlighted
1485     // if can't be positioned above, then position below row.
1486     alternativeYPosition = AlternatePopUpPositionRelativeToCursor();
1487
1488     mCopyPastePopup.actor.SetY( alternativeYPosition );
1489   }
1490
1491   void SetUpPopupPositionNotifications()
1492   {
1493     // Note Property notifications ignore any set anchor point so conditions must allow for this.  Default is Top Left.
1494
1495     // Exceeding vertical boundary
1496
1497     const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1498
1499     PropertyNotification verticalExceedNotification = mCopyPastePopup.actor.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1500                                                                                                      OutsideCondition( mBoundingBox.y + popupHeight * 0.5f,
1501                                                                                                                        mBoundingBox.w - popupHeight * 0.5f ) );
1502
1503     verticalExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesVerticalBoundary );
1504   }
1505
1506   void GetConstrainedPopupPosition( Vector3& requiredPopupPosition, const Vector3& popupDistanceFromAnchorPoint, Actor parent, const Vector4& boundingRectangleWorld )
1507   {
1508     DALI_ASSERT_DEBUG ( "Popup parent not on stage" && parent.OnStage() )
1509
1510     // Parent must already by added to Stage for these Get calls to work
1511     const Vector3 parentWorldPositionLeftAnchor = parent.GetCurrentWorldPosition() - parent.GetCurrentSize() * parent.GetCurrentAnchorPoint();
1512     const Vector3 popupWorldPosition = parentWorldPositionLeftAnchor + requiredPopupPosition;  // Parent World position plus popup local position gives World Position
1513
1514     // Calculate distance to move popup (in local space) so fits within the boundary
1515     float xOffSetToKeepWithinBounds = 0.0f;
1516     if( popupWorldPosition.x - popupDistanceFromAnchorPoint.x < boundingRectangleWorld.x )
1517     {
1518       xOffSetToKeepWithinBounds = boundingRectangleWorld.x - ( popupWorldPosition.x - popupDistanceFromAnchorPoint.x );
1519     }
1520     else if( popupWorldPosition.x +  popupDistanceFromAnchorPoint.x > boundingRectangleWorld.z )
1521     {
1522       xOffSetToKeepWithinBounds = boundingRectangleWorld.z - ( popupWorldPosition.x +  popupDistanceFromAnchorPoint.x );
1523     }
1524
1525     // Ensure initial display of Popup is in alternative position if can not fit above. As Property notification will be a frame behind.
1526     if( popupWorldPosition.y - popupDistanceFromAnchorPoint.y < boundingRectangleWorld.y )
1527     {
1528       requiredPopupPosition.y = AlternatePopUpPositionRelativeToCursor();
1529     }
1530
1531     requiredPopupPosition.x = requiredPopupPosition.x + xOffSetToKeepWithinBounds;
1532
1533     // Prevent pixel mis-alignment by rounding down.
1534     requiredPopupPosition.x = floor( requiredPopupPosition.x );
1535     requiredPopupPosition.y = floor( requiredPopupPosition.y );
1536   }
1537
1538   void SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image )
1539   {
1540     HandleImpl& handle = mHandle[handleType];
1541     handle.size = Size( image.GetWidth(), image.GetHeight() );
1542
1543     mHandleImages[handleType][handleImageType] = image;
1544   }
1545
1546   void SetScrollThreshold( float threshold )
1547   {
1548     mScrollThreshold = threshold;
1549   }
1550
1551   float GetScrollThreshold() const
1552   {
1553     return mScrollThreshold;
1554   }
1555
1556   void SetScrollSpeed( float speed )
1557   {
1558     mScrollSpeed = speed;
1559     mScrollDistance = speed * SCROLL_TICK_INTERVAL * TO_SECONDS;
1560   }
1561
1562   float GetScrollSpeed() const
1563   {
1564     return mScrollSpeed;
1565   }
1566
1567   void NotifyEndOfScroll()
1568   {
1569     StopScrollTimer();
1570
1571     if( mScrollTimer )
1572     {
1573       mNotifyEndOfScroll = true;
1574     }
1575   }
1576
1577   /**
1578    * Creates and starts a timer to scroll the text when handles are close to the edges of the text.
1579    *
1580    * It only starts the timer if it's already created.
1581    */
1582   void StartScrollTimer()
1583   {
1584     if( !mScrollTimer )
1585     {
1586       mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1587       mScrollTimer.TickSignal().Connect( this, &Decorator::Impl::OnScrollTimerTick );
1588     }
1589
1590     if( !mScrollTimer.IsRunning() )
1591     {
1592       mScrollTimer.Start();
1593     }
1594   }
1595
1596   /**
1597    * Stops the timer used to scroll the text.
1598    */
1599   void StopScrollTimer()
1600   {
1601     if( mScrollTimer )
1602     {
1603       mScrollTimer.Stop();
1604     }
1605   }
1606
1607   /**
1608    * Callback called by the timer used to scroll the text.
1609    *
1610    * It calculates and sets a new scroll position.
1611    */
1612   bool OnScrollTimerTick()
1613   {
1614     if( HANDLE_TYPE_COUNT != mHandleScrolling )
1615     {
1616       mController.DecorationEvent( mHandleScrolling,
1617                                    HANDLE_SCROLLING,
1618                                    mScrollDirection == SCROLL_RIGHT ? mScrollDistance : -mScrollDistance,
1619                                    0.f );
1620     }
1621
1622     return true;
1623   }
1624
1625   ControllerInterface& mController;
1626
1627   TapGestureDetector  mTapDetector;
1628   PanGestureDetector  mPanGestureDetector;
1629   Timer               mCursorBlinkTimer;          ///< Timer to signal cursor to blink
1630   Timer               mScrollTimer;               ///< Timer used to scroll the text when the grab handle is moved close to the edges.
1631
1632   Layer                mActiveLayer;                       ///< Layer for active handles and alike that ensures they are above all else.
1633   PropertyNotification mVerticalLessThanNotification;      ///< Notifies when the 'y' coord of the active layer is less than a given value.
1634   PropertyNotification mVerticalGreaterThanNotification;   ///< Notifies when the 'y' coord of the active layer is grater than a given value.
1635   PropertyNotification mHorizontalLessThanNotification;    ///< Notifies when the 'x' coord of the active layer is less than a given value.
1636   PropertyNotification mHorizontalGreaterThanNotification; ///< Notifies when the 'x' coord of the active layer is grater than a given value.
1637   ImageActor           mPrimaryCursor;
1638   ImageActor           mSecondaryCursor;
1639
1640   Actor               mHighlightActor;            ///< Actor to display highlight
1641   Renderer            mHighlightRenderer;
1642   Material            mHighlightMaterial;         ///< Material used for highlight
1643   Property::Map       mQuadVertexFormat;
1644   Property::Map       mQuadIndexFormat;
1645   PopupImpl           mCopyPastePopup;
1646   TextSelectionPopup::Buttons mEnabledPopupButtons; /// Bit mask of currently enabled Popup buttons
1647   TextSelectionPopupCallbackInterface& mTextSelectionPopupCallbackInterface;
1648
1649   Image               mHandleImages[HANDLE_TYPE_COUNT][HANDLE_IMAGE_TYPE_COUNT];
1650   Vector4             mHandleColor;
1651
1652   CursorImpl          mCursor[CURSOR_COUNT];
1653   HandleImpl          mHandle[HANDLE_TYPE_COUNT];
1654
1655   PropertyBuffer      mQuadVertices;
1656   PropertyBuffer      mQuadIndices;
1657   Geometry            mQuadGeometry;
1658   QuadContainer       mHighlightQuadList;         ///< Sub-selections that combine to create the complete selection highlight
1659
1660   Vector4             mBoundingBox;               ///< The bounding box in world coords.
1661   Vector4             mHighlightColor;            ///< Color of the highlight
1662   Vector2             mHighlightPosition;         ///< The position of the highlight actor.
1663   Vector2             mControlSize;               ///< The control's size. Set by the Relayout.
1664
1665   unsigned int        mActiveCursor;
1666   unsigned int        mCursorBlinkInterval;
1667   float               mCursorBlinkDuration;
1668   float               mCursorWidth;             ///< The width of the cursors in pixels.
1669   HandleType          mHandleScrolling;         ///< The handle which is scrolling.
1670   ScrollDirection     mScrollDirection;         ///< The direction of the scroll.
1671   float               mScrollThreshold;         ///< Defines a square area inside the control, close to the edge. A cursor entering this area will trigger scroll events.
1672   float               mScrollSpeed;             ///< The scroll speed in pixels per second.
1673   float               mScrollDistance;          ///< Distance the text scrolls during a scroll interval.
1674   int                 mTextDepth;               ///< The depth used to render the text.
1675
1676   bool                mActiveCopyPastePopup              : 1;
1677   bool                mPopupSetNewPosition               : 1;
1678   bool                mCursorBlinkStatus                 : 1; ///< Flag to switch between blink on and blink off.
1679   bool                mDelayCursorBlink                  : 1; ///< Used to avoid cursor blinking when entering text.
1680   bool                mPrimaryCursorVisible              : 1; ///< Whether the primary cursor is visible.
1681   bool                mSecondaryCursorVisible            : 1; ///< Whether the secondary cursor is visible.
1682   bool                mFlipSelectionHandlesOnCross       : 1; ///< Whether to flip the selection handles as soon as they cross.
1683   bool                mFlipLeftSelectionHandleDirection  : 1; ///< Whether to flip the left selection handle image because of the character's direction.
1684   bool                mFlipRightSelectionHandleDirection : 1; ///< Whether to flip the right selection handle image because of the character's direction.
1685   bool                mHandlePanning                     : 1; ///< Whether any of the handles is moving.
1686   bool                mHandleCurrentCrossed              : 1; ///< Whether the handles are crossed.
1687   bool                mHandlePreviousCrossed             : 1; ///< Whether the handles where crossed at the last handle touch up.
1688   bool                mNotifyEndOfScroll                 : 1; ///< Whether to notify the end of the scroll.
1689 };
1690
1691 DecoratorPtr Decorator::New( ControllerInterface& controller,
1692                              TextSelectionPopupCallbackInterface& callbackInterface )
1693 {
1694   return DecoratorPtr( new Decorator( controller,
1695                                       callbackInterface ) );
1696 }
1697
1698 void Decorator::SetBoundingBox( const Rect<int>& boundingBox )
1699 {
1700   LocalToWorldCoordinatesBoundingBox( boundingBox, mImpl->mBoundingBox );
1701 }
1702
1703 void Decorator::GetBoundingBox( Rect<int>& boundingBox ) const
1704 {
1705   WorldToLocalCoordinatesBoundingBox( mImpl->mBoundingBox, boundingBox );
1706 }
1707
1708 void Decorator::Relayout( const Vector2& size )
1709 {
1710   mImpl->Relayout( size );
1711 }
1712
1713 void Decorator::UpdatePositions( const Vector2& scrollOffset )
1714 {
1715   mImpl->UpdatePositions( scrollOffset );
1716 }
1717
1718 /** Cursor **/
1719
1720 void Decorator::SetActiveCursor( ActiveCursor activeCursor )
1721 {
1722   mImpl->mActiveCursor = activeCursor;
1723 }
1724
1725 unsigned int Decorator::GetActiveCursor() const
1726 {
1727   return mImpl->mActiveCursor;
1728 }
1729
1730 void Decorator::SetPosition( Cursor cursor, float x, float y, float cursorHeight, float lineHeight )
1731 {
1732   mImpl->mCursor[cursor].position.x = x;
1733   mImpl->mCursor[cursor].position.y = y;
1734   mImpl->mCursor[cursor].cursorHeight = cursorHeight;
1735   mImpl->mCursor[cursor].lineHeight = lineHeight;
1736 }
1737
1738 void Decorator::GetPosition( Cursor cursor, float& x, float& y, float& cursorHeight, float& lineHeight ) const
1739 {
1740   x = mImpl->mCursor[cursor].position.x;
1741   y = mImpl->mCursor[cursor].position.y;
1742   cursorHeight = mImpl->mCursor[cursor].cursorHeight;
1743   lineHeight = mImpl->mCursor[cursor].lineHeight;
1744 }
1745
1746 const Vector2& Decorator::GetPosition( Cursor cursor ) const
1747 {
1748   return mImpl->mCursor[cursor].position;
1749 }
1750
1751 void Decorator::SetCursorColor( Cursor cursor, const Dali::Vector4& color )
1752 {
1753   mImpl->mCursor[cursor].color = color;
1754 }
1755
1756 const Dali::Vector4& Decorator::GetColor( Cursor cursor ) const
1757 {
1758   return mImpl->mCursor[cursor].color;
1759 }
1760
1761 void Decorator::StartCursorBlink()
1762 {
1763   if ( !mImpl->mCursorBlinkTimer )
1764   {
1765     mImpl->mCursorBlinkTimer = Timer::New( mImpl->mCursorBlinkInterval );
1766     mImpl->mCursorBlinkTimer.TickSignal().Connect( mImpl, &Decorator::Impl::OnCursorBlinkTimerTick );
1767   }
1768
1769   if ( !mImpl->mCursorBlinkTimer.IsRunning() )
1770   {
1771     mImpl->mCursorBlinkTimer.Start();
1772   }
1773 }
1774
1775 void Decorator::StopCursorBlink()
1776 {
1777   if ( mImpl->mCursorBlinkTimer )
1778   {
1779     mImpl->mCursorBlinkTimer.Stop();
1780   }
1781
1782   mImpl->mCursorBlinkStatus = true; // Keep cursor permanently shown
1783 }
1784
1785 void Decorator::DelayCursorBlink()
1786 {
1787   mImpl->mCursorBlinkStatus = true; // Show cursor for a bit longer
1788   mImpl->mDelayCursorBlink = true;
1789 }
1790
1791 void Decorator::SetCursorBlinkInterval( float seconds )
1792 {
1793   mImpl->mCursorBlinkInterval = static_cast<unsigned int>( seconds * TO_MILLISECONDS ); // Convert to milliseconds
1794 }
1795
1796 float Decorator::GetCursorBlinkInterval() const
1797 {
1798   return static_cast<float>( mImpl->mCursorBlinkInterval ) * TO_SECONDS;
1799 }
1800
1801 void Decorator::SetCursorBlinkDuration( float seconds )
1802 {
1803   mImpl->mCursorBlinkDuration = seconds;
1804 }
1805
1806 float Decorator::GetCursorBlinkDuration() const
1807 {
1808   return mImpl->mCursorBlinkDuration;
1809 }
1810
1811 void Decorator::SetCursorWidth( int width )
1812 {
1813   mImpl->mCursorWidth = static_cast<float>( width );
1814 }
1815
1816 int Decorator::GetCursorWidth() const
1817 {
1818   return static_cast<int>( mImpl->mCursorWidth );
1819 }
1820
1821 /** Handles **/
1822
1823 void Decorator::SetHandleActive( HandleType handleType, bool active )
1824 {
1825   mImpl->mHandle[handleType].active = active;
1826
1827   if( !active )
1828   {
1829     if( ( LEFT_SELECTION_HANDLE == handleType ) || ( RIGHT_SELECTION_HANDLE == handleType ) )
1830     {
1831       mImpl->mHandlePreviousCrossed = false;
1832     }
1833
1834     // TODO: this is a work-around.
1835     // The problem is the handle actor does not receive the touch event with the Interrupt
1836     // state when the power button is pressed and the application goes to background.
1837     mImpl->mHandle[handleType].pressed = false;
1838     Image imageReleased = mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED];
1839     ImageActor imageActor = mImpl->mHandle[handleType].actor;
1840     if( imageReleased && imageActor )
1841     {
1842        imageActor.SetImage( imageReleased );
1843     }
1844   }
1845
1846 }
1847
1848 bool Decorator::IsHandleActive( HandleType handleType ) const
1849 {
1850   return mImpl->mHandle[handleType].active ;
1851 }
1852
1853 void Decorator::SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image )
1854 {
1855   mImpl->SetHandleImage( handleType, handleImageType, image );
1856 }
1857
1858 Dali::Image Decorator::GetHandleImage( HandleType handleType, HandleImageType handleImageType ) const
1859 {
1860   return mImpl->mHandleImages[handleType][handleImageType];
1861 }
1862
1863 void Decorator::SetHandleColor( const Vector4& color )
1864 {
1865   mImpl->mHandleColor = color;
1866 }
1867
1868 const Vector4& Decorator::GetHandleColor() const
1869 {
1870   return mImpl->mHandleColor;
1871 }
1872
1873 void Decorator::SetPosition( HandleType handleType, float x, float y, float height )
1874 {
1875   // Adjust grab handle displacement
1876   Impl::HandleImpl& handle = mImpl->mHandle[handleType];
1877
1878   handle.grabDisplacementX -= x - handle.position.x;
1879   handle.grabDisplacementY -= y - handle.position.y;
1880
1881   handle.position.x = x;
1882   handle.position.y = y;
1883   handle.lineHeight = height;
1884 }
1885
1886 void Decorator::GetPosition( HandleType handleType, float& x, float& y, float& height ) const
1887 {
1888   Impl::HandleImpl& handle = mImpl->mHandle[handleType];
1889
1890   x = handle.position.x;
1891   y = handle.position.y;
1892   height = handle.lineHeight;
1893 }
1894
1895 const Vector2& Decorator::GetPosition( HandleType handleType ) const
1896 {
1897   return mImpl->mHandle[handleType].position;
1898 }
1899
1900 void Decorator::FlipHandleVertically( HandleType handleType, bool flip )
1901 {
1902   mImpl->mHandle[handleType].verticallyFlippedPreferred = flip;
1903 }
1904
1905 bool Decorator::IsHandleVerticallyFlipped( HandleType handleType ) const
1906 {
1907   return mImpl->mHandle[handleType].verticallyFlippedPreferred;
1908 }
1909
1910 void Decorator::FlipSelectionHandlesOnCrossEnabled( bool enable )
1911 {
1912   mImpl->mFlipSelectionHandlesOnCross = enable;
1913 }
1914
1915 void Decorator::SetSelectionHandleFlipState( bool indicesSwapped, bool left, bool right )
1916 {
1917   mImpl->mHandleCurrentCrossed = indicesSwapped;
1918   mImpl->mFlipLeftSelectionHandleDirection = left;
1919   mImpl->mFlipRightSelectionHandleDirection = right;
1920 }
1921
1922 void Decorator::AddHighlight( float x1, float y1, float x2, float y2 )
1923 {
1924   mImpl->mHighlightQuadList.push_back( QuadCoordinates(x1, y1, x2, y2) );
1925 }
1926
1927 void Decorator::ClearHighlights()
1928 {
1929   mImpl->mHighlightQuadList.clear();
1930   mImpl->mHighlightPosition = Vector2::ZERO;
1931 }
1932
1933 void Decorator::SetHighlightColor( const Vector4& color )
1934 {
1935   mImpl->mHighlightColor = color;
1936 }
1937
1938 const Vector4& Decorator::GetHighlightColor() const
1939 {
1940   return mImpl->mHighlightColor;
1941 }
1942
1943 void Decorator::SetTextDepth( int textDepth )
1944 {
1945   mImpl->mTextDepth = textDepth;
1946 }
1947
1948 void Decorator::SetPopupActive( bool active )
1949 {
1950   mImpl->mActiveCopyPastePopup = active;
1951 }
1952
1953 bool Decorator::IsPopupActive() const
1954 {
1955   return mImpl->mActiveCopyPastePopup ;
1956 }
1957
1958 void Decorator::SetEnabledPopupButtons( TextSelectionPopup::Buttons& enabledButtonsBitMask )
1959 {
1960   mImpl->mEnabledPopupButtons = enabledButtonsBitMask;
1961
1962   if ( !mImpl->mCopyPastePopup.actor )
1963   {
1964     mImpl->mCopyPastePopup.actor = TextSelectionPopup::New( &mImpl->mTextSelectionPopupCallbackInterface );
1965 #ifdef DECORATOR_DEBUG
1966     mImpl->mCopyPastePopup.actor.SetName("mCopyPastePopup");
1967 #endif
1968     mImpl->mCopyPastePopup.actor.SetAnchorPoint( AnchorPoint::CENTER );
1969     mImpl->mCopyPastePopup.actor.OnRelayoutSignal().Connect( mImpl,  &Decorator::Impl::PopupRelayoutComplete  ); // Position popup after size negotiation
1970   }
1971
1972   mImpl->mCopyPastePopup.actor.EnableButtons( mImpl->mEnabledPopupButtons );
1973 }
1974
1975 TextSelectionPopup::Buttons& Decorator::GetEnabledPopupButtons()
1976 {
1977   return mImpl->mEnabledPopupButtons;
1978 }
1979
1980 /** Scroll **/
1981
1982 void Decorator::SetScrollThreshold( float threshold )
1983 {
1984   mImpl->SetScrollThreshold( threshold );
1985 }
1986
1987 float Decorator::GetScrollThreshold() const
1988 {
1989   return mImpl->GetScrollThreshold();
1990 }
1991
1992 void Decorator::SetScrollSpeed( float speed )
1993 {
1994   mImpl->SetScrollSpeed( speed );
1995 }
1996
1997 float Decorator::GetScrollSpeed() const
1998 {
1999   return mImpl->GetScrollSpeed();
2000 }
2001
2002 void Decorator::NotifyEndOfScroll()
2003 {
2004   mImpl->NotifyEndOfScroll();
2005 }
2006
2007 Decorator::~Decorator()
2008 {
2009   delete mImpl;
2010 }
2011
2012 Decorator::Decorator( ControllerInterface& controller,
2013                       TextSelectionPopupCallbackInterface& callbackInterface )
2014 : mImpl( NULL )
2015 {
2016   mImpl = new Decorator::Impl( controller, callbackInterface );
2017 }
2018
2019 } // namespace Text
2020
2021 } // namespace Toolkit
2022
2023 } // namespace Dali