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