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