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