Fixed cursor behavior when no text is set
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / decorator / text-decorator.cpp
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/internal/text/decorator/text-decorator.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/integration-api/debug.h>
23
24 #include <dali/public-api/adaptor-framework/timer.h>
25 #include <dali/public-api/actors/layer.h>
26 #include <dali/public-api/common/stage.h>
27 #include <dali/public-api/events/touch-event.h>
28 #include <dali/public-api/events/pan-gesture.h>
29 #include <dali/public-api/images/resource-image.h>
30 #include <dali/public-api/object/property-notification.h>
31
32 #include <dali/devel-api/rendering/geometry.h>
33 #include <dali/devel-api/rendering/renderer.h>
34
35 // INTERNAL INCLUDES
36 #include <dali-toolkit/public-api/controls/control-depth-index-ranges.h>
37 #include <dali-toolkit/public-api/controls/default-controls/solid-color-actor.h>
38
39 #ifdef DEBUG_ENABLED
40 #define DECORATOR_DEBUG
41
42 #endif
43
44 #define MAKE_SHADER(A)#A
45
46 namespace
47 {
48 const char* VERTEX_SHADER = MAKE_SHADER(
49 attribute mediump vec2    aPosition;
50 uniform   mediump mat4    uMvpMatrix;
51 uniform   mediump vec3    uSize;
52
53 void main()
54 {
55   mediump vec4 position = vec4( aPosition, 0.0, 1.0 );
56   position.xyz *= uSize;
57   gl_Position = uMvpMatrix * position;
58 }
59 );
60
61 const char* FRAGMENT_SHADER = MAKE_SHADER(
62 uniform      lowp vec4 uColor;
63
64 void main()
65 {
66   gl_FragColor = uColor;
67 }
68 );
69 }
70
71 namespace Dali
72 {
73 namespace Internal
74 {
75 namespace
76 {
77 #ifdef DECORATOR_DEBUG
78 Integration::Log::Filter* gLogFilter( Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_TEXT_DECORATOR") );
79 #endif
80 }
81 }
82 }
83
84
85 // Local Data
86 namespace
87 {
88
89 const char* DEFAULT_GRAB_HANDLE_IMAGE_RELEASED( DALI_IMAGE_DIR "cursor_handler_center.png" );
90
91 const int DEFAULT_POPUP_OFFSET( -100.0f ); // Vertical offset of Popup from cursor or handles position.
92
93 const Dali::Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.25f, 1.5f, 1.0f );
94 const Dali::Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.25f, 1.5f, 1.0f );
95
96 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.
97
98 const Dali::Vector4 HANDLE_COLOR( 0.0f, (183.0f / 255.0f), (229.0f / 255.0f), 1.0f  );
99
100 const unsigned int CURSOR_BLINK_INTERVAL = 500u; // Cursor blink interval
101 const float TO_MILLISECONDS = 1000.f;
102 const float TO_SECONDS = 1.f / TO_MILLISECONDS;
103
104 const unsigned int SCROLL_TICK_INTERVAL = 50u;
105
106 const float SCROLL_THRESHOLD = 10.f;
107 const float SCROLL_SPEED = 300.f;
108 const float SCROLL_DISTANCE = SCROLL_SPEED * SCROLL_TICK_INTERVAL * TO_SECONDS;
109
110 const float CURSOR_WIDTH = 1.f;
111
112 /**
113  * structure to hold coordinates of each quad, which will make up the mesh.
114  */
115 struct QuadCoordinates
116 {
117   /**
118    * Default constructor
119    */
120   QuadCoordinates()
121   {
122   }
123
124   /**
125    * Constructor
126    * @param[in] x1 left co-ordinate
127    * @param[in] y1 top co-ordinate
128    * @param[in] x2 right co-ordinate
129    * @param[in] y2 bottom co-ordinate
130    */
131   QuadCoordinates(float x1, float y1, float x2, float y2)
132   : min(x1, y1),
133     max(x2, y2)
134   {
135   }
136
137   Dali::Vector2 min;                          ///< top-left (minimum) position of quad
138   Dali::Vector2 max;                          ///< bottom-right (maximum) position of quad
139 };
140
141 typedef std::vector<QuadCoordinates> QuadContainer;
142
143 /**
144  * @brief Takes a bounding rectangle in the local coordinates of an actor and returns the world coordinates Bounding Box.
145  * @param[in] boundingRectangle local bounding
146  * @param[out] Vector4 World coordinate bounding Box.
147  */
148 void LocalToWorldCoordinatesBoundingBox( const Dali::Rect<int>& boundingRectangle, Dali::Vector4& boundingBox )
149 {
150   // Convert to world coordinates and store as a Vector4 to be compatible with Property Notifications.
151   Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
152
153   const float originX = boundingRectangle.x - 0.5f * stageSize.width;
154   const float originY = boundingRectangle.y - 0.5f * stageSize.height;
155
156   boundingBox = Dali::Vector4( originX,
157                                originY,
158                                originX + boundingRectangle.width,
159                                originY + boundingRectangle.height );
160 }
161
162
163 } // end of namespace
164
165 namespace Dali
166 {
167
168 namespace Toolkit
169 {
170
171 namespace Text
172 {
173
174 struct Decorator::Impl : public ConnectionTracker
175 {
176   enum ScrollDirection
177   {
178     SCROLL_NONE,
179     SCROLL_RIGHT,
180     SCROLL_LEFT,
181     SCROLL_TOP,
182     SCROLL_BOTTOM
183   };
184
185   struct CursorImpl
186   {
187     CursorImpl()
188     : color( Dali::Color::BLACK ),
189       position(),
190       cursorHeight( 0.0f ),
191       lineHeight( 0.0f )
192     {
193     }
194
195     Vector4 color;
196     Vector2 position;
197     float cursorHeight;
198     float lineHeight;
199   };
200
201   struct HandleImpl
202   {
203     HandleImpl()
204     : position(),
205       lineHeight( 0.0f ),
206       grabDisplacementX( 0.f ),
207       grabDisplacementY( 0.f ),
208       active( false ),
209       visible( false ),
210       pressed( false ),
211       flipped( false )
212     {
213     }
214
215     ImageActor actor;
216     Actor grabArea;
217     ImageActor markerActor;
218
219     Vector2 position;
220     float lineHeight; ///< Not the handle height
221     float grabDisplacementX;
222     float grabDisplacementY;
223     bool active  : 1;
224     bool visible : 1;
225     bool pressed : 1;
226     bool flipped : 1;
227   };
228
229   struct PopupImpl
230   {
231     PopupImpl()
232     : position(),
233       offset( DEFAULT_POPUP_OFFSET )
234     {
235     }
236
237     TextSelectionPopup actor;
238     Vector3 position;
239     int offset;
240   };
241
242   Impl( ControllerInterface& controller,
243         TextSelectionPopupCallbackInterface& callbackInterface )
244   : mController( controller ),
245     mEnabledPopupButtons( TextSelectionPopup::NONE ),
246     mTextSelectionPopupCallbackInterface( callbackInterface ),
247     mHandleColor( HANDLE_COLOR ),
248     mBoundingBox( Rect<int>() ),
249     mHighlightColor( LIGHT_BLUE ),
250     mHighlightPosition( Vector2::ZERO ),
251     mActiveCursor( ACTIVE_CURSOR_NONE ),
252     mCursorBlinkInterval( CURSOR_BLINK_INTERVAL ),
253     mCursorBlinkDuration( 0.0f ),
254     mCursorWidth( CURSOR_WIDTH ),
255     mHandleScrolling( HANDLE_TYPE_COUNT ),
256     mScrollDirection( SCROLL_NONE ),
257     mScrollThreshold( SCROLL_THRESHOLD ),
258     mScrollSpeed( SCROLL_SPEED ),
259     mScrollDistance( SCROLL_DISTANCE ),
260     mTextDepth( 0u ),
261     mActiveCopyPastePopup( false ),
262     mCursorBlinkStatus( true ),
263     mPrimaryCursorVisible( false ),
264     mSecondaryCursorVisible( false ),
265     mSwapSelectionHandles( false ),
266     mNotifyEndOfScroll( false )
267   {
268     mQuadVertexFormat[ "aPosition" ] = Property::VECTOR2;
269     mQuadIndexFormat[ "indices" ] = Property::INTEGER;
270     mHighlightMaterial = Material::New( Shader::New( VERTEX_SHADER, FRAGMENT_SHADER ) );
271   }
272
273   /**
274    * Relayout of the decorations owned by the decorator.
275    * @param[in] size The Size of the UI control the decorator is adding it's decorations to.
276    */
277   void Relayout( const Vector2& size )
278   {
279     // TODO - Remove this if nothing is active
280     CreateActiveLayer();
281
282     // Show or hide the cursors
283     CreateCursors();
284
285     if( mPrimaryCursor )
286     {
287       const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
288       mPrimaryCursorVisible = ( cursor.position.x <= size.width ) && ( cursor.position.x >= 0.f );
289       if( mPrimaryCursorVisible )
290       {
291         Vector2 position = cursor.position;
292
293         mPrimaryCursor.SetPosition( position.x,
294                                     position.y );
295         mPrimaryCursor.SetSize( Size( mCursorWidth, cursor.cursorHeight ) );
296       }
297       mPrimaryCursor.SetVisible( mPrimaryCursorVisible );
298     }
299     if( mSecondaryCursor )
300     {
301       const CursorImpl& cursor = mCursor[SECONDARY_CURSOR];
302       mSecondaryCursorVisible = ( cursor.position.x <= size.width ) && ( cursor.position.x >= 0.f );
303       if( mSecondaryCursorVisible )
304       {
305         mSecondaryCursor.SetPosition( cursor.position.x,
306                                       cursor.position.y );
307         mSecondaryCursor.SetSize( Size( mCursorWidth, cursor.cursorHeight ) );
308       }
309       mSecondaryCursor.SetVisible( mSecondaryCursorVisible );
310     }
311
312     // Show or hide the grab handle
313     HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
314     if( grabHandle.active )
315     {
316       Vector2 position = grabHandle.position;
317
318       const bool isVisible = ( position.x <= size.width ) && ( position.x >= 0.f );
319
320       if( isVisible )
321       {
322         SetupTouchEvents();
323
324         CreateGrabHandle();
325
326         grabHandle.actor.SetPosition( position.x - floor( 0.5f * mCursorWidth ),
327                                       position.y + grabHandle.lineHeight ); // TODO : Fix for multiline.
328       }
329       grabHandle.actor.SetVisible( isVisible );
330     }
331     else if( grabHandle.actor )
332     {
333       grabHandle.actor.Unparent();
334     }
335
336     // Show or hide the selection handles/highlight
337     HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
338     HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
339     if( primary.active || secondary.active )
340     {
341       Vector2 primaryPosition = primary.position;
342       Vector2 secondaryPosition = secondary.position;
343
344       const bool isPrimaryVisible = ( primaryPosition.x <= size.width ) && ( primaryPosition.x >= 0.f );
345       const bool isSecondaryVisible = ( secondaryPosition.x <= size.width ) && ( secondaryPosition.x >= 0.f );
346
347       if( isPrimaryVisible || isSecondaryVisible )
348       {
349         SetupTouchEvents();
350
351         CreateSelectionHandles();
352
353         if( isPrimaryVisible )
354         {
355           primary.actor.SetPosition( primaryPosition.x,
356                                      primaryPosition.y + primary.lineHeight ); // TODO : Fix for multiline.
357
358           SetSelectionHandleMarkerSize( primary );
359         }
360
361         if( isSecondaryVisible )
362         {
363           secondary.actor.SetPosition( secondaryPosition.x,
364                                        secondaryPosition.y + secondary.lineHeight ); // TODO : Fix for multiline.
365
366           SetSelectionHandleMarkerSize( secondary );
367         }
368       }
369       primary.actor.SetVisible( isPrimaryVisible );
370       secondary.actor.SetVisible( isSecondaryVisible );
371
372       CreateHighlight();
373       UpdateHighlight();
374     }
375     else
376     {
377       if( primary.actor )
378       {
379         primary.actor.Unparent();
380       }
381       if( secondary.actor )
382       {
383         secondary.actor.Unparent();
384       }
385       if( mHighlightActor )
386       {
387         mHighlightActor.Unparent();
388       }
389     }
390
391     if( mActiveCopyPastePopup )
392     {
393       ShowPopup();
394     }
395     else
396     {
397       if( mCopyPastePopup.actor )
398       {
399         mCopyPastePopup.actor.HidePopup();
400       }
401     }
402   }
403
404   void UpdatePositions( const Vector2& scrollOffset )
405   {
406     mCursor[PRIMARY_CURSOR].position += scrollOffset;
407     mCursor[SECONDARY_CURSOR].position += scrollOffset;
408     mHandle[ GRAB_HANDLE ].position += scrollOffset;
409     mHandle[ LEFT_SELECTION_HANDLE ].position += scrollOffset;
410     mHandle[ RIGHT_SELECTION_HANDLE ].position += scrollOffset;
411     mHighlightPosition += scrollOffset;
412     DeterminePositionPopup();
413   }
414
415   void ShowPopup()
416   {
417     if ( !mCopyPastePopup.actor )
418     {
419       return;
420     }
421
422     if( !mCopyPastePopup.actor.GetParent() )
423     {
424       mActiveLayer.Add( mCopyPastePopup.actor );
425     }
426
427     mCopyPastePopup.actor.RaiseAbove( mActiveLayer );
428     mCopyPastePopup.actor.ShowPopup();
429   }
430
431   void DeterminePositionPopup()
432   {
433     if ( !mActiveCopyPastePopup )
434     {
435       return;
436     }
437
438     if ( mHandle[LEFT_SELECTION_HANDLE].active || mHandle[RIGHT_SELECTION_HANDLE].active )
439     {
440       float minHandleXPosition = std::min (  mHandle[LEFT_SELECTION_HANDLE].position.x, mHandle[RIGHT_SELECTION_HANDLE].position.x );
441       float maxHandleXPosition = std::max (  mHandle[LEFT_SELECTION_HANDLE].position.x, mHandle[RIGHT_SELECTION_HANDLE].position.x );
442
443       float minHandleYPosition = std::min (  mHandle[LEFT_SELECTION_HANDLE].position.y, mHandle[RIGHT_SELECTION_HANDLE].position.y );
444
445       mCopyPastePopup.position.x = minHandleXPosition + ( ( maxHandleXPosition - minHandleXPosition ) *0.5f );
446       mCopyPastePopup.position.y = minHandleYPosition + mCopyPastePopup.offset;
447     }
448     else
449     {
450       mCopyPastePopup.position = Vector3( mCursor[PRIMARY_CURSOR].position.x, mCursor[PRIMARY_CURSOR].position.y -100.0f , 0.0f ); //todo 100 to be an offset Property
451     }
452
453     Vector3 popupSize = Vector3( mCopyPastePopup.actor.GetRelayoutSize( Dimension::WIDTH ), mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT ), 0.0f );
454
455     GetConstrainedPopupPosition( mCopyPastePopup.position, popupSize, AnchorPoint::CENTER, mActiveLayer, mBoundingBox );
456
457     SetUpPopupPositionNotifications();
458
459     mCopyPastePopup.actor.SetPosition( mCopyPastePopup.position );
460   }
461
462   void PopupRelayoutComplete( Actor actor )
463   {
464     // Size negotiation for CopyPastePopup complete so can get the size and constrain position within bounding box.
465
466     DeterminePositionPopup();
467   }
468
469   void CreateCursor( ImageActor& cursor, const Vector4& color )
470   {
471     cursor = CreateSolidColorActor( color );
472     cursor.SetSortModifier( DECORATION_DEPTH_INDEX );
473     cursor.SetParentOrigin( ParentOrigin::TOP_LEFT ); // Need to set the default parent origin as CreateSolidColorActor() sets a different one.
474     cursor.SetAnchorPoint( AnchorPoint::TOP_RIGHT );
475   }
476
477   // Add or Remove cursor(s) from parent
478   void CreateCursors()
479   {
480     if( mActiveCursor == ACTIVE_CURSOR_NONE )
481     {
482       if( mPrimaryCursor )
483       {
484         mPrimaryCursor.Unparent();
485       }
486       if( mSecondaryCursor )
487       {
488         mSecondaryCursor.Unparent();
489       }
490     }
491     else
492     {
493       // Create Primary and or Secondary Cursor(s) if active and add to parent
494       if ( mActiveCursor == ACTIVE_CURSOR_PRIMARY ||
495            mActiveCursor == ACTIVE_CURSOR_BOTH )
496       {
497         if ( !mPrimaryCursor )
498         {
499           CreateCursor( mPrimaryCursor, mCursor[PRIMARY_CURSOR].color );
500 #ifdef DECORATOR_DEBUG
501           mPrimaryCursor.SetName( "PrimaryCursorActor" );
502 #endif
503         }
504
505         if( !mPrimaryCursor.GetParent() )
506         {
507           mActiveLayer.Add( mPrimaryCursor );
508         }
509       }
510
511       if ( mActiveCursor == ACTIVE_CURSOR_BOTH )
512       {
513         if ( !mSecondaryCursor )
514         {
515           CreateCursor( mSecondaryCursor, mCursor[SECONDARY_CURSOR].color );
516 #ifdef DECORATOR_DEBUG
517           mSecondaryCursor.SetName( "SecondaryCursorActor" );
518 #endif
519         }
520
521         if( !mSecondaryCursor.GetParent() )
522         {
523           mActiveLayer.Add( mSecondaryCursor );
524         }
525       }
526       else
527       {
528         if( mSecondaryCursor )
529         {
530           mSecondaryCursor.Unparent();
531         }
532       }
533     }
534   }
535
536   bool OnCursorBlinkTimerTick()
537   {
538     // Cursor blinking
539     if ( mPrimaryCursor )
540     {
541       mPrimaryCursor.SetVisible( mPrimaryCursorVisible && mCursorBlinkStatus );
542     }
543     if ( mSecondaryCursor )
544     {
545       mSecondaryCursor.SetVisible( mSecondaryCursorVisible && mCursorBlinkStatus );
546     }
547
548     mCursorBlinkStatus = !mCursorBlinkStatus;
549
550     return true;
551   }
552
553   void SetupTouchEvents()
554   {
555     if ( !mTapDetector )
556     {
557       mTapDetector = TapGestureDetector::New();
558       mTapDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnTap );
559     }
560
561     if ( !mPanGestureDetector )
562     {
563       mPanGestureDetector = PanGestureDetector::New();
564       mPanGestureDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnPan );
565     }
566   }
567
568   void CreateActiveLayer()
569   {
570     if( !mActiveLayer )
571     {
572       mActiveLayer = Layer::New();
573 #ifdef DECORATOR_DEBUG
574       mActiveLayer.SetName ( "ActiveLayerActor" );
575 #endif
576
577       mActiveLayer.SetParentOrigin( ParentOrigin::CENTER );
578       mActiveLayer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
579       mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
580
581       // Add the active layer telling the controller it doesn't need clipping.
582       mController.AddDecoration( mActiveLayer, false );
583     }
584
585     mActiveLayer.RaiseToTop();
586   }
587
588   void SetSelectionHandleMarkerSize( HandleImpl& handle )
589   {
590     if ( handle.markerActor )
591     {
592       handle.markerActor.SetResizePolicy ( ResizePolicy::FIXED, Dimension::HEIGHT );
593       handle.markerActor.SetSize( 0, handle.lineHeight );
594     }
595   }
596
597   void CreateGrabHandle()
598   {
599     HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
600     if( !grabHandle.actor )
601     {
602       if( !mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] )
603       {
604         mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] = ResourceImage::New( DEFAULT_GRAB_HANDLE_IMAGE_RELEASED );
605       }
606
607       grabHandle.actor = ImageActor::New( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] );
608       grabHandle.actor.SetSortModifier( DECORATION_DEPTH_INDEX );
609       grabHandle.actor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
610       // Area that Grab handle responds to, larger than actual handle so easier to move
611 #ifdef DECORATOR_DEBUG
612       grabHandle.actor.SetName( "GrabHandleActor" );
613       if ( Dali::Internal::gLogFilter->IsEnabledFor( Debug::Verbose ) )
614       {
615         grabHandle.grabArea = Toolkit::CreateSolidColorActor( Vector4(0.0f, 0.0f, 0.0f, 0.0f), true, Color::RED, 1 );
616         grabHandle.grabArea.SetName( "GrabArea" );
617       }
618       else
619       {
620         grabHandle.grabArea = Actor::New();
621         grabHandle.grabArea.SetName( "GrabArea" );
622       }
623 #else
624       grabHandle.grabArea = Actor::New();
625 #endif
626
627       grabHandle.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
628       grabHandle.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
629       grabHandle.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
630       grabHandle.grabArea.SetSizeModeFactor( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE );
631       grabHandle.actor.Add( grabHandle.grabArea );
632       grabHandle.actor.SetColor( mHandleColor );
633
634       grabHandle.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnGrabHandleTouched );
635       mTapDetector.Attach( grabHandle.grabArea );
636       mPanGestureDetector.Attach( grabHandle.grabArea );
637
638       mActiveLayer.Add( grabHandle.actor );
639     }
640
641     if( !grabHandle.actor.GetParent() )
642     {
643       mActiveLayer.Add( grabHandle.actor );
644     }
645   }
646
647   void CreateHandleMarker( HandleImpl& handle, Image& image, HandleType handleType )
648   {
649     if ( image)
650     {
651       handle.markerActor = ImageActor::New( image );
652       handle.markerActor.SetColor( mHandleColor );
653       handle.actor.Add( handle.markerActor );
654
655       if ( LEFT_SELECTION_HANDLE == handleType )
656       {
657         handle.markerActor.SetAnchorPoint( AnchorPoint::BOTTOM_RIGHT );
658         handle.markerActor.SetParentOrigin( ParentOrigin::TOP_RIGHT );
659       }
660       else if ( RIGHT_SELECTION_HANDLE == handleType )
661       {
662         handle.markerActor.SetAnchorPoint( AnchorPoint::BOTTOM_LEFT );
663         handle.markerActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
664       }
665     }
666   }
667
668   void CreateSelectionHandles()
669   {
670     HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
671     if( !primary.actor )
672     {
673       primary.actor = ImageActor::New( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
674 #ifdef DECORATOR_DEBUG
675       primary.actor.SetName("SelectionHandleOne");
676 #endif
677       primary.actor.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
678       primary.actor.SetSortModifier( DECORATION_DEPTH_INDEX );
679       primary.flipped = false;
680       primary.actor.SetColor( mHandleColor );
681
682       primary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
683 #ifdef DECORATOR_DEBUG
684       primary.grabArea.SetName("SelectionHandleOneGrabArea");
685 #endif
686       primary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
687       primary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
688       primary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
689       primary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
690
691       mTapDetector.Attach( primary.grabArea );
692       mPanGestureDetector.Attach( primary.grabArea );
693       primary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleOneTouched );
694
695       primary.actor.Add( primary.grabArea );
696
697       CreateHandleMarker( primary, mHandleImages[LEFT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], LEFT_SELECTION_HANDLE );
698     }
699
700     if( !primary.actor.GetParent() )
701     {
702       mActiveLayer.Add( primary.actor );
703     }
704
705     HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
706     if( !secondary.actor )
707     {
708       secondary.actor = ImageActor::New( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
709 #ifdef DECORATOR_DEBUG
710       secondary.actor.SetName("SelectionHandleTwo");
711 #endif
712       secondary.actor.SetAnchorPoint( AnchorPoint::TOP_LEFT ); // Change to BOTTOM_LEFT if Look'n'Feel requires handle above text.
713       secondary.actor.SetSortModifier( DECORATION_DEPTH_INDEX );
714       secondary.flipped = false;
715       secondary.actor.SetColor( mHandleColor );
716
717       secondary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
718 #ifdef DECORATOR_DEBUG
719       secondary.grabArea.SetName("SelectionHandleTwoGrabArea");
720 #endif
721       secondary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
722       secondary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
723       secondary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
724       secondary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
725
726       mTapDetector.Attach( secondary.grabArea );
727       mPanGestureDetector.Attach( secondary.grabArea );
728       secondary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleTwoTouched );
729
730       secondary.actor.Add( secondary.grabArea );
731
732       CreateHandleMarker( secondary, mHandleImages[RIGHT_SELECTION_HANDLE_MARKER][HANDLE_IMAGE_RELEASED], RIGHT_SELECTION_HANDLE  );
733     }
734
735     if( !secondary.actor.GetParent() )
736     {
737       mActiveLayer.Add( secondary.actor );
738     }
739   }
740
741   void CreateHighlight()
742   {
743     if( !mHighlightActor )
744     {
745       mHighlightActor = Actor::New();
746
747 #ifdef DECORATOR_DEBUG
748       mHighlightActor.SetName( "HighlightActor" );
749 #endif
750       mHighlightActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
751       mHighlightActor.SetSize( 1.0f, 1.0f );
752       mHighlightActor.SetColor( mHighlightColor );
753       mHighlightActor.SetColorMode( USE_OWN_COLOR );
754     }
755
756     // Add the highlight box telling the controller it needs clipping.
757     mController.AddDecoration( mHighlightActor, true );
758   }
759
760   void UpdateHighlight()
761   {
762     if ( mHighlightActor )
763     {
764       if( !mHighlightQuadList.empty() )
765       {
766         Vector< Vector2 > vertices;
767         Vector< unsigned int> indices;
768         Vector2 vertex;
769
770         std::vector<QuadCoordinates>::iterator iter = mHighlightQuadList.begin();
771         std::vector<QuadCoordinates>::iterator endIter = mHighlightQuadList.end();
772
773         for( std::size_t v = 0; iter != endIter; ++iter,v+=4 )
774         {
775           QuadCoordinates& quad = *iter;
776
777           // top-left (v+0)
778           vertex.x = quad.min.x;
779           vertex.y = quad.min.y;
780           vertices.PushBack( vertex );
781
782           // top-right (v+1)
783           vertex.x = quad.max.x;
784           vertex.y = quad.min.y;
785           vertices.PushBack( vertex );
786
787           // bottom-left (v+2)
788           vertex.x = quad.min.x;
789           vertex.y = quad.max.y;
790           vertices.PushBack( vertex );
791
792           // bottom-right (v+3)
793           vertex.x = quad.max.x;
794           vertex.y = quad.max.y;
795           vertices.PushBack( vertex );
796
797           // triangle A (3, 1, 0)
798           indices.PushBack( v + 3 );
799           indices.PushBack( v + 1 );
800           indices.PushBack( v );
801
802           // triangle B (0, 2, 3)
803           indices.PushBack( v );
804           indices.PushBack( v + 2 );
805           indices.PushBack( v + 3 );
806         }
807
808         if( mQuadVertices )
809         {
810           mQuadVertices.SetSize( vertices.Size() );
811         }
812         else
813         {
814           mQuadVertices = PropertyBuffer::New( mQuadVertexFormat, vertices.Size() );
815         }
816
817         if( mQuadIndices )
818         {
819           mQuadIndices.SetSize( indices.Size() );
820         }
821         else
822         {
823           mQuadIndices = PropertyBuffer::New( mQuadIndexFormat, indices.Size() );
824         }
825
826         mQuadVertices.SetData( &vertices[ 0 ] );
827         mQuadIndices.SetData( &indices[ 0 ] );
828
829         if( !mQuadGeometry )
830         {
831           mQuadGeometry = Geometry::New();
832           mQuadGeometry.AddVertexBuffer( mQuadVertices );
833         }
834         mQuadGeometry.SetIndexBuffer( mQuadIndices );
835
836         if( !mHighlightRenderer )
837         {
838           mHighlightRenderer = Dali::Renderer::New( mQuadGeometry, mHighlightMaterial );
839           mHighlightActor.AddRenderer( mHighlightRenderer );
840         }
841       }
842
843       mHighlightActor.SetPosition( mHighlightPosition.x,
844                                    mHighlightPosition.y );
845
846       mHighlightQuadList.clear();
847
848       if( mHighlightRenderer )
849       {
850         mHighlightRenderer.SetDepthIndex( mTextDepth - 2u ); // text is rendered at mTextDepth and text's shadow at mTextDepth -1u.
851       }
852     }
853   }
854
855   void OnTap( Actor actor, const TapGesture& tap )
856   {
857     if( actor == mHandle[GRAB_HANDLE].actor )
858     {
859       // TODO
860     }
861   }
862
863   void DoPan( HandleImpl& handle, HandleType type, const PanGesture& gesture )
864   {
865     if( Gesture::Started == gesture.state )
866     {
867       handle.grabDisplacementX = handle.grabDisplacementY = 0;
868     }
869
870     handle.grabDisplacementX += gesture.displacement.x;
871     handle.grabDisplacementY += gesture.displacement.y;
872
873     const float x = handle.position.x + handle.grabDisplacementX;
874     const float y = handle.position.y + handle.lineHeight*0.5f + handle.grabDisplacementY;
875
876     if( Gesture::Started    == gesture.state ||
877         Gesture::Continuing == gesture.state )
878     {
879       Vector2 targetSize;
880       mController.GetTargetSize( targetSize );
881
882       if( x < mScrollThreshold )
883       {
884         mScrollDirection = SCROLL_RIGHT;
885         mHandleScrolling = type;
886         StartScrollTimer();
887       }
888       else if( x > targetSize.width - mScrollThreshold )
889       {
890         mScrollDirection = SCROLL_LEFT;
891         mHandleScrolling = type;
892         StartScrollTimer();
893       }
894       else
895       {
896         mHandleScrolling = HANDLE_TYPE_COUNT;
897         StopScrollTimer();
898         mController.DecorationEvent( type, HANDLE_PRESSED, x, y );
899       }
900     }
901     else if( Gesture::Finished  == gesture.state ||
902              Gesture::Cancelled == gesture.state )
903     {
904       if( mScrollTimer &&
905           ( mScrollTimer.IsRunning() || mNotifyEndOfScroll ) )
906       {
907         mNotifyEndOfScroll = false;
908         mHandleScrolling = HANDLE_TYPE_COUNT;
909         StopScrollTimer();
910         mController.DecorationEvent( type, HANDLE_STOP_SCROLLING, x, y );
911       }
912       else
913       {
914         mController.DecorationEvent( type, HANDLE_RELEASED, x, y );
915       }
916
917       if( GRAB_HANDLE == type )
918       {
919         handle.actor.SetImage( mHandleImages[type][HANDLE_IMAGE_RELEASED] );
920       }
921       else
922       {
923         HandleType selectionHandleType = type;
924
925         if( mSwapSelectionHandles != handle.flipped )
926         {
927           selectionHandleType = ( LEFT_SELECTION_HANDLE == type ) ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE;
928         }
929
930         handle.actor.SetImage( mHandleImages[selectionHandleType][HANDLE_IMAGE_RELEASED] );
931       }
932       handle.pressed = false;
933     }
934   }
935
936   void OnPan( Actor actor, const PanGesture& gesture )
937   {
938     HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
939     HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
940     HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
941
942     if( actor == grabHandle.grabArea )
943     {
944       DoPan( grabHandle, GRAB_HANDLE, gesture );
945     }
946     else if( actor == primarySelectionHandle.grabArea )
947     {
948       DoPan( primarySelectionHandle, LEFT_SELECTION_HANDLE, gesture );
949     }
950     else if( actor == secondarySelectionHandle.grabArea )
951     {
952       DoPan( secondarySelectionHandle, RIGHT_SELECTION_HANDLE, gesture );
953     }
954   }
955
956   bool OnGrabHandleTouched( Actor actor, const TouchEvent& event )
957   {
958     // Switch between pressed/release grab-handle images
959     if( event.GetPointCount() > 0 &&
960         mHandle[GRAB_HANDLE].actor )
961     {
962       const TouchPoint& point = event.GetPoint(0);
963
964       if( TouchPoint::Down == point.state )
965       {
966         mHandle[GRAB_HANDLE].pressed = true;
967         Image imagePressed = mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_PRESSED];
968         if( imagePressed )
969         {
970           mHandle[GRAB_HANDLE].actor.SetImage( imagePressed );
971         }
972       }
973       else if( ( TouchPoint::Up == point.state ) ||
974                ( TouchPoint::Interrupted == point.state ) )
975       {
976         mHandle[GRAB_HANDLE].pressed = false;
977         Image imageReleased = mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED];
978         if( imageReleased )
979         {
980           mHandle[GRAB_HANDLE].actor.SetImage( imageReleased );
981         }
982       }
983     }
984
985     // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
986     return true;
987   }
988
989   bool OnHandleOneTouched( Actor actor, const TouchEvent& event )
990   {
991     // Switch between pressed/release selection handle images
992     if( event.GetPointCount() > 0 &&
993         mHandle[LEFT_SELECTION_HANDLE].actor )
994     {
995       const TouchPoint& point = event.GetPoint(0);
996
997       const bool flip = mSwapSelectionHandles != mHandle[LEFT_SELECTION_HANDLE].flipped;
998       if( TouchPoint::Down == point.state )
999       {
1000         mHandle[LEFT_SELECTION_HANDLE].pressed = true;
1001         Image imagePressed = mHandleImages[flip ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE][HANDLE_IMAGE_PRESSED];
1002         if( imagePressed )
1003         {
1004           mHandle[LEFT_SELECTION_HANDLE].actor.SetImage( imagePressed );
1005         }
1006       }
1007       else if( ( TouchPoint::Up == point.state ) ||
1008                ( TouchPoint::Interrupted == point.state ) )
1009       {
1010         mHandle[LEFT_SELECTION_HANDLE].pressed = false;
1011         Image imageReleased = mHandleImages[flip ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED];
1012         if( imageReleased )
1013         {
1014           mHandle[LEFT_SELECTION_HANDLE].actor.SetImage( imageReleased );
1015         }
1016       }
1017     }
1018
1019     // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1020     return true;
1021   }
1022
1023   bool OnHandleTwoTouched( Actor actor, const TouchEvent& event )
1024   {
1025     // Switch between pressed/release selection handle images
1026     if( event.GetPointCount() > 0 &&
1027         mHandle[RIGHT_SELECTION_HANDLE].actor )
1028     {
1029       const TouchPoint& point = event.GetPoint(0);
1030
1031       const bool flip = mSwapSelectionHandles != mHandle[RIGHT_SELECTION_HANDLE].flipped;
1032       if( TouchPoint::Down == point.state )
1033       {
1034         Image imagePressed = mHandleImages[flip ? LEFT_SELECTION_HANDLE : RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_PRESSED];
1035         mHandle[RIGHT_SELECTION_HANDLE].pressed = true;
1036         if( imagePressed )
1037         {
1038           mHandle[RIGHT_SELECTION_HANDLE].actor.SetImage( imagePressed );
1039         }
1040       }
1041       else if( ( TouchPoint::Up == point.state ) ||
1042                ( TouchPoint::Interrupted == point.state ) )
1043       {
1044         Image imageReleased = mHandleImages[flip ? LEFT_SELECTION_HANDLE : RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED];
1045         mHandle[RIGHT_SELECTION_HANDLE].pressed = false;
1046         if( imageReleased )
1047         {
1048           mHandle[RIGHT_SELECTION_HANDLE].actor.SetImage( imageReleased );
1049         }
1050       }
1051     }
1052
1053     // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1054     return true;
1055   }
1056
1057   // Popup
1058
1059   float AlternatePopUpPositionRelativeToCursor()
1060   {
1061     float alternativePosition=0.0f;;
1062
1063     if ( mPrimaryCursor ) // Secondary cursor not used for paste
1064     {
1065       Cursor cursor = PRIMARY_CURSOR;
1066       alternativePosition = mCursor[cursor].position.y;
1067     }
1068
1069     const float popupHeight = 120.0f; // todo Set as a MaxSize Property in Control or retrieve from CopyPastePopup class.
1070
1071     if( mHandle[GRAB_HANDLE].active )
1072     {
1073       // If grab handle enabled then position pop-up below the grab handle.
1074       const Vector2 grabHandleSize( 59.0f, 56.0f ); // todo
1075       const float BOTTOM_HANDLE_BOTTOM_OFFSET = 1.5; //todo Should be a property
1076       alternativePosition +=  grabHandleSize.height  + popupHeight + BOTTOM_HANDLE_BOTTOM_OFFSET ;
1077     }
1078     else
1079     {
1080       alternativePosition += popupHeight;
1081     }
1082
1083     return alternativePosition;
1084   }
1085
1086   void PopUpLeavesVerticalBoundary( PropertyNotification& source )
1087   {
1088     float alternativeYPosition=0.0f;
1089     // todo use AlternatePopUpPositionRelativeToSelectionHandles() if text is highlighted
1090     // if can't be positioned above, then position below row.
1091     alternativeYPosition = AlternatePopUpPositionRelativeToCursor();
1092
1093     mCopyPastePopup.actor.SetY( alternativeYPosition );
1094   }
1095
1096
1097   void SetUpPopupPositionNotifications( )
1098   {
1099     // Note Property notifications ignore any set anchor point so conditions must allow for this.  Default is Top Left.
1100
1101     // Exceeding vertical boundary
1102
1103     Vector4 worldCoordinatesBoundingBox;
1104     LocalToWorldCoordinatesBoundingBox( mBoundingBox, worldCoordinatesBoundingBox );
1105
1106     float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT);
1107
1108     PropertyNotification verticalExceedNotification = mCopyPastePopup.actor.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1109                                                       OutsideCondition( worldCoordinatesBoundingBox.y + popupHeight * 0.5f,
1110                                                                         worldCoordinatesBoundingBox.w - popupHeight * 0.5f ) );
1111
1112     verticalExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesVerticalBoundary );
1113   }
1114
1115   void GetConstrainedPopupPosition( Vector3& requiredPopupPosition, Vector3& popupSize, Vector3 anchorPoint, Actor& parent, Rect<int>& boundingBox )
1116   {
1117     DALI_ASSERT_DEBUG ( "Popup parent not on stage" && parent.OnStage() )
1118
1119     // Parent must already by added to Stage for these Get calls to work
1120     Vector3 parentAnchorPoint = parent.GetCurrentAnchorPoint();
1121     Vector3 parentWorldPositionLeftAnchor = parent.GetCurrentWorldPosition() - parent.GetCurrentSize()*parentAnchorPoint;
1122     Vector3 popupWorldPosition = parentWorldPositionLeftAnchor + requiredPopupPosition;  // Parent World position plus popup local position gives World Position
1123     Vector3 popupDistanceFromAnchorPoint = popupSize*anchorPoint;
1124
1125     // Bounding rectangle is supplied as screen coordinates, bounding will be done in world coordinates.
1126     Vector4 boundingRectangleWorld;
1127     LocalToWorldCoordinatesBoundingBox( boundingBox, boundingRectangleWorld );
1128
1129     // Calculate distance to move popup (in local space) so fits within the boundary
1130     float xOffSetToKeepWithinBounds = 0.0f;
1131     if( popupWorldPosition.x - popupDistanceFromAnchorPoint.x < boundingRectangleWorld.x )
1132     {
1133       xOffSetToKeepWithinBounds = boundingRectangleWorld.x - ( popupWorldPosition.x - popupDistanceFromAnchorPoint.x );
1134     }
1135     else if ( popupWorldPosition.x +  popupDistanceFromAnchorPoint.x > boundingRectangleWorld.z )
1136     {
1137       xOffSetToKeepWithinBounds = boundingRectangleWorld.z - ( popupWorldPosition.x +  popupDistanceFromAnchorPoint.x );
1138     }
1139
1140     // Ensure initial display of Popup is in alternative position if can not fit above. As Property notification will be a frame behind.
1141     if ( popupWorldPosition.y - popupDistanceFromAnchorPoint.y < boundingRectangleWorld.y )
1142     {
1143       requiredPopupPosition.y = AlternatePopUpPositionRelativeToCursor();
1144     }
1145
1146     requiredPopupPosition.x = requiredPopupPosition.x + xOffSetToKeepWithinBounds;
1147
1148     // Prevent pixel mis-alignment by rounding down.
1149     requiredPopupPosition.x = static_cast<int>( requiredPopupPosition.x );
1150     requiredPopupPosition.y = static_cast<int>( requiredPopupPosition.y );
1151
1152   }
1153
1154   void FlipSelectionHandleImages()
1155   {
1156     SetupTouchEvents();
1157
1158     CreateSelectionHandles();
1159
1160     HandleImpl& leftHandle = mHandle[LEFT_SELECTION_HANDLE];
1161     HandleImpl& rightHandle = mHandle[RIGHT_SELECTION_HANDLE];
1162
1163     // If handle pressed and pressed image exists then use pressed image else stick with released image
1164     const HandleImageType leftImageType = ( leftHandle.pressed && mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_PRESSED] ) ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED;
1165     const HandleImageType rightImageType = ( rightHandle.pressed && mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_PRESSED] ) ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED;
1166
1167     const bool leftFlipped = mSwapSelectionHandles != leftHandle.flipped;
1168     const bool rightFlipped = mSwapSelectionHandles != rightHandle.flipped;
1169
1170     leftHandle.actor.SetImage( leftFlipped ? mHandleImages[RIGHT_SELECTION_HANDLE][leftImageType] : mHandleImages[LEFT_SELECTION_HANDLE][leftImageType] );
1171
1172     leftHandle.actor.SetAnchorPoint( leftFlipped ? AnchorPoint::TOP_LEFT : AnchorPoint::TOP_RIGHT );
1173
1174     rightHandle.actor.SetImage( rightFlipped ? mHandleImages[LEFT_SELECTION_HANDLE][rightImageType] : mHandleImages[RIGHT_SELECTION_HANDLE][rightImageType] );
1175
1176     rightHandle.actor.SetAnchorPoint( rightFlipped ? AnchorPoint::TOP_RIGHT : AnchorPoint::TOP_LEFT );
1177
1178     if ( leftHandle.markerActor )
1179     {
1180       leftHandle.markerActor.SetImage( leftFlipped ? mHandleImages[RIGHT_SELECTION_HANDLE_MARKER][leftImageType] : mHandleImages[LEFT_SELECTION_HANDLE_MARKER][leftImageType] );
1181     }
1182     if ( rightHandle.markerActor )
1183     {
1184       rightHandle.markerActor.SetImage( rightFlipped ? mHandleImages[LEFT_SELECTION_HANDLE_MARKER][rightImageType] : mHandleImages[RIGHT_SELECTION_HANDLE_MARKER][rightImageType] );
1185     }
1186   }
1187
1188   void SetScrollThreshold( float threshold )
1189   {
1190     mScrollThreshold = threshold;
1191   }
1192
1193   float GetScrollThreshold() const
1194   {
1195     return mScrollThreshold;
1196   }
1197
1198   void SetScrollSpeed( float speed )
1199   {
1200     mScrollSpeed = speed;
1201     mScrollDistance = speed * SCROLL_TICK_INTERVAL * TO_SECONDS;
1202   }
1203
1204   float GetScrollSpeed() const
1205   {
1206     return mScrollSpeed;
1207   }
1208
1209   void NotifyEndOfScroll()
1210   {
1211     StopScrollTimer();
1212
1213     if( mScrollTimer )
1214     {
1215       mNotifyEndOfScroll = true;
1216     }
1217   }
1218
1219   /**
1220    * Creates and starts a timer to scroll the text when handles are close to the edges of the text.
1221    *
1222    * It only starts the timer if it's already created.
1223    */
1224   void StartScrollTimer()
1225   {
1226     if( !mScrollTimer )
1227     {
1228       mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1229       mScrollTimer.TickSignal().Connect( this, &Decorator::Impl::OnScrollTimerTick );
1230     }
1231
1232     if( !mScrollTimer.IsRunning() )
1233     {
1234       mScrollTimer.Start();
1235     }
1236   }
1237
1238   /**
1239    * Stops the timer used to scroll the text.
1240    */
1241   void StopScrollTimer()
1242   {
1243     if( mScrollTimer )
1244     {
1245       mScrollTimer.Stop();
1246     }
1247   }
1248
1249   /**
1250    * Callback called by the timer used to scroll the text.
1251    *
1252    * It calculates and sets a new scroll position.
1253    */
1254   bool OnScrollTimerTick()
1255   {
1256     if( HANDLE_TYPE_COUNT != mHandleScrolling )
1257     {
1258       mController.DecorationEvent( mHandleScrolling,
1259                                    HANDLE_SCROLLING,
1260                                    mScrollDirection == SCROLL_RIGHT ? mScrollDistance : -mScrollDistance,
1261                                    0.f );
1262     }
1263
1264     return true;
1265   }
1266
1267   ControllerInterface& mController;
1268
1269   TapGestureDetector  mTapDetector;
1270   PanGestureDetector  mPanGestureDetector;
1271   Timer               mCursorBlinkTimer;          ///< Timer to signal cursor to blink
1272   Timer               mScrollTimer;               ///< Timer used to scroll the text when the grab handle is moved close to the edges.
1273
1274   Layer               mActiveLayer;               ///< Layer for active handles and alike that ensures they are above all else.
1275   ImageActor          mPrimaryCursor;
1276   ImageActor          mSecondaryCursor;
1277
1278   Actor               mHighlightActor;        ///< Actor to display highlight
1279   Renderer            mHighlightRenderer;
1280   Material            mHighlightMaterial;         ///< Material used for highlight
1281   Property::Map       mQuadVertexFormat;
1282   Property::Map       mQuadIndexFormat;
1283   PopupImpl           mCopyPastePopup;
1284   TextSelectionPopup::Buttons mEnabledPopupButtons; /// Bit mask of currently enabled Popup buttons
1285   TextSelectionPopupCallbackInterface& mTextSelectionPopupCallbackInterface;
1286
1287   Image               mHandleImages[HANDLE_TYPE_COUNT][HANDLE_IMAGE_TYPE_COUNT];
1288   Vector4             mHandleColor;
1289
1290   CursorImpl          mCursor[CURSOR_COUNT];
1291   HandleImpl          mHandle[HANDLE_TYPE_COUNT];
1292
1293   PropertyBuffer      mQuadVertices;
1294   PropertyBuffer      mQuadIndices;
1295   Geometry            mQuadGeometry;
1296   QuadContainer       mHighlightQuadList;         ///< Sub-selections that combine to create the complete selection highlight
1297
1298   Rect<int>           mBoundingBox;
1299   Vector4             mHighlightColor;            ///< Color of the highlight
1300   Vector2             mHighlightPosition;         ///< The position of the highlight actor.
1301
1302   unsigned int        mActiveCursor;
1303   unsigned int        mCursorBlinkInterval;
1304   float               mCursorBlinkDuration;
1305   float               mCursorWidth;             ///< The width of the cursors in pixels.
1306   HandleType          mHandleScrolling;         ///< The handle which is scrolling.
1307   ScrollDirection     mScrollDirection;         ///< The direction of the scroll.
1308   float               mScrollThreshold;         ///< Defines a square area inside the control, close to the edge. A cursor entering this area will trigger scroll events.
1309   float               mScrollSpeed;             ///< The scroll speed in pixels per second.
1310   float               mScrollDistance;          ///< Distance the text scrolls during a scroll interval.
1311   int                 mTextDepth;               ///< The depth used to render the text.
1312
1313   bool                mActiveCopyPastePopup   : 1;
1314   bool                mCursorBlinkStatus      : 1; ///< Flag to switch between blink on and blink off.
1315   bool                mPrimaryCursorVisible   : 1; ///< Whether the primary cursor is visible.
1316   bool                mSecondaryCursorVisible : 1; ///< Whether the secondary cursor is visible.
1317   bool                mSwapSelectionHandles   : 1; ///< Whether to swap the selection handle images.
1318   bool                mNotifyEndOfScroll      : 1; ///< Whether to notify the end of the scroll.
1319 };
1320
1321 DecoratorPtr Decorator::New( ControllerInterface& controller,
1322                              TextSelectionPopupCallbackInterface& callbackInterface )
1323 {
1324   return DecoratorPtr( new Decorator( controller,
1325                                       callbackInterface ) );
1326 }
1327
1328 void Decorator::SetBoundingBox( const Rect<int>& boundingBox )
1329 {
1330   mImpl->mBoundingBox = boundingBox;
1331 }
1332
1333 const Rect<int>& Decorator::GetBoundingBox() const
1334 {
1335   return mImpl->mBoundingBox;
1336 }
1337
1338 void Decorator::Relayout( const Vector2& size )
1339 {
1340   mImpl->Relayout( size );
1341 }
1342
1343 void Decorator::UpdatePositions( const Vector2& scrollOffset )
1344 {
1345   mImpl->UpdatePositions( scrollOffset );
1346 }
1347
1348 /** Cursor **/
1349
1350 void Decorator::SetActiveCursor( ActiveCursor activeCursor )
1351 {
1352   mImpl->mActiveCursor = activeCursor;
1353 }
1354
1355 unsigned int Decorator::GetActiveCursor() const
1356 {
1357   return mImpl->mActiveCursor;
1358 }
1359
1360 void Decorator::SetPosition( Cursor cursor, float x, float y, float cursorHeight, float lineHeight )
1361 {
1362   mImpl->mCursor[cursor].position.x = x;
1363   mImpl->mCursor[cursor].position.y = y;
1364   mImpl->mCursor[cursor].cursorHeight = cursorHeight;
1365   mImpl->mCursor[cursor].lineHeight = lineHeight;
1366 }
1367
1368 void Decorator::GetPosition( Cursor cursor, float& x, float& y, float& cursorHeight, float& lineHeight ) const
1369 {
1370   x = mImpl->mCursor[cursor].position.x;
1371   y = mImpl->mCursor[cursor].position.y;
1372   cursorHeight = mImpl->mCursor[cursor].cursorHeight;
1373   lineHeight = mImpl->mCursor[cursor].lineHeight;
1374 }
1375
1376 const Vector2& Decorator::GetPosition( Cursor cursor ) const
1377 {
1378   return mImpl->mCursor[cursor].position;
1379 }
1380
1381 void Decorator::SetCursorColor( Cursor cursor, const Dali::Vector4& color )
1382 {
1383   mImpl->mCursor[cursor].color = color;
1384 }
1385
1386 const Dali::Vector4& Decorator::GetColor( Cursor cursor ) const
1387 {
1388   return mImpl->mCursor[cursor].color;
1389 }
1390
1391 void Decorator::StartCursorBlink()
1392 {
1393   if ( !mImpl->mCursorBlinkTimer )
1394   {
1395     mImpl->mCursorBlinkTimer = Timer::New( mImpl->mCursorBlinkInterval );
1396     mImpl->mCursorBlinkTimer.TickSignal().Connect( mImpl, &Decorator::Impl::OnCursorBlinkTimerTick );
1397   }
1398
1399   if ( !mImpl->mCursorBlinkTimer.IsRunning() )
1400   {
1401     mImpl->mCursorBlinkTimer.Start();
1402   }
1403 }
1404
1405 void Decorator::StopCursorBlink()
1406 {
1407   if ( mImpl->mCursorBlinkTimer )
1408   {
1409     mImpl->mCursorBlinkTimer.Stop();
1410   }
1411 }
1412
1413 void Decorator::SetCursorBlinkInterval( float seconds )
1414 {
1415   mImpl->mCursorBlinkInterval = static_cast<unsigned int>( seconds * TO_MILLISECONDS ); // Convert to milliseconds
1416 }
1417
1418 float Decorator::GetCursorBlinkInterval() const
1419 {
1420   return static_cast<float>( mImpl->mCursorBlinkInterval ) * TO_SECONDS;
1421 }
1422
1423 void Decorator::SetCursorBlinkDuration( float seconds )
1424 {
1425   mImpl->mCursorBlinkDuration = seconds;
1426 }
1427
1428 float Decorator::GetCursorBlinkDuration() const
1429 {
1430   return mImpl->mCursorBlinkDuration;
1431 }
1432
1433 void Decorator::SetCursorWidth( int width )
1434 {
1435   mImpl->mCursorWidth = static_cast<float>( width );
1436 }
1437
1438 int Decorator::GetCursorWidth() const
1439 {
1440   return static_cast<int>( mImpl->mCursorWidth );
1441 }
1442
1443 /** Handles **/
1444
1445 void Decorator::SetHandleActive( HandleType handleType, bool active )
1446 {
1447   mImpl->mHandle[handleType].active = active;
1448
1449   if( !active )
1450   {
1451     // TODO: this is a work-around.
1452     // The problem is the handle actor does not receive the touch event with the Interrupt
1453     // state when the power button is pressed and the application goes to background.
1454     mImpl->mHandle[handleType].pressed = false;
1455     Image imageReleased = mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED];
1456     ImageActor imageActor = mImpl->mHandle[handleType].actor;
1457     if( imageReleased && imageActor )
1458     {
1459        imageActor.SetImage( imageReleased );
1460     }
1461   }
1462 }
1463
1464 bool Decorator::IsHandleActive( HandleType handleType ) const
1465 {
1466   return mImpl->mHandle[handleType].active ;
1467 }
1468
1469 void Decorator::SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image )
1470 {
1471   mImpl->mHandleImages[handleType][handleImageType] = image;
1472 }
1473
1474 Dali::Image Decorator::GetHandleImage( HandleType handleType, HandleImageType handleImageType ) const
1475 {
1476   return mImpl->mHandleImages[handleType][handleImageType];
1477 }
1478
1479 void Decorator::SetHandleColor( const Vector4& color )
1480 {
1481   mImpl->mHandleColor = color;
1482 }
1483
1484 const Vector4& Decorator::GetHandleColor() const
1485 {
1486   return mImpl->mHandleColor;
1487 }
1488
1489 void Decorator::SetPosition( HandleType handleType, float x, float y, float height )
1490 {
1491   // Adjust grab handle displacement
1492   Impl::HandleImpl& handle = mImpl->mHandle[handleType];
1493
1494   handle.grabDisplacementX -= x - handle.position.x;
1495   handle.grabDisplacementY -= y - handle.position.y;
1496
1497   handle.position.x = x;
1498   handle.position.y = y;
1499   handle.lineHeight = height;
1500 }
1501
1502 void Decorator::GetPosition( HandleType handleType, float& x, float& y, float& height ) const
1503 {
1504   Impl::HandleImpl& handle = mImpl->mHandle[handleType];
1505
1506   x = handle.position.x;
1507   y = handle.position.y;
1508   height = handle.lineHeight;
1509 }
1510
1511 const Vector2& Decorator::GetPosition( HandleType handleType ) const
1512 {
1513   return mImpl->mHandle[handleType].position;
1514 }
1515
1516 void Decorator::SwapSelectionHandlesEnabled( bool enable )
1517 {
1518   mImpl->mSwapSelectionHandles = enable;
1519
1520   mImpl->FlipSelectionHandleImages();
1521 }
1522
1523 void Decorator::AddHighlight( float x1, float y1, float x2, float y2 )
1524 {
1525   mImpl->mHighlightQuadList.push_back( QuadCoordinates(x1, y1, x2, y2) );
1526 }
1527
1528 void Decorator::ClearHighlights()
1529 {
1530   mImpl->mHighlightQuadList.clear();
1531   mImpl->mHighlightPosition = Vector2::ZERO;
1532 }
1533
1534 void Decorator::SetHighlightColor( const Vector4& color )
1535 {
1536   mImpl->mHighlightColor = color;
1537 }
1538
1539 const Vector4& Decorator::GetHighlightColor() const
1540 {
1541   return mImpl->mHighlightColor;
1542 }
1543
1544 void Decorator::SetTextDepth( int textDepth )
1545 {
1546   mImpl->mTextDepth = textDepth;
1547 }
1548
1549 void Decorator::SetPopupActive( bool active )
1550 {
1551   mImpl->mActiveCopyPastePopup = active;
1552 }
1553
1554 bool Decorator::IsPopupActive() const
1555 {
1556   return mImpl->mActiveCopyPastePopup ;
1557 }
1558
1559 void Decorator::SetEnabledPopupButtons( TextSelectionPopup::Buttons& enabledButtonsBitMask )
1560 {
1561    mImpl->mEnabledPopupButtons = enabledButtonsBitMask;
1562
1563    if ( !mImpl->mCopyPastePopup.actor )
1564    {
1565      mImpl->mCopyPastePopup.actor = TextSelectionPopup::New( &mImpl->mTextSelectionPopupCallbackInterface );
1566 #ifdef DECORATOR_DEBUG
1567      mImpl->mCopyPastePopup.actor.SetName("mCopyPastePopup");
1568 #endif
1569      mImpl->mCopyPastePopup.actor.SetAnchorPoint( AnchorPoint::CENTER );
1570      mImpl->mCopyPastePopup.actor.OnRelayoutSignal().Connect( mImpl,  &Decorator::Impl::PopupRelayoutComplete  ); // Position popup after size negotiation
1571    }
1572
1573    mImpl->mCopyPastePopup.actor.EnableButtons( mImpl->mEnabledPopupButtons );
1574 }
1575
1576 TextSelectionPopup::Buttons& Decorator::GetEnabledPopupButtons()
1577 {
1578   return mImpl->mEnabledPopupButtons;
1579 }
1580
1581 /** Scroll **/
1582
1583 void Decorator::SetScrollThreshold( float threshold )
1584 {
1585   mImpl->SetScrollThreshold( threshold );
1586 }
1587
1588 float Decorator::GetScrollThreshold() const
1589 {
1590   return mImpl->GetScrollThreshold();
1591 }
1592
1593 void Decorator::SetScrollSpeed( float speed )
1594 {
1595   mImpl->SetScrollSpeed( speed );
1596 }
1597
1598 float Decorator::GetScrollSpeed() const
1599 {
1600   return mImpl->GetScrollSpeed();
1601 }
1602
1603 void Decorator::NotifyEndOfScroll()
1604 {
1605   mImpl->NotifyEndOfScroll();
1606 }
1607
1608 Decorator::~Decorator()
1609 {
1610   delete mImpl;
1611 }
1612
1613 Decorator::Decorator( ControllerInterface& controller,
1614                       TextSelectionPopupCallbackInterface& callbackInterface )
1615 : mImpl( NULL )
1616 {
1617   mImpl = new Decorator::Impl( controller, callbackInterface );
1618 }
1619
1620 } // namespace Text
1621
1622 } // namespace Toolkit
1623
1624 } // namespace Dali