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