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