text-decorator to use ImageView instead of ImageActors
[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 #include <dali-toolkit/public-api/controls/image-view/image-view.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       mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
641
642       // Add the active layer telling the controller it doesn't need clipping.
643       mController.AddDecoration( mActiveLayer, false );
644     }
645
646     mActiveLayer.RaiseToTop();
647   }
648
649   void SetSelectionHandleMarkerSize( HandleImpl& handle )
650   {
651     if( handle.markerActor )
652     {
653       handle.markerActor.SetSize( 0, handle.lineHeight );
654     }
655   }
656
657   void CreateGrabHandle()
658   {
659     HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
660     if( !grabHandle.actor )
661     {
662       grabHandle.actor = ImageView::New( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] );
663       GetImpl( grabHandle.actor).SetDepthIndex( DECORATION_DEPTH_INDEX );
664       grabHandle.actor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
665       // Area that Grab handle responds to, larger than actual handle so easier to move
666 #ifdef DECORATOR_DEBUG
667       grabHandle.actor.SetName( "GrabHandleActor" );
668       if ( Dali::Internal::gLogFilter->IsEnabledFor( Debug::Verbose ) )
669       {
670         grabHandle.grabArea = Control::New();
671         Toolkit::Control control = Toolkit::Control::DownCast( grabHandle.grabArea );
672         control.SetBackgroundColor( Vector4( 1.0f, 1.0f, 1.0f, 0.5f ) );
673         grabHandle.grabArea.SetName( "GrabArea" );
674       }
675       else
676       {
677         grabHandle.grabArea = Actor::New();
678         grabHandle.grabArea.SetName( "GrabArea" );
679       }
680 #else
681       grabHandle.grabArea = Actor::New();
682 #endif
683
684       grabHandle.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
685       grabHandle.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
686       grabHandle.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
687       grabHandle.grabArea.SetSizeModeFactor( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE );
688       grabHandle.actor.Add( grabHandle.grabArea );
689       grabHandle.actor.SetColor( mHandleColor );
690
691       grabHandle.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnGrabHandleTouched );
692       mTapDetector.Attach( grabHandle.grabArea );
693       mPanGestureDetector.Attach( grabHandle.grabArea );
694
695       mActiveLayer.Add( grabHandle.actor );
696     }
697
698     if( !grabHandle.actor.GetParent() )
699     {
700       mActiveLayer.Add( grabHandle.actor );
701     }
702   }
703
704   void CreateHandleMarker( HandleImpl& handle, Image& image, HandleType handleType )
705   {
706     if( image )
707     {
708       handle.markerActor = ImageView::New( image );
709       handle.markerActor.SetColor( mHandleColor );
710       handle.actor.Add( handle.markerActor );
711
712       handle.markerActor.SetResizePolicy ( ResizePolicy::FIXED, Dimension::HEIGHT );
713
714       if( LEFT_SELECTION_HANDLE == handleType )
715       {
716         handle.markerActor.SetAnchorPoint( AnchorPoint::BOTTOM_RIGHT );
717         handle.markerActor.SetParentOrigin( ParentOrigin::TOP_RIGHT );
718       }
719       else if( RIGHT_SELECTION_HANDLE == handleType )
720       {
721         handle.markerActor.SetAnchorPoint( AnchorPoint::BOTTOM_LEFT );
722         handle.markerActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
723       }
724     }
725   }
726
727   void CreateSelectionHandles()
728   {
729     HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
730     if( !primary.actor )
731     {
732       primary.actor = ImageView::New( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
733 #ifdef DECORATOR_DEBUG
734       primary.actor.SetName("SelectionHandleOne");
735 #endif
736       primary.actor.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
737       GetImpl( primary.actor ).SetDepthIndex( DECORATION_DEPTH_INDEX );
738       primary.actor.SetColor( mHandleColor );
739
740       primary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
741 #ifdef DECORATOR_DEBUG
742       primary.grabArea.SetName("SelectionHandleOneGrabArea");
743 #endif
744       primary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
745       primary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
746       primary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
747       primary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
748
749       mTapDetector.Attach( primary.grabArea );
750       mPanGestureDetector.Attach( primary.grabArea );
751       primary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleOneTouched );
752
753       primary.actor.Add( primary.grabArea );
754
755       CreateHandleMarker( primary, mHandleImages[LEFT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], LEFT_SELECTION_HANDLE );
756     }
757
758     if( !primary.actor.GetParent() )
759     {
760       mActiveLayer.Add( primary.actor );
761     }
762
763     HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
764     if( !secondary.actor )
765     {
766       secondary.actor = ImageView::New( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
767 #ifdef DECORATOR_DEBUG
768       secondary.actor.SetName("SelectionHandleTwo");
769 #endif
770       secondary.actor.SetAnchorPoint( AnchorPoint::TOP_LEFT ); // Change to BOTTOM_LEFT if Look'n'Feel requires handle above text.
771       GetImpl( secondary.actor ).SetDepthIndex( DECORATION_DEPTH_INDEX );
772       secondary.actor.SetColor( mHandleColor );
773
774       secondary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
775 #ifdef DECORATOR_DEBUG
776       secondary.grabArea.SetName("SelectionHandleTwoGrabArea");
777 #endif
778       secondary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
779       secondary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
780       secondary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
781       secondary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
782
783       mTapDetector.Attach( secondary.grabArea );
784       mPanGestureDetector.Attach( secondary.grabArea );
785       secondary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleTwoTouched );
786
787       secondary.actor.Add( secondary.grabArea );
788
789       CreateHandleMarker( secondary, mHandleImages[RIGHT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], RIGHT_SELECTION_HANDLE  );
790     }
791
792     if( !secondary.actor.GetParent() )
793     {
794       mActiveLayer.Add( secondary.actor );
795     }
796   }
797
798   void CalculateHandleWorldCoordinates( HandleImpl& handle, Vector2& position )
799   {
800     // Gets the world position of the active layer. The active layer is where the handles are added.
801     const Vector3 parentWorldPosition = mActiveLayer.GetCurrentWorldPosition();
802
803     // The grab handle position in world coords.
804     // The active layer's world position is the center of the active layer. The origin of the
805     // coord system of the handles is the top left of the active layer.
806     position.x = parentWorldPosition.x - 0.5f * mControlSize.width + handle.position.x;
807     position.y = parentWorldPosition.y - 0.5f * mControlSize.height + handle.position.y;
808   }
809
810   void SetGrabHandlePosition()
811   {
812     // Reference to the grab handle.
813     HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
814
815     // Transforms the handle position into world coordinates.
816     // @note This is not the same value as grabHandle.actor.GetCurrentWorldPosition()
817     // as it's transforming the handle's position set by the text-controller and not
818     // the final position set to the actor. Another difference is the GetCurrentWorldPosition()
819     // retrieves the position of the center of the actor but the handle's position set
820     // by the text controller is not the center of the actor.
821     Vector2 grabHandleWorldPosition;
822     CalculateHandleWorldCoordinates( grabHandle, grabHandleWorldPosition );
823
824     // Check if the grab handle exceeds the boundaries of the decoration box.
825     // At the moment only the height is checked for the grab handle.
826
827     grabHandle.verticallyFlipped = ( grabHandle.verticallyFlippedPreferred &&
828                                      ( ( grabHandleWorldPosition.y - grabHandle.size.height ) > mBoundingBox.y ) ) ||
829                                    ( grabHandleWorldPosition.y + grabHandle.lineHeight + grabHandle.size.height > mBoundingBox.w );
830
831     // The grab handle 'y' position in local coords.
832     // If the grab handle exceeds the bottom of the decoration box,
833     // set the 'y' position to the top of the line.
834     // The SetGrabHandleImage() method will change the orientation.
835     const float yLocalPosition = grabHandle.verticallyFlipped ? grabHandle.position.y : grabHandle.position.y + grabHandle.lineHeight;
836
837     grabHandle.actor.SetPosition( grabHandle.position.x + floor( 0.5f * mCursorWidth ),
838                                   yLocalPosition ); // TODO : Fix for multiline.
839   }
840
841   void SetSelectionHandlePosition( HandleType type )
842   {
843     const bool isPrimaryHandle = LEFT_SELECTION_HANDLE == type;
844
845     // Reference to the selection handle.
846     HandleImpl& handle = mHandle[type];
847
848     // Transforms the handle position into world coordinates.
849     // @note This is not the same value as handle.actor.GetCurrentWorldPosition()
850     // as it's transforming the handle's position set by the text-controller and not
851     // the final position set to the actor. Another difference is the GetCurrentWorldPosition()
852     // retrieves the position of the center of the actor but the handle's position set
853     // by the text controller is not the center of the actor.
854     Vector2 handleWorldPosition;
855     CalculateHandleWorldCoordinates( handle, handleWorldPosition );
856
857     // Whether to flip the handle (horizontally).
858     bool flipHandle = isPrimaryHandle ? mFlipLeftSelectionHandleDirection : mFlipRightSelectionHandleDirection;
859
860     // Whether to flip the handles if they are crossed.
861     bool crossFlip = false;
862     if( mFlipSelectionHandlesOnCross || !mHandlePanning )
863     {
864       crossFlip = mHandleCurrentCrossed;
865     }
866
867     // Does not flip if both conditions are true (double flip)
868     flipHandle = flipHandle != ( crossFlip || mHandlePreviousCrossed );
869
870     // Will flip the handles vertically if the user prefers it.
871     bool verticallyFlippedPreferred = handle.verticallyFlippedPreferred;
872
873     if( crossFlip || mHandlePreviousCrossed )
874     {
875       if( isPrimaryHandle )
876       {
877         verticallyFlippedPreferred = mHandle[RIGHT_SELECTION_HANDLE].verticallyFlippedPreferred;
878       }
879       else
880       {
881         verticallyFlippedPreferred = mHandle[LEFT_SELECTION_HANDLE].verticallyFlippedPreferred;
882       }
883     }
884
885     // Check if the selection handle exceeds the boundaries of the decoration box.
886     const bool exceedsLeftEdge = ( isPrimaryHandle ? !flipHandle : flipHandle ) && ( handleWorldPosition.x - handle.size.width < mBoundingBox.x );
887     const bool exceedsRightEdge = ( isPrimaryHandle ? flipHandle : !flipHandle ) && ( handleWorldPosition.x + handle.size.width > mBoundingBox.z );
888
889     // Does not flip if both conditions are true (double flip)
890     flipHandle = flipHandle != ( exceedsLeftEdge || exceedsRightEdge );
891
892     if( flipHandle )
893     {
894       if( !handle.horizontallyFlipped )
895       {
896         // Change the anchor point to flip the image.
897         handle.actor.SetAnchorPoint( isPrimaryHandle ? AnchorPoint::TOP_LEFT : AnchorPoint::TOP_RIGHT );
898
899         handle.horizontallyFlipped = true;
900       }
901     }
902     else
903     {
904       if( handle.horizontallyFlipped )
905       {
906         // Reset the anchor point.
907         handle.actor.SetAnchorPoint( isPrimaryHandle ? AnchorPoint::TOP_RIGHT : AnchorPoint::TOP_LEFT );
908
909         handle.horizontallyFlipped = false;
910       }
911     }
912
913     // Whether to flip the handle vertically.
914     handle.verticallyFlipped = ( verticallyFlippedPreferred &&
915                                  ( ( handleWorldPosition.y - handle.size.height ) > mBoundingBox.y ) ) ||
916                                ( handleWorldPosition.y + handle.lineHeight + handle.size.height > mBoundingBox.w );
917
918     // The primary selection handle 'y' position in local coords.
919     // If the handle exceeds the bottom of the decoration box,
920     // set the 'y' position to the top of the line.
921     // The SetHandleImage() method will change the orientation.
922     const float yLocalPosition = handle.verticallyFlipped ? handle.position.y : handle.position.y + handle.lineHeight;
923
924     handle.actor.SetPosition( handle.position.x,
925                               yLocalPosition ); // TODO : Fix for multiline.
926   }
927
928   void SetHandleImage( HandleType type )
929   {
930     HandleImpl& handle = mHandle[type];
931
932     HandleType markerType = HANDLE_TYPE_COUNT;
933     // If the selection handle is flipped it chooses the image of the other selection handle. Does nothing for the grab handle.
934     if( LEFT_SELECTION_HANDLE == type )
935     {
936       type = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE;
937       markerType = handle.horizontallyFlipped ? RIGHT_SELECTION_HANDLE_MARKER : LEFT_SELECTION_HANDLE_MARKER;
938     }
939     else if( RIGHT_SELECTION_HANDLE == type )
940     {
941       type = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE : RIGHT_SELECTION_HANDLE;
942       markerType = handle.horizontallyFlipped ? LEFT_SELECTION_HANDLE_MARKER : RIGHT_SELECTION_HANDLE_MARKER;
943     }
944
945     // Chooses between the released or pressed image. It checks whether the pressed image exists.
946     if( handle.actor )
947     {
948       const HandleImageType imageType = ( handle.pressed ? ( mHandleImages[type][HANDLE_IMAGE_PRESSED] ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
949
950       handle.actor.SetImage( mHandleImages[type][imageType] );
951     }
952
953     if( HANDLE_TYPE_COUNT != markerType )
954     {
955       if( handle.markerActor )
956       {
957         const HandleImageType markerImageType = ( handle.pressed ? ( mHandleImages[markerType][HANDLE_IMAGE_PRESSED] ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED ) : HANDLE_IMAGE_RELEASED );
958         handle.markerActor.SetImage( mHandleImages[markerType][markerImageType] );
959       }
960     }
961
962     // Whether to flip the handle vertically.
963     if( handle.actor )
964     {
965       handle.actor.SetOrientation( handle.verticallyFlipped ? ANGLE_180 : ANGLE_0, Vector3::XAXIS );
966     }
967   }
968
969   void CreateHighlight()
970   {
971     if( !mHighlightActor )
972     {
973       mHighlightActor = Actor::New();
974
975 #ifdef DECORATOR_DEBUG
976       mHighlightActor.SetName( "HighlightActor" );
977 #endif
978       mHighlightActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
979       mHighlightActor.SetSize( 1.0f, 1.0f );
980       mHighlightActor.SetColor( mHighlightColor );
981       mHighlightActor.SetColorMode( USE_OWN_COLOR );
982     }
983
984     // Add the highlight box telling the controller it needs clipping.
985     mController.AddDecoration( mHighlightActor, true );
986   }
987
988   void UpdateHighlight()
989   {
990     if ( mHighlightActor )
991     {
992       if( !mHighlightQuadList.empty() )
993       {
994         Vector< Vector2 > vertices;
995         Vector< unsigned int> indices;
996         Vector2 vertex;
997
998         std::vector<QuadCoordinates>::iterator iter = mHighlightQuadList.begin();
999         std::vector<QuadCoordinates>::iterator endIter = mHighlightQuadList.end();
1000
1001         for( std::size_t v = 0; iter != endIter; ++iter,v+=4 )
1002         {
1003           QuadCoordinates& quad = *iter;
1004
1005           // top-left (v+0)
1006           vertex.x = quad.min.x;
1007           vertex.y = quad.min.y;
1008           vertices.PushBack( vertex );
1009
1010           // top-right (v+1)
1011           vertex.x = quad.max.x;
1012           vertex.y = quad.min.y;
1013           vertices.PushBack( vertex );
1014
1015           // bottom-left (v+2)
1016           vertex.x = quad.min.x;
1017           vertex.y = quad.max.y;
1018           vertices.PushBack( vertex );
1019
1020           // bottom-right (v+3)
1021           vertex.x = quad.max.x;
1022           vertex.y = quad.max.y;
1023           vertices.PushBack( vertex );
1024
1025           // triangle A (3, 1, 0)
1026           indices.PushBack( v + 3 );
1027           indices.PushBack( v + 1 );
1028           indices.PushBack( v );
1029
1030           // triangle B (0, 2, 3)
1031           indices.PushBack( v );
1032           indices.PushBack( v + 2 );
1033           indices.PushBack( v + 3 );
1034         }
1035
1036         if( mQuadVertices )
1037         {
1038           mQuadVertices.SetSize( vertices.Size() );
1039         }
1040         else
1041         {
1042           mQuadVertices = PropertyBuffer::New( mQuadVertexFormat, vertices.Size() );
1043         }
1044
1045         if( mQuadIndices )
1046         {
1047           mQuadIndices.SetSize( indices.Size() );
1048         }
1049         else
1050         {
1051           mQuadIndices = PropertyBuffer::New( mQuadIndexFormat, indices.Size() );
1052         }
1053
1054         mQuadVertices.SetData( &vertices[ 0 ] );
1055         mQuadIndices.SetData( &indices[ 0 ] );
1056
1057         if( !mQuadGeometry )
1058         {
1059           mQuadGeometry = Geometry::New();
1060           mQuadGeometry.AddVertexBuffer( mQuadVertices );
1061         }
1062         mQuadGeometry.SetIndexBuffer( mQuadIndices );
1063
1064         if( !mHighlightRenderer )
1065         {
1066           mHighlightRenderer = Dali::Renderer::New( mQuadGeometry, mHighlightMaterial );
1067           mHighlightActor.AddRenderer( mHighlightRenderer );
1068         }
1069       }
1070
1071       mHighlightActor.SetPosition( mHighlightPosition.x,
1072                                    mHighlightPosition.y );
1073
1074       mHighlightQuadList.clear();
1075
1076       if( mHighlightRenderer )
1077       {
1078         mHighlightRenderer.SetDepthIndex( mTextDepth - 2u ); // text is rendered at mTextDepth and text's shadow at mTextDepth -1u.
1079       }
1080     }
1081   }
1082
1083   void OnTap( Actor actor, const TapGesture& tap )
1084   {
1085     if( actor == mHandle[GRAB_HANDLE].actor )
1086     {
1087       // TODO
1088     }
1089   }
1090
1091   void DoPan( HandleImpl& handle, HandleType type, const PanGesture& gesture )
1092   {
1093     if( Gesture::Started == gesture.state )
1094     {
1095       handle.grabDisplacementX = handle.grabDisplacementY = 0;
1096     }
1097
1098     handle.grabDisplacementX += gesture.displacement.x;
1099     handle.grabDisplacementY += gesture.displacement.y;
1100
1101     const float x = handle.position.x + handle.grabDisplacementX;
1102     const float y = handle.position.y + handle.lineHeight*0.5f + handle.grabDisplacementY;
1103
1104     if( Gesture::Started    == gesture.state ||
1105         Gesture::Continuing == gesture.state )
1106     {
1107       Vector2 targetSize;
1108       mController.GetTargetSize( targetSize );
1109
1110       if( x < mScrollThreshold )
1111       {
1112         mScrollDirection = SCROLL_RIGHT;
1113         mHandleScrolling = type;
1114         StartScrollTimer();
1115       }
1116       else if( x > targetSize.width - mScrollThreshold )
1117       {
1118         mScrollDirection = SCROLL_LEFT;
1119         mHandleScrolling = type;
1120         StartScrollTimer();
1121       }
1122       else
1123       {
1124         mHandleScrolling = HANDLE_TYPE_COUNT;
1125         StopScrollTimer();
1126         mController.DecorationEvent( type, HANDLE_PRESSED, x, y );
1127       }
1128
1129       mHandlePanning = true;
1130     }
1131     else if( Gesture::Finished  == gesture.state ||
1132              Gesture::Cancelled == gesture.state )
1133     {
1134       if( mScrollTimer &&
1135           ( mScrollTimer.IsRunning() || mNotifyEndOfScroll ) )
1136       {
1137         mNotifyEndOfScroll = false;
1138         mHandleScrolling = HANDLE_TYPE_COUNT;
1139         StopScrollTimer();
1140         mController.DecorationEvent( type, HANDLE_STOP_SCROLLING, x, y );
1141       }
1142       else
1143       {
1144         mController.DecorationEvent( type, HANDLE_RELEASED, x, y );
1145       }
1146
1147       handle.actor.SetImage( mHandleImages[type][HANDLE_IMAGE_RELEASED] );
1148       handle.pressed = false;
1149
1150       mHandlePanning = false;
1151     }
1152   }
1153
1154   void OnPan( Actor actor, const PanGesture& gesture )
1155   {
1156     HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1157     HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
1158     HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
1159
1160     if( actor == grabHandle.grabArea )
1161     {
1162       DoPan( grabHandle, GRAB_HANDLE, gesture );
1163     }
1164     else if( actor == primarySelectionHandle.grabArea )
1165     {
1166       DoPan( primarySelectionHandle, LEFT_SELECTION_HANDLE, gesture );
1167     }
1168     else if( actor == secondarySelectionHandle.grabArea )
1169     {
1170       DoPan( secondarySelectionHandle, RIGHT_SELECTION_HANDLE, gesture );
1171     }
1172   }
1173
1174   bool OnGrabHandleTouched( Actor actor, const TouchEvent& event )
1175   {
1176     // Switch between pressed/release grab-handle images
1177     if( event.GetPointCount() > 0 &&
1178         mHandle[GRAB_HANDLE].actor )
1179     {
1180       const TouchPoint& point = event.GetPoint(0);
1181
1182       if( TouchPoint::Down == point.state )
1183       {
1184         mHandle[GRAB_HANDLE].pressed = true;
1185       }
1186       else if( ( TouchPoint::Up == point.state ) ||
1187                ( TouchPoint::Interrupted == point.state ) )
1188       {
1189         mHandle[GRAB_HANDLE].pressed = false;
1190       }
1191
1192       SetHandleImage( GRAB_HANDLE );
1193     }
1194
1195     // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1196     return true;
1197   }
1198
1199   bool OnHandleOneTouched( Actor actor, const TouchEvent& event )
1200   {
1201     // Switch between pressed/release selection handle images
1202     if( event.GetPointCount() > 0 &&
1203         mHandle[LEFT_SELECTION_HANDLE].actor )
1204     {
1205       const TouchPoint& point = event.GetPoint(0);
1206
1207       if( TouchPoint::Down == point.state )
1208       {
1209         mHandle[LEFT_SELECTION_HANDLE].pressed = true;
1210       }
1211       else if( ( TouchPoint::Up == point.state ) ||
1212                ( TouchPoint::Interrupted == point.state ) )
1213       {
1214         mHandle[LEFT_SELECTION_HANDLE].pressed = false;
1215         mHandlePreviousCrossed = mHandleCurrentCrossed;
1216         mHandlePanning = false;
1217       }
1218
1219       SetHandleImage( LEFT_SELECTION_HANDLE );
1220     }
1221
1222     // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1223     return true;
1224   }
1225
1226   bool OnHandleTwoTouched( Actor actor, const TouchEvent& event )
1227   {
1228     // Switch between pressed/release selection handle images
1229     if( event.GetPointCount() > 0 &&
1230         mHandle[RIGHT_SELECTION_HANDLE].actor )
1231     {
1232       const TouchPoint& point = event.GetPoint(0);
1233
1234       if( TouchPoint::Down == point.state )
1235       {
1236         mHandle[RIGHT_SELECTION_HANDLE].pressed = true;
1237       }
1238       else if( ( TouchPoint::Up == point.state ) ||
1239                ( TouchPoint::Interrupted == point.state ) )
1240       {
1241         mHandle[RIGHT_SELECTION_HANDLE].pressed = false;
1242         mHandlePreviousCrossed = mHandleCurrentCrossed;
1243         mHandlePanning = false;
1244       }
1245
1246       SetHandleImage( RIGHT_SELECTION_HANDLE );
1247     }
1248
1249     // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1250     return true;
1251   }
1252
1253   void HandleResetPosition( PropertyNotification& source )
1254   {
1255     const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1256
1257     if( grabHandle.active )
1258     {
1259       // Sets the grab handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1260       SetGrabHandlePosition();
1261
1262       // Sets the grab handle image according if it's pressed, flipped, etc.
1263       SetHandleImage( GRAB_HANDLE );
1264     }
1265     else
1266     {
1267       // Sets the primary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1268       SetSelectionHandlePosition( LEFT_SELECTION_HANDLE );
1269
1270       // Sets the primary handle image according if it's pressed, flipped, etc.
1271       SetHandleImage( LEFT_SELECTION_HANDLE );
1272
1273       // Sets the secondary selection handle position and calculates if it needs to be vertically flipped if it exceeds the boundary box.
1274       SetSelectionHandlePosition( RIGHT_SELECTION_HANDLE );
1275
1276       // Sets the secondary handle image according if it's pressed, flipped, etc.
1277       SetHandleImage( RIGHT_SELECTION_HANDLE );
1278     }
1279   }
1280
1281   void SetupActiveLayerPropertyNotifications()
1282   {
1283     if( !mActiveLayer )
1284     {
1285       return;
1286     }
1287
1288     // Vertical notifications.
1289
1290     // Disconnect any previous connected callback.
1291     if( mVerticalLessThanNotification )
1292     {
1293       mVerticalLessThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1294       mActiveLayer.RemovePropertyNotification( mVerticalLessThanNotification );
1295     }
1296
1297     if( mVerticalGreaterThanNotification )
1298     {
1299       mVerticalGreaterThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1300       mActiveLayer.RemovePropertyNotification( mVerticalGreaterThanNotification );
1301     }
1302
1303     const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1304     const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1305     const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1306
1307     if( grabHandle.active )
1308     {
1309       if( grabHandle.verticallyFlipped )
1310       {
1311         // The grab handle is vertically flipped. Never is going to exceed the bottom edje of the display.
1312         mVerticalGreaterThanNotification.Reset();
1313
1314         // The vertical distance from the center of the active layer to the top edje of the display.
1315         const float topHeight = 0.5f * mControlSize.height - grabHandle.position.y + grabHandle.size.height;
1316
1317         mVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1318                                                                               LessThanCondition( mBoundingBox.y + topHeight ) );
1319
1320         // Notifies the change from false to true and from true to false.
1321         mVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1322
1323         // Connects the signals with the callbacks.
1324         mVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1325       }
1326       else
1327       {
1328         // The grab handle is not vertically flipped. Never is going to exceed the top edje of the display.
1329         mVerticalLessThanNotification.Reset();
1330
1331         // The vertical distance from the center of the active layer to the bottom edje of the display.
1332         const float bottomHeight = -0.5f * mControlSize.height + grabHandle.position.y + grabHandle.lineHeight + grabHandle.size.height;
1333
1334         mVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1335                                                                                  GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1336
1337         // Notifies the change from false to true and from true to false.
1338         mVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1339
1340         // Connects the signals with the callbacks.
1341         mVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1342       }
1343     }
1344     else // The selection handles are active
1345     {
1346       if( primaryHandle.verticallyFlipped && secondaryHandle.verticallyFlipped )
1347       {
1348         // Both selection handles are vertically flipped. Never are going to exceed the bottom edje of the display.
1349         mVerticalGreaterThanNotification.Reset();
1350
1351         // The vertical distance from the center of the active layer to the top edje of the display.
1352         const float topHeight = 0.5f * mControlSize.height + std::max( -primaryHandle.position.y + primaryHandle.size.height, -secondaryHandle.position.y + secondaryHandle.size.height );
1353
1354         mVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1355                                                                               LessThanCondition( mBoundingBox.y + topHeight ) );
1356
1357         // Notifies the change from false to true and from true to false.
1358         mVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1359
1360         // Connects the signals with the callbacks.
1361         mVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1362       }
1363       else if( !primaryHandle.verticallyFlipped && !secondaryHandle.verticallyFlipped )
1364       {
1365         // Both selection handles aren't vertically flipped. Never are going to exceed the top edje of the display.
1366         mVerticalLessThanNotification.Reset();
1367
1368         // The vertical distance from the center of the active layer to the bottom edje of the display.
1369         const float bottomHeight = -0.5f * mControlSize.height + std::max( primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height,
1370                                                                            secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height );
1371
1372         mVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1373                                                                                  GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1374
1375         // Notifies the change from false to true and from true to false.
1376         mVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1377
1378         // Connects the signals with the callbacks.
1379         mVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1380       }
1381       else
1382       {
1383         // Only one of the selection handles is vertically flipped. Both vertical notifications are needed.
1384
1385         // The vertical distance from the center of the active layer to the top edje of the display.
1386         const float topHeight = 0.5f * mControlSize.height + ( primaryHandle.verticallyFlipped                              ?
1387                                                                -primaryHandle.position.y + primaryHandle.size.height        :
1388                                                                -secondaryHandle.position.y + secondaryHandle.size.height );
1389
1390         mVerticalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1391                                                                               LessThanCondition( mBoundingBox.y + topHeight ) );
1392
1393         // Notifies the change from false to true and from true to false.
1394         mVerticalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1395
1396         // Connects the signals with the callbacks.
1397         mVerticalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1398
1399         // The vertical distance from the center of the active layer to the bottom edje of the display.
1400         const float bottomHeight = -0.5f * mControlSize.height + ( primaryHandle.verticallyFlipped                                                       ?
1401                                                                    secondaryHandle.position.y + secondaryHandle.lineHeight + secondaryHandle.size.height :
1402                                                                    primaryHandle.position.y + primaryHandle.lineHeight + primaryHandle.size.height );
1403
1404         mVerticalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1405                                                                                  GreaterThanCondition( mBoundingBox.w - bottomHeight ) );
1406
1407         // Notifies the change from false to true and from true to false.
1408         mVerticalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1409
1410         // Connects the signals with the callbacks.
1411         mVerticalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1412       }
1413     }
1414
1415     // Horizontal notifications.
1416
1417     // Disconnect any previous connected callback.
1418     if( mHorizontalLessThanNotification )
1419     {
1420       mHorizontalLessThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1421       mActiveLayer.RemovePropertyNotification( mHorizontalLessThanNotification );
1422     }
1423
1424     if( mHorizontalGreaterThanNotification )
1425     {
1426       mHorizontalGreaterThanNotification.NotifySignal().Disconnect( this, &Decorator::Impl::HandleResetPosition );
1427       mActiveLayer.RemovePropertyNotification( mHorizontalGreaterThanNotification );
1428     }
1429
1430     if( primaryHandle.active || secondaryHandle.active )
1431     {
1432       // The horizontal distance from the center of the active layer to the left edje of the display.
1433       const float leftWidth = 0.5f * mControlSize.width + std::max( -primaryHandle.position.x + primaryHandle.size.width,
1434                                                                     -secondaryHandle.position.x + secondaryHandle.size.width );
1435
1436       mHorizontalLessThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_X,
1437                                                                               LessThanCondition( mBoundingBox.x + leftWidth ) );
1438
1439       // Notifies the change from false to true and from true to false.
1440       mHorizontalLessThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1441
1442       // Connects the signals with the callbacks.
1443       mHorizontalLessThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1444
1445       // The horizontal distance from the center of the active layer to the right edje of the display.
1446       const float rightWidth = -0.5f * mControlSize.width + std::max( primaryHandle.position.x + primaryHandle.size.width,
1447                                                                       secondaryHandle.position.x + secondaryHandle.size.width );
1448
1449       mHorizontalGreaterThanNotification = mActiveLayer.AddPropertyNotification( Actor::Property::WORLD_POSITION_X,
1450                                                                                  GreaterThanCondition( mBoundingBox.z - rightWidth ) );
1451
1452       // Notifies the change from false to true and from true to false.
1453       mHorizontalGreaterThanNotification.SetNotifyMode( PropertyNotification::NotifyOnChanged );
1454
1455       // Connects the signals with the callbacks.
1456       mHorizontalGreaterThanNotification.NotifySignal().Connect( this, &Decorator::Impl::HandleResetPosition );
1457     }
1458   }
1459
1460   // Popup
1461
1462   float AlternatePopUpPositionRelativeToCursor()
1463   {
1464     float alternativePosition = 0.0f;
1465
1466     const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1467
1468     const HandleImpl& primaryHandle = mHandle[LEFT_SELECTION_HANDLE];
1469     const HandleImpl& secondaryHandle = mHandle[RIGHT_SELECTION_HANDLE];
1470     const HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
1471     const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
1472
1473     if( primaryHandle.active || secondaryHandle.active )
1474     {
1475       const float maxHandleHeight = std::max( primaryHandle.size.height, secondaryHandle.size.height );
1476       alternativePosition = 0.5f * popupHeight + cursor.lineHeight + maxHandleHeight + std::min( primaryHandle.position.y, secondaryHandle.position.y );
1477     }
1478     else
1479     {
1480       alternativePosition = 0.5f * popupHeight + cursor.lineHeight + grabHandle.size.height + cursor.position.y;
1481     }
1482
1483     return alternativePosition;
1484   }
1485
1486   void PopUpLeavesVerticalBoundary( PropertyNotification& source )
1487   {
1488     float alternativeYPosition = 0.0f;
1489     // todo use AlternatePopUpPositionRelativeToSelectionHandles() if text is highlighted
1490     // if can't be positioned above, then position below row.
1491     alternativeYPosition = AlternatePopUpPositionRelativeToCursor();
1492
1493     mCopyPastePopup.actor.SetY( alternativeYPosition );
1494   }
1495
1496   void SetUpPopupPositionNotifications()
1497   {
1498     // Note Property notifications ignore any set anchor point so conditions must allow for this.  Default is Top Left.
1499
1500     // Exceeding vertical boundary
1501
1502     const float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT );
1503
1504     PropertyNotification verticalExceedNotification = mCopyPastePopup.actor.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1505                                                                                                      OutsideCondition( mBoundingBox.y + popupHeight * 0.5f,
1506                                                                                                                        mBoundingBox.w - popupHeight * 0.5f ) );
1507
1508     verticalExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesVerticalBoundary );
1509   }
1510
1511   void GetConstrainedPopupPosition( Vector3& requiredPopupPosition, const Vector3& popupDistanceFromAnchorPoint, Actor parent, const Vector4& boundingRectangleWorld )
1512   {
1513     DALI_ASSERT_DEBUG ( "Popup parent not on stage" && parent.OnStage() )
1514
1515     // Parent must already by added to Stage for these Get calls to work
1516     const Vector3 parentWorldPositionLeftAnchor = parent.GetCurrentWorldPosition() - parent.GetCurrentSize() * parent.GetCurrentAnchorPoint();
1517     const Vector3 popupWorldPosition = parentWorldPositionLeftAnchor + requiredPopupPosition;  // Parent World position plus popup local position gives World Position
1518
1519     // Calculate distance to move popup (in local space) so fits within the boundary
1520     float xOffSetToKeepWithinBounds = 0.0f;
1521     if( popupWorldPosition.x - popupDistanceFromAnchorPoint.x < boundingRectangleWorld.x )
1522     {
1523       xOffSetToKeepWithinBounds = boundingRectangleWorld.x - ( popupWorldPosition.x - popupDistanceFromAnchorPoint.x );
1524     }
1525     else if( popupWorldPosition.x +  popupDistanceFromAnchorPoint.x > boundingRectangleWorld.z )
1526     {
1527       xOffSetToKeepWithinBounds = boundingRectangleWorld.z - ( popupWorldPosition.x +  popupDistanceFromAnchorPoint.x );
1528     }
1529
1530     // Ensure initial display of Popup is in alternative position if can not fit above. As Property notification will be a frame behind.
1531     if( popupWorldPosition.y - popupDistanceFromAnchorPoint.y < boundingRectangleWorld.y )
1532     {
1533       requiredPopupPosition.y = AlternatePopUpPositionRelativeToCursor();
1534     }
1535
1536     requiredPopupPosition.x = requiredPopupPosition.x + xOffSetToKeepWithinBounds;
1537
1538     // Prevent pixel mis-alignment by rounding down.
1539     requiredPopupPosition.x = floor( requiredPopupPosition.x );
1540     requiredPopupPosition.y = floor( requiredPopupPosition.y );
1541   }
1542
1543   void SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image )
1544   {
1545     HandleImpl& handle = mHandle[handleType];
1546     handle.size = Size( image.GetWidth(), image.GetHeight() );
1547
1548     mHandleImages[handleType][handleImageType] = image;
1549   }
1550
1551   void SetScrollThreshold( float threshold )
1552   {
1553     mScrollThreshold = threshold;
1554   }
1555
1556   float GetScrollThreshold() const
1557   {
1558     return mScrollThreshold;
1559   }
1560
1561   void SetScrollSpeed( float speed )
1562   {
1563     mScrollSpeed = speed;
1564     mScrollDistance = speed * SCROLL_TICK_INTERVAL * TO_SECONDS;
1565   }
1566
1567   float GetScrollSpeed() const
1568   {
1569     return mScrollSpeed;
1570   }
1571
1572   void NotifyEndOfScroll()
1573   {
1574     StopScrollTimer();
1575
1576     if( mScrollTimer )
1577     {
1578       mNotifyEndOfScroll = true;
1579     }
1580   }
1581
1582   /**
1583    * Creates and starts a timer to scroll the text when handles are close to the edges of the text.
1584    *
1585    * It only starts the timer if it's already created.
1586    */
1587   void StartScrollTimer()
1588   {
1589     if( !mScrollTimer )
1590     {
1591       mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1592       mScrollTimer.TickSignal().Connect( this, &Decorator::Impl::OnScrollTimerTick );
1593     }
1594
1595     if( !mScrollTimer.IsRunning() )
1596     {
1597       mScrollTimer.Start();
1598     }
1599   }
1600
1601   /**
1602    * Stops the timer used to scroll the text.
1603    */
1604   void StopScrollTimer()
1605   {
1606     if( mScrollTimer )
1607     {
1608       mScrollTimer.Stop();
1609     }
1610   }
1611
1612   /**
1613    * Callback called by the timer used to scroll the text.
1614    *
1615    * It calculates and sets a new scroll position.
1616    */
1617   bool OnScrollTimerTick()
1618   {
1619     if( HANDLE_TYPE_COUNT != mHandleScrolling )
1620     {
1621       mController.DecorationEvent( mHandleScrolling,
1622                                    HANDLE_SCROLLING,
1623                                    mScrollDirection == SCROLL_RIGHT ? mScrollDistance : -mScrollDistance,
1624                                    0.f );
1625     }
1626
1627     return true;
1628   }
1629
1630   ControllerInterface& mController;
1631
1632   TapGestureDetector  mTapDetector;
1633   PanGestureDetector  mPanGestureDetector;
1634   Timer               mCursorBlinkTimer;          ///< Timer to signal cursor to blink
1635   Timer               mScrollTimer;               ///< Timer used to scroll the text when the grab handle is moved close to the edges.
1636
1637   Layer                mActiveLayer;                       ///< Layer for active handles and alike that ensures they are above all else.
1638   PropertyNotification mVerticalLessThanNotification;      ///< Notifies when the 'y' coord of the active layer is less than a given value.
1639   PropertyNotification mVerticalGreaterThanNotification;   ///< Notifies when the 'y' coord of the active layer is grater than a given value.
1640   PropertyNotification mHorizontalLessThanNotification;    ///< Notifies when the 'x' coord of the active layer is less than a given value.
1641   PropertyNotification mHorizontalGreaterThanNotification; ///< Notifies when the 'x' coord of the active layer is grater than a given value.
1642   Control              mPrimaryCursor;
1643   Control              mSecondaryCursor;
1644
1645   Actor               mHighlightActor;            ///< Actor to display highlight
1646   Renderer            mHighlightRenderer;
1647   Material            mHighlightMaterial;         ///< Material used for highlight
1648   Property::Map       mQuadVertexFormat;
1649   Property::Map       mQuadIndexFormat;
1650   PopupImpl           mCopyPastePopup;
1651   TextSelectionPopup::Buttons mEnabledPopupButtons; /// Bit mask of currently enabled Popup buttons
1652   TextSelectionPopupCallbackInterface& mTextSelectionPopupCallbackInterface;
1653
1654   Image               mHandleImages[HANDLE_TYPE_COUNT][HANDLE_IMAGE_TYPE_COUNT];
1655   Vector4             mHandleColor;
1656
1657   CursorImpl          mCursor[CURSOR_COUNT];
1658   HandleImpl          mHandle[HANDLE_TYPE_COUNT];
1659
1660   PropertyBuffer      mQuadVertices;
1661   PropertyBuffer      mQuadIndices;
1662   Geometry            mQuadGeometry;
1663   QuadContainer       mHighlightQuadList;         ///< Sub-selections that combine to create the complete selection highlight
1664
1665   Vector4             mBoundingBox;               ///< The bounding box in world coords.
1666   Vector4             mHighlightColor;            ///< Color of the highlight
1667   Vector2             mHighlightPosition;         ///< The position of the highlight actor.
1668   Vector2             mControlSize;               ///< The control's size. Set by the Relayout.
1669
1670   unsigned int        mActiveCursor;
1671   unsigned int        mCursorBlinkInterval;
1672   float               mCursorBlinkDuration;
1673   float               mCursorWidth;             ///< The width of the cursors in pixels.
1674   HandleType          mHandleScrolling;         ///< The handle which is scrolling.
1675   ScrollDirection     mScrollDirection;         ///< The direction of the scroll.
1676   float               mScrollThreshold;         ///< Defines a square area inside the control, close to the edge. A cursor entering this area will trigger scroll events.
1677   float               mScrollSpeed;             ///< The scroll speed in pixels per second.
1678   float               mScrollDistance;          ///< Distance the text scrolls during a scroll interval.
1679   int                 mTextDepth;               ///< The depth used to render the text.
1680
1681   bool                mActiveCopyPastePopup              : 1;
1682   bool                mPopupSetNewPosition               : 1;
1683   bool                mCursorBlinkStatus                 : 1; ///< Flag to switch between blink on and blink off.
1684   bool                mDelayCursorBlink                  : 1; ///< Used to avoid cursor blinking when entering text.
1685   bool                mPrimaryCursorVisible              : 1; ///< Whether the primary cursor is visible.
1686   bool                mSecondaryCursorVisible            : 1; ///< Whether the secondary cursor is visible.
1687   bool                mFlipSelectionHandlesOnCross       : 1; ///< Whether to flip the selection handles as soon as they cross.
1688   bool                mFlipLeftSelectionHandleDirection  : 1; ///< Whether to flip the left selection handle image because of the character's direction.
1689   bool                mFlipRightSelectionHandleDirection : 1; ///< Whether to flip the right selection handle image because of the character's direction.
1690   bool                mHandlePanning                     : 1; ///< Whether any of the handles is moving.
1691   bool                mHandleCurrentCrossed              : 1; ///< Whether the handles are crossed.
1692   bool                mHandlePreviousCrossed             : 1; ///< Whether the handles where crossed at the last handle touch up.
1693   bool                mNotifyEndOfScroll                 : 1; ///< Whether to notify the end of the scroll.
1694 };
1695
1696 DecoratorPtr Decorator::New( ControllerInterface& controller,
1697                              TextSelectionPopupCallbackInterface& callbackInterface )
1698 {
1699   return DecoratorPtr( new Decorator( controller,
1700                                       callbackInterface ) );
1701 }
1702
1703 void Decorator::SetBoundingBox( const Rect<int>& boundingBox )
1704 {
1705   LocalToWorldCoordinatesBoundingBox( boundingBox, mImpl->mBoundingBox );
1706 }
1707
1708 void Decorator::GetBoundingBox( Rect<int>& boundingBox ) const
1709 {
1710   WorldToLocalCoordinatesBoundingBox( mImpl->mBoundingBox, boundingBox );
1711 }
1712
1713 void Decorator::Relayout( const Vector2& size )
1714 {
1715   mImpl->Relayout( size );
1716 }
1717
1718 void Decorator::UpdatePositions( const Vector2& scrollOffset )
1719 {
1720   mImpl->UpdatePositions( scrollOffset );
1721 }
1722
1723 /** Cursor **/
1724
1725 void Decorator::SetActiveCursor( ActiveCursor activeCursor )
1726 {
1727   mImpl->mActiveCursor = activeCursor;
1728 }
1729
1730 unsigned int Decorator::GetActiveCursor() const
1731 {
1732   return mImpl->mActiveCursor;
1733 }
1734
1735 void Decorator::SetPosition( Cursor cursor, float x, float y, float cursorHeight, float lineHeight )
1736 {
1737   mImpl->mCursor[cursor].position.x = x;
1738   mImpl->mCursor[cursor].position.y = y;
1739   mImpl->mCursor[cursor].cursorHeight = cursorHeight;
1740   mImpl->mCursor[cursor].lineHeight = lineHeight;
1741 }
1742
1743 void Decorator::GetPosition( Cursor cursor, float& x, float& y, float& cursorHeight, float& lineHeight ) const
1744 {
1745   x = mImpl->mCursor[cursor].position.x;
1746   y = mImpl->mCursor[cursor].position.y;
1747   cursorHeight = mImpl->mCursor[cursor].cursorHeight;
1748   lineHeight = mImpl->mCursor[cursor].lineHeight;
1749 }
1750
1751 const Vector2& Decorator::GetPosition( Cursor cursor ) const
1752 {
1753   return mImpl->mCursor[cursor].position;
1754 }
1755
1756 void Decorator::SetCursorColor( Cursor cursor, const Dali::Vector4& color )
1757 {
1758   mImpl->mCursor[cursor].color = color;
1759 }
1760
1761 const Dali::Vector4& Decorator::GetColor( Cursor cursor ) const
1762 {
1763   return mImpl->mCursor[cursor].color;
1764 }
1765
1766 void Decorator::StartCursorBlink()
1767 {
1768   if ( !mImpl->mCursorBlinkTimer )
1769   {
1770     mImpl->mCursorBlinkTimer = Timer::New( mImpl->mCursorBlinkInterval );
1771     mImpl->mCursorBlinkTimer.TickSignal().Connect( mImpl, &Decorator::Impl::OnCursorBlinkTimerTick );
1772   }
1773
1774   if ( !mImpl->mCursorBlinkTimer.IsRunning() )
1775   {
1776     mImpl->mCursorBlinkTimer.Start();
1777   }
1778 }
1779
1780 void Decorator::StopCursorBlink()
1781 {
1782   if ( mImpl->mCursorBlinkTimer )
1783   {
1784     mImpl->mCursorBlinkTimer.Stop();
1785   }
1786
1787   mImpl->mCursorBlinkStatus = true; // Keep cursor permanently shown
1788 }
1789
1790 void Decorator::DelayCursorBlink()
1791 {
1792   mImpl->mCursorBlinkStatus = true; // Show cursor for a bit longer
1793   mImpl->mDelayCursorBlink = true;
1794 }
1795
1796 void Decorator::SetCursorBlinkInterval( float seconds )
1797 {
1798   mImpl->mCursorBlinkInterval = static_cast<unsigned int>( seconds * TO_MILLISECONDS ); // Convert to milliseconds
1799 }
1800
1801 float Decorator::GetCursorBlinkInterval() const
1802 {
1803   return static_cast<float>( mImpl->mCursorBlinkInterval ) * TO_SECONDS;
1804 }
1805
1806 void Decorator::SetCursorBlinkDuration( float seconds )
1807 {
1808   mImpl->mCursorBlinkDuration = seconds;
1809 }
1810
1811 float Decorator::GetCursorBlinkDuration() const
1812 {
1813   return mImpl->mCursorBlinkDuration;
1814 }
1815
1816 void Decorator::SetCursorWidth( int width )
1817 {
1818   mImpl->mCursorWidth = static_cast<float>( width );
1819 }
1820
1821 int Decorator::GetCursorWidth() const
1822 {
1823   return static_cast<int>( mImpl->mCursorWidth );
1824 }
1825
1826 /** Handles **/
1827
1828 void Decorator::SetHandleActive( HandleType handleType, bool active )
1829 {
1830   mImpl->mHandle[handleType].active = active;
1831
1832   if( !active )
1833   {
1834     if( ( LEFT_SELECTION_HANDLE == handleType ) || ( RIGHT_SELECTION_HANDLE == handleType ) )
1835     {
1836       mImpl->mHandlePreviousCrossed = false;
1837     }
1838
1839     // TODO: this is a work-around.
1840     // The problem is the handle actor does not receive the touch event with the Interrupt
1841     // state when the power button is pressed and the application goes to background.
1842     mImpl->mHandle[handleType].pressed = false;
1843     Image imageReleased = mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED];
1844     ImageView imageView = mImpl->mHandle[handleType].actor;
1845     if( imageReleased && imageView )
1846     {
1847       imageView.SetImage( imageReleased );
1848     }
1849   }
1850
1851 }
1852
1853 bool Decorator::IsHandleActive( HandleType handleType ) const
1854 {
1855   return mImpl->mHandle[handleType].active ;
1856 }
1857
1858 void Decorator::SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image )
1859 {
1860   mImpl->SetHandleImage( handleType, handleImageType, image );
1861 }
1862
1863 Dali::Image Decorator::GetHandleImage( HandleType handleType, HandleImageType handleImageType ) const
1864 {
1865   return mImpl->mHandleImages[handleType][handleImageType];
1866 }
1867
1868 void Decorator::SetHandleColor( const Vector4& color )
1869 {
1870   mImpl->mHandleColor = color;
1871 }
1872
1873 const Vector4& Decorator::GetHandleColor() const
1874 {
1875   return mImpl->mHandleColor;
1876 }
1877
1878 void Decorator::SetPosition( HandleType handleType, float x, float y, float height )
1879 {
1880   // Adjust handle's displacement
1881   Impl::HandleImpl& handle = mImpl->mHandle[handleType];
1882
1883   handle.grabDisplacementX -= x - handle.position.x;
1884   handle.grabDisplacementY -= y - handle.position.y;
1885
1886   handle.position.x = x;
1887   handle.position.y = y;
1888   handle.lineHeight = height;
1889 }
1890
1891 void Decorator::GetPosition( HandleType handleType, float& x, float& y, float& height ) const
1892 {
1893   Impl::HandleImpl& handle = mImpl->mHandle[handleType];
1894
1895   x = handle.position.x;
1896   y = handle.position.y;
1897   height = handle.lineHeight;
1898 }
1899
1900 const Vector2& Decorator::GetPosition( HandleType handleType ) const
1901 {
1902   return mImpl->mHandle[handleType].position;
1903 }
1904
1905 void Decorator::FlipHandleVertically( HandleType handleType, bool flip )
1906 {
1907   mImpl->mHandle[handleType].verticallyFlippedPreferred = flip;
1908 }
1909
1910 bool Decorator::IsHandleVerticallyFlipped( HandleType handleType ) const
1911 {
1912   return mImpl->mHandle[handleType].verticallyFlippedPreferred;
1913 }
1914
1915 void Decorator::FlipSelectionHandlesOnCrossEnabled( bool enable )
1916 {
1917   mImpl->mFlipSelectionHandlesOnCross = enable;
1918 }
1919
1920 void Decorator::SetSelectionHandleFlipState( bool indicesSwapped, bool left, bool right )
1921 {
1922   mImpl->mHandleCurrentCrossed = indicesSwapped;
1923   mImpl->mFlipLeftSelectionHandleDirection = left;
1924   mImpl->mFlipRightSelectionHandleDirection = right;
1925 }
1926
1927 void Decorator::AddHighlight( float x1, float y1, float x2, float y2 )
1928 {
1929   mImpl->mHighlightQuadList.push_back( QuadCoordinates(x1, y1, x2, y2) );
1930 }
1931
1932 void Decorator::ClearHighlights()
1933 {
1934   mImpl->mHighlightQuadList.clear();
1935   mImpl->mHighlightPosition = Vector2::ZERO;
1936 }
1937
1938 void Decorator::SetHighlightColor( const Vector4& color )
1939 {
1940   mImpl->mHighlightColor = color;
1941 }
1942
1943 const Vector4& Decorator::GetHighlightColor() const
1944 {
1945   return mImpl->mHighlightColor;
1946 }
1947
1948 void Decorator::SetTextDepth( int textDepth )
1949 {
1950   mImpl->mTextDepth = textDepth;
1951 }
1952
1953 void Decorator::SetPopupActive( bool active )
1954 {
1955   mImpl->mActiveCopyPastePopup = active;
1956 }
1957
1958 bool Decorator::IsPopupActive() const
1959 {
1960   return mImpl->mActiveCopyPastePopup ;
1961 }
1962
1963 void Decorator::SetEnabledPopupButtons( TextSelectionPopup::Buttons& enabledButtonsBitMask )
1964 {
1965   mImpl->mEnabledPopupButtons = enabledButtonsBitMask;
1966
1967   if ( !mImpl->mCopyPastePopup.actor )
1968   {
1969     mImpl->mCopyPastePopup.actor = TextSelectionPopup::New( &mImpl->mTextSelectionPopupCallbackInterface );
1970 #ifdef DECORATOR_DEBUG
1971     mImpl->mCopyPastePopup.actor.SetName("mCopyPastePopup");
1972 #endif
1973     mImpl->mCopyPastePopup.actor.SetAnchorPoint( AnchorPoint::CENTER );
1974     mImpl->mCopyPastePopup.actor.OnRelayoutSignal().Connect( mImpl,  &Decorator::Impl::PopupRelayoutComplete  ); // Position popup after size negotiation
1975   }
1976
1977   mImpl->mCopyPastePopup.actor.EnableButtons( mImpl->mEnabledPopupButtons );
1978 }
1979
1980 TextSelectionPopup::Buttons& Decorator::GetEnabledPopupButtons()
1981 {
1982   return mImpl->mEnabledPopupButtons;
1983 }
1984
1985 /** Scroll **/
1986
1987 void Decorator::SetScrollThreshold( float threshold )
1988 {
1989   mImpl->SetScrollThreshold( threshold );
1990 }
1991
1992 float Decorator::GetScrollThreshold() const
1993 {
1994   return mImpl->GetScrollThreshold();
1995 }
1996
1997 void Decorator::SetScrollSpeed( float speed )
1998 {
1999   mImpl->SetScrollSpeed( speed );
2000 }
2001
2002 float Decorator::GetScrollSpeed() const
2003 {
2004   return mImpl->GetScrollSpeed();
2005 }
2006
2007 void Decorator::NotifyEndOfScroll()
2008 {
2009   mImpl->NotifyEndOfScroll();
2010 }
2011
2012 Decorator::~Decorator()
2013 {
2014   delete mImpl;
2015 }
2016
2017 Decorator::Decorator( ControllerInterface& controller,
2018                       TextSelectionPopupCallbackInterface& callbackInterface )
2019 : mImpl( NULL )
2020 {
2021   mImpl = new Decorator::Impl( controller, callbackInterface );
2022 }
2023
2024 } // namespace Text
2025
2026 } // namespace Toolkit
2027
2028 } // namespace Dali