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