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