TextInput Adding new Handles class and matching API in Decorator
[platform/core/uifw/dali-toolkit.git] / base / dali-toolkit / internal / controls / text-input / text-input-decorator-impl.cpp
1 //
2 // Copyright (c) 2014 Samsung Electronics Co., Ltd.
3 //
4 // Licensed under the Flora License, Version 1.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://floralicense.org/license/
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 #include <dali/integration-api/debug.h>
18
19 // INTERNAL INCLUDES
20 #include <dali-toolkit/internal/controls/text-input/text-input-decorator-impl.h>
21
22 #include <dali-toolkit/internal/controls/text-input/text-input-handles-impl.h>
23
24 using namespace Dali;
25
26 namespace
27 {
28 #if defined(DEBUG_ENABLED)
29   Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_TEXT_INPUT_DECORATOR");
30 #endif
31
32 const Vector3 DEFAULT_SELECTION_HANDLE_SIZE( 51.0f, 79.0f, 0.0f );
33 const float TOP_HANDLE_TOP_OFFSET(-1.5f);                                   // Offset between top handle and cutCopyPaste pop-up
34 const float BOTTOM_HANDLE_BOTTOM_OFFSET(1.5f);                              // Offset between bottom handle and cutCopyPaste pop-up
35 const float UI_Z_OFFSET( 0.2f );                                            // Text Selection Handles/Cursor z-offset.
36 const Vector3 UI_OFFSET(0.0f, 0.0f, UI_Z_OFFSET);                           // Text Selection Handles/Cursor offset.
37 const char* DEFAULT_CURSOR( DALI_IMAGE_DIR "cursor.png" );
38 const Vector4 DEFAULT_CURSOR_IMAGE_9_BORDER( 2.0f, 2.0f, 2.0f, 2.0f );
39 const std::size_t CURSOR_BLINK_INTERVAL = 500;                              // Cursor blink interval
40 const float CURSOR_THICKNESS(6.0f);
41 const Degree CURSOR_ANGLE_OFFSET(2.0f);                                     // Offset from the angle
42
43 const unsigned int SCROLL_TICK_INTERVAL = 50u;
44 const float SCROLL_THRESHOLD = 10.f;
45 const float SCROLL_SPEED = 15.f;
46
47 /**
48  * Whether the given position plus the cursor size offset is inside the given boundary.
49  *
50  * @param[in] position The given position.
51  * @param[in] cursorSize The cursor size.
52  * @param[in] controlSize The given boundary.
53  * @param[in] threshold imaginary indent around boundary that will trigger the position to be outside of control.
54  *
55  * @return whether the given position is inside the given boundary.
56  */
57 bool IsPositionWithinControl( const Vector3& position, const Size& cursorSize, const Vector3& controlSize, const Vector2 threshold = Vector2::ZERO )
58 {
59   return ( position.x >= -Math::MACHINE_EPSILON_1000 + threshold.x ) &&
60          ( position.x <= controlSize.width - threshold.x + Math::MACHINE_EPSILON_1000 ) &&
61          ( position.y - cursorSize.height >= -Math::MACHINE_EPSILON_1000 + threshold.y ) &&
62          ( position.y <= controlSize.height + Math::MACHINE_EPSILON_1000 - threshold.y);
63 }
64
65 }
66
67 namespace Dali
68 {
69
70 namespace Toolkit
71 {
72
73 namespace Internal
74 {
75
76 Decorator::Decorator( TextViewCharacterPositioning& textViewManager, TextInputTextStyle& textStyle ):
77   mTextViewCharacterPositioning( textViewManager ),
78   mTextStyle( textStyle ),
79   mCursorPosition( 0 ),
80   mTextHighlight( textViewManager ),
81   mCursorBlinkStatus( true ),
82   mCursorVisibility( true ),
83   mGrabHandleEnabled( true )
84 {
85 }
86
87 Decorator::~Decorator()
88 {
89 }
90
91 /**
92  * Bounding Box
93  */
94 void Decorator::SetBoundingBox( const Rect<float>& boundingRectangle )
95 {
96   // Convert to world coordinates and store as a Vector4 to be compatible with Property Notifications.
97   Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
98
99   const float originX = boundingRectangle.x - 0.5f * stageSize.width;
100   const float originY = boundingRectangle.y - 0.5f * stageSize.height;
101
102   const Vector4 boundary( originX,
103                           originY,
104                           originX + boundingRectangle.width,
105                           originY + boundingRectangle.height );
106
107   mBoundingRectangleWorldCoordinates = boundary;
108 }
109
110 Vector4 Decorator::GetBoundingBox() const
111 {
112   return mBoundingRectangleWorldCoordinates;
113 }
114
115 /**
116  * Selection Handles
117  */
118 void Decorator::OnHandlePan(Actor actor, PanGesture gesture)
119 {
120   Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
121   Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
122
123   switch (gesture.state)
124   {
125     case Gesture::Started:
126     // fall through so code not duplicated
127     case Gesture::Continuing:
128     {
129       if ( actor.GetParent() == mTextInputHandles.GetSelectionHandleOne() )
130       {
131         MoveSelectionHandle( selectionHandleOne, mSelectionHandleOneActualPosition, mSelectionHandleOnePosition, gesture.displacement );
132         HidePopUp();
133       }
134       else if ( actor.GetParent() == mTextInputHandles.GetSelectionHandleTwo() )
135       {
136         MoveSelectionHandle( selectionHandleTwo, mSelectionHandleTwoActualPosition, mSelectionHandleTwoPosition, gesture.displacement );
137         HidePopUp();
138       }
139       else if ( actor.GetParent() == mTextInputHandles.GetGrabHandle() )
140       {
141         SetCursorVisibility( true );
142         ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
143         MoveGrabHandle( gesture.displacement );
144         HidePopUp(); // Do not show popup while handle is moving
145       }
146     }
147     break;
148
149     case Gesture::Finished:
150     {
151       // Revert back to non-pressed selection handle images
152       if ( actor.GetParent() == mTextInputHandles.GetSelectionHandleOne() )
153       {
154          mSelectionHandleOneActualPosition = MoveSelectionHandle( selectionHandleOne, mSelectionHandleOneActualPosition, mSelectionHandleOnePosition, gesture.displacement );
155         ShowPopupCutCopyPaste();
156       }
157       else if ( actor.GetParent() == mTextInputHandles.GetSelectionHandleTwo() )
158       {
159         mSelectionHandleTwoActualPosition = MoveSelectionHandle( selectionHandleTwo, mSelectionHandleTwoActualPosition, mSelectionHandleTwoPosition, gesture.displacement );
160         ShowPopupCutCopyPaste();
161       }
162       else if ( actor.GetParent() == mTextInputHandles.GetGrabHandle() )
163       {
164         MoveGrabHandle( gesture.displacement );
165         SetCursorVisibility( true );
166         ShowPopupCutCopyPaste();
167       }
168     }
169     break;
170     default:
171       break;
172   }
173 }
174
175 void Decorator::CreateSelectionHandles( Actor targetParent )
176 {
177   if ( !mPanGestureDetector )
178   {
179     mPanGestureDetector = PanGestureDetector::New();
180     mPanGestureDetector.DetectedSignal().Connect(this, &Decorator::OnHandlePan);
181   }
182
183   if ( !mTextInputHandles.GetSelectionHandleOne() )
184   {
185     mTextInputHandles.CreateSelectionHandles();
186
187     mTextInputHandles.AttachSelectionHandlesToGivenPanGesture( mPanGestureDetector );
188
189     targetParent.Add( mTextInputHandles.GetSelectionHandleOne() );
190     targetParent.Add( mTextInputHandles.GetSelectionHandleTwo() );
191
192     SetUpHandlePropertyNotifications();
193   }
194 }
195
196 void Decorator::RemoveSelectionHandles()
197 {
198   mTextInputHandles.DestorySelectionHandles();
199 }
200
201 Vector3 Decorator::GetSelectionHandleSize()
202 {
203   return DEFAULT_SELECTION_HANDLE_SIZE;
204 }
205
206 std::size_t Decorator::GetHandleOnePosition() const
207 {
208   return mSelectionHandleOnePosition;
209 }
210
211 std::size_t Decorator::GetHandleTwoPosition() const
212 {
213   return mSelectionHandleTwoPosition;
214 }
215
216 Vector3 Decorator::PositionSelectionHandle( Actor selectionHandle, std::size_t position )
217 {
218   bool direction(false);
219   Vector3 alternatePosition;
220   bool alternatePositionValid(false);
221
222   Vector3 actualPositionOfSelectionHandle = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( position, direction, alternatePosition,alternatePositionValid );
223
224   return PositionSelectionHandle( selectionHandle, actualPositionOfSelectionHandle, position );
225
226 }
227
228 Vector3 Decorator::PositionSelectionHandle( Actor selectionHandle, Vector3& actualPosition, std::size_t position )
229 {
230   const Vector3 DEFAULT_HANDLE_OFFSET(0.0f, -5.0f, 0.0f);
231
232   selectionHandle.SetPosition( actualPosition += DEFAULT_HANDLE_OFFSET );
233
234   if( mTextViewCharacterPositioning.IsScrollEnabled() )
235   {
236     const Vector3 controlSize = mTextViewCharacterPositioning.GetTextView().GetCurrentSize();
237     const Size cursorSize( GetCursorSizeAt( position ) );
238     bool handleVisible = IsPositionWithinControl( actualPosition, Vector2(DEFAULT_HANDLE_OFFSET.width, DEFAULT_HANDLE_OFFSET.height), controlSize );
239
240     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::PositionSelectionHandle controlSize[%f,%f] cursorSize[%f,%f] actualPos[%f,%f] visible[%s] \n",
241               controlSize.x, controlSize.y, cursorSize.x, cursorSize.y, actualPosition.x, actualPosition.y, ( handleVisible )?"true":"false" );
242
243     selectionHandle.SetVisible( handleVisible );
244   }
245   return actualPosition;
246 }
247
248 void Decorator::SetSelectionHandlesVisibility(bool visible )
249 {
250   mTextInputHandles.SetSelectionHandleOneVisibility( visible );
251   mTextInputHandles.SetSelectionHandleTwoVisibility( visible );
252 }
253
254 void Decorator::PositionSelectionHandles( std::size_t start, std::size_t end )
255 {
256   mSelectionHandleOnePosition = start;
257   mSelectionHandleTwoPosition = end;
258
259   mTextViewCharacterPositioning.UpdateTextLayoutInfo();
260
261   mSelectionHandleOneActualPosition = PositionSelectionHandle( mTextInputHandles.GetSelectionHandleOne(), mSelectionHandleOnePosition );
262   mSelectionHandleTwoActualPosition = PositionSelectionHandle( mTextInputHandles.GetSelectionHandleTwo(), mSelectionHandleTwoPosition );
263 }
264
265 Vector3 Decorator::MoveSelectionHandle( Actor selectionHandle,
266                                                           Vector3& actualSelectionHandlePosition,
267                                                           std::size_t& currentSelectionHandlePosition,
268                                                           const Vector2& displacement )
269 {
270   Vector3 actualHandlePosition;
271   actualSelectionHandlePosition.x += displacement.x * selectionHandle.GetCurrentScale().x;
272   actualSelectionHandlePosition.y += displacement.y * selectionHandle.GetCurrentScale().y;;
273
274   // Selection handles should jump to the nearest character
275   std::size_t newHandlePosition = 0;
276   newHandlePosition = mTextViewCharacterPositioning.ReturnClosestIndex( actualSelectionHandlePosition.GetVectorXY() );
277
278   bool direction(false);
279   Vector3 alternatePosition;
280   bool alternatePositionValid(false);
281   actualHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newHandlePosition,direction, alternatePosition, alternatePositionValid  );
282
283   bool handleVisible = true;
284
285   Vector2 min, max;
286   if( mTextViewCharacterPositioning.IsScrollEnabled() )
287   {
288     const Vector3 controlSize = mTextViewCharacterPositioning.GetTextView().GetCurrentSize();
289     const Size cursorSize( GetCursorSizeAt( newHandlePosition ) );
290
291     handleVisible = IsPositionWithinControl( actualHandlePosition,
292                                              cursorSize,
293                                              controlSize );
294
295     if( handleVisible )
296     {
297       StopScrollTimer();
298       mCurrentHandlePosition = actualHandlePosition;
299       mScrollDisplacement = Vector2::ZERO;
300     }
301     else
302     {
303
304       if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
305       {
306         mScrollDisplacement.x = -SCROLL_SPEED;
307       }
308       else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
309       {
310         mScrollDisplacement.x = SCROLL_SPEED;
311       }
312       if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
313       {
314         mScrollDisplacement.y = -SCROLL_SPEED;
315       }
316       else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
317       {
318         mScrollDisplacement.y = SCROLL_SPEED;
319       }
320       DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::MoveSelectionHandle Handle not visible scroll displacement [%f]\n", mScrollDisplacement.x);
321
322       StartScrollTimer();
323    }
324   }
325
326   if ( handleVisible &&                                          // Ensure the handle is visible.
327      ( newHandlePosition != currentSelectionHandlePosition ) &&  // Ensure the handle has moved.
328      ( newHandlePosition != mSelectionHandleTwoPosition ) &&     // Ensure new handle position not the same position as an existing handle.
329      ( newHandlePosition != mSelectionHandleOnePosition ) )
330   {
331     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::MoveSelectionHandle Handle visible and moved]\n");
332
333     currentSelectionHandlePosition = newHandlePosition;
334
335     PositionSelectionHandle( selectionHandle, actualHandlePosition, newHandlePosition );
336
337     ShowUpdatedHighlight();
338
339     // Set Active Style to that of first character in selection
340     std::size_t firstHandleInSelection = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
341
342     const TextStyle inputStyle = mTextViewCharacterPositioning.GetStyleAt( firstHandleInSelection );
343     mTextStyle.SetInputStyle( inputStyle );
344   }
345   return actualHandlePosition; // Returns Handle position passed in if new value not assigned.
346 }
347
348 /**
349  * GrabHandle
350  */
351 void Decorator::PositionGrabHandle( std::size_t positionInText )
352 {
353   bool direction(false);
354   Vector3 alternatePosition;
355   bool alternatePositionValid(false);
356
357   mGrabHandlePosition = positionInText;
358
359   mTextViewCharacterPositioning.UpdateTextLayoutInfo();
360   mActualGrabHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( positionInText, direction, alternatePosition,alternatePositionValid );
361
362   mTextInputHandles.GetGrabHandle().SetPosition( mActualGrabHandlePosition );
363 }
364
365 void Decorator::MoveGrabHandle( const Vector2& displacement /*, std::size_t currentHandlePosition */)
366 {
367   mActualGrabHandlePosition.x += displacement.x;
368   mActualGrabHandlePosition.y += displacement.y;
369
370   // Grab handle should jump to the nearest character and take cursor with it
371   std::size_t newHandlePosition = mTextViewCharacterPositioning.ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY() );
372
373   Vector3 actualHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newHandlePosition );
374
375   bool handleVisible = true;
376
377   if( mTextViewCharacterPositioning.IsScrollEnabled() )
378   {
379     const Vector3 controlSize = mTextViewCharacterPositioning.GetTextView().GetCurrentSize();
380     const Size cursorSize( GetCursorSizeAt( newHandlePosition ) );
381     // Scrolls the text if the handle is not in a visible position
382     handleVisible = IsPositionWithinControl( actualHandlePosition,
383                                                 cursorSize,
384                                                 controlSize );
385
386     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::MoveGrabHandle handleVisible[%s]\n", ( handleVisible )?"true":"false");
387
388     if( handleVisible )
389     {
390       StopScrollTimer();
391       mCurrentHandlePosition = actualHandlePosition;
392       mScrollDisplacement = Vector2::ZERO;
393     }
394     else
395     {
396       if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
397       {
398         mScrollDisplacement.x = -SCROLL_SPEED;
399       }
400       else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
401       {
402         mScrollDisplacement.x = SCROLL_SPEED;
403       }
404       if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
405       {
406         mScrollDisplacement.y = -SCROLL_SPEED;
407       }
408       else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
409       {
410         mScrollDisplacement.y = SCROLL_SPEED;
411       }
412       StartScrollTimer();
413     }
414   }
415
416     if( ( newHandlePosition != mGrabHandlePosition ) &&                           // Only redraw cursor and do updates if position changed
417         ( handleVisible ) )// and the new position is visible (if scroll is not enabled, it's always true).
418     {
419       mActualGrabHandlePosition = actualHandlePosition;
420       mTextInputHandles.GetGrabHandle().SetPosition( mActualGrabHandlePosition );
421
422       //PositionGrabHandle( newHandlePosition );
423       mGrabHandlePosition = newHandlePosition;
424       SetCurrentCursorPosition( mGrabHandlePosition );
425       DrawCursor( mGrabHandlePosition );
426
427       const std::size_t cursorPosition = GetCurrentCursorPosition();
428
429       // Let keyboard know the new cursor position so can 're-capture' for prediction.
430       mCursorRePositionedSignal.Emit();
431
432       // Set Input Style to that of cursor position
433       if ( !mTextViewCharacterPositioning.IsStyledTextEmpty()  && ( cursorPosition > 0 ) )
434       {
435         DALI_ASSERT_DEBUG( ( 0 <= cursorPosition-1 ) && ( cursorPosition-1 < mTextViewCharacterPositioning.StyledTextSize() ) );
436         const TextStyle inputStyle = mTextViewCharacterPositioning.GetStyleAt( cursorPosition-1 );
437         mTextStyle.SetInputStyle( inputStyle );
438       }
439     }
440 }
441
442 void Decorator::ShowGrabHandle( bool visible )
443 {
444   mGrabHandleVisibility = visible;
445   mTextInputHandles.SetGrabHandleVisibility( visible );
446 }
447
448 void Decorator::CreateGrabHandle( Actor targetParent )
449 {
450   if ( !mPanGestureDetector )
451   {
452     mPanGestureDetector = PanGestureDetector::New();
453     mPanGestureDetector.DetectedSignal().Connect(this, &Decorator::OnHandlePan);
454   }
455
456   if ( !mTextInputHandles.GetGrabHandle() )
457   {
458     mTextInputHandles.CreateGrabHandle();
459     mTextInputHandles.AttachGrabHandleToGivenPanGesture( mPanGestureDetector );
460     targetParent.Add( mTextInputHandles.GetGrabHandle() );
461   }
462 }
463
464 void Decorator::SetGrabHandleImage( Image image )
465 {
466   mTextInputHandles.SetGrabHandleImage( image );
467 }
468
469 void Decorator::EnableGrabHandle( bool toggle)
470 {
471   // enables grab handle with will in turn de-activate magnifier
472   mGrabHandleEnabled = toggle;
473 }
474
475 bool Decorator::IsGrabHandleEnabled()
476 {
477   // if false then magnifier will be shown instead.
478   return mGrabHandleEnabled;
479 }
480
481 /**
482  * Cursor
483  */
484 std::size_t Decorator::GetCurrentCursorPosition() const
485 {
486   return mCursorPosition;
487 }
488
489 void Decorator::SetCurrentCursorPosition( std::size_t newCursorPosition )
490 {
491   mCursorPosition = newCursorPosition;
492 }
493
494 void Decorator::SetCursorVisibility( bool visible )
495 {
496   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::SetCursorVisibility[%s]\n", ( visible )?"true":"false");
497
498   mCursorVisibility = visible;
499   mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
500   mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
501 }
502
503 void Decorator::DrawCursor(const std::size_t nthChar)
504 {
505   std::size_t cursorPosition = GetCurrentCursorPosition();
506
507   // Get height of cursor and set its size
508   Size size( CURSOR_THICKNESS, 0.0f );
509   if ( !mTextViewCharacterPositioning.IsTextEmpty() )
510   {
511     Vector2 min, max; // out parameters for GetRowRectFromCharacterPosition
512     size.height = mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( mTextViewCharacterPositioning.GetVisualPosition( cursorPosition ), min, max  ).height;
513   }
514   else
515   {
516     // Measure Font so know how big text will be if no initial text to measure.
517     size.height = mTextViewCharacterPositioning.GetLineHeight( nthChar );
518   }
519
520   mCursor.SetSize(size);
521
522   // If the character is italic then the cursor also tilts.
523   if ( !mTextViewCharacterPositioning.IsStyledTextEmpty()  && ( cursorPosition > 0 ) )
524   {
525     DALI_ASSERT_DEBUG( ( 0 <= cursorPosition-1 ) && ( cursorPosition-1 < mTextViewCharacterPositioning.StyledTextSize() ) );
526     const TextStyle styleAtCursor = mTextViewCharacterPositioning.GetStyleAt( cursorPosition-1 );
527     mCursor.SetRotation( styleAtCursor.GetItalics() ? Degree( styleAtCursor.GetItalicsAngle() - CURSOR_ANGLE_OFFSET ) : Degree( 0.f ), Vector3::ZAXIS );
528   }
529
530   DALI_ASSERT_DEBUG( cursorPosition <= mTextViewCharacterPositioning.GetNumberOfCharactersInText() );
531   if ( ( cursorPosition <= mTextViewCharacterPositioning.GetNumberOfCharactersInText() ) )
532   {
533     Vector3 altPosition;    // Alternate (i.e. opposite direction) cursor position.
534     bool altPositionValid( false );  // Alternate cursor validity flag.
535     bool directionRTL( false );      // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
536     Vector3 position = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( cursorPosition, directionRTL, altPosition, altPositionValid );
537
538     SetAltCursorEnabled( altPositionValid );
539
540     if(!altPositionValid)
541     {
542       mCursor.SetPosition( position + UI_OFFSET );
543     }
544     else
545     {
546       size.height *= 0.5f;
547       mCursor.SetSize(size);
548       mCursor.SetPosition( position + UI_OFFSET - Vector3(0.0f, directionRTL ? 0.0f : size.height, 0.0f) );
549       Vector2 min, max; // out parameters for GetRowRectFromCharacterPosition
550       Size rowSize = mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( mTextViewCharacterPositioning.GetVisualPosition( cursorPosition ), min, max );
551       size.height = rowSize.height * 0.5f;
552       mCursorRTL.SetSize(size);
553       mCursorRTL.SetPosition( altPosition + UI_OFFSET - Vector3(0.0f, directionRTL ? size.height : 0.0f, 0.0f) );
554     }
555
556     if( mTextViewCharacterPositioning.IsScrollEnabled() )
557     {
558       // Whether cursor and grab handle are inside the boundaries of the text-input when text scroll is enabled.
559       const Vector3& controlSize = mTextViewCharacterPositioning.GetTextView().GetCurrentSize();
560       mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionWithinControl( position, size, controlSize );
561     }
562   }
563 }
564
565 void Decorator::SetAltCursorEnabled( bool enabled )
566 {
567   mCursorRTLEnabled = enabled;
568   mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
569 }
570
571 void Decorator::SetCursorImage(Dali::Image image, const Vector4& border )
572 {
573   DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
574
575   if ( image )
576   {
577     mCursor.SetImage( image );
578     mCursor.SetNinePatchBorder( border );
579   }
580 }
581
582 void Decorator::SetRTLCursorImage( Image image, const Vector4& border )
583 {
584   DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
585
586   if ( image )
587   {
588     mCursorRTL.SetImage( image );
589     mCursorRTL.SetNinePatchBorder(  border );
590   }
591 }
592
593 ImageActor Decorator::CreateCursor( Image cursorImage, const Vector4& border, const std::string& cursorName )
594 {
595   ImageActor cursor;
596
597   if ( cursorImage )
598   {
599     cursor = ImageActor::New( cursorImage );
600   }
601   else
602   {
603     cursor = ImageActor::New( Image::New( DEFAULT_CURSOR ) );
604   }
605
606   cursor.SetStyle(ImageActor::STYLE_NINE_PATCH);
607   cursor.SetNinePatchBorder( border );
608   cursor.SetAnchorPoint(AnchorPoint::BOTTOM_CENTER);
609   cursor.SetVisible(false);
610   cursor.SetName( cursorName );
611   return cursor;
612 }
613
614 void Decorator::CreateCursors( Actor targetParent )
615 {
616   Image mCursorImage = Image::New( DEFAULT_CURSOR );
617   mCursor = CreateCursor (mCursorImage, DEFAULT_CURSOR_IMAGE_9_BORDER , "mainCursor");
618   mCursorRTL = CreateCursor ( mCursorImage, DEFAULT_CURSOR_IMAGE_9_BORDER, "rtlCursor");
619   targetParent.Add( mCursor );
620   targetParent.Add( mCursorRTL );
621 }
622
623 Size Decorator::GetCursorSizeAt( std::size_t positionWithinTextToGetCursorSize )
624 {
625   std::size_t visualPosition = mTextViewCharacterPositioning.GetVisualPosition( positionWithinTextToGetCursorSize );
626
627   Vector2 min, max;
628
629   const Size cursorSize( CURSOR_THICKNESS,
630       mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( visualPosition, min, max ).height );
631
632   return cursorSize;
633 }
634
635 void Decorator::StartCursorBlinkTimer()
636 {
637   if ( !mCursorBlinkTimer )
638   {
639     mCursorBlinkTimer = Timer::New( CURSOR_BLINK_INTERVAL );
640     mCursorBlinkTimer.TickSignal().Connect( this, &Decorator::OnCursorBlinkTimerTick );
641   }
642
643   if ( !mCursorBlinkTimer.IsRunning() )
644   {
645     mCursorBlinkTimer.Start();
646   }
647 }
648
649 void Decorator::StopCursorBlinkTimer()
650 {
651   if ( mCursorBlinkTimer )
652   {
653     mCursorBlinkTimer.Stop();
654   }
655 }
656
657 bool Decorator::OnCursorBlinkTimerTick()
658 {
659   // Cursor blinking
660   mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
661   if ( mCursorRTLEnabled )
662   {
663     mCursorRTL.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
664   }
665   mCursorBlinkStatus = !mCursorBlinkStatus;
666
667   return true;
668 }
669
670 /**
671  * Highlight
672  */
673 void Decorator::ShowUpdatedHighlight()
674 {
675   Toolkit::TextView::TextLayoutInfo textLayoutInfo = mTextViewCharacterPositioning.GetLayoutInfo();
676   TextHighlight::HighlightInfo highlightInfo = mTextHighlight.CalculateHighlightInfo( mSelectionHandleOnePosition, mSelectionHandleTwoPosition, textLayoutInfo );
677
678   // Clamp highlightInfo so they don't exceed the boundary of the control.
679   const Vector3& controlSize = mTextViewCharacterPositioning.GetTextView().GetCurrentSize();
680   highlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
681
682   mTextHighlight.UpdateHighlight( highlightInfo );
683 }
684
685 void Decorator::CreateHighlight( Actor parent )
686 {
687   DALI_ASSERT_DEBUG( parent && "Highlight target parent does not exist" );
688
689   if ( !mHighlightMeshActor )
690   {
691     mHighlightMeshActor = MeshActor::New( mTextHighlight.CreateHighLightMesh() );
692     mHighlightMeshActor.SetName( "HighlightMeshActor" );
693     mHighlightMeshActor.SetInheritShaderEffect( false );
694     mHighlightMeshActor.SetAffectedByLighting(false);
695     parent.Add( mHighlightMeshActor );
696   }
697 }
698
699 void Decorator::RemoveHighlight()
700 {
701   if ( mHighlightMeshActor )
702   {
703     mHighlightMeshActor.Unparent();
704     mHighlightMeshActor.Reset();
705     // NOTE: We cannot dereference mHighlightMesh, due to a how the scene-graph MeshRenderer uses the Mesh data.
706   }
707 }
708
709 void Decorator::HighlightVisibility( bool visiblility )
710 {
711   if ( mHighlightMeshActor )
712   {
713     mHighlightMeshActor.SetVisible( visiblility );
714   }
715 }
716
717 /**
718  *  Callbacks connected to be Property notifications for Boundary checking.
719  */
720 // Note If PropertyNotification signal definition included Actor we would not need to duplicate functions.
721 void Decorator::OnHandleOneLeavesBoundary( PropertyNotification& source)
722 {
723   mTextInputHandles.GetSelectionHandleOne().SetOpacity(0.0f);
724 }
725
726 void Decorator::OnHandleOneWithinBoundary(PropertyNotification& source)
727 {
728   mTextInputHandles.GetSelectionHandleOne().SetOpacity(1.0f);
729 }
730
731 void Decorator::OnHandleTwoLeavesBoundary( PropertyNotification& source)
732 {
733   mTextInputHandles.GetSelectionHandleTwo().SetOpacity(0.0f);
734 }
735
736 void Decorator::OnHandleTwoWithinBoundary(PropertyNotification& source)
737 {
738   mTextInputHandles.GetSelectionHandleTwo().SetOpacity(1.0f);
739 }
740
741 void Decorator::OnLeftBoundaryExceeded(PropertyNotification& source)
742 {
743   DALI_LOG_INFO(gLogFilter, Debug::General, "TextInputDecorationLayouter::OnLeftBoundaryExceeded\n");
744   Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
745   selectionHandleOne.SetScale( -1.0f, 1.0f, 1.0f );
746   selectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_LEFT);
747 }
748
749 void Decorator::OnReturnToLeftBoundary(PropertyNotification& source)
750 {
751   DALI_LOG_INFO(gLogFilter, Debug::General, "TextInputDecorationLayouter::OnReturnToLeftBoundary\n");
752   Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
753   selectionHandleOne.SetScale( 1.0f, 1.0f, 1.0f );
754   selectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
755 }
756
757 void Decorator::OnRightBoundaryExceeded(PropertyNotification& source)
758 {
759   Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
760   selectionHandleTwo.SetScale( -1.0f, 1.0f, 1.0f );
761   selectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
762 }
763
764 void Decorator::OnReturnToRightBoundary(PropertyNotification& source)
765 {
766   Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
767   selectionHandleTwo.SetScale( 1.0f, 1.0f, 1.0f );
768   selectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT);
769 }
770
771 void Decorator::SetUpHandlePropertyNotifications()
772 {
773   /* Property notifications for handles exceeding the boundary and returning back within boundary */
774
775   Vector3 handlesize = GetSelectionHandleSize();
776
777   Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
778   Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
779
780   // Exceeding horizontal boundary
781   PropertyNotification leftNotification = selectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.x + handlesize.x) );
782   leftNotification.NotifySignal().Connect( this, &Decorator::OnLeftBoundaryExceeded );
783
784   PropertyNotification rightNotification = selectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.z - handlesize.x ) );
785   rightNotification.NotifySignal().Connect( this, &Decorator::OnRightBoundaryExceeded );
786
787   // Within horizontal boundary
788   PropertyNotification leftLeaveNotification = selectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.x + 2*handlesize.x ) );
789   leftLeaveNotification.NotifySignal().Connect( this, &Decorator::OnReturnToLeftBoundary );
790
791   PropertyNotification rightLeaveNotification = selectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.z - 2*handlesize.x ) );
792   rightLeaveNotification.NotifySignal().Connect( this, &Decorator::OnReturnToRightBoundary );
793
794   // Exceeding vertical boundary
795   PropertyNotification verticalExceedNotificationOne = selectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
796                                                        OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
797                                                                          mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
798   verticalExceedNotificationOne.NotifySignal().Connect( this, &Decorator::OnHandleOneLeavesBoundary );
799
800   PropertyNotification verticalExceedNotificationTwo = selectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
801                                                        OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
802                                                                          mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
803   verticalExceedNotificationTwo.NotifySignal().Connect( this, &Decorator::OnHandleTwoLeavesBoundary );
804
805   // Within vertical boundary
806   PropertyNotification verticalWithinNotificationOne = selectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
807                                                        InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
808                                                                         mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
809   verticalWithinNotificationOne.NotifySignal().Connect( this, &Decorator::OnHandleOneWithinBoundary );
810
811   PropertyNotification verticalWithinNotificationTwo = selectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
812                                                        InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
813                                                                         mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
814   verticalWithinNotificationTwo.NotifySignal().Connect( this, &Decorator::OnHandleTwoWithinBoundary );
815 }
816
817 /**
818  * PopUp
819  */
820 Vector3 Decorator::PositionOfPopUpRelativeToSelectionHandles()
821 {
822   Vector3 position;
823   Vector2 min, max;
824   Vector3 topHandle;
825   Size rowSize;
826
827   // When text is selected, show popup above top handle (and text), or below bottom handle.
828
829   // topHandle: referring to the top most point of the handle or the top line of selection.
830   if ( mSelectionHandleTwoActualPosition.y > mSelectionHandleOneActualPosition.y ) // Handle may switch positions so calculate which is top.
831   {
832     topHandle = mSelectionHandleOneActualPosition;
833     rowSize= mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( mSelectionHandleOnePosition, min, max );
834   }
835   else
836   {
837     topHandle = mSelectionHandleTwoActualPosition;
838     rowSize = mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( mSelectionHandleTwoPosition, min, max );
839   }
840   topHandle.y += TOP_HANDLE_TOP_OFFSET - rowSize.height;
841   position = Vector3(topHandle.x, topHandle.y, 0.0f);
842
843   return position;
844 }
845
846 Vector3 Decorator::AlternatePopUpPositionRelativeToSelectionHandles()
847 {
848   // alternativePosition: referring to the bottom most point of the handle or the bottom line of selection.
849   Vector3 alternativePosition;
850   alternativePosition.y = std::max ( mSelectionHandleTwoActualPosition.y , mSelectionHandleOneActualPosition.y );
851   alternativePosition.y += GetSelectionHandleSize().y + mPopUpPanel.GetSize().y + BOTTOM_HANDLE_BOTTOM_OFFSET;
852
853   return alternativePosition;
854 }
855
856 Vector3 Decorator::PositionOfPopUpRelativeToCursor()
857 {
858   // When no text is selected, show PopUp at position of cursor
859   Vector3 position;
860   Vector2 min, max;
861   std::size_t cursorPosition = GetCurrentCursorPosition();
862   position = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( cursorPosition );
863   const Size rowSize = mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( cursorPosition, min, max );
864   position.y -= rowSize.height;
865
866   return position;
867 }
868
869 Vector3 Decorator::AlternatePopUpPositionRelativeToCursor()
870 {
871   std::size_t cursorPosition = GetCurrentCursorPosition();
872   Vector3 alternativePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( cursorPosition );
873
874   if (  mTextInputHandles.GetGrabHandle() )
875   {
876     // If grab handle enabled then position pop-up below the grab handle.
877     alternativePosition.y +=  mTextInputHandles.GetGrabHandle().GetCurrentSize().height  + mPopUpPanel.GetSize().y + BOTTOM_HANDLE_BOTTOM_OFFSET ;
878   }
879   else
880   {
881     alternativePosition.y += mPopUpPanel.GetSize().y;
882   }
883
884   return alternativePosition;
885
886 }
887
888 Vector3 Decorator::PositionOfPopUpRelativeToGrabHandle()
889 {
890    return Vector3::ZERO;
891 }
892
893 void Decorator::ShowPopUp()
894 {
895   Vector3 position;
896   Vector3 alternativePosition;
897   Size rowSize;
898
899   DALI_ASSERT_DEBUG( mPopUpTarget && "PopUp Target Actor does not exist" );
900
901   if( mHighlightMeshActor ) // Text Selection mode
902   {
903     position = PositionOfPopUpRelativeToSelectionHandles();
904   }
905   else // Not in Text Selection mode so position relative to cursor.
906   {
907     position = PositionOfPopUpRelativeToCursor();
908   }
909
910   // reposition popup above the desired cursor position.
911   mPopUpPanel.Show( mPopUpTarget, true );
912   mPopUpPanel.Self().SetPosition( position );
913   mPopUpPanel.PressedSignal().Connect( this, &Decorator::OnPopupButtonPressed );
914
915   SetUpPopUpPositionNotifications();
916   mPopUpPanel.ApplyConfinementConstraint( mBoundingRectangleWorldCoordinates );
917 }
918
919 void Decorator::ShowPopUp( Actor target )
920 {
921   mPopUpTarget = target;
922   ShowPopupCutCopyPaste();
923 }
924
925 void Decorator::ShowPopupCutCopyPaste()
926 {
927   bool isAllTextSelectedAlready = ( mTextViewCharacterPositioning.StyledTextSize() == GetSelectedText().size() );
928   bool isTextEmpty = mTextViewCharacterPositioning.IsStyledTextEmpty() ;
929   bool isSubsetOfTextAlreadySelected = ( !isAllTextSelectedAlready ) && mHighlightMeshActor;
930
931   Clipboard clipboard = Clipboard::Get();
932   bool hasClipboardGotContent = clipboard.NumberOfItems();
933
934   mPopUpPanel.CreateCutCopyPastePopUp( isAllTextSelectedAlready, isTextEmpty, hasClipboardGotContent, isSubsetOfTextAlreadySelected );
935   ShowPopUp();
936 }
937
938 void Decorator::HidePopUp( bool animate, bool signalFinished )
939 {
940   if ( ( mPopUpPanel.GetState() == TextInputPopupNew::StateShowing ) || ( mPopUpPanel.GetState() == TextInputPopupNew::StateShown )  )
941   {
942     mPopUpPanel.Hide( animate );
943   }
944 }
945
946 void Decorator::AddPopupOption(const std::string& name, const std::string& caption, const Image icon, bool finalOption)
947 {
948   mPopUpPanel.AddButton(name, caption, icon, finalOption);
949 }
950
951 void Decorator::ClearPopup()
952 {
953   mPopUpPanel.Clear();
954 }
955
956 void Decorator::PopUpLeavesVerticalBoundary( PropertyNotification& source)
957 {
958   Vector3 position, alternativePosition;
959
960   if( mHighlightMeshActor ) // Text Selection mode
961   {
962     alternativePosition = AlternatePopUpPositionRelativeToSelectionHandles();
963   }
964   else // Not in Text Selection mode
965   {
966     alternativePosition = AlternatePopUpPositionRelativeToCursor();
967     // if can't be positioned above, then position below row.
968   }
969   // reposition popup above the desired cursor position.
970   mPopUpPanel.Self().SetPosition( alternativePosition );
971 }
972
973 void Decorator::SetUpPopUpPositionNotifications( )
974 {
975   // Note Property notifications ignore any set anchor point so conditions must allow for this.  Default is Top Left.
976
977   // Exceeding vertical boundary
978   PropertyNotification verticalExceedNotificationOne = mPopUpPanel.Self().AddPropertyNotification( Actor::WORLD_POSITION_Y,
979                                                        OutsideCondition( mBoundingRectangleWorldCoordinates.y + mPopUpPanel.GetSize().y/2,
980                                                                          mBoundingRectangleWorldCoordinates.w - mPopUpPanel.GetSize().y/2 ) );
981   verticalExceedNotificationOne.NotifySignal().Connect( this, &Decorator::PopUpLeavesVerticalBoundary );
982 }
983
984 bool Decorator::OnPopupButtonPressed( Toolkit::Button button )
985 {
986   mPopUpButtonPressedSignal.Emit( button );
987   return false;
988 }
989
990 Decorator::PressedSignal& Decorator::PopUpButtonPressedSignal()
991 {
992   return mPopUpButtonPressedSignal;
993 }
994
995 Decorator::CursorPositionedSignal& Decorator::CursorRePositionedSignal()
996 {
997   return mCursorRePositionedSignal;
998 }
999
1000 /**
1001  *  Decoration Positioning during Scrolling
1002  */
1003 void Decorator::TextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition )
1004 {
1005   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::TextViewScrolled\n");
1006
1007   const Vector3& controlSize = mTextViewCharacterPositioning.GetTextView().GetCurrentSize(); // todo Could store size and only update in Control Size change.
1008   Size cursorSize( CURSOR_THICKNESS, 0.f );
1009
1010   // Updates the cursor and grab handle position and visibility.
1011   if( mTextInputHandles.GetGrabHandle() || mCursor )
1012   {
1013     Vector2 min, max;
1014     size_t cursorTextPosition = GetCurrentCursorPosition();
1015     cursorSize.height  = mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( cursorTextPosition, min, max ).height;
1016
1017     const Vector3 cursorPosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( cursorTextPosition );
1018
1019     bool mIsCursorInScrollArea = IsPositionWithinControl( cursorPosition, cursorSize, controlSize );
1020     bool mIsGrabHandleInScrollArea = mIsCursorInScrollArea;
1021
1022     Vector2 actualGrabHandlePosition = cursorPosition.GetVectorXY();
1023
1024     if( mTextInputHandles.GetGrabHandle() )
1025     {
1026       ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1027       PositionGrabHandle( cursorTextPosition );
1028     }
1029
1030     if( mCursor )
1031     {
1032       mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
1033       DrawCursor( cursorTextPosition );
1034       mCursor.SetPosition( Vector3(actualGrabHandlePosition) + UI_OFFSET );
1035     }
1036   }
1037
1038   Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
1039   Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
1040
1041   // Updates the selection handles and highlighted text position and visibility.
1042   if( mTextInputHandles.GetSelectionHandleOne() && mTextInputHandles.GetSelectionHandleTwo() )
1043   {
1044     const Vector3 cursorPositionOne = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition );
1045     const Vector3 cursorPositionTwo = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition );
1046
1047     Size cursorSize( GetCursorSizeAt( mSelectionHandleOnePosition ) );
1048     const bool isSelectionHandleOneVisible = IsPositionWithinControl( cursorPositionOne, cursorSize, controlSize );
1049
1050     cursorSize =  GetCursorSizeAt( mSelectionHandleTwoPosition );
1051     const bool isSelectionHandleTwoVisible = IsPositionWithinControl( cursorPositionTwo, cursorSize, controlSize );
1052
1053     mSelectionHandleOneActualPosition = cursorPositionOne.GetVectorXY();
1054     mSelectionHandleTwoActualPosition = cursorPositionTwo.GetVectorXY();
1055
1056     selectionHandleOne.SetVisible( isSelectionHandleOneVisible );
1057     selectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
1058
1059     PositionSelectionHandle( selectionHandleOne, mSelectionHandleOneActualPosition, mSelectionHandleOnePosition );
1060     PositionSelectionHandle( selectionHandleTwo, mSelectionHandleTwoActualPosition, mSelectionHandleTwoPosition );
1061
1062     if( mHighlightMeshActor )
1063     {
1064       mHighlightMeshActor.SetVisible( true );
1065       ShowUpdatedHighlight();
1066     }
1067   }
1068 }
1069
1070 void Decorator::StartScrollTimer()
1071 {
1072   if( !mScrollTimer )
1073   {
1074     mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1075     mScrollTimer.TickSignal().Connect( this, &Decorator::OnScrollTimerTick );
1076   }
1077
1078   if( !mScrollTimer.IsRunning() )
1079   {
1080     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::StartScrollTimer\n");
1081     mScrollTimer.Start();
1082   }
1083 }
1084
1085 void Decorator::StopScrollTimer()
1086 {
1087   if( mScrollTimer )
1088   {
1089     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::StopScrollTimer\n");
1090
1091     mScrollTimer.Stop();
1092     mScrollTimer.Reset();
1093   }
1094 }
1095
1096 bool Decorator::OnScrollTimerTick()
1097 {
1098   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::OnScrollTimerTick\n");
1099
1100   if ( mGrabHandleVisibility && mTextInputHandles.GetGrabHandle() )
1101   {
1102     std::size_t newGrabHandlePosition = mTextViewCharacterPositioning.ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY() );
1103     if ( mGrabHandlePosition != newGrabHandlePosition )
1104     {
1105       Vector2 scrollPosition = mTextViewCharacterPositioning.GetScrollPosition();
1106       Vector2 scrollDelta = ( mActualGrabHandlePosition - mCurrentHandlePosition ).GetVectorXY();
1107       DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::OnScrollTimerTick scrollPosition(%f) scrollDelta(%f)\n", scrollPosition.x, scrollDelta.x);
1108       scrollPosition += scrollDelta;
1109       mTextViewCharacterPositioning.SetScrollPosition( scrollPosition );
1110
1111       // If scroll position goes too far TextView will trim it to fit.
1112       if ( mTextViewCharacterPositioning.IsScrollPositionTrimmed() )
1113       {
1114         StopScrollTimer();
1115       }
1116
1117       mActualGrabHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newGrabHandlePosition ).GetVectorXY();
1118     }
1119   }
1120
1121   Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
1122   Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
1123
1124   if ( selectionHandleOne && selectionHandleTwo )
1125   {
1126     std::size_t newHandleOnePosition = mTextViewCharacterPositioning.ReturnClosestIndex( mSelectionHandleOneActualPosition.GetVectorXY() );
1127
1128     // todo duplicated code should be a function
1129
1130     if ( mSelectionHandleOnePosition != newHandleOnePosition )
1131     {
1132       const Vector3 actualPosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newHandleOnePosition );
1133
1134       Vector2 scrollDelta = ( actualPosition - mSelectionHandleOneActualPosition ).GetVectorXY();
1135
1136       Vector2 scrollPosition = mTextViewCharacterPositioning.GetScrollPosition();
1137       scrollPosition += scrollDelta;
1138       mTextViewCharacterPositioning.SetScrollPosition( scrollPosition );
1139
1140       if( mTextViewCharacterPositioning.IsScrollPositionTrimmed() )
1141       {
1142         StopScrollTimer();
1143       }
1144
1145       mSelectionHandleOnePosition = newHandleOnePosition;
1146       mSelectionHandleOneActualPosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition ).GetVectorXY();
1147     }
1148     else
1149     {
1150       mSelectionHandleOneActualPosition.x += mScrollDisplacement.x;
1151       mSelectionHandleOneActualPosition.y += mScrollDisplacement.y;
1152     }
1153
1154     std::size_t newHandleTwoPosition = mTextViewCharacterPositioning.ReturnClosestIndex( mSelectionHandleTwoActualPosition.GetVectorXY() );
1155
1156     if ( mSelectionHandleTwoPosition != newHandleTwoPosition )
1157     {
1158       const Vector3 actualPosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newHandleTwoPosition );
1159
1160       Vector2 scrollDelta = ( actualPosition - mSelectionHandleTwoActualPosition ).GetVectorXY();
1161
1162       Vector2 scrollPosition = mTextViewCharacterPositioning.GetScrollPosition();
1163       scrollPosition += scrollDelta;
1164       mTextViewCharacterPositioning.SetScrollPosition( scrollPosition );
1165
1166       if( mTextViewCharacterPositioning.IsScrollPositionTrimmed() )
1167       {
1168         StopScrollTimer();
1169       }
1170
1171       mSelectionHandleTwoPosition = newHandleTwoPosition;
1172       mCurrentHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition ).GetVectorXY();
1173
1174     }
1175     else
1176     {
1177       mSelectionHandleTwoActualPosition.x += mScrollDisplacement.x;
1178       mSelectionHandleTwoActualPosition.y += mScrollDisplacement.y;
1179     }
1180   }
1181
1182   return true;
1183 }
1184
1185 /**
1186  *  Text Selection
1187  */
1188 MarkupProcessor::StyledTextArray Decorator::GetSelectedText()
1189 {
1190   MarkupProcessor::StyledTextArray currentSelectedText;
1191
1192   if ( mHighlightMeshActor ) // Text Selected
1193   {
1194     MarkupProcessor::StyledTextArray::iterator it = mTextViewCharacterPositioning.GetStyledTextArray().begin() + std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
1195     MarkupProcessor::StyledTextArray::iterator end = mTextViewCharacterPositioning.GetStyledTextArray().begin() + std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
1196
1197     for(; it != end; ++it)
1198     {
1199       MarkupProcessor::StyledText& styledText( *it );
1200       currentSelectedText.push_back( styledText );
1201     }
1202   }
1203   return currentSelectedText;
1204 }
1205
1206 } // Internal
1207
1208 } // namespace Toolkit
1209
1210 } // namespace Dali
1211