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