Stop CreateSolidActor from creating an RGBA BufferImage when it isn't needed
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / decorator / text-decorator.cpp
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/internal/text/decorator/text-decorator.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/integration-api/debug.h>
23
24 #include <dali/public-api/adaptor-framework/timer.h>
25 #include <dali/public-api/actors/layer.h>
26 #include <dali/public-api/common/stage.h>
27 #include <dali/public-api/events/touch-event.h>
28 #include <dali/public-api/events/pan-gesture.h>
29 #include <dali/public-api/images/resource-image.h>
30 #include <dali/public-api/object/property-notification.h>
31
32 #include <dali/devel-api/rendering/geometry.h>
33 #include <dali/devel-api/rendering/renderer.h>
34
35 // INTERNAL INCLUDES
36 #include <dali-toolkit/public-api/controls/control-depth-index-ranges.h>
37 #include <dali-toolkit/public-api/controls/default-controls/solid-color-actor.h>
38
39 #ifdef DEBUG_ENABLED
40 #define DECORATOR_DEBUG
41
42 #endif
43
44 #define MAKE_SHADER(A)#A
45
46 namespace
47 {
48 const char* VERTEX_SHADER = MAKE_SHADER(
49 attribute mediump vec2    aPosition;
50 uniform   mediump mat4    uMvpMatrix;
51 uniform   mediump vec3    uSize;
52
53 void main()
54 {
55   mediump vec4 position = vec4( aPosition, 0.0, 1.0 );
56   position.xyz *= uSize;
57   gl_Position = uMvpMatrix * position;
58 }
59 );
60
61 const char* FRAGMENT_SHADER = MAKE_SHADER(
62 uniform      lowp vec4 uColor;
63
64 void main()
65 {
66   gl_FragColor = uColor;
67 }
68 );
69 }
70
71 namespace Dali
72 {
73 namespace Internal
74 {
75 namespace
76 {
77 #ifdef DECORATOR_DEBUG
78 Integration::Log::Filter* gLogFilter( Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_TEXT_DECORATOR") );
79 #endif
80 }
81 }
82 }
83
84
85 // Local Data
86 namespace
87 {
88
89 const char* DEFAULT_GRAB_HANDLE_IMAGE_RELEASED( DALI_IMAGE_DIR "cursor_handler_center.png" );
90 const char* DEFAULT_SELECTION_HANDLE_ONE_RELEASED( DALI_IMAGE_DIR "selection_handle_left.png" );
91 const char* DEFAULT_SELECTION_HANDLE_TWO_RELEASED( DALI_IMAGE_DIR "selection_handle_right.png" );
92
93 const int DEFAULT_POPUP_OFFSET( -100.0f ); // Vertical offset of Popup from cursor or handles position.
94
95 const Dali::Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.25f, 1.5f, 1.0f );
96 const Dali::Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.25f, 1.5f, 1.0f );
97
98 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.
99
100 const Dali::Vector4 HANDLE_COLOR( 0.0f, (183.0f / 255.0f), (229.0f / 255.0f), 1.0f  );
101
102 const unsigned int CURSOR_BLINK_INTERVAL = 500u; // Cursor blink interval
103 const float TO_MILLISECONDS = 1000.f;
104 const float TO_SECONDS = 1.f / TO_MILLISECONDS;
105
106 const unsigned int SCROLL_TICK_INTERVAL = 50u;
107
108 const float SCROLL_THRESHOLD = 10.f;
109 const float SCROLL_SPEED = 300.f;
110 const float SCROLL_DISTANCE = SCROLL_SPEED * SCROLL_TICK_INTERVAL * TO_SECONDS;
111
112 /**
113  * structure to hold coordinates of each quad, which will make up the mesh.
114  */
115 struct QuadCoordinates
116 {
117   /**
118    * Default constructor
119    */
120   QuadCoordinates()
121   {
122   }
123
124   /**
125    * Constructor
126    * @param[in] x1 left co-ordinate
127    * @param[in] y1 top co-ordinate
128    * @param[in] x2 right co-ordinate
129    * @param[in] y2 bottom co-ordinate
130    */
131   QuadCoordinates(float x1, float y1, float x2, float y2)
132   : min(x1, y1),
133     max(x2, y2)
134   {
135   }
136
137   Dali::Vector2 min;                          ///< top-left (minimum) position of quad
138   Dali::Vector2 max;                          ///< bottom-right (maximum) position of quad
139 };
140
141 typedef std::vector<QuadCoordinates> QuadContainer;
142
143 /**
144  * @brief Takes a bounding rectangle in the local coordinates of an actor and returns the world coordinates Bounding Box.
145  * @param[in] boundingRectangle local bounding
146  * @param[out] Vector4 World coordinate bounding Box.
147  */
148 void LocalToWorldCoordinatesBoundingBox( const Dali::Rect<int>& boundingRectangle, Dali::Vector4& boundingBox )
149 {
150   // Convert to world coordinates and store as a Vector4 to be compatible with Property Notifications.
151   Dali::Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
152
153   const float originX = boundingRectangle.x - 0.5f * stageSize.width;
154   const float originY = boundingRectangle.y - 0.5f * stageSize.height;
155
156   boundingBox = Dali::Vector4( originX,
157                                originY,
158                                originX + boundingRectangle.width,
159                                originY + boundingRectangle.height );
160 }
161
162
163 } // end of namespace
164
165 namespace Dali
166 {
167
168 namespace Toolkit
169 {
170
171 namespace Text
172 {
173
174 struct Decorator::Impl : public ConnectionTracker
175 {
176   enum ScrollDirection
177   {
178     SCROLL_NONE,
179     SCROLL_RIGHT,
180     SCROLL_LEFT,
181     SCROLL_TOP,
182     SCROLL_BOTTOM
183   };
184
185   struct CursorImpl
186   {
187     CursorImpl()
188     : color( Dali::Color::BLACK ),
189       position(),
190       cursorHeight( 0.0f ),
191       lineHeight( 0.0f )
192     {
193     }
194
195     Vector4 color;
196     Vector2 position;
197     float cursorHeight;
198     float lineHeight;
199   };
200
201   struct HandleImpl
202   {
203     HandleImpl()
204     : position(),
205       lineHeight( 0.0f ),
206       grabDisplacementX( 0.f ),
207       grabDisplacementY( 0.f ),
208       active( false ),
209       visible( false ),
210       pressed( false ),
211       flipped( false )
212     {
213     }
214
215     ImageActor actor;
216     Actor grabArea;
217
218     Vector2 position;
219     float lineHeight; ///< Not the handle height
220     float grabDisplacementX;
221     float grabDisplacementY;
222     bool active  : 1;
223     bool visible : 1;
224     bool pressed : 1;
225     bool flipped : 1;
226   };
227
228   struct PopupImpl
229   {
230     PopupImpl()
231     : position(),
232       offset( DEFAULT_POPUP_OFFSET )
233     {
234     }
235
236     TextSelectionPopup actor;
237     Vector3 position;
238     int offset;
239   };
240
241   Impl( ControllerInterface& controller,
242         TextSelectionPopupCallbackInterface& callbackInterface )
243   : mController( controller ),
244     mEnabledPopupButtons( TextSelectionPopup::NONE ),
245     mTextSelectionPopupCallbackInterface( callbackInterface ),
246     mHandleColor( HANDLE_COLOR ),
247     mBoundingBox( Rect<int>() ),
248     mHighlightColor( LIGHT_BLUE ),
249     mHighlightPosition( Vector2::ZERO ),
250     mActiveCursor( ACTIVE_CURSOR_NONE ),
251     mCursorBlinkInterval( CURSOR_BLINK_INTERVAL ),
252     mCursorBlinkDuration( 0.0f ),
253     mHandleScrolling( HANDLE_TYPE_COUNT ),
254     mScrollDirection( SCROLL_NONE ),
255     mScrollThreshold( SCROLL_THRESHOLD ),
256     mScrollSpeed( SCROLL_SPEED ),
257     mScrollDistance( SCROLL_DISTANCE ),
258     mTextDepth( 0u ),
259     mActiveCopyPastePopup( false ),
260     mCursorBlinkStatus( true ),
261     mPrimaryCursorVisible( false ),
262     mSecondaryCursorVisible( false ),
263     mSwapSelectionHandles( false ),
264     mNotifyEndOfScroll( false )
265   {
266     mQuadVertexFormat[ "aPosition" ] = Property::VECTOR2;
267     mQuadIndexFormat[ "indices" ] = Property::INTEGER;
268     mHighlightMaterial = Material::New( Shader::New( VERTEX_SHADER, FRAGMENT_SHADER ) );
269   }
270
271   /**
272    * Relayout of the decorations owned by the decorator.
273    * @param[in] size The Size of the UI control the decorator is adding it's decorations to.
274    */
275   void Relayout( const Vector2& size )
276   {
277     // TODO - Remove this if nothing is active
278     CreateActiveLayer();
279
280     // Show or hide the cursors
281     CreateCursors();
282
283     if( mPrimaryCursor )
284     {
285       const CursorImpl& cursor = mCursor[PRIMARY_CURSOR];
286       mPrimaryCursorVisible = ( cursor.position.x <= size.width ) && ( cursor.position.x >= 0.f );
287       if( mPrimaryCursorVisible )
288       {
289         Vector2 position = cursor.position;
290
291         mPrimaryCursor.SetPosition( position.x,
292                                     position.y );
293         mPrimaryCursor.SetSize( Size( 1.0f, cursor.cursorHeight ) );
294       }
295       mPrimaryCursor.SetVisible( mPrimaryCursorVisible );
296     }
297     if( mSecondaryCursor )
298     {
299       const CursorImpl& cursor = mCursor[SECONDARY_CURSOR];
300       mSecondaryCursorVisible = ( cursor.position.x <= size.width ) && ( cursor.position.x >= 0.f );
301       if( mSecondaryCursorVisible )
302       {
303         mSecondaryCursor.SetPosition( cursor.position.x,
304                                       cursor.position.y );
305         mSecondaryCursor.SetSize( Size( 1.0f, cursor.cursorHeight ) );
306       }
307       mSecondaryCursor.SetVisible( mSecondaryCursorVisible );
308     }
309
310     // Show or hide the grab handle
311     HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
312     if( grabHandle.active )
313     {
314       Vector2 position = grabHandle.position;
315
316       const bool isVisible = ( position.x <= size.width ) && ( position.x >= 0.f );
317
318       if( isVisible )
319       {
320         SetupTouchEvents();
321
322         CreateGrabHandle();
323
324         grabHandle.actor.SetPosition( position.x,
325                                       grabHandle.lineHeight ); // TODO : Fix for multiline.
326       }
327       grabHandle.actor.SetVisible( isVisible );
328     }
329     else if( grabHandle.actor )
330     {
331       grabHandle.actor.Unparent();
332     }
333
334     // Show or hide the selection handles/highlight
335     HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
336     HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
337     if( primary.active || secondary.active )
338     {
339       Vector2 primaryPosition = primary.position;
340       Vector2 secondaryPosition = secondary.position;
341
342       const bool isPrimaryVisible = ( primaryPosition.x <= size.width ) && ( primaryPosition.x >= 0.f );
343       const bool isSecondaryVisible = ( secondaryPosition.x <= size.width ) && ( secondaryPosition.x >= 0.f );
344
345       if( isPrimaryVisible || isSecondaryVisible )
346       {
347         SetupTouchEvents();
348
349         CreateSelectionHandles();
350
351         if( isPrimaryVisible )
352         {
353           primary.actor.SetPosition( primaryPosition.x,
354                                      primary.lineHeight ); // TODO : Fix for multiline.
355         }
356
357         if( isSecondaryVisible )
358         {
359           secondary.actor.SetPosition( secondaryPosition.x,
360                                        secondary.lineHeight ); // TODO : Fix for multiline.
361         }
362       }
363       primary.actor.SetVisible( isPrimaryVisible );
364       secondary.actor.SetVisible( isSecondaryVisible );
365
366       CreateHighlight();
367       UpdateHighlight();
368     }
369     else
370     {
371       if( primary.actor )
372       {
373         primary.actor.Unparent();
374       }
375       if( secondary.actor )
376       {
377         secondary.actor.Unparent();
378       }
379       if( mHighlightActor )
380       {
381         mHighlightActor.Unparent();
382       }
383     }
384
385     if( mActiveCopyPastePopup )
386     {
387       if( !mCopyPastePopup.actor.GetParent() )
388       {
389         mActiveLayer.Add( mCopyPastePopup.actor );
390       }
391
392       mCopyPastePopup.actor.RaiseAbove( mActiveLayer );
393     }
394     else
395     {
396       if( mCopyPastePopup.actor )
397       {
398         mCopyPastePopup.actor.Unparent();
399       }
400     }
401   }
402
403   void UpdatePositions( const Vector2& scrollOffset )
404   {
405     mCursor[PRIMARY_CURSOR].position += scrollOffset;
406     mCursor[SECONDARY_CURSOR].position += scrollOffset;
407     mHandle[ GRAB_HANDLE ].position += scrollOffset;
408     mHandle[ LEFT_SELECTION_HANDLE ].position += scrollOffset;
409     mHandle[ RIGHT_SELECTION_HANDLE ].position += scrollOffset;
410     mHighlightPosition += scrollOffset;
411     DeterminePositionPopup();
412   }
413
414   void DeterminePositionPopup()
415   {
416     if ( !mActiveCopyPastePopup )
417     {
418       return;
419     }
420
421     if ( mHandle[LEFT_SELECTION_HANDLE].active || mHandle[RIGHT_SELECTION_HANDLE].active )
422     {
423       float minHandleXPosition = std::min (  mHandle[LEFT_SELECTION_HANDLE].position.x, mHandle[RIGHT_SELECTION_HANDLE].position.x );
424       float maxHandleXPosition = std::max (  mHandle[LEFT_SELECTION_HANDLE].position.x, mHandle[RIGHT_SELECTION_HANDLE].position.x );
425
426       float minHandleYPosition = std::min (  mHandle[LEFT_SELECTION_HANDLE].position.y, mHandle[RIGHT_SELECTION_HANDLE].position.y );
427
428       mCopyPastePopup.position.x = minHandleXPosition + ( ( maxHandleXPosition - minHandleXPosition ) *0.5f );
429       mCopyPastePopup.position.y = minHandleYPosition + mCopyPastePopup.offset;
430     }
431     else
432     {
433       mCopyPastePopup.position = Vector3( mCursor[PRIMARY_CURSOR].position.x, mCursor[PRIMARY_CURSOR].position.y -100.0f , 0.0f ); //todo 100 to be an offset Property
434     }
435
436     Vector3 popupSize = Vector3( mCopyPastePopup.actor.GetRelayoutSize( Dimension::WIDTH ), mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT ), 0.0f );
437
438     GetConstrainedPopupPosition( mCopyPastePopup.position, popupSize, AnchorPoint::CENTER, mActiveLayer, mBoundingBox );
439
440     SetUpPopupPositionNotifications();
441
442     mCopyPastePopup.actor.SetPosition( mCopyPastePopup.position );
443   }
444
445   void PopupRelayoutComplete( Actor actor )
446   {
447     // Size negotiation for CopyPastePopup complete so can get the size and constrain position within bounding box.
448     mCopyPastePopup.actor.OnRelayoutSignal().Disconnect( this, &Decorator::Impl::PopupRelayoutComplete  );
449
450     DeterminePositionPopup();
451   }
452
453   void CreateCursor( ImageActor& cursor, const Vector4& color )
454   {
455     cursor = CreateSolidColorActor( color );
456     cursor.SetSortModifier( DECORATION_DEPTH_INDEX );
457     cursor.SetParentOrigin( ParentOrigin::TOP_LEFT ); // Need to set the default parent origin as CreateSolidColorActor() sets a different one.
458     cursor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
459   }
460
461   // Add or Remove cursor(s) from parent
462   void CreateCursors()
463   {
464     if( mActiveCursor == ACTIVE_CURSOR_NONE )
465     {
466       if( mPrimaryCursor )
467       {
468         mPrimaryCursor.Unparent();
469       }
470       if( mSecondaryCursor )
471       {
472         mSecondaryCursor.Unparent();
473       }
474     }
475     else
476     {
477       // Create Primary and or Secondary Cursor(s) if active and add to parent
478       if ( mActiveCursor == ACTIVE_CURSOR_PRIMARY ||
479            mActiveCursor == ACTIVE_CURSOR_BOTH )
480       {
481         if ( !mPrimaryCursor )
482         {
483           CreateCursor( mPrimaryCursor, mCursor[PRIMARY_CURSOR].color );
484 #ifdef DECORATOR_DEBUG
485           mPrimaryCursor.SetName( "PrimaryCursorActor" );
486 #endif
487         }
488
489         if( !mPrimaryCursor.GetParent() )
490         {
491           mActiveLayer.Add( mPrimaryCursor );
492         }
493       }
494
495       if ( mActiveCursor == ACTIVE_CURSOR_BOTH )
496       {
497         if ( !mSecondaryCursor )
498         {
499           CreateCursor( mSecondaryCursor, mCursor[SECONDARY_CURSOR].color );
500 #ifdef DECORATOR_DEBUG
501           mSecondaryCursor.SetName( "SecondaryCursorActor" );
502 #endif
503         }
504
505         if( !mSecondaryCursor.GetParent() )
506         {
507           mActiveLayer.Add( mSecondaryCursor );
508         }
509       }
510       else
511       {
512         if( mSecondaryCursor )
513         {
514           mSecondaryCursor.Unparent();
515         }
516       }
517     }
518   }
519
520   bool OnCursorBlinkTimerTick()
521   {
522     // Cursor blinking
523     if ( mPrimaryCursor )
524     {
525       mPrimaryCursor.SetVisible( mPrimaryCursorVisible && mCursorBlinkStatus );
526     }
527     if ( mSecondaryCursor )
528     {
529       mSecondaryCursor.SetVisible( mSecondaryCursorVisible && mCursorBlinkStatus );
530     }
531
532     mCursorBlinkStatus = !mCursorBlinkStatus;
533
534     return true;
535   }
536
537   void SetupTouchEvents()
538   {
539     if ( !mTapDetector )
540     {
541       mTapDetector = TapGestureDetector::New();
542       mTapDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnTap );
543     }
544
545     if ( !mPanGestureDetector )
546     {
547       mPanGestureDetector = PanGestureDetector::New();
548       mPanGestureDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnPan );
549     }
550   }
551
552   void CreateActiveLayer()
553   {
554     if( !mActiveLayer )
555     {
556       mActiveLayer = Layer::New();
557 #ifdef DECORATOR_DEBUG
558       mActiveLayer.SetName ( "ActiveLayerActor" );
559 #endif
560
561       mActiveLayer.SetParentOrigin( ParentOrigin::CENTER );
562       mActiveLayer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
563       mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
564
565       // Add the active layer telling the controller it doesn't need clipping.
566       mController.AddDecoration( mActiveLayer, false );
567     }
568
569     mActiveLayer.RaiseToTop();
570   }
571
572   void CreateGrabHandle()
573   {
574     HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
575     if( !grabHandle.actor )
576     {
577       if( !mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] )
578       {
579         mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] = ResourceImage::New( DEFAULT_GRAB_HANDLE_IMAGE_RELEASED );
580       }
581
582       grabHandle.actor = ImageActor::New( mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED] );
583       grabHandle.actor.SetSortModifier( DECORATION_DEPTH_INDEX );
584       grabHandle.actor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
585       // Area that Grab handle responds to, larger than actual handle so easier to move
586 #ifdef DECORATOR_DEBUG
587       grabHandle.actor.SetName( "GrabHandleActor" );
588       if ( Dali::Internal::gLogFilter->IsEnabledFor( Debug::Verbose ) )
589       {
590         grabHandle.grabArea = Toolkit::CreateSolidColorActor( Vector4(0.0f, 0.0f, 0.0f, 0.0f), true, Color::RED, 1 );
591         grabHandle.grabArea.SetName( "GrabArea" );
592       }
593       else
594       {
595         grabHandle.grabArea = Actor::New();
596         grabHandle.grabArea.SetName( "GrabArea" );
597       }
598 #else
599       grabHandle.grabArea = Actor::New();
600 #endif
601
602       grabHandle.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
603       grabHandle.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
604       grabHandle.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
605       grabHandle.grabArea.SetSizeModeFactor( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE );
606       grabHandle.actor.Add( grabHandle.grabArea );
607       grabHandle.actor.SetColor( mHandleColor );
608
609       grabHandle.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnGrabHandleTouched );
610       mTapDetector.Attach( grabHandle.grabArea );
611       mPanGestureDetector.Attach( grabHandle.grabArea );
612
613       mActiveLayer.Add( grabHandle.actor );
614     }
615
616     if( !grabHandle.actor.GetParent() )
617     {
618       mActiveLayer.Add( grabHandle.actor );
619     }
620   }
621
622   void CreateSelectionHandles()
623   {
624     HandleImpl& primary = mHandle[ LEFT_SELECTION_HANDLE ];
625     if( !primary.actor )
626     {
627       if( !mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] )
628       {
629         mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] = ResourceImage::New( DEFAULT_SELECTION_HANDLE_ONE_RELEASED );
630       }
631
632       primary.actor = ImageActor::New( mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
633 #ifdef DECORATOR_DEBUG
634       primary.actor.SetName("SelectionHandleOne");
635 #endif
636       primary.actor.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
637       primary.actor.SetSortModifier( DECORATION_DEPTH_INDEX );
638       primary.flipped = false;
639       primary.actor.SetColor( mHandleColor );
640
641       primary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
642 #ifdef DECORATOR_DEBUG
643       primary.grabArea.SetName("SelectionHandleOneGrabArea");
644 #endif
645       primary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
646       primary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
647       primary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
648       primary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
649
650       mTapDetector.Attach( primary.grabArea );
651       mPanGestureDetector.Attach( primary.grabArea );
652       primary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleOneTouched );
653
654       primary.actor.Add( primary.grabArea );
655     }
656
657     if( !primary.actor.GetParent() )
658     {
659       mActiveLayer.Add( primary.actor );
660     }
661
662     HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
663     if( !secondary.actor )
664     {
665       if( !mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] )
666       {
667         mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] = ResourceImage::New( DEFAULT_SELECTION_HANDLE_TWO_RELEASED );
668       }
669
670       secondary.actor = ImageActor::New( mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
671 #ifdef DECORATOR_DEBUG
672       secondary.actor.SetName("SelectionHandleTwo");
673 #endif
674       secondary.actor.SetAnchorPoint( AnchorPoint::TOP_LEFT ); // Change to BOTTOM_LEFT if Look'n'Feel requires handle above text.
675       secondary.actor.SetSortModifier( DECORATION_DEPTH_INDEX );
676       secondary.flipped = false;
677       secondary.actor.SetColor( mHandleColor );
678
679       secondary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
680 #ifdef DECORATOR_DEBUG
681       secondary.grabArea.SetName("SelectionHandleTwoGrabArea");
682 #endif
683       secondary.grabArea.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
684       secondary.grabArea.SetParentOrigin( ParentOrigin::TOP_CENTER );
685       secondary.grabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER );
686       secondary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE );
687
688       mTapDetector.Attach( secondary.grabArea );
689       mPanGestureDetector.Attach( secondary.grabArea );
690       secondary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleTwoTouched );
691
692       secondary.actor.Add( secondary.grabArea );
693     }
694
695     if( !secondary.actor.GetParent() )
696     {
697       mActiveLayer.Add( secondary.actor );
698     }
699   }
700
701   void CreateHighlight()
702   {
703     if( !mHighlightActor )
704     {
705       mHighlightActor = Actor::New();
706
707 #ifdef DECORATOR_DEBUG
708       mHighlightActor.SetName( "HighlightActor" );
709 #endif
710       mHighlightActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
711       mHighlightActor.SetSize( 1.0f, 1.0f );
712       mHighlightActor.SetColor( mHighlightColor );
713       mHighlightActor.SetColorMode( USE_OWN_COLOR );
714     }
715
716     // Add the highlight box telling the controller it needs clipping.
717     mController.AddDecoration( mHighlightActor, true );
718   }
719
720   void UpdateHighlight()
721   {
722     if ( mHighlightActor )
723     {
724       if( !mHighlightQuadList.empty() )
725       {
726         Vector< Vector2 > vertices;
727         Vector< unsigned int> indices;
728         Vector2 vertex;
729
730         std::vector<QuadCoordinates>::iterator iter = mHighlightQuadList.begin();
731         std::vector<QuadCoordinates>::iterator endIter = mHighlightQuadList.end();
732
733         for( std::size_t v = 0; iter != endIter; ++iter,v+=4 )
734         {
735
736           QuadCoordinates& quad = *iter;
737
738           // top-left (v+0)
739           vertex.x = quad.min.x;
740           vertex.y = quad.min.y;
741           vertices.PushBack( vertex );
742
743           // top-right (v+1)
744           vertex.x = quad.max.x;
745           vertex.y = quad.min.y;
746           vertices.PushBack( vertex );
747
748           // bottom-left (v+2)
749           vertex.x = quad.min.x;
750           vertex.y = quad.max.y;
751           vertices.PushBack( vertex );
752
753           // bottom-right (v+3)
754           vertex.x = quad.max.x;
755           vertex.y = quad.max.y;
756           vertices.PushBack( vertex );
757
758           // triangle A (3, 1, 0)
759           indices.PushBack( v + 3 );
760           indices.PushBack( v + 1 );
761           indices.PushBack( v );
762
763           // triangle B (0, 2, 3)
764           indices.PushBack( v );
765           indices.PushBack( v + 2 );
766           indices.PushBack( v + 3 );
767         }
768
769         if( mQuadVertices )
770         {
771           mQuadVertices.SetSize( vertices.Size() );
772         }
773         else
774         {
775           mQuadVertices = PropertyBuffer::New( mQuadVertexFormat, vertices.Size() );
776         }
777
778         if( mQuadIndices )
779         {
780           mQuadIndices.SetSize( indices.Size() );
781         }
782         else
783         {
784           mQuadIndices = PropertyBuffer::New( mQuadIndexFormat, indices.Size() );
785         }
786
787         mQuadVertices.SetData( &vertices[ 0 ] );
788         mQuadIndices.SetData( &indices[ 0 ] );
789
790         if( !mQuadGeometry )
791         {
792           mQuadGeometry = Geometry::New();
793           mQuadGeometry.AddVertexBuffer( mQuadVertices );
794         }
795         mQuadGeometry.SetIndexBuffer( mQuadIndices );
796
797         if( !mHighlightRenderer )
798         {
799           mHighlightRenderer = Dali::Renderer::New( mQuadGeometry, mHighlightMaterial );
800           mHighlightActor.AddRenderer( mHighlightRenderer );
801         }
802       }
803
804       mHighlightActor.SetPosition( mHighlightPosition.x,
805                                    mHighlightPosition.y );
806
807       mHighlightQuadList.clear();
808
809       mHighlightRenderer.SetDepthIndex( mTextDepth - 2u ); // text is rendered at mTextDepth and text's shadow at mTextDepth -1u.
810     }
811   }
812
813   void OnTap( Actor actor, const TapGesture& tap )
814   {
815     if( actor == mHandle[GRAB_HANDLE].actor )
816     {
817       // TODO
818     }
819   }
820
821   void DoPan( HandleImpl& handle, HandleType type, const PanGesture& gesture )
822   {
823     if( Gesture::Started == gesture.state )
824     {
825       handle.grabDisplacementX = handle.grabDisplacementY = 0;
826     }
827
828     handle.grabDisplacementX += gesture.displacement.x;
829     handle.grabDisplacementY += gesture.displacement.y;
830
831     const float x = handle.position.x + handle.grabDisplacementX;
832     const float y = handle.position.y + handle.lineHeight*0.5f + handle.grabDisplacementY;
833
834     if( Gesture::Started    == gesture.state ||
835         Gesture::Continuing == gesture.state )
836     {
837       Vector2 targetSize;
838       mController.GetTargetSize( targetSize );
839
840       if( x < mScrollThreshold )
841       {
842         mScrollDirection = SCROLL_RIGHT;
843         mHandleScrolling = type;
844         StartScrollTimer();
845       }
846       else if( x > targetSize.width - mScrollThreshold )
847       {
848         mScrollDirection = SCROLL_LEFT;
849         mHandleScrolling = type;
850         StartScrollTimer();
851       }
852       else
853       {
854         mHandleScrolling = HANDLE_TYPE_COUNT;
855         StopScrollTimer();
856         mController.DecorationEvent( type, HANDLE_PRESSED, x, y );
857       }
858     }
859     else if( Gesture::Finished  == gesture.state ||
860              Gesture::Cancelled == gesture.state )
861     {
862       if( mScrollTimer &&
863           ( mScrollTimer.IsRunning() || mNotifyEndOfScroll ) )
864       {
865         mNotifyEndOfScroll = false;
866         mHandleScrolling = HANDLE_TYPE_COUNT;
867         StopScrollTimer();
868         mController.DecorationEvent( type, HANDLE_STOP_SCROLLING, x, y );
869       }
870       else
871       {
872         mController.DecorationEvent( type, HANDLE_RELEASED, x, y );
873       }
874
875       if( GRAB_HANDLE == type )
876       {
877         handle.actor.SetImage( mHandleImages[type][HANDLE_IMAGE_RELEASED] );
878       }
879       else
880       {
881         HandleType selectionHandleType = type;
882
883         if( mSwapSelectionHandles != handle.flipped )
884         {
885           selectionHandleType = ( LEFT_SELECTION_HANDLE == type ) ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE;
886         }
887
888         handle.actor.SetImage( mHandleImages[selectionHandleType][HANDLE_IMAGE_RELEASED] );
889       }
890       handle.pressed = false;
891     }
892   }
893
894   void OnPan( Actor actor, const PanGesture& gesture )
895   {
896     HandleImpl& grabHandle = mHandle[GRAB_HANDLE];
897     HandleImpl& primarySelectionHandle = mHandle[LEFT_SELECTION_HANDLE];
898     HandleImpl& secondarySelectionHandle = mHandle[RIGHT_SELECTION_HANDLE];
899
900     if( actor == grabHandle.grabArea )
901     {
902       DoPan( grabHandle, GRAB_HANDLE, gesture );
903     }
904     else if( actor == primarySelectionHandle.grabArea )
905     {
906       DoPan( primarySelectionHandle, LEFT_SELECTION_HANDLE, gesture );
907     }
908     else if( actor == secondarySelectionHandle.grabArea )
909     {
910       DoPan( secondarySelectionHandle, RIGHT_SELECTION_HANDLE, gesture );
911     }
912   }
913
914   bool OnGrabHandleTouched( Actor actor, const TouchEvent& event )
915   {
916     // Switch between pressed/release grab-handle images
917     if( event.GetPointCount() > 0 &&
918         mHandle[GRAB_HANDLE].actor )
919     {
920       const TouchPoint& point = event.GetPoint(0);
921
922       if( TouchPoint::Down == point.state )
923       {
924         mHandle[GRAB_HANDLE].pressed = true;
925         Image imagePressed = mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_PRESSED];
926         if( imagePressed )
927         {
928           mHandle[GRAB_HANDLE].actor.SetImage( imagePressed );
929         }
930       }
931       else if( ( TouchPoint::Up == point.state ) ||
932                ( TouchPoint::Interrupted == point.state ) )
933       {
934         mHandle[GRAB_HANDLE].pressed = false;
935         Image imageReleased = mHandleImages[GRAB_HANDLE][HANDLE_IMAGE_RELEASED];
936         if( imageReleased )
937         {
938           mHandle[GRAB_HANDLE].actor.SetImage( imageReleased );
939         }
940       }
941     }
942
943     // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
944     return true;
945   }
946
947   bool OnHandleOneTouched( Actor actor, const TouchEvent& event )
948   {
949     // Switch between pressed/release selection handle images
950     if( event.GetPointCount() > 0 &&
951         mHandle[LEFT_SELECTION_HANDLE].actor )
952     {
953       const TouchPoint& point = event.GetPoint(0);
954
955       const bool flip = mSwapSelectionHandles != mHandle[LEFT_SELECTION_HANDLE].flipped;
956       if( TouchPoint::Down == point.state )
957       {
958         mHandle[LEFT_SELECTION_HANDLE].pressed = true;
959         Image imagePressed = mHandleImages[flip ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE][HANDLE_IMAGE_PRESSED];
960         if( imagePressed )
961         {
962           mHandle[LEFT_SELECTION_HANDLE].actor.SetImage( imagePressed );
963         }
964       }
965       else if( ( TouchPoint::Up == point.state ) ||
966                ( TouchPoint::Interrupted == point.state ) )
967       {
968         mHandle[LEFT_SELECTION_HANDLE].pressed = false;
969         Image imageReleased = mHandleImages[flip ? RIGHT_SELECTION_HANDLE : LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED];
970         if( imageReleased )
971         {
972           mHandle[LEFT_SELECTION_HANDLE].actor.SetImage( imageReleased );
973         }
974       }
975     }
976
977     // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
978     return true;
979   }
980
981   bool OnHandleTwoTouched( Actor actor, const TouchEvent& event )
982   {
983     // Switch between pressed/release selection handle images
984     if( event.GetPointCount() > 0 &&
985         mHandle[RIGHT_SELECTION_HANDLE].actor )
986     {
987       const TouchPoint& point = event.GetPoint(0);
988
989       const bool flip = mSwapSelectionHandles != mHandle[RIGHT_SELECTION_HANDLE].flipped;
990       if( TouchPoint::Down == point.state )
991       {
992         Image imagePressed = mHandleImages[flip ? LEFT_SELECTION_HANDLE : RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_PRESSED];
993         mHandle[RIGHT_SELECTION_HANDLE].pressed = true;
994         if( imagePressed )
995         {
996           mHandle[RIGHT_SELECTION_HANDLE].actor.SetImage( imagePressed );
997         }
998       }
999       else if( ( TouchPoint::Up == point.state ) ||
1000                ( TouchPoint::Interrupted == point.state ) )
1001       {
1002         Image imageReleased = mHandleImages[flip ? LEFT_SELECTION_HANDLE : RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED];
1003         mHandle[RIGHT_SELECTION_HANDLE].pressed = false;
1004         if( imageReleased )
1005         {
1006           mHandle[RIGHT_SELECTION_HANDLE].actor.SetImage( imageReleased );
1007         }
1008       }
1009     }
1010
1011     // Consume to avoid pop-ups accidentally closing, when handle is outside of pop-up area
1012     return true;
1013   }
1014
1015   // Popup
1016
1017   float AlternatePopUpPositionRelativeToCursor()
1018   {
1019     float alternativePosition=0.0f;;
1020
1021     if ( mPrimaryCursor ) // Secondary cursor not used for paste
1022     {
1023       Cursor cursor = PRIMARY_CURSOR;
1024       alternativePosition = mCursor[cursor].position.y;
1025     }
1026
1027     const float popupHeight = 120.0f; // todo Set as a MaxSize Property in Control or retrieve from CopyPastePopup class.
1028
1029     if( mHandle[GRAB_HANDLE].active )
1030     {
1031       // If grab handle enabled then position pop-up below the grab handle.
1032       const Vector2 grabHandleSize( 59.0f, 56.0f ); // todo
1033       const float BOTTOM_HANDLE_BOTTOM_OFFSET = 1.5; //todo Should be a property
1034       alternativePosition +=  grabHandleSize.height  + popupHeight + BOTTOM_HANDLE_BOTTOM_OFFSET ;
1035     }
1036     else
1037     {
1038       alternativePosition += popupHeight;
1039     }
1040
1041     return alternativePosition;
1042   }
1043
1044   void PopUpLeavesVerticalBoundary( PropertyNotification& source )
1045   {
1046     float alternativeYPosition=0.0f;
1047     // todo use AlternatePopUpPositionRelativeToSelectionHandles() if text is highlighted
1048     // if can't be positioned above, then position below row.
1049     alternativeYPosition = AlternatePopUpPositionRelativeToCursor();
1050
1051     mCopyPastePopup.actor.SetY( alternativeYPosition );
1052   }
1053
1054
1055   void SetUpPopupPositionNotifications( )
1056   {
1057     // Note Property notifications ignore any set anchor point so conditions must allow for this.  Default is Top Left.
1058
1059     // Exceeding vertical boundary
1060
1061     Vector4 worldCoordinatesBoundingBox;
1062     LocalToWorldCoordinatesBoundingBox( mBoundingBox, worldCoordinatesBoundingBox );
1063
1064     float popupHeight = mCopyPastePopup.actor.GetRelayoutSize( Dimension::HEIGHT);
1065
1066     PropertyNotification verticalExceedNotification = mCopyPastePopup.actor.AddPropertyNotification( Actor::Property::WORLD_POSITION_Y,
1067                                                       OutsideCondition( worldCoordinatesBoundingBox.y + popupHeight * 0.5f,
1068                                                                         worldCoordinatesBoundingBox.w - popupHeight * 0.5f ) );
1069
1070     verticalExceedNotification.NotifySignal().Connect( this, &Decorator::Impl::PopUpLeavesVerticalBoundary );
1071   }
1072
1073   void GetConstrainedPopupPosition( Vector3& requiredPopupPosition, Vector3& popupSize, Vector3 anchorPoint, Actor& parent, Rect<int>& boundingBox )
1074   {
1075     DALI_ASSERT_DEBUG ( "Popup parent not on stage" && parent.OnStage() )
1076
1077     // Parent must already by added to Stage for these Get calls to work
1078     Vector3 parentAnchorPoint = parent.GetCurrentAnchorPoint();
1079     Vector3 parentWorldPositionLeftAnchor = parent.GetCurrentWorldPosition() - parent.GetCurrentSize()*parentAnchorPoint;
1080     Vector3 popupWorldPosition = parentWorldPositionLeftAnchor + requiredPopupPosition;  // Parent World position plus popup local position gives World Position
1081     Vector3 popupDistanceFromAnchorPoint = popupSize*anchorPoint;
1082
1083     // Bounding rectangle is supplied as screen coordinates, bounding will be done in world coordinates.
1084     Vector4 boundingRectangleWorld;
1085     LocalToWorldCoordinatesBoundingBox( boundingBox, boundingRectangleWorld );
1086
1087     // Calculate distance to move popup (in local space) so fits within the boundary
1088     float xOffSetToKeepWithinBounds = 0.0f;
1089     if( popupWorldPosition.x - popupDistanceFromAnchorPoint.x < boundingRectangleWorld.x )
1090     {
1091       xOffSetToKeepWithinBounds = boundingRectangleWorld.x - ( popupWorldPosition.x - popupDistanceFromAnchorPoint.x );
1092     }
1093     else if ( popupWorldPosition.x +  popupDistanceFromAnchorPoint.x > boundingRectangleWorld.z )
1094     {
1095       xOffSetToKeepWithinBounds = boundingRectangleWorld.z - ( popupWorldPosition.x +  popupDistanceFromAnchorPoint.x );
1096     }
1097
1098     // Ensure initial display of Popup is in alternative position if can not fit above. As Property notification will be a frame behind.
1099     if ( popupWorldPosition.y - popupDistanceFromAnchorPoint.y < boundingRectangleWorld.y )
1100     {
1101       requiredPopupPosition.y = AlternatePopUpPositionRelativeToCursor();
1102     }
1103
1104     requiredPopupPosition.x = requiredPopupPosition.x + xOffSetToKeepWithinBounds;
1105
1106     // Prevent pixel mis-alignment by rounding down.
1107     requiredPopupPosition.x = static_cast<int>( requiredPopupPosition.x );
1108     requiredPopupPosition.y = static_cast<int>( requiredPopupPosition.y );
1109
1110   }
1111
1112   void FlipSelectionHandleImages()
1113   {
1114     SetupTouchEvents();
1115
1116     CreateSelectionHandles();
1117
1118     HandleImpl& leftHandle = mHandle[LEFT_SELECTION_HANDLE];
1119     HandleImpl& rightHandle = mHandle[RIGHT_SELECTION_HANDLE];
1120
1121     // If handle pressed and pressed image exists then use pressed image else stick with released image
1122     const HandleImageType leftImageType = ( leftHandle.pressed && mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_PRESSED] ) ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED;
1123     const HandleImageType rightImageType = ( rightHandle.pressed && mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_PRESSED] ) ? HANDLE_IMAGE_PRESSED : HANDLE_IMAGE_RELEASED;
1124
1125     const bool leftFlipped = mSwapSelectionHandles != leftHandle.flipped;
1126     const bool rightFlipped = mSwapSelectionHandles != rightHandle.flipped;
1127
1128     leftHandle.actor.SetImage( leftFlipped ? mHandleImages[RIGHT_SELECTION_HANDLE][leftImageType] : mHandleImages[LEFT_SELECTION_HANDLE][leftImageType] );
1129
1130     leftHandle.actor.SetAnchorPoint( leftFlipped ? AnchorPoint::TOP_LEFT : AnchorPoint::TOP_RIGHT );
1131
1132     rightHandle.actor.SetImage( rightFlipped ? mHandleImages[LEFT_SELECTION_HANDLE][rightImageType] : mHandleImages[RIGHT_SELECTION_HANDLE][rightImageType] );
1133
1134     rightHandle.actor.SetAnchorPoint( rightFlipped ? AnchorPoint::TOP_RIGHT : AnchorPoint::TOP_LEFT );
1135   }
1136
1137   void SetScrollThreshold( float threshold )
1138   {
1139     mScrollThreshold = threshold;
1140   }
1141
1142   float GetScrollThreshold() const
1143   {
1144     return mScrollThreshold;
1145   }
1146
1147   void SetScrollSpeed( float speed )
1148   {
1149     mScrollSpeed = speed;
1150     mScrollDistance = speed * SCROLL_TICK_INTERVAL * TO_SECONDS;
1151   }
1152
1153   float GetScrollSpeed() const
1154   {
1155     return mScrollSpeed;
1156   }
1157
1158   void NotifyEndOfScroll()
1159   {
1160     StopScrollTimer();
1161
1162     if( mScrollTimer )
1163     {
1164       mNotifyEndOfScroll = true;
1165     }
1166   }
1167
1168   /**
1169    * Creates and starts a timer to scroll the text when handles are close to the edges of the text.
1170    *
1171    * It only starts the timer if it's already created.
1172    */
1173   void StartScrollTimer()
1174   {
1175     if( !mScrollTimer )
1176     {
1177       mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1178       mScrollTimer.TickSignal().Connect( this, &Decorator::Impl::OnScrollTimerTick );
1179     }
1180
1181     if( !mScrollTimer.IsRunning() )
1182     {
1183       mScrollTimer.Start();
1184     }
1185   }
1186
1187   /**
1188    * Stops the timer used to scroll the text.
1189    */
1190   void StopScrollTimer()
1191   {
1192     if( mScrollTimer )
1193     {
1194       mScrollTimer.Stop();
1195     }
1196   }
1197
1198   /**
1199    * Callback called by the timer used to scroll the text.
1200    *
1201    * It calculates and sets a new scroll position.
1202    */
1203   bool OnScrollTimerTick()
1204   {
1205     if( HANDLE_TYPE_COUNT != mHandleScrolling )
1206     {
1207       mController.DecorationEvent( mHandleScrolling,
1208                                    HANDLE_SCROLLING,
1209                                    mScrollDirection == SCROLL_RIGHT ? mScrollDistance : -mScrollDistance,
1210                                    0.f );
1211     }
1212
1213     return true;
1214   }
1215
1216   ControllerInterface& mController;
1217
1218   TapGestureDetector  mTapDetector;
1219   PanGestureDetector  mPanGestureDetector;
1220   Timer               mCursorBlinkTimer;          ///< Timer to signal cursor to blink
1221   Timer               mScrollTimer;               ///< Timer used to scroll the text when the grab handle is moved close to the edges.
1222
1223   Layer               mActiveLayer;               ///< Layer for active handles and alike that ensures they are above all else.
1224   ImageActor          mPrimaryCursor;
1225   ImageActor          mSecondaryCursor;
1226
1227   Actor               mHighlightActor;        ///< Actor to display highlight
1228   Renderer            mHighlightRenderer;
1229   Material            mHighlightMaterial;         ///< Material used for highlight
1230   Property::Map       mQuadVertexFormat;
1231   Property::Map       mQuadIndexFormat;
1232   PopupImpl           mCopyPastePopup;
1233   TextSelectionPopup::Buttons mEnabledPopupButtons; /// Bit mask of currently enabled Popup buttons
1234   TextSelectionPopupCallbackInterface& mTextSelectionPopupCallbackInterface;
1235
1236   Image               mHandleImages[HANDLE_TYPE_COUNT][HANDLE_IMAGE_TYPE_COUNT];
1237   Vector4             mHandleColor;
1238
1239   CursorImpl          mCursor[CURSOR_COUNT];
1240   HandleImpl          mHandle[HANDLE_TYPE_COUNT];
1241
1242   PropertyBuffer      mQuadVertices;
1243   PropertyBuffer      mQuadIndices;
1244   Geometry            mQuadGeometry;
1245   QuadContainer       mHighlightQuadList;         ///< Sub-selections that combine to create the complete selection highlight
1246
1247   Rect<int>           mBoundingBox;
1248   Vector4             mHighlightColor;            ///< Color of the highlight
1249   Vector2             mHighlightPosition;         ///< The position of the highlight actor.
1250
1251   unsigned int        mActiveCursor;
1252   unsigned int        mCursorBlinkInterval;
1253   float               mCursorBlinkDuration;
1254   HandleType          mHandleScrolling;         ///< The handle which is scrolling.
1255   ScrollDirection     mScrollDirection;         ///< The direction of the scroll.
1256   float               mScrollThreshold;         ///< Defines a square area inside the control, close to the edge. A cursor entering this area will trigger scroll events.
1257   float               mScrollSpeed;             ///< The scroll speed in pixels per second.
1258   float               mScrollDistance;          ///< Distance the text scrolls during a scroll interval.
1259   int                 mTextDepth;               ///< The depth used to render the text.
1260
1261   bool                mActiveCopyPastePopup   : 1;
1262   bool                mCursorBlinkStatus      : 1; ///< Flag to switch between blink on and blink off.
1263   bool                mPrimaryCursorVisible   : 1; ///< Whether the primary cursor is visible.
1264   bool                mSecondaryCursorVisible : 1; ///< Whether the secondary cursor is visible.
1265   bool                mSwapSelectionHandles   : 1; ///< Whether to swap the selection handle images.
1266   bool                mNotifyEndOfScroll      : 1; ///< Whether to notify the end of the scroll.
1267 };
1268
1269 DecoratorPtr Decorator::New( ControllerInterface& controller,
1270                              TextSelectionPopupCallbackInterface& callbackInterface )
1271 {
1272   return DecoratorPtr( new Decorator( controller,
1273                                       callbackInterface ) );
1274 }
1275
1276 void Decorator::SetBoundingBox( const Rect<int>& boundingBox )
1277 {
1278   mImpl->mBoundingBox = boundingBox;
1279 }
1280
1281 const Rect<int>& Decorator::GetBoundingBox() const
1282 {
1283   return mImpl->mBoundingBox;
1284 }
1285
1286 void Decorator::Relayout( const Vector2& size )
1287 {
1288   mImpl->Relayout( size );
1289 }
1290
1291 void Decorator::UpdatePositions( const Vector2& scrollOffset )
1292 {
1293   mImpl->UpdatePositions( scrollOffset );
1294 }
1295
1296 /** Cursor **/
1297
1298 void Decorator::SetActiveCursor( ActiveCursor activeCursor )
1299 {
1300   mImpl->mActiveCursor = activeCursor;
1301 }
1302
1303 unsigned int Decorator::GetActiveCursor() const
1304 {
1305   return mImpl->mActiveCursor;
1306 }
1307
1308 void Decorator::SetPosition( Cursor cursor, float x, float y, float cursorHeight, float lineHeight )
1309 {
1310   mImpl->mCursor[cursor].position.x = x;
1311   mImpl->mCursor[cursor].position.y = y;
1312   mImpl->mCursor[cursor].cursorHeight = cursorHeight;
1313   mImpl->mCursor[cursor].lineHeight = lineHeight;
1314 }
1315
1316 void Decorator::GetPosition( Cursor cursor, float& x, float& y, float& cursorHeight, float& lineHeight ) const
1317 {
1318   x = mImpl->mCursor[cursor].position.x;
1319   y = mImpl->mCursor[cursor].position.y;
1320   cursorHeight = mImpl->mCursor[cursor].cursorHeight;
1321   lineHeight = mImpl->mCursor[cursor].lineHeight;
1322 }
1323
1324 const Vector2& Decorator::GetPosition( Cursor cursor ) const
1325 {
1326   return mImpl->mCursor[cursor].position;
1327 }
1328
1329 void Decorator::SetCursorColor( Cursor cursor, const Dali::Vector4& color )
1330 {
1331   mImpl->mCursor[cursor].color = color;
1332 }
1333
1334 const Dali::Vector4& Decorator::GetColor( Cursor cursor ) const
1335 {
1336   return mImpl->mCursor[cursor].color;
1337 }
1338
1339 void Decorator::StartCursorBlink()
1340 {
1341   if ( !mImpl->mCursorBlinkTimer )
1342   {
1343     mImpl->mCursorBlinkTimer = Timer::New( mImpl->mCursorBlinkInterval );
1344     mImpl->mCursorBlinkTimer.TickSignal().Connect( mImpl, &Decorator::Impl::OnCursorBlinkTimerTick );
1345   }
1346
1347   if ( !mImpl->mCursorBlinkTimer.IsRunning() )
1348   {
1349     mImpl->mCursorBlinkTimer.Start();
1350   }
1351 }
1352
1353 void Decorator::StopCursorBlink()
1354 {
1355   if ( mImpl->mCursorBlinkTimer )
1356   {
1357     mImpl->mCursorBlinkTimer.Stop();
1358   }
1359 }
1360
1361 void Decorator::SetCursorBlinkInterval( float seconds )
1362 {
1363   mImpl->mCursorBlinkInterval = static_cast<unsigned int>( seconds * TO_MILLISECONDS ); // Convert to milliseconds
1364 }
1365
1366 float Decorator::GetCursorBlinkInterval() const
1367 {
1368   return static_cast<float>( mImpl->mCursorBlinkInterval ) * TO_SECONDS;
1369 }
1370
1371 void Decorator::SetCursorBlinkDuration( float seconds )
1372 {
1373   mImpl->mCursorBlinkDuration = seconds;
1374 }
1375
1376 float Decorator::GetCursorBlinkDuration() const
1377 {
1378   return mImpl->mCursorBlinkDuration;
1379 }
1380
1381 /** Handles **/
1382
1383 void Decorator::SetHandleActive( HandleType handleType, bool active )
1384 {
1385   mImpl->mHandle[handleType].active = active;
1386
1387   if( !active )
1388   {
1389     // TODO: this is a work-around.
1390     // The problem is the handle actor does not receive the touch event with the Interrupt
1391     // state when the power button is pressed and the application goes to background.
1392     mImpl->mHandle[handleType].pressed = false;
1393     Image imageReleased = mImpl->mHandleImages[handleType][HANDLE_IMAGE_RELEASED];
1394     ImageActor imageActor = mImpl->mHandle[handleType].actor;
1395     if( imageReleased && imageActor )
1396     {
1397        imageActor.SetImage( imageReleased );
1398     }
1399   }
1400 }
1401
1402 bool Decorator::IsHandleActive( HandleType handleType ) const
1403 {
1404   return mImpl->mHandle[handleType].active ;
1405 }
1406
1407 void Decorator::SetHandleImage( HandleType handleType, HandleImageType handleImageType, Dali::Image image )
1408 {
1409   mImpl->mHandleImages[handleType][handleImageType] = image;
1410 }
1411
1412 Dali::Image Decorator::GetHandleImage( HandleType handleType, HandleImageType handleImageType ) const
1413 {
1414   return mImpl->mHandleImages[handleType][handleImageType];
1415 }
1416
1417 void Decorator::SetHandleColor( const Vector4& color )
1418 {
1419   mImpl->mHandleColor = color;
1420 }
1421
1422 const Vector4& Decorator::GetHandleColor() const
1423 {
1424   return mImpl->mHandleColor;
1425 }
1426
1427 void Decorator::SetPosition( HandleType handleType, float x, float y, float height )
1428 {
1429   // Adjust grab handle displacement
1430   Impl::HandleImpl& handle = mImpl->mHandle[handleType];
1431
1432   handle.grabDisplacementX -= x - handle.position.x;
1433   handle.grabDisplacementY -= y - handle.position.y;
1434
1435   handle.position.x = x;
1436   handle.position.y = y;
1437   handle.lineHeight = height;
1438 }
1439
1440 void Decorator::GetPosition( HandleType handleType, float& x, float& y, float& height ) const
1441 {
1442   Impl::HandleImpl& handle = mImpl->mHandle[handleType];
1443
1444   x = handle.position.x;
1445   y = handle.position.y;
1446   height = handle.lineHeight;
1447 }
1448
1449 const Vector2& Decorator::GetPosition( HandleType handleType ) const
1450 {
1451   return mImpl->mHandle[handleType].position;
1452 }
1453
1454 void Decorator::SwapSelectionHandlesEnabled( bool enable )
1455 {
1456   mImpl->mSwapSelectionHandles = enable;
1457
1458   mImpl->FlipSelectionHandleImages();
1459 }
1460
1461 void Decorator::AddHighlight( float x1, float y1, float x2, float y2 )
1462 {
1463   mImpl->mHighlightQuadList.push_back( QuadCoordinates(x1, y1, x2, y2) );
1464 }
1465
1466 void Decorator::ClearHighlights()
1467 {
1468   mImpl->mHighlightQuadList.clear();
1469   mImpl->mHighlightPosition = Vector2::ZERO;
1470 }
1471
1472 void Decorator::SetHighlightColor( const Vector4& color )
1473 {
1474   mImpl->mHighlightColor = color;
1475 }
1476
1477 const Vector4& Decorator::GetHighlightColor() const
1478 {
1479   return mImpl->mHighlightColor;
1480 }
1481
1482 void Decorator::SetTextDepth( int textDepth )
1483 {
1484   mImpl->mTextDepth = textDepth;
1485 }
1486
1487 void Decorator::SetPopupActive( bool active )
1488 {
1489   mImpl->mActiveCopyPastePopup = active;
1490 }
1491
1492 bool Decorator::IsPopupActive() const
1493 {
1494   return mImpl->mActiveCopyPastePopup ;
1495 }
1496
1497 void Decorator::SetEnabledPopupButtons( TextSelectionPopup::Buttons& enabledButtonsBitMask )
1498 {
1499    mImpl->mEnabledPopupButtons = enabledButtonsBitMask;
1500
1501    UnparentAndReset( mImpl->mCopyPastePopup.actor );
1502    mImpl->mCopyPastePopup.actor = TextSelectionPopup::New( mImpl->mEnabledPopupButtons,
1503                                                            &mImpl->mTextSelectionPopupCallbackInterface );
1504 #ifdef DECORATOR_DEBUG
1505    mImpl->mCopyPastePopup.actor.SetName("mCopyPastePopup");
1506 #endif
1507    mImpl->mCopyPastePopup.actor.SetAnchorPoint( AnchorPoint::CENTER );
1508    mImpl->mCopyPastePopup.actor.OnRelayoutSignal().Connect( mImpl,  &Decorator::Impl::PopupRelayoutComplete  ); // Position popup after size negotiation
1509
1510    if( mImpl->mActiveLayer )
1511    {
1512      mImpl->mActiveLayer.Add( mImpl->mCopyPastePopup.actor );
1513      mImpl->mCopyPastePopup.actor.ShowPopup();
1514    }
1515 }
1516
1517 TextSelectionPopup::Buttons& Decorator::GetEnabledPopupButtons()
1518 {
1519   return mImpl->mEnabledPopupButtons;
1520 }
1521
1522 /** Scroll **/
1523
1524 void Decorator::SetScrollThreshold( float threshold )
1525 {
1526   mImpl->SetScrollThreshold( threshold );
1527 }
1528
1529 float Decorator::GetScrollThreshold() const
1530 {
1531   return mImpl->GetScrollThreshold();
1532 }
1533
1534 void Decorator::SetScrollSpeed( float speed )
1535 {
1536   mImpl->SetScrollSpeed( speed );
1537 }
1538
1539 float Decorator::GetScrollSpeed() const
1540 {
1541   return mImpl->GetScrollSpeed();
1542 }
1543
1544 void Decorator::NotifyEndOfScroll()
1545 {
1546   mImpl->NotifyEndOfScroll();
1547 }
1548
1549 Decorator::~Decorator()
1550 {
1551   delete mImpl;
1552 }
1553
1554 Decorator::Decorator( ControllerInterface& controller,
1555                       TextSelectionPopupCallbackInterface& callbackInterface )
1556 : mImpl( NULL )
1557 {
1558   mImpl = new Decorator::Impl( controller, callbackInterface );
1559 }
1560
1561 } // namespace Text
1562
1563 } // namespace Toolkit
1564
1565 } // namespace Dali