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