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