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