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