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