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