c4d0a6826403028b0cfce2fadf07b28edf6dfa7e
[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,
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_CENTER );
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
776           QuadCoordinates& quad = *iter;
777
778           // top-left (v+0)
779           vertex.x = quad.min.x;
780           vertex.y = quad.min.y;
781           vertices.PushBack( vertex );
782
783           // top-right (v+1)
784           vertex.x = quad.max.x;
785           vertex.y = quad.min.y;
786           vertices.PushBack( vertex );
787
788           // bottom-left (v+2)
789           vertex.x = quad.min.x;
790           vertex.y = quad.max.y;
791           vertices.PushBack( vertex );
792
793           // bottom-right (v+3)
794           vertex.x = quad.max.x;
795           vertex.y = quad.max.y;
796           vertices.PushBack( vertex );
797
798           // triangle A (3, 1, 0)
799           indices.PushBack( v + 3 );
800           indices.PushBack( v + 1 );
801           indices.PushBack( v );
802
803           // triangle B (0, 2, 3)
804           indices.PushBack( v );
805           indices.PushBack( v + 2 );
806           indices.PushBack( v + 3 );
807         }
808
809         if( mQuadVertices )
810         {
811           mQuadVertices.SetSize( vertices.Size() );
812         }
813         else
814         {
815           mQuadVertices = PropertyBuffer::New( mQuadVertexFormat, vertices.Size() );
816         }
817
818         if( mQuadIndices )
819         {
820           mQuadIndices.SetSize( indices.Size() );
821         }
822         else
823         {
824           mQuadIndices = PropertyBuffer::New( mQuadIndexFormat, indices.Size() );
825         }
826
827         mQuadVertices.SetData( &vertices[ 0 ] );
828         mQuadIndices.SetData( &indices[ 0 ] );
829
830         if( !mQuadGeometry )
831         {
832           mQuadGeometry = Geometry::New();
833           mQuadGeometry.AddVertexBuffer( mQuadVertices );
834         }
835         mQuadGeometry.SetIndexBuffer( mQuadIndices );
836
837         if( !mHighlightRenderer )
838         {
839           mHighlightRenderer = Dali::Renderer::New( mQuadGeometry, mHighlightMaterial );
840           mHighlightActor.AddRenderer( mHighlightRenderer );
841         }
842       }
843
844       mHighlightActor.SetPosition( mHighlightPosition.x,
845                                    mHighlightPosition.y );
846
847       mHighlightQuadList.clear();
848
849       mHighlightRenderer.SetDepthIndex( mTextDepth - 2u ); // text is rendered at mTextDepth and text's shadow at mTextDepth -1u.
850     }
851   }
852
853   void OnTap( Actor actor, const TapGesture& tap )
854   {
855     if( actor == mHandle[GRAB_HANDLE].actor )
856     {
857       // TODO
858     }
859   }
860
861   void DoPan( HandleImpl& handle, HandleType type, const PanGesture& gesture )
862   {
863     if( Gesture::Started == gesture.state )
864     {
865       handle.grabDisplacementX = handle.grabDisplacementY = 0;
866     }
867
868     handle.grabDisplacementX += gesture.displacement.x;
869     handle.grabDisplacementY += gesture.displacement.y;
870
871     const float x = handle.position.x + handle.grabDisplacementX;
872     const float y = handle.position.y + handle.lineHeight*0.5f + handle.grabDisplacementY;
873
874     if( Gesture::Started    == gesture.state ||
875         Gesture::Continuing == gesture.state )
876     {
877       Vector2 targetSize;
878       mController.GetTargetSize( targetSize );
879
880       if( x < mScrollThreshold )
881       {
882         mScrollDirection = SCROLL_RIGHT;
883         mHandleScrolling = type;
884         StartScrollTimer();
885       }
886       else if( x > targetSize.width - mScrollThreshold )
887       {
888         mScrollDirection = SCROLL_LEFT;
889         mHandleScrolling = type;
890         StartScrollTimer();
891       }
892       else
893       {
894         mHandleScrolling = HANDLE_TYPE_COUNT;
895         StopScrollTimer();
896         mController.DecorationEvent( type, HANDLE_PRESSED, x, y );
897       }
898     }
899     else if( Gesture::Finished  == gesture.state ||
900              Gesture::Cancelled == gesture.state )
901     {
902       if( mScrollTimer &&
903           ( mScrollTimer.IsRunning() || mNotifyEndOfScroll ) )
904       {
905         mNotifyEndOfScroll = false;
906         mHandleScrolling = HANDLE_TYPE_COUNT;
907         StopScrollTimer();
908         mController.DecorationEvent( type, HANDLE_STOP_SCROLLING, x, y );
909       }
910       else
911       {
912         mController.DecorationEvent( type, HANDLE_RELEASED, x, y );
913       }
914
915       if( GRAB_HANDLE == type )
916       {
917         handle.actor.SetImage( mHandleImages[type][HANDLE_IMAGE_RELEASED] );
918       }
919       else
920       {
921         HandleType selectionHandleType = type;
922
923         if( mSwapSelectionHandles != handle.flipped )
924         {
925           selectionHandleType = ( LEFT_SELECTION_HANDLE == type ) ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE;
926         }
927
928         handle.actor.SetImage( mHandleImages[selectionHandleType][HANDLE_IMAGE_RELEASED] );
929       }
930       handle.pressed = false;
931     }
932   }
933
934   void OnPan( Actor actor, const PanGesture& gesture )
935   {
936     HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
937     HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
938     HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
939
940     if( actor == grabHandle.grabArea )
941     {
942       DoPan( grabHandle, GRAB_HANDLE, gesture );
943     }
944     else if( actor == primarySelectionHandle.grabArea )
945     {
946       DoPan( primarySelectionHandle, LEFT_SELECTION_HANDLE, gesture );
947     }
948     else if( actor == secondarySelectionHandle.grabArea )
949     {
950       DoPan( secondarySelectionHandle, RIGHT_SELECTION_HANDLE, gesture );
951     }
952   }
953
954   bool OnGrabHandleTouched( Actor actor, const TouchEvent& event )
955   {
956     // Switch between pressed/release grab-handle images
957     if( event.GetPointCount() > 0 &&
958         mHandle[GRAB_HANDLE].actor )
959     {
960       const TouchPoint& point = event.GetPoint(0);
961
962       if( TouchPoint::Down == point.state )
963       {
964         mHandle[GRAB_HANDLE].pressed = true;
965         Image imagePressed = mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_PRESSED];
966         if( imagePressed )
967         {
968           mHandle[GRAB_HANDLE].actor.SetImage( imagePressed );
969         }
970       }
971       else if( ( TouchPoint::Up == point.state ) ||
972                ( TouchPoint::Interrupted == point.state ) )
973       {
974         mHandle[GRAB_HANDLE].pressed = false;
975         Image imageReleased = mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED];
976         if( imageReleased )
977         {
978           mHandle[GRAB_HANDLE].actor.SetImage( imageReleased );
979         }
980       }
981     }
982
983     // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
984     return true;
985   }
986
987   bool OnHandleOneTouched( Actor actor, const TouchEvent& event )
988   {
989     // Switch between pressed/release selection handle images
990     if( event.GetPointCount() > 0 &&
991         mHandle[LEFT_SELECTION_HANDLE].actor )
992     {
993       const TouchPoint& point = event.GetPoint(0);
994
995       const bool flip = mSwapSelectionHandles != mHandle[LEFT_SELECTION_HANDLE].flipped;
996       if( TouchPoint::Down == point.state )
997       {
998         mHandle[LEFT_SELECTION_HANDLE].pressed = true;
999         Image imagePressed = mHandleImages[flip ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE][HANDLE_IMAGE_PRESSED];
1000         if( imagePressed )
1001         {
1002           mHandle[LEFT_SELECTION_HANDLE].actor.SetImage( imagePressed );
1003         }
1004       }
1005       else if( ( TouchPoint::Up == point.state ) ||
1006                ( TouchPoint::Interrupted == point.state ) )
1007       {
1008         mHandle[LEFT_SELECTION_HANDLE].pressed = false;
1009         Image imageReleased = mHandleImages[flip ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED];
1010         if( imageReleased )
1011         {
1012           mHandle[LEFT_SELECTION_HANDLE].actor.SetImage( imageReleased );
1013         }
1014       }
1015     }
1016
1017     // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1018     return true;
1019   }
1020
1021   bool OnHandleTwoTouched( Actor actor, const TouchEvent& event )
1022   {
1023     // Switch between pressed/release selection handle images
1024     if( event.GetPointCount() > 0 &&
1025         mHandle[RIGHT_SELECTION_HANDLE].actor )
1026     {
1027       const TouchPoint& point = event.GetPoint(0);
1028
1029       const bool flip = mSwapSelectionHandles != mHandle[RIGHT_SELECTION_HANDLE].flipped;
1030       if( TouchPoint::Down == point.state )
1031       {
1032         Image imagePressed = mHandleImages[flip ? LEFT_SELECTION_HANDLE : RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_PRESSED];
1033         mHandle[RIGHT_SELECTION_HANDLE].pressed = true;
1034         if( imagePressed )
1035         {
1036           mHandle[RIGHT_SELECTION_HANDLE].actor.SetImage( imagePressed );
1037         }
1038       }
1039       else if( ( TouchPoint::Up == point.state ) ||
1040                ( TouchPoint::Interrupted == point.state ) )
1041       {
1042         Image imageReleased = mHandleImages[flip ? LEFT_SELECTION_HANDLE : RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED];
1043         mHandle[RIGHT_SELECTION_HANDLE].pressed = false;
1044         if( imageReleased )
1045         {
1046           mHandle[RIGHT_SELECTION_HANDLE].actor.SetImage( imageReleased );
1047         }
1048       }
1049     }
1050
1051     // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1052     return true;
1053   }
1054
1055   // Popup
1056
1057   float AlternatePopUpPositionRelativeToCursor()
1058   {
1059     float alternativePosition=0.0f;;
1060
1061     if ( mPrimaryCursor ) // Secondary cursor not used for paste
1062     {
1063       Cursor cursor = PRIMARY_CURSOR;
1064       alternativePosition = mCursor[cursor].position.y;
1065     }
1066
1067     const float popupHeight = 120.0f; // todo Set as a MaxSize Property in Control or retrieve from CopyPastePopup class.
1068
1069     if( mHandle[GRAB_HANDLE].active )
1070     {
1071       // If grab handle enabled then position pop-up below the grab handle.
1072       const Vector2 grabHandleSize( 59.0f, 56.0f ); // todo
1073       const float BOTTOM_HANDLE_BOTTOM_OFFSET = 1.5; //todo Should be a property
1074       alternativePosition +=  grabHandleSize.height  + popupHeight + BOTTOM_HANDLE_BOTTOM_OFFSET ;
1075     }
1076     else
1077     {
1078       alternativePosition += popupHeight;
1079     }
1080
1081     return alternativePosition;
1082   }
1083
1084   void PopUpLeavesVerticalBoundary( PropertyNotification& source )
1085   {
1086     float alternativeYPosition=0.0f;
1087     // todo use AlternatePopUpPositionRelativeToSelectionHandles() if text is highlighted
1088     // if can't be positioned above, then position below row.
1089     alternativeYPosition = AlternatePopUpPositionRelativeToCursor();
1090
1091     mCopyPastePopup.actor.SetY( alternativeYPosition );
1092   }
1093
1094
1095   void SetUpPopupPositionNotifications( )
1096   {
1097     // Note Property notifications ignore any set anchor point so conditions must allow for this.  Default is Top Left.
1098
1099     // Exceeding vertical boundary
1100
1101     Vector4 worldCoordinatesBoundingBox;
1102     LocalToWorldCoordinatesBoundingBox( mBoundingBox, worldCoordinatesBoundingBox );
1103
1104     float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT);
1105
1106     PropertyNotification verticalExceedNotification = mCopyPastePopup.actor.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1107                                                       OutsideCondition( worldCoordinatesBoundingBox.y + popupHeight * 0.5f,
1108                                                                         worldCoordinatesBoundingBox.w - popupHeight * 0.5f ) );
1109
1110     verticalExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesVerticalBoundary );
1111   }
1112
1113   void GetConstrainedPopupPosition( Vector3& requiredPopupPosition, Vector3& popupSize, Vector3 anchorPoint, Actor& parent, Rect<int>& boundingBox )
1114   {
1115     DALI_ASSERT_DEBUG ( "Popup parent not on stage" && parent.OnStage() )
1116
1117     // Parent must already by added to Stage for these Get calls to work
1118     Vector3 parentAnchorPoint = parent.GetCurrentAnchorPoint();
1119     Vector3 parentWorldPositionLeftAnchor = parent.GetCurrentWorldPosition() - parent.GetCurrentSize()*parentAnchorPoint;
1120     Vector3 popupWorldPosition = parentWorldPositionLeftAnchor + requiredPopupPosition;  // Parent World position plus popup local position gives World Position
1121     Vector3 popupDistanceFromAnchorPoint = popupSize*anchorPoint;
1122
1123     // Bounding rectangle is supplied as screen coordinates, bounding will be done in world coordinates.
1124     Vector4 boundingRectangleWorld;
1125     LocalToWorldCoordinatesBoundingBox( boundingBox, boundingRectangleWorld );
1126
1127     // Calculate distance to move popup (in local space) so fits within the boundary
1128     float xOffSetToKeepWithinBounds = 0.0f;
1129     if( popupWorldPosition.x - popupDistanceFromAnchorPoint.x < boundingRectangleWorld.x )
1130     {
1131       xOffSetToKeepWithinBounds = boundingRectangleWorld.x - ( popupWorldPosition.x - popupDistanceFromAnchorPoint.x );
1132     }
1133     else if ( popupWorldPosition.x +  popupDistanceFromAnchorPoint.x > boundingRectangleWorld.z )
1134     {
1135       xOffSetToKeepWithinBounds = boundingRectangleWorld.z - ( popupWorldPosition.x +  popupDistanceFromAnchorPoint.x );
1136     }
1137
1138     // Ensure initial display of Popup is in alternative position if can not fit above. As Property notification will be a frame behind.
1139     if ( popupWorldPosition.y - popupDistanceFromAnchorPoint.y < boundingRectangleWorld.y )
1140     {
1141       requiredPopupPosition.y = AlternatePopUpPositionRelativeToCursor();
1142     }
1143
1144     requiredPopupPosition.x = requiredPopupPosition.x + xOffSetToKeepWithinBounds;
1145
1146     // Prevent pixel mis-alignment by rounding down.
1147     requiredPopupPosition.x = static_cast<int>( requiredPopupPosition.x );
1148     requiredPopupPosition.y = static_cast<int>( requiredPopupPosition.y );
1149
1150   }
1151
1152   void FlipSelectionHandleImages()
1153   {
1154     SetupTouchEvents();
1155
1156     CreateSelectionHandles();
1157
1158     HandleImpl& leftHandle = mHandle[LEFT_SELECTION_HANDLE];
1159     HandleImpl& rightHandle = mHandle[RIGHT_SELECTION_HANDLE];
1160
1161     // If handle pressed and pressed image exists then use pressed image else stick with released image
1162     const HandleImageType leftImageType = ( leftHandle.pressed && mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_PRESSED] ) ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED;
1163     const HandleImageType rightImageType = ( rightHandle.pressed && mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_PRESSED] ) ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED;
1164
1165     const bool leftFlipped = mSwapSelectionHandles != leftHandle.flipped;
1166     const bool rightFlipped = mSwapSelectionHandles != rightHandle.flipped;
1167
1168     leftHandle.actor.SetImage( leftFlipped ? mHandleImages[RIGHT_SELECTION_HANDLE][leftImageType] : mHandleImages[LEFT_SELECTION_HANDLE][leftImageType] );
1169
1170     leftHandle.actor.SetAnchorPoint( leftFlipped ? AnchorPoint::TOP_LEFT : AnchorPoint::TOP_RIGHT );
1171
1172     rightHandle.actor.SetImage( rightFlipped ? mHandleImages[LEFT_SELECTION_HANDLE][rightImageType] : mHandleImages[RIGHT_SELECTION_HANDLE][rightImageType] );
1173
1174     rightHandle.actor.SetAnchorPoint( rightFlipped ? AnchorPoint::TOP_RIGHT : AnchorPoint::TOP_LEFT );
1175
1176     if ( leftHandle.markerActor )
1177     {
1178       leftHandle.markerActor.SetImage( leftFlipped ? mHandleImages[RIGHT_SELECTION_HANDLE_MARKER][leftImageType] : mHandleImages[LEFT_SELECTION_HANDLE_MARKER][leftImageType] );
1179     }
1180     if ( rightHandle.markerActor )
1181     {
1182       rightHandle.markerActor.SetImage( rightFlipped ? mHandleImages[LEFT_SELECTION_HANDLE_MARKER][rightImageType] : mHandleImages[RIGHT_SELECTION_HANDLE_MARKER][rightImageType] );
1183     }
1184   }
1185
1186   void SetScrollThreshold( float threshold )
1187   {
1188     mScrollThreshold = threshold;
1189   }
1190
1191   float GetScrollThreshold() const
1192   {
1193     return mScrollThreshold;
1194   }
1195
1196   void SetScrollSpeed( float speed )
1197   {
1198     mScrollSpeed = speed;
1199     mScrollDistance = speed * SCROLL_TICK_INTERVAL * TO_SECONDS;
1200   }
1201
1202   float GetScrollSpeed() const
1203   {
1204     return mScrollSpeed;
1205   }
1206
1207   void NotifyEndOfScroll()
1208   {
1209     StopScrollTimer();
1210
1211     if( mScrollTimer )
1212     {
1213       mNotifyEndOfScroll = true;
1214     }
1215   }
1216
1217   /**
1218    * Creates and starts a timer to scroll the text when handles are close to the edges of the text.
1219    *
1220    * It only starts the timer if it's already created.
1221    */
1222   void StartScrollTimer()
1223   {
1224     if( !mScrollTimer )
1225     {
1226       mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1227       mScrollTimer.TickSignal().Connect( this, &Decorator::Impl::OnScrollTimerTick );
1228     }
1229
1230     if( !mScrollTimer.IsRunning() )
1231     {
1232       mScrollTimer.Start();
1233     }
1234   }
1235
1236   /**
1237    * Stops the timer used to scroll the text.
1238    */
1239   void StopScrollTimer()
1240   {
1241     if( mScrollTimer )
1242     {
1243       mScrollTimer.Stop();
1244     }
1245   }
1246
1247   /**
1248    * Callback called by the timer used to scroll the text.
1249    *
1250    * It calculates and sets a new scroll position.
1251    */
1252   bool OnScrollTimerTick()
1253   {
1254     if( HANDLE_TYPE_COUNT != mHandleScrolling )
1255     {
1256       mController.DecorationEvent( mHandleScrolling,
1257                                    HANDLE_SCROLLING,
1258                                    mScrollDirection == SCROLL_RIGHT ? mScrollDistance : -mScrollDistance,
1259                                    0.f );
1260     }
1261
1262     return true;
1263   }
1264
1265   ControllerInterface& mController;
1266
1267   TapGestureDetector  mTapDetector;
1268   PanGestureDetector  mPanGestureDetector;
1269   Timer               mCursorBlinkTimer;          ///< Timer to signal cursor to blink
1270   Timer               mScrollTimer;               ///< Timer used to scroll the text when the grab handle is moved close to the edges.
1271
1272   Layer               mActiveLayer;               ///< Layer for active handles and alike that ensures they are above all else.
1273   ImageActor          mPrimaryCursor;
1274   ImageActor          mSecondaryCursor;
1275
1276   Actor               mHighlightActor;        ///< Actor to display highlight
1277   Renderer            mHighlightRenderer;
1278   Material            mHighlightMaterial;         ///< Material used for highlight
1279   Property::Map       mQuadVertexFormat;
1280   Property::Map       mQuadIndexFormat;
1281   PopupImpl           mCopyPastePopup;
1282   TextSelectionPopup::Buttons mEnabledPopupButtons; /// Bit mask of currently enabled Popup buttons
1283   TextSelectionPopupCallbackInterface& mTextSelectionPopupCallbackInterface;
1284
1285   Image               mHandleImages[HANDLE_TYPE_COUNT][HANDLE_IMAGE_TYPE_COUNT];
1286   Vector4             mHandleColor;
1287
1288   CursorImpl          mCursor[CURSOR_COUNT];
1289   HandleImpl          mHandle[HANDLE_TYPE_COUNT];
1290
1291   PropertyBuffer      mQuadVertices;
1292   PropertyBuffer      mQuadIndices;
1293   Geometry            mQuadGeometry;
1294   QuadContainer       mHighlightQuadList;         ///< Sub-selections that combine to create the complete selection highlight
1295
1296   Rect<int>           mBoundingBox;
1297   Vector4             mHighlightColor;            ///< Color of the highlight
1298   Vector2             mHighlightPosition;         ///< The position of the highlight actor.
1299
1300   unsigned int        mActiveCursor;
1301   unsigned int        mCursorBlinkInterval;
1302   float               mCursorBlinkDuration;
1303   float               mCursorWidth;             ///< The width of the cursors in pixels.
1304   HandleType          mHandleScrolling;         ///< The handle which is scrolling.
1305   ScrollDirection     mScrollDirection;         ///< The direction of the scroll.
1306   float               mScrollThreshold;         ///< Defines a square area inside the control, close to the edge. A cursor entering this area will trigger scroll events.
1307   float               mScrollSpeed;             ///< The scroll speed in pixels per second.
1308   float               mScrollDistance;          ///< Distance the text scrolls during a scroll interval.
1309   int                 mTextDepth;               ///< The depth used to render the text.
1310
1311   bool                mActiveCopyPastePopup   : 1;
1312   bool                mCursorBlinkStatus      : 1; ///< Flag to switch between blink on and blink off.
1313   bool                mPrimaryCursorVisible   : 1; ///< Whether the primary cursor is visible.
1314   bool                mSecondaryCursorVisible : 1; ///< Whether the secondary cursor is visible.
1315   bool                mSwapSelectionHandles   : 1; ///< Whether to swap the selection handle images.
1316   bool                mNotifyEndOfScroll      : 1; ///< Whether to notify the end of the scroll.
1317 };
1318
1319 DecoratorPtr Decorator::New( ControllerInterface& controller,
1320                              TextSelectionPopupCallbackInterface& callbackInterface )
1321 {
1322   return DecoratorPtr( new Decorator( controller,
1323                                       callbackInterface ) );
1324 }
1325
1326 void Decorator::SetBoundingBox( const Rect<int>& boundingBox )
1327 {
1328   mImpl->mBoundingBox = boundingBox;
1329 }
1330
1331 const Rect<int>& Decorator::GetBoundingBox() const
1332 {
1333   return mImpl->mBoundingBox;
1334 }
1335
1336 void Decorator::Relayout( const Vector2& size )
1337 {
1338   mImpl->Relayout( size );
1339 }
1340
1341 void Decorator::UpdatePositions( const Vector2& scrollOffset )
1342 {
1343   mImpl->UpdatePositions( scrollOffset );
1344 }
1345
1346 /** Cursor **/
1347
1348 void Decorator::SetActiveCursor( ActiveCursor activeCursor )
1349 {
1350   mImpl->mActiveCursor = activeCursor;
1351 }
1352
1353 unsigned int Decorator::GetActiveCursor() const
1354 {
1355   return mImpl->mActiveCursor;
1356 }
1357
1358 void Decorator::SetPosition( Cursor cursor, float x, float y, float cursorHeight, float lineHeight )
1359 {
1360   mImpl->mCursor[cursor].position.x = x;
1361   mImpl->mCursor[cursor].position.y = y;
1362   mImpl->mCursor[cursor].cursorHeight = cursorHeight;
1363   mImpl->mCursor[cursor].lineHeight = lineHeight;
1364 }
1365
1366 void Decorator::GetPosition( Cursor cursor, float& x, float& y, float& cursorHeight, float& lineHeight ) const
1367 {
1368   x = mImpl->mCursor[cursor].position.x;
1369   y = mImpl->mCursor[cursor].position.y;
1370   cursorHeight = mImpl->mCursor[cursor].cursorHeight;
1371   lineHeight = mImpl->mCursor[cursor].lineHeight;
1372 }
1373
1374 const Vector2& Decorator::GetPosition( Cursor cursor ) const
1375 {
1376   return mImpl->mCursor[cursor].position;
1377 }
1378
1379 void Decorator::SetCursorColor( Cursor cursor, const Dali::Vector4& color )
1380 {
1381   mImpl->mCursor[cursor].color = color;
1382 }
1383
1384 const Dali::Vector4& Decorator::GetColor( Cursor cursor ) const
1385 {
1386   return mImpl->mCursor[cursor].color;
1387 }
1388
1389 void Decorator::StartCursorBlink()
1390 {
1391   if ( !mImpl->mCursorBlinkTimer )
1392   {
1393     mImpl->mCursorBlinkTimer = Timer::New( mImpl->mCursorBlinkInterval );
1394     mImpl->mCursorBlinkTimer.TickSignal().Connect( mImpl, &Decorator::Impl::OnCursorBlinkTimerTick );
1395   }
1396
1397   if ( !mImpl->mCursorBlinkTimer.IsRunning() )
1398   {
1399     mImpl->mCursorBlinkTimer.Start();
1400   }
1401 }
1402
1403 void Decorator::StopCursorBlink()
1404 {
1405   if ( mImpl->mCursorBlinkTimer )
1406   {
1407     mImpl->mCursorBlinkTimer.Stop();
1408   }
1409 }
1410
1411 void Decorator::SetCursorBlinkInterval( float seconds )
1412 {
1413   mImpl->mCursorBlinkInterval = static_cast<unsigned int>( seconds * TO_MILLISECONDS ); // Convert to milliseconds
1414 }
1415
1416 float Decorator::GetCursorBlinkInterval() const
1417 {
1418   return static_cast<float>( mImpl->mCursorBlinkInterval ) * TO_SECONDS;
1419 }
1420
1421 void Decorator::SetCursorBlinkDuration( float seconds )
1422 {
1423   mImpl->mCursorBlinkDuration = seconds;
1424 }
1425
1426 float Decorator::GetCursorBlinkDuration() const
1427 {
1428   return mImpl->mCursorBlinkDuration;
1429 }
1430
1431 void Decorator::SetCursorWidth( int width )
1432 {
1433   mImpl->mCursorWidth = static_cast<float>( width );
1434 }
1435
1436 int Decorator::GetCursorWidth() const
1437 {
1438   return static_cast<int>( mImpl->mCursorWidth );
1439 }
1440
1441 /** Handles **/
1442
1443 void Decorator::SetHandleActive( HandleType handleType, bool active )
1444 {
1445   mImpl->mHandle[handleType].active = active;
1446
1447   if( !active )
1448   {
1449     // TODO: this is a work-around.
1450     // The problem is the handle actor does not receive the touch event with the Interrupt
1451     // state when the power button is pressed and the application goes to background.
1452     mImpl->mHandle[handleType].pressed = false;
1453     Image imageReleased = mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED];
1454     ImageActor imageActor = mImpl->mHandle[handleType].actor;
1455     if( imageReleased && imageActor )
1456     {
1457        imageActor.SetImage( imageReleased );
1458     }
1459   }
1460 }
1461
1462 bool Decorator::IsHandleActive( HandleType handleType ) const
1463 {
1464   return mImpl->mHandle[handleType].active ;
1465 }
1466
1467 void Decorator::SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image )
1468 {
1469   mImpl->mHandleImages[handleType][handleImageType] = image;
1470 }
1471
1472 Dali::Image Decorator::GetHandleImage( HandleType handleType, HandleImageType handleImageType ) const
1473 {
1474   return mImpl->mHandleImages[handleType][handleImageType];
1475 }
1476
1477 void Decorator::SetHandleColor( const Vector4& color )
1478 {
1479   mImpl->mHandleColor = color;
1480 }
1481
1482 const Vector4& Decorator::GetHandleColor() const
1483 {
1484   return mImpl->mHandleColor;
1485 }
1486
1487 void Decorator::SetPosition( HandleType handleType, float x, float y, float height )
1488 {
1489   // Adjust grab handle displacement
1490   Impl::HandleImpl& handle = mImpl->mHandle[handleType];
1491
1492   handle.grabDisplacementX -= x - handle.position.x;
1493   handle.grabDisplacementY -= y - handle.position.y;
1494
1495   handle.position.x = x;
1496   handle.position.y = y;
1497   handle.lineHeight = height;
1498 }
1499
1500 void Decorator::GetPosition( HandleType handleType, float& x, float& y, float& height ) const
1501 {
1502   Impl::HandleImpl& handle = mImpl->mHandle[handleType];
1503
1504   x = handle.position.x;
1505   y = handle.position.y;
1506   height = handle.lineHeight;
1507 }
1508
1509 const Vector2& Decorator::GetPosition( HandleType handleType ) const
1510 {
1511   return mImpl->mHandle[handleType].position;
1512 }
1513
1514 void Decorator::SwapSelectionHandlesEnabled( bool enable )
1515 {
1516   mImpl->mSwapSelectionHandles = enable;
1517
1518   mImpl->FlipSelectionHandleImages();
1519 }
1520
1521 void Decorator::AddHighlight( float x1, float y1, float x2, float y2 )
1522 {
1523   mImpl->mHighlightQuadList.push_back( QuadCoordinates(x1, y1, x2, y2) );
1524 }
1525
1526 void Decorator::ClearHighlights()
1527 {
1528   mImpl->mHighlightQuadList.clear();
1529   mImpl->mHighlightPosition = Vector2::ZERO;
1530 }
1531
1532 void Decorator::SetHighlightColor( const Vector4& color )
1533 {
1534   mImpl->mHighlightColor = color;
1535 }
1536
1537 const Vector4& Decorator::GetHighlightColor() const
1538 {
1539   return mImpl->mHighlightColor;
1540 }
1541
1542 void Decorator::SetTextDepth( int textDepth )
1543 {
1544   mImpl->mTextDepth = textDepth;
1545 }
1546
1547 void Decorator::SetPopupActive( bool active )
1548 {
1549   mImpl->mActiveCopyPastePopup = active;
1550 }
1551
1552 bool Decorator::IsPopupActive() const
1553 {
1554   return mImpl->mActiveCopyPastePopup ;
1555 }
1556
1557 void Decorator::SetEnabledPopupButtons( TextSelectionPopup::Buttons& enabledButtonsBitMask )
1558 {
1559    mImpl->mEnabledPopupButtons = enabledButtonsBitMask;
1560
1561    if ( !mImpl->mCopyPastePopup.actor )
1562    {
1563      mImpl->mCopyPastePopup.actor = TextSelectionPopup::New( &mImpl->mTextSelectionPopupCallbackInterface );
1564 #ifdef DECORATOR_DEBUG
1565      mImpl->mCopyPastePopup.actor.SetName("mCopyPastePopup");
1566 #endif
1567      mImpl->mCopyPastePopup.actor.SetAnchorPoint( AnchorPoint::CENTER );
1568      mImpl->mCopyPastePopup.actor.OnRelayoutSignal().Connect( mImpl,  &Decorator::Impl::PopupRelayoutComplete  ); // Position popup after size negotiation
1569    }
1570
1571    mImpl->mCopyPastePopup.actor.EnableButtons( mImpl->mEnabledPopupButtons );
1572 }
1573
1574 TextSelectionPopup::Buttons& Decorator::GetEnabledPopupButtons()
1575 {
1576   return mImpl->mEnabledPopupButtons;
1577 }
1578
1579 /** Scroll **/
1580
1581 void Decorator::SetScrollThreshold( float threshold )
1582 {
1583   mImpl->SetScrollThreshold( threshold );
1584 }
1585
1586 float Decorator::GetScrollThreshold() const
1587 {
1588   return mImpl->GetScrollThreshold();
1589 }
1590
1591 void Decorator::SetScrollSpeed( float speed )
1592 {
1593   mImpl->SetScrollSpeed( speed );
1594 }
1595
1596 float Decorator::GetScrollSpeed() const
1597 {
1598   return mImpl->GetScrollSpeed();
1599 }
1600
1601 void Decorator::NotifyEndOfScroll()
1602 {
1603   mImpl->NotifyEndOfScroll();
1604 }
1605
1606 Decorator::~Decorator()
1607 {
1608   delete mImpl;
1609 }
1610
1611 Decorator::Decorator( ControllerInterface& controller,
1612                       TextSelectionPopupCallbackInterface& callbackInterface )
1613 : mImpl( NULL )
1614 {
1615   mImpl = new Decorator::Impl( controller, callbackInterface );
1616 }
1617
1618 } // namespace Text
1619
1620 } // namespace Toolkit
1621
1622 } // namespace Dali