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