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