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