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