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