TextInput PopUp supports scrolling if buttons do not fit in visible area.
[platform/core/uifw/dali-toolkit.git] / base / dali-toolkit / internal / controls / text-input / text-input-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/dali.h>
19
20 #include <dali-toolkit/internal/controls/text-input/text-input-impl.h>
21 #include <dali-toolkit/internal/controls/text-view/text-processor.h>
22 #include <dali-toolkit/public-api/controls/buttons/push-button.h>
23 #include <dali-toolkit/public-api/controls/alignment/alignment.h>
24
25 #include <dali/integration-api/debug.h>
26
27 #include <math.h>
28 #include <sstream>
29 #include <algorithm>
30
31 using namespace std;
32 using namespace Dali;
33
34 // Local Data
35 namespace
36 {
37
38 #if defined(DEBUG_ENABLED)
39 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_TEXT_INPUT");
40 #endif
41
42 const std::size_t DEFAULT_MAX_SIZE( std::numeric_limits<std::size_t>::max() ); // Max possible number
43 const std::size_t DEFAULT_NUMBER_OF_LINES_LIMIT( std::numeric_limits<std::size_t>::max() ); // Max possible number
44 const Vector3 DEFAULT_SELECTION_HANDLE_SIZE( 51.0f, 79.0f, 0.0f );  // Selection cursor image size
45 const Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.5f, 2.0f, 1.0f );
46 const Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.5f, 1.5f, 1.0f );
47 const Vector4 LIGHTBLUE( 0.07f, 0.41f, 0.59f, 1.0f );    // Used for Selection highlight
48
49 const char* DEFAULT_GRAB_HANDLE( DALI_IMAGE_DIR "insertpoint-icon.png" );
50 const char* DEFAULT_SELECTION_HANDLE_ONE( DALI_IMAGE_DIR "text-input-selection-handle-left.png" );
51 const char* DEFAULT_SELECTION_HANDLE_TWO( DALI_IMAGE_DIR "text-input-selection-handle-right.png" );
52 const char* DEFAULT_SELECTION_HANDLE_ONE_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-left-press.png" );
53 const char* DEFAULT_SELECTION_HANDLE_TWO_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-right-press.png" );
54 const char* DEFAULT_CURSOR( DALI_IMAGE_DIR "cursor.png" );
55
56 const Vector4 DEFAULT_CURSOR_IMAGE_9_BORDER( 2.0f, 2.0f, 2.0f, 2.0f );
57
58 const std::size_t CURSOR_BLINK_INTERVAL = 500;                              ///< Cursor blink interval
59 const float CHARACTER_THRESHOLD( 2.5f );                                    ///< the threshold of a line.
60 const float DISPLAYED_HIGHLIGHT_Z_OFFSET( 0.0f );                           ///< 1. Highlight rendered (z-offset).
61 const float DISPLAYED_TEXT_VIEW_Z_OFFSET( 0.1f );                           ///< 2. Text rendered (z-offset).
62 const float UI_Z_OFFSET( 0.2f );                                            ///< 3. Text Selection Handles/Cursor z-offset.
63
64 const Vector3 UI_OFFSET(0.0f, 0.0f, UI_Z_OFFSET);                           ///< Text Selection Handles/Cursor offset.
65 const Vector3 DEFAULT_HANDLE_ONE_OFFSET(0.0f, -5.0f, 0.0f);                 ///< Handle One's Offset
66 const Vector3 DEFAULT_HANDLE_TWO_OFFSET(0.0f, -5.0f, 0.0f);                 ///< Handle Two's Offset
67 const float TOP_HANDLE_TOP_OFFSET(-1.5f);                                   ///< Offset between top handle and cutCopyPaste pop-up
68 const float BOTTOM_HANDLE_BOTTOM_OFFSET(1.5f);                              ///< Offset between bottom handle and cutCopyPaste pop-up
69 const float CURSOR_THICKNESS(6.0f);
70 const Degree CURSOR_ANGLE_OFFSET(2.0f);                                     ///< Offset from the angle of italic angle.
71
72 const std::string NEWLINE( "\n" );
73
74 const TextStyle DEFAULT_TEXT_STYLE;
75
76 const unsigned int SCROLL_TICK_INTERVAL = 50u;
77 const float SCROLL_THRESHOLD = 10.f;
78 const float SCROLL_SPEED = 15.f;
79
80 /**
81  * Selection state enumeration (FSM)
82  */
83 enum SelectionState
84 {
85   SelectionNone,                            ///< Currently not encountered selected section.
86   SelectionStarted,                         ///< Encountered selected section
87   SelectionFinished                         ///< Finished selected section
88 };
89
90 /**
91  * Whether the given style is the default style or not.
92  * @param[in] style The given style.
93  * @return \e true if the given style is the default. Otherwise it returns \e false.
94  */
95 bool IsDefaultStyle( const TextStyle& style )
96 {
97   return DEFAULT_TEXT_STYLE == style;
98 }
99
100 /**
101  * Whether the given styled text is using the default style or not.
102  * @param[in] textArray The given text.
103  * @return \e true if the given styled text is using the default style. Otherwise it returns \e false.
104  */
105 bool IsTextDefaultStyle( const Toolkit::MarkupProcessor::StyledTextArray& textArray )
106 {
107   for( Toolkit::MarkupProcessor::StyledTextArray::const_iterator it = textArray.begin(), endIt = textArray.end(); it != endIt; ++it )
108   {
109     const TextStyle& style( (*it).mStyle );
110
111     if( !IsDefaultStyle( style ) )
112     {
113       return false;
114     }
115   }
116
117   return true;
118 }
119
120 std::size_t FindVisibleCharacterLeft( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable )
121 {
122   for( Toolkit::TextView::CharacterLayoutInfoContainer::const_reverse_iterator it = characterLayoutInfoTable.rbegin() + characterLayoutInfoTable.size() - cursorPosition, endIt = characterLayoutInfoTable.rend();
123        it != endIt;
124        ++it )
125   {
126     if( ( *it ).mIsVisible )
127     {
128       return --cursorPosition;
129     }
130
131     --cursorPosition;
132   }
133
134   return 0;
135 }
136
137 std::size_t FindVisibleCharacterRight( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable  )
138 {
139   for( Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = characterLayoutInfoTable.begin() + cursorPosition, endIt = characterLayoutInfoTable.end(); it < endIt; ++it )
140   {
141     if( ( *it ).mIsVisible )
142     {
143       return cursorPosition;
144     }
145
146     ++cursorPosition;
147   }
148
149   return cursorPosition;
150 }
151
152 /**
153  * Whether the given position plus the cursor size offset is inside the given boundary.
154  *
155  * @param[in] position The given position.
156  * @param[in] cursorSize The cursor size.
157  * @param[in] controlSize The given boundary.
158  *
159  * @return whether the given position is inside the given boundary.
160  */
161 bool IsPositionInsideBoundaries( const Vector3& position, const Size& cursorSize, const Vector3& controlSize )
162 {
163   return ( position.x >= -Math::MACHINE_EPSILON_1000 ) &&
164          ( position.x <= controlSize.width + Math::MACHINE_EPSILON_1000 ) &&
165          ( position.y - cursorSize.height >= -Math::MACHINE_EPSILON_1000 ) &&
166          ( position.y <= controlSize.height + Math::MACHINE_EPSILON_1000 );
167 }
168
169 /**
170  * Splits a text in two halves.
171  *
172  * If the text's number of characters is odd, firstHalf has one more character.
173  *
174  * @param[in] text The text to be split.
175  * @param[out] firstHalf The first half of the text.
176  * @param[out] secondHalf The second half of the text.
177  */
178 void SplitText( const Toolkit::MarkupProcessor::StyledTextArray& text,
179                       Toolkit::MarkupProcessor::StyledTextArray& firstHalf,
180                       Toolkit::MarkupProcessor::StyledTextArray& secondHalf )
181 {
182   firstHalf.clear();
183   secondHalf.clear();
184
185   const std::size_t textLength = text.size();
186   const std::size_t half = ( textLength / 2 ) + ( textLength % 2 );
187
188   firstHalf.insert( firstHalf.end(), text.begin(), text.begin() + half );
189   secondHalf.insert( secondHalf.end(), text.begin() + half, text.end() );
190 }
191
192 } // end of namespace
193
194 namespace Dali
195 {
196
197 namespace Toolkit
198 {
199 // Properties
200 const Property::Index TextInput::HIGHLIGHT_COLOR_PROPERTY                     = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX;
201 const Property::Index TextInput::CUT_AND_PASTE_COLOR_PROPERTY                 = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+1;
202 const Property::Index TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY         = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+2;
203
204 const Property::Index TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY        = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+3;
205 const Property::Index TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY       = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+4;
206 const Property::Index TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY      = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+5;
207 const Property::Index TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY     = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+6;
208 const Property::Index TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+7;
209 const Property::Index TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY  = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+8;
210
211 namespace Internal
212 {
213
214 namespace
215 {
216
217 BaseHandle Create()
218 {
219   return Toolkit::TextInput::New();
220 }
221
222 TypeRegistration typeRegistration( typeid(Toolkit::TextInput), typeid(Toolkit::Control), Create );
223
224 SignalConnectorType signalConnector1( typeRegistration, Toolkit::TextInput::SIGNAL_START_INPUT,                  &TextInput::DoConnectSignal );
225 SignalConnectorType signalConnector2( typeRegistration, Toolkit::TextInput::SIGNAL_END_INPUT,                    &TextInput::DoConnectSignal );
226 SignalConnectorType signalConnector3( typeRegistration, Toolkit::TextInput::SIGNAL_STYLE_CHANGED,                &TextInput::DoConnectSignal );
227 SignalConnectorType signalConnector4( typeRegistration, Toolkit::TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED, &TextInput::DoConnectSignal );
228 SignalConnectorType signalConnector5( typeRegistration, Toolkit::TextInput::SIGNAL_TOOLBAR_DISPLAYED,            &TextInput::DoConnectSignal );
229 SignalConnectorType signalConnector6( typeRegistration, Toolkit::TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES,       &TextInput::DoConnectSignal );
230
231 }
232
233 PropertyRegistration property1( typeRegistration, "highlight-color",  Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
234 PropertyRegistration property2( typeRegistration, "cut-and-paste-bg-color",  Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
235 PropertyRegistration property3( typeRegistration, "cut-and-paste-pressed-color",  Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
236 PropertyRegistration property4( typeRegistration, "cut-button-position-priority",  Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
237 PropertyRegistration property5( typeRegistration, "copy-button-position-priority",  Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
238 PropertyRegistration property6( typeRegistration, "paste-button-position-priority",  Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
239 PropertyRegistration property7( typeRegistration, "select-button-position-priority",  Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
240 PropertyRegistration property8( typeRegistration, "select-all-button-position-priority",  Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
241 PropertyRegistration property9( typeRegistration, "clipboard-button-position-priority",  Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
242
243
244 // [TextInput::HighlightInfo] /////////////////////////////////////////////////
245
246 void TextInput::HighlightInfo::AddQuad( float x1, float y1, float x2, float y2 )
247 {
248   QuadCoordinates quad(x1, y1, x2, y2);
249   mQuadList.push_back( quad );
250 }
251
252 void TextInput::HighlightInfo::Clamp2D(const Vector2& min, const Vector2& max)
253 {
254   for(std::size_t i = 0;i < mQuadList.size(); i++)
255   {
256     QuadCoordinates& quad = mQuadList[i];
257
258     quad.min.Clamp(min, max);
259     quad.max.Clamp(min, max);
260   } // end for
261 }
262
263 // [TextInput] ////////////////////////////////////////////////////////////////
264
265 Dali::Toolkit::TextInput TextInput::New()
266 {
267   // Create the implementation
268   TextInputPtr textInput(new TextInput());
269   // Pass ownership to CustomActor via derived handle
270   Dali::Toolkit::TextInput handle(*textInput);
271
272   textInput->Initialize();
273
274   return handle;
275 }
276
277 TextInput::TextInput()
278 :Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS | REQUIRES_STYLE_CHANGE_SIGNALS ) ),
279  mState( StateEdit ),
280  mStyledText(),
281  mInputStyle(),
282  mLineHeight( 0.f ),
283  mDisplayedTextView(),
284  mStyledPlaceHolderText(),
285  mMaxStringLength( DEFAULT_MAX_SIZE ),
286  mNumberOflinesLimit( DEFAULT_NUMBER_OF_LINES_LIMIT ),
287  mCursorPosition( 0 ),
288  mActualGrabHandlePosition( 0.0f, 0.0f, 0.0f ),
289  mIsSelectionHandleOneFlipped( false ),
290  mIsSelectionHandleTwoFlipped( false ),
291  mSelectionHandleOneOffset( DEFAULT_HANDLE_ONE_OFFSET ),
292  mSelectionHandleTwoOffset( DEFAULT_HANDLE_TWO_OFFSET ),
293  mSelectionHandleOneActualPosition( 0.0f, 0.0f , 0.0f ),
294  mSelectionHandleTwoActualPosition( 0.0f, 0.0f , 0.0f ),
295  mSelectionHandleOnePosition( 0 ),
296  mSelectionHandleTwoPosition( 0 ),
297  mPreEditString(),
298  mPreEditStartPosition( 0 ),
299  mPreEditLength ( 0 ),
300  mNumberOfSurroundingCharactersDeleted( 0 ),
301  mTouchStartTime( 0 ),
302  mTextLayoutInfo(),
303  mCurrentCopySelecton(),
304  mPopUpPanel(),
305  mScrollTimer(),
306  mScrollDisplacement(),
307  mCurrentHandlePosition(),
308  mCurrentSelectionId(),
309  mCurrentSelectionHandlePosition(),
310  mRequestedSelection( 0, 0 ),
311  mSelectionHandleFlipMargin( 0.0f, 0.0f, 0.0f, 0.0f ),
312  mBoundingRectangleWorldCoordinates( 0.0f, 0.0f, 0.0f, 0.0f ),
313  mClipboard(),
314  mMaterialColor( LIGHTBLUE ),
315  mOverrideAutomaticAlignment( false ),
316  mCursorRTLEnabled( false ),
317  mClosestCursorPositionEOL ( false ),
318  mCursorBlinkStatus( true ),
319  mCursorVisibility( false ),
320  mGrabHandleVisibility( false ),
321  mIsCursorInScrollArea( true ),
322  mIsGrabHandleInScrollArea( true ),
323  mEditModeActive( false ),
324  mEditOnTouch( true ),
325  mTextSelection( true ),
326  mExceedEnabled( true ),
327  mGrabHandleEnabled( true ),
328  mIsSelectionHandleFlipEnabled( true ),
329  mPreEditFlag( false ),
330  mIgnoreCommitFlag( false ),
331  mIgnoreFirstCommitFlag( false ),
332  mSelectingText( false ),
333  mPreserveCursorPosition( false ),
334  mSelectTextOnCommit( false ),
335  mUnderlinedPriorToPreEdit ( false ),
336  mCommitByKeyInput( false ),
337  mPlaceHolderSet( false ),
338  mMarkUpEnabled( false )
339 {
340   // Updates the line height accordingly with the input style.
341   UpdateLineHeight();
342 }
343
344 TextInput::~TextInput()
345 {
346   StopCursorBlinkTimer();
347 }
348
349 // Public
350
351 std::string TextInput::GetText() const
352 {
353   std::string text;
354
355   // Return text-view's text only if the text-input's text is not empty
356   // in order to not to return the placeholder text.
357   if( !mStyledText.empty() )
358   {
359     text = mDisplayedTextView.GetText();
360   }
361
362   return text;
363 }
364
365 std::string TextInput::GetMarkupText() const
366 {
367   std::string markupString;
368   MarkupProcessor::GetMarkupString( mStyledText, markupString );
369
370   return markupString;
371 }
372
373 void TextInput::SetPlaceholderText( const std::string& placeHolderText )
374 {
375   // Get the placeholder styled text array from the markup string.
376   MarkupProcessor::GetStyledTextArray( placeHolderText, mStyledPlaceHolderText, IsMarkupProcessingEnabled() );
377
378   if( mStyledText.empty() )
379   {
380     // Set the placeholder text only if the styled text is empty.
381     mDisplayedTextView.SetText( mStyledPlaceHolderText );
382     mPlaceHolderSet = true;
383   }
384 }
385
386 std::string TextInput::GetPlaceholderText()
387 {
388   // Traverses the styled placeholder array getting only the text.
389   //  Note that for some languages a 'character' could be represented by more than one 'char'
390
391   std::string placeholderText;
392   for( MarkupProcessor::StyledTextArray::const_iterator it = mStyledPlaceHolderText.begin(), endIt = mStyledPlaceHolderText.end(); it != endIt; ++it )
393   {
394     placeholderText.append( (*it).mText.GetText() );
395   }
396
397   return placeholderText ;
398 }
399
400 void TextInput::SetInitialText(const std::string& initialText)
401 {
402   DALI_LOG_INFO(gLogFilter, Debug::General, "SetInitialText string[%s]\n", initialText.c_str() );
403
404   if ( mPreEditFlag ) // If in the pre-edit state and text is being set then discard text being inserted.
405   {
406     mPreEditFlag = false;
407     mIgnoreCommitFlag = true;
408   }
409
410   SetText( initialText );
411   PreEditReset( false ); // Reset keyboard as text changed
412 }
413
414 void TextInput::SetText(const std::string& initialText)
415 {
416   DALI_LOG_INFO(gLogFilter, Debug::General, "SetText string[%s]\n", initialText.c_str() );
417
418   GetStyledTextArray( initialText, mStyledText, IsMarkupProcessingEnabled() );
419
420   if( mStyledText.empty() )
421   {
422     // If the initial text is empty, set the placeholder text.
423     mDisplayedTextView.SetText( mStyledPlaceHolderText );
424     mPlaceHolderSet = true;
425   }
426   else
427   {
428     mDisplayedTextView.SetText( mStyledText );
429     mPlaceHolderSet = false;
430   }
431
432   GetTextLayoutInfo();
433
434   mCursorPosition = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
435
436   ImfManager imfManager = ImfManager::Get();
437   if ( imfManager )
438   {
439     imfManager.SetCursorPosition( mCursorPosition );
440     imfManager.SetSurroundingText( initialText );
441     imfManager.NotifyCursorPosition();
442   }
443
444   if( IsScrollEnabled() )
445   {
446     ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
447   }
448
449   ShowGrabHandleAndSetVisibility( false );
450
451   RemoveHighlight();
452
453   DrawCursor();
454
455   EmitTextModified();
456 }
457
458 void TextInput::SetText( const MarkupProcessor::StyledTextArray& styleText )
459 {
460   DALI_LOG_INFO(gLogFilter, Debug::General, "SetText markup text\n" );
461
462   mDisplayedTextView.SetText( styleText );
463   mPlaceHolderSet = false;
464
465   // If text alignment hasn't been manually set by application developer, then we
466   // automatically determine the alignment based on the content of the text i.e. what
467   // language the text begins with.
468   // TODO: This should determine different alignments for each line (broken by '\n') of text.
469   if(!mOverrideAutomaticAlignment)
470   {
471     // Determine bidi direction of first character (skipping past whitespace, numbers, and symbols)
472     bool leftToRight(true);
473
474     if( !styleText.empty() )
475     {
476       bool breakOut(false);
477
478       for( MarkupProcessor::StyledTextArray::const_iterator textIter = styleText.begin(), textEndIter = styleText.end(); ( textIter != textEndIter ) && ( !breakOut ); ++textIter )
479       {
480         const Text& text = textIter->mText;
481
482         for( std::size_t i = 0; i < text.GetLength(); ++i )
483         {
484           Character character( text[i] );
485           if( character.GetCharacterDirection() != Character::Neutral )
486           {
487             leftToRight = ( character.GetCharacterDirection() == Character::LeftToRight );
488             breakOut = true;
489             break;
490           }
491         }
492       }
493     }
494
495     // Based on this direction, either left or right align text if not manually set by application developer.
496     mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(
497                                            ( leftToRight ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight) |
498                                              Toolkit::Alignment::VerticalTop ) );
499     mDisplayedTextView.SetLineJustification( leftToRight ? Toolkit::TextView::Left : Toolkit::TextView::Right);
500   }
501
502   EmitTextModified();
503 }
504
505 void TextInput::SetMaxCharacterLength(std::size_t maxChars)
506 {
507   mMaxStringLength = maxChars;
508 }
509
510 void TextInput::SetNumberOfLinesLimit(std::size_t maxLines)
511 {
512   DALI_ASSERT_DEBUG( maxLines > 0 )
513
514   if ( maxLines > 0)
515   {
516     mNumberOflinesLimit = maxLines;
517   }
518 }
519
520 std::size_t TextInput::GetNumberOfLinesLimit() const
521 {
522   return mNumberOflinesLimit;
523 }
524
525 std::size_t TextInput::GetNumberOfCharacters() const
526 {
527   return mStyledText.size();
528 }
529
530 // Styling
531 void TextInput::SetMaterialDiffuseColor( const Vector4& color )
532 {
533   mMaterialColor = color;
534   if ( mCustomMaterial )
535   {
536     mCustomMaterial.SetDiffuseColor( mMaterialColor );
537     mMeshData.SetMaterial( mCustomMaterial );
538   }
539 }
540
541 const Vector4& TextInput::GetMaterialDiffuseColor() const
542 {
543   return mMaterialColor;
544 }
545
546 // Signals
547
548 Toolkit::TextInput::InputSignalV2& TextInput::InputStartedSignal()
549 {
550   return mInputStartedSignalV2;
551 }
552
553 Toolkit::TextInput::InputSignalV2& TextInput::InputFinishedSignal()
554 {
555   return mInputFinishedSignalV2;
556 }
557
558 Toolkit::TextInput::InputSignalV2& TextInput::CutAndPasteToolBarDisplayedSignal()
559 {
560   return mCutAndPasteToolBarDisplayedV2;
561 }
562
563 Toolkit::TextInput::StyleChangedSignalV2& TextInput::StyleChangedSignal()
564 {
565   return mStyleChangedSignalV2;
566 }
567
568 Toolkit::TextInput::TextModifiedSignalType& TextInput::TextModifiedSignal()
569 {
570   return mTextModifiedSignal;
571 }
572
573 Toolkit::TextInput::MaxInputCharactersReachedSignalV2& TextInput::MaxInputCharactersReachedSignal()
574 {
575   return mMaxInputCharactersReachedSignalV2;
576 }
577
578 Toolkit::TextInput::InputTextExceedBoundariesSignalV2& TextInput::InputTextExceedBoundariesSignal()
579 {
580   return mInputTextExceedBoundariesSignalV2;
581 }
582
583 bool TextInput::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
584 {
585   Dali::BaseHandle handle( object );
586
587   bool connected( true );
588   Toolkit::TextInput textInput = Toolkit::TextInput::DownCast(handle);
589
590   if( Toolkit::TextInput::SIGNAL_START_INPUT == signalName )
591   {
592     textInput.InputStartedSignal().Connect( tracker, functor );
593   }
594   else if( Toolkit::TextInput::SIGNAL_END_INPUT == signalName )
595   {
596     textInput.InputFinishedSignal().Connect( tracker, functor );
597   }
598   else if( Toolkit::TextInput::SIGNAL_STYLE_CHANGED == signalName )
599   {
600     textInput.StyleChangedSignal().Connect( tracker, functor );
601   }
602   else if( Toolkit::TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED == signalName )
603   {
604     textInput.MaxInputCharactersReachedSignal().Connect( tracker, functor );
605   }
606   else if( Toolkit::TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES == signalName )
607   {
608     textInput.InputTextExceedBoundariesSignal().Connect( tracker, functor );
609   }
610   else
611   {
612     // signalName does not match any signal
613     connected = false;
614   }
615
616   return connected;
617 }
618
619 void TextInput::SetEditable(bool editMode, bool setCursorOnTouchPoint, const Vector2& touchPoint)
620 {
621   if(editMode)
622   {
623     // update line height before calculate the actual position.
624     UpdateLineHeight();
625
626     if(!mEditModeActive)
627     {
628       if( setCursorOnTouchPoint )
629       {
630         // Sets the cursor position for the given touch point.
631         ReturnClosestIndex( touchPoint, mCursorPosition );
632
633         // Creates the grab handle.
634         if( IsGrabHandleEnabled() )
635         {
636           const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
637
638           CreateGrabHandle();
639
640           mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
641           mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
642           mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
643           ShowGrabHandleAndSetVisibility( true );
644
645           // Scrolls the text-view if needed.
646           if( IsScrollEnabled() )
647           {
648             ScrollTextViewToMakeCursorVisible( cursorPosition );
649           }
650         }
651       }
652       else
653       {
654         mCursorPosition = mStyledText.size(); // Initially set cursor position to end of string.
655       }
656     }
657
658     StartEditMode();
659   }
660   else
661   {
662     EndEditMode();
663   }
664 }
665
666 bool TextInput::IsEditable() const
667 {
668   return mEditModeActive;
669 }
670
671 void TextInput::SetEditOnTouch( bool editOnTouch )
672 {
673   mEditOnTouch = editOnTouch;
674 }
675
676 bool TextInput::IsEditOnTouch() const
677 {
678   return mEditOnTouch;
679 }
680
681 void TextInput::SetTextSelectable( bool textSelectable )
682 {
683   mTextSelection = textSelectable;
684 }
685
686 bool TextInput::IsTextSelectable() const
687 {
688   return mTextSelection;
689 }
690
691 bool TextInput::IsTextSelected() const
692 {
693   return mHighlightMeshActor;
694 }
695
696 void TextInput::DeSelectText()
697 {
698   RemoveHighlight();
699   HidePopup();
700   CursorUpdate();
701 }
702
703 void TextInput::SetGrabHandleImage(Dali::Image image )
704 {
705   if (image)
706   {
707     CreateGrabHandle(image);
708   }
709 }
710
711 void TextInput::SetCursorImage(Dali::Image image, const Vector4& border )
712 {
713   DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
714
715   if ( image )
716   {
717     mCursor.SetImage( image );
718     mCursor.SetNinePatchBorder( border );
719   }
720 }
721
722 Vector3 TextInput::GetSelectionHandleSize()
723 {
724   return DEFAULT_SELECTION_HANDLE_SIZE;
725 }
726
727 void TextInput::SetRTLCursorImage(Dali::Image image, const Vector4& border )
728 {
729   DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
730
731   if ( image )
732   {
733     mCursorRTL.SetImage( image);
734     mCursorRTL.SetNinePatchBorder(  border );
735   }
736 }
737
738 void TextInput::EnableGrabHandle(bool toggle)
739 {
740   // enables grab handle with will in turn de-activate magnifier
741   mGrabHandleEnabled = toggle;
742 }
743
744 bool TextInput::IsGrabHandleEnabled()
745 {
746   // if false then magnifier will be shown instead.
747   return mGrabHandleEnabled;
748 }
749
750 void TextInput::EnableSelectionHandleFlip( bool toggle )
751 {
752   // Deprecated function.  To be removed.
753   mIsSelectionHandleFlipEnabled = toggle;
754 }
755
756 bool TextInput::IsSelectionHandleFlipEnabled()
757 {
758   // Deprecated function, To be removed. Returns true as handle flipping always enabled by default so handles do not exceed screen.
759   return true;
760 }
761
762 void TextInput::SetSelectionHandleFlipMargin( const Vector4& margin )
763 {
764   // Deprecated function, now just stores margin for retreival, remove completely once depricated Public API removed.
765   Vector3 textInputSize = mDisplayedTextView.GetCurrentSize();
766   const Vector4 flipBoundary( -margin.x, -margin.y, textInputSize.width + margin.z, textInputSize.height + margin.w );
767
768   mSelectionHandleFlipMargin = margin;
769 }
770
771 void TextInput::SetBoundingRectangle( const Rect<float>& boundingRectangle )
772 {
773   // Convert to world coordinates and store as a Vector4 to be compatiable with Property Notifications.
774   Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
775
776   const float originX = boundingRectangle.x - 0.5f * stageSize.width;
777   const float originY = boundingRectangle.y - 0.5f * stageSize.height;
778
779   const Vector4 boundary( originX,
780                           originY,
781                           originX + boundingRectangle.width,
782                           originY + boundingRectangle.height );
783
784   mBoundingRectangleWorldCoordinates = boundary;
785
786   // Set Boundary for Popup so it keeps the Pop-up within the area also.
787   mPopUpPanel.SetPopupBoundary( boundingRectangle );
788 }
789
790 const Rect<float> TextInput::GetBoundingRectangle() const
791 {
792   Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
793
794   const float originX = mBoundingRectangleWorldCoordinates.x + 0.5f * stageSize.width;
795   const float originY = mBoundingRectangleWorldCoordinates.y + 0.5f * stageSize.height;
796
797   Rect<float>boundingRect( originX, originY, mBoundingRectangleWorldCoordinates.z - mBoundingRectangleWorldCoordinates.x, mBoundingRectangleWorldCoordinates.w - mBoundingRectangleWorldCoordinates.y);
798
799   return boundingRect;
800 }
801
802 const Vector4& TextInput::GetSelectionHandleFlipMargin()
803 {
804   return mSelectionHandleFlipMargin;
805 }
806
807 void TextInput::SetTextColor( const Vector4& color )
808 {
809   mDisplayedTextView.SetColor( color );
810 }
811
812 void TextInput::SetActiveStyle( const TextStyle& style, const TextStyle::Mask mask )
813 {
814   if( style != mInputStyle )
815   {
816     // different style.
817     bool emitSignal = false;
818
819     // mask: modify style according to mask, if different emit signal.
820     const TextStyle oldInputStyle( mInputStyle );
821
822     // Copy the new style.
823     mInputStyle.Copy( style, mask );
824
825     // if style has changed, emit signal.
826     if( oldInputStyle != mInputStyle )
827     {
828       emitSignal = true;
829     }
830
831     // Updates the line height accordingly with the input style.
832     UpdateLineHeight();
833
834     // Changing font point size will require the cursor to be re-sized
835     DrawCursor();
836
837     if( emitSignal )
838     {
839       EmitStyleChangedSignal();
840     }
841   }
842 }
843
844 void TextInput::ApplyStyle( const TextStyle& style, const TextStyle::Mask mask )
845 {
846   if ( IsTextSelected() )
847   {
848     const std::size_t begin = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
849     const std::size_t end = std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition) - 1;
850
851     if( !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
852     {
853       ApplyStyleToRange(style, mask, mTextLayoutInfo.mCharacterLogicalToVisualMap[begin], mTextLayoutInfo.mCharacterLogicalToVisualMap[end]);
854     }
855
856     // Keeps the old style to be compared with the new one.
857     const TextStyle oldInputStyle( mInputStyle );
858
859     // Copy only those parameters from the style which are set in the mask.
860     mInputStyle.Copy( style, mask );
861
862     if( mInputStyle != oldInputStyle )
863     {
864       // Updates the line height accordingly with the input style.
865       UpdateLineHeight();
866
867       EmitStyleChangedSignal();
868     }
869   }
870 }
871
872 void TextInput::ApplyStyleToAll( const TextStyle& style, const TextStyle::Mask mask )
873 {
874   if( !mStyledText.empty() )
875   {
876     ApplyStyleToRange( style, mask, 0, mStyledText.size() - 1 );
877   }
878 }
879
880 TextStyle TextInput::GetStyleAtCursor() const
881 {
882   TextStyle style;
883
884   if ( !mStyledText.empty() && ( mCursorPosition > 0 ) )
885   {
886     DALI_ASSERT_DEBUG( ( 0 <= mCursorPosition-1 ) && ( mCursorPosition-1 < mStyledText.size() ) );
887     style = mStyledText.at( mCursorPosition-1 ).mStyle;
888   }
889   else // No text.
890   {
891     style = mInputStyle;
892
893     if ( mInputStyle.GetFontPointSize() <  Math::MACHINE_EPSILON_1000 )
894     {
895       Dali::Font defaultFont = Dali::Font::New();
896       style.SetFontPointSize( PointSize( defaultFont.GetPointSize()) );
897     }
898   }
899
900   return style;
901 }
902
903 TextStyle TextInput::GetStyleAt( std::size_t position ) const
904 {
905   DALI_ASSERT_DEBUG( ( 0 <= position ) && ( position <= mStyledText.size() ) );
906
907   if( position >= mStyledText.size() )
908   {
909     position = mStyledText.size() - 1;
910   }
911
912   return mStyledText.at( position ).mStyle;
913 }
914
915 void TextInput::SetTextAlignment( Toolkit::Alignment::Type align )
916 {
917   mDisplayedTextView.SetTextAlignment( align );
918   mOverrideAutomaticAlignment = true;
919 }
920
921 void TextInput::SetTextLineJustification( Toolkit::TextView::LineJustification justification )
922 {
923   mDisplayedTextView.SetLineJustification( justification );
924   mOverrideAutomaticAlignment = true;
925 }
926
927 void TextInput::SetFadeBoundary( const Toolkit::TextView::FadeBoundary& fadeBoundary )
928 {
929   mDisplayedTextView.SetFadeBoundary( fadeBoundary );
930 }
931
932 const Toolkit::TextView::FadeBoundary& TextInput::GetFadeBoundary() const
933 {
934   return mDisplayedTextView.GetFadeBoundary();
935 }
936
937 Toolkit::Alignment::Type TextInput::GetTextAlignment() const
938 {
939   return mDisplayedTextView.GetTextAlignment();
940 }
941
942 void TextInput::SetMultilinePolicy( Toolkit::TextView::MultilinePolicy policy )
943 {
944   mDisplayedTextView.SetMultilinePolicy( policy );
945 }
946
947 Toolkit::TextView::MultilinePolicy TextInput::GetMultilinePolicy() const
948 {
949   return mDisplayedTextView.GetMultilinePolicy();
950 }
951
952 void TextInput::SetWidthExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
953 {
954   mDisplayedTextView.SetWidthExceedPolicy( policy );
955 }
956
957 Toolkit::TextView::ExceedPolicy TextInput::GetWidthExceedPolicy() const
958 {
959   return mDisplayedTextView.GetWidthExceedPolicy();
960 }
961
962 void TextInput::SetHeightExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
963 {
964   mDisplayedTextView.SetHeightExceedPolicy( policy );
965 }
966
967 Toolkit::TextView::ExceedPolicy TextInput::GetHeightExceedPolicy() const
968 {
969   return mDisplayedTextView.GetHeightExceedPolicy();
970 }
971
972 void TextInput::SetExceedEnabled( bool enable )
973 {
974   mExceedEnabled = enable;
975 }
976
977 bool TextInput::GetExceedEnabled() const
978 {
979   return mExceedEnabled;
980 }
981
982 void TextInput::SetBackground(Dali::Image image )
983 {
984   // TODO Should add this function and add public api to match.
985 }
986
987 bool TextInput::OnTouchEvent(const TouchEvent& event)
988 {
989   return false;
990 }
991
992 bool TextInput::OnKeyEvent(const KeyEvent& event)
993 {
994   switch( event.state )
995   {
996     case KeyEvent::Down:
997     {
998       return OnKeyDownEvent(event);
999     }
1000     break;
1001
1002     case KeyEvent::Up:
1003     {
1004       return OnKeyUpEvent(event);
1005     }
1006     break;
1007
1008     default:
1009     {
1010       return false;
1011     }
1012     break;
1013   }
1014 }
1015
1016 void TextInput::OnKeyInputFocusGained()
1017 {
1018   DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusGained\n" );
1019
1020   mEditModeActive = true;
1021
1022   mActiveLayer.RaiseToTop(); // Ensure layer holding handles is on top
1023
1024   mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1025
1026   // Updates the line height accordingly with the input style.
1027   UpdateLineHeight();
1028
1029   // Connect the signals to use in text input.
1030   VirtualKeyboard::StatusChangedSignal().Connect( this, &TextInput::KeyboardStatusChanged );
1031   VirtualKeyboard::LanguageChangedSignal().Connect( this, &TextInput::SetTextDirection );
1032
1033   // Set the text direction if empty and connect to the signal to ensure we change direction when the language changes.
1034   SetTextDirection();
1035
1036   GetTextLayoutInfo();
1037
1038   DrawCursor();
1039   SetCursorVisibility( true );
1040   StartCursorBlinkTimer();
1041
1042   Toolkit::TextInput handle( GetOwner() );
1043   mInputStartedSignalV2.Emit( handle );
1044
1045   ImfManager imfManager = ImfManager::Get();
1046
1047   if ( imfManager )
1048   {
1049     imfManager.EventReceivedSignal().Connect(this, &TextInput::ImfEventReceived);
1050
1051     // Notify that the text editing start.
1052     imfManager.Activate();
1053
1054     // When window gain lost focus, the imf manager is deactivated. Thus when window gain focus again, the imf manager must be activated.
1055     imfManager.SetRestoreAferFocusLost( true );
1056
1057     imfManager.SetCursorPosition( mCursorPosition );
1058     imfManager.NotifyCursorPosition();
1059   }
1060
1061   mClipboard = Clipboard::Get(); // Store handle to clipboard
1062
1063   // Now in edit mode we can accept string to paste from clipboard
1064   if( Adaptor::IsAvailable() )
1065   {
1066     ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1067     if ( notifier )
1068     {
1069       notifier.ContentSelectedSignal().Connect( this, &TextInput::OnClipboardTextSelected );
1070     }
1071   }
1072 }
1073
1074 void TextInput::OnKeyInputFocusLost()
1075 {
1076   DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusLost\n" );
1077
1078   if( mPreEditFlag )
1079   {
1080     // If key input focus is lost, it removes the
1081     // underline from the last pre-edit text.
1082     RemovePreEditStyle();
1083     const std::size_t numberOfCharactersDeleted = DeletePreEdit();
1084     InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersDeleted );
1085     EmitTextModified();
1086   }
1087
1088   ImfManager imfManager = ImfManager::Get();
1089   if ( imfManager )
1090   {
1091     // The text editing is finished. Therefore the imf manager don't have restore activation.
1092     imfManager.SetRestoreAferFocusLost( false );
1093
1094     // Notify that the text editing finish.
1095     imfManager.Deactivate();
1096
1097     imfManager.EventReceivedSignal().Disconnect(this, &TextInput::ImfEventReceived);
1098   }
1099   // Disconnect signal used the text input.
1100   VirtualKeyboard::LanguageChangedSignal().Disconnect( this, &TextInput::SetTextDirection );
1101
1102   Toolkit::TextInput handle( GetOwner() );
1103   mInputFinishedSignalV2.Emit( handle );
1104   mEditModeActive = false;
1105   mPreEditFlag = false;
1106   RemoveHighlight();
1107   SetCursorVisibility( false );
1108   StopCursorBlinkTimer();
1109
1110   ShowGrabHandleAndSetVisibility( false );
1111
1112   mClipboard.Reset();
1113   // No longer in edit mode so do not want to receive string from clipboard
1114   if( Adaptor::IsAvailable() )
1115   {
1116     ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1117     if ( notifier )
1118     {
1119       notifier.ContentSelectedSignal().Disconnect( this, &TextInput::OnClipboardTextSelected );
1120     }
1121     Clipboard clipboard = Clipboard::Get();
1122
1123     if ( clipboard )
1124     {
1125       clipboard.HideClipboard();
1126     }
1127   }
1128 }
1129
1130 void TextInput::OnControlStageConnection()
1131 {
1132   Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
1133
1134   if ( mBoundingRectangleWorldCoordinates == Vector4::ZERO )
1135   {
1136     SetBoundingRectangle( Rect<float>( 0.0f, 0.0f, stageSize.width, stageSize.height ));
1137   }
1138 }
1139
1140 void TextInput::CreateActiveLayer()
1141 {
1142   Actor self = Self();
1143   mActiveLayer = Layer::New();
1144
1145   mActiveLayer.SetAnchorPoint( AnchorPoint::CENTER);
1146   mActiveLayer.SetParentOrigin( ParentOrigin::CENTER);
1147   mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
1148
1149   self.Add( mActiveLayer );
1150   mActiveLayer.RaiseToTop();
1151 }
1152
1153 void TextInput::OnInitialize()
1154 {
1155   CreateTextViewActor();
1156
1157   SetUpTouchEvents();
1158
1159   // Create 2 cursors (standard LTR and RTL cursor for when text can be added at
1160   // different positions depending on language)
1161   Image mCursorImage = Image::New( DEFAULT_CURSOR );
1162   mCursor = CreateCursor( mCursorImage, DEFAULT_CURSOR_IMAGE_9_BORDER );
1163   mCursorRTL = CreateCursor( mCursorImage, DEFAULT_CURSOR_IMAGE_9_BORDER );
1164
1165   Actor self = Self();
1166   self.Add( mCursor );
1167   self.Add( mCursorRTL );
1168
1169   mCursorVisibility = false;
1170
1171   CreateActiveLayer(); // todo move this so layer only created when needed.
1172
1173   // Assign names to image actors
1174   mCursor.SetName("mainCursor");
1175   mCursorRTL.SetName("rtlCursor");
1176 }
1177
1178 void TextInput::OnControlSizeSet(const Vector3& targetSize)
1179 {
1180   mDisplayedTextView.SetSize( targetSize );
1181   GetTextLayoutInfo();
1182   mActiveLayer.SetSize(targetSize);
1183 }
1184
1185 void TextInput::OnRelaidOut( Vector2 size, ActorSizeContainer& container )
1186 {
1187   Relayout( mDisplayedTextView, size, container );
1188   GetTextLayoutInfo();
1189
1190   DrawCursor();
1191 }
1192
1193 Vector3 TextInput::GetNaturalSize()
1194 {
1195   Vector3 naturalSize = mDisplayedTextView.GetNaturalSize();
1196
1197   if( mEditModeActive && ( Vector3::ZERO == naturalSize ) )
1198   {
1199     // If the natural is zero, it means there is no text. Let's return the cursor height as the natural height.
1200     naturalSize.height = mLineHeight;
1201   }
1202
1203   return naturalSize;
1204 }
1205
1206 float TextInput::GetHeightForWidth( float width )
1207 {
1208   float height = mDisplayedTextView.GetHeightForWidth( width );
1209
1210   if( mEditModeActive && ( fabsf( height ) < Math::MACHINE_EPSILON_1000 ) )
1211   {
1212     // If the height is zero, it means there is no text. Let's return the cursor height.
1213     height = mLineHeight;
1214   }
1215
1216   return height;
1217 }
1218
1219 /*end of Virtual methods from parent*/
1220
1221 // Private Internal methods
1222
1223 void TextInput::OnHandlePan(Actor actor, PanGesture gesture)
1224 {
1225   switch (gesture.state)
1226   {
1227     case Gesture::Started:
1228     // fall through so code not duplicated
1229     case Gesture::Continuing:
1230     {
1231       if (actor == mGrabArea)
1232       {
1233         SetCursorVisibility( true );
1234         ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1235         MoveGrabHandle( gesture.displacement );
1236         HidePopup(); // Do not show popup whilst handle is moving
1237       }
1238       else if (actor == mHandleOneGrabArea)
1239       {
1240         // the displacement in PanGesture is affected by the actor's rotation.
1241         mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1242         mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1243
1244         MoveSelectionHandle( HandleOne, gesture.displacement );
1245
1246         mState = StateDraggingHandle;
1247         HidePopup();
1248       }
1249       else if (actor == mHandleTwoGrabArea)
1250       {
1251         // the displacement in PanGesture is affected by the actor's rotation.
1252         mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1253         mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1254
1255         MoveSelectionHandle( HandleTwo, gesture.displacement );
1256
1257         mState = StateDraggingHandle;
1258         HidePopup();
1259       }
1260     }
1261     break;
1262
1263     case Gesture::Finished:
1264     {
1265       // Revert back to non-pressed selection handle images
1266       if (actor == mGrabArea)
1267       {
1268         mActualGrabHandlePosition = MoveGrabHandle( gesture.displacement );
1269         SetCursorVisibility( true );
1270         SetUpPopUpSelection();
1271         ShowPopup();
1272       }
1273       if (actor == mHandleOneGrabArea)
1274       {
1275         // the displacement in PanGesture is affected by the actor's rotation.
1276         mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1277         mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1278
1279         mSelectionHandleOneActualPosition = MoveSelectionHandle( HandleOne, gesture.displacement );
1280
1281         mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1282         mState = StateEdit;
1283         ShowPopupCutCopyPaste();
1284       }
1285       if (actor == mHandleTwoGrabArea)
1286       {
1287         // the displacement in PanGesture is affected by the actor's rotation.
1288         mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1289         mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1290
1291         mSelectionHandleTwoActualPosition = MoveSelectionHandle( HandleTwo, gesture.displacement );
1292
1293         mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1294         mState = StateEdit;
1295         ShowPopupCutCopyPaste();
1296       }
1297     }
1298     break;
1299     default:
1300       break;
1301   }
1302 }
1303
1304 // Stop the flashing animation so easy to see when moved.
1305 bool TextInput::OnPressDown(Dali::Actor actor, const TouchEvent& touch)
1306 {
1307   if (touch.GetPoint(0).state == TouchPoint::Down)
1308   {
1309     SetCursorVisibility( true );
1310     StopCursorBlinkTimer();
1311   }
1312   else if (touch.GetPoint(0).state == TouchPoint::Up)
1313   {
1314     SetCursorVisibility( true );
1315     StartCursorBlinkTimer();
1316   }
1317   return false;
1318 }
1319
1320 // selection handle one
1321 bool TextInput::OnHandleOneTouched(Dali::Actor actor, const TouchEvent& touch)
1322 {
1323   if (touch.GetPoint(0).state == TouchPoint::Down)
1324   {
1325     mSelectionHandleOne.SetImage( mSelectionHandleOneImagePressed );
1326   }
1327   else if (touch.GetPoint(0).state == TouchPoint::Up)
1328   {
1329     mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1330   }
1331   return false;
1332 }
1333
1334 // selection handle two
1335 bool TextInput::OnHandleTwoTouched(Dali::Actor actor, const TouchEvent& touch)
1336 {
1337   if (touch.GetPoint(0).state == TouchPoint::Down)
1338   {
1339     mSelectionHandleTwo.SetImage( mSelectionHandleTwoImagePressed );
1340   }
1341   else if (touch.GetPoint(0).state == TouchPoint::Up)
1342   {
1343     mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1344   }
1345   return false;
1346 }
1347
1348 void TextInput::OnDoubleTap(Dali::Actor actor, Dali::TapGesture tap)
1349 {
1350    // If text exists then select nearest word.
1351    if ( !mStyledText.empty())
1352    {
1353      HidePopup();
1354
1355      ShowGrabHandleAndSetVisibility( false );
1356
1357
1358      if ( mPreEditFlag )
1359      {
1360        // PreEdit will be committed here without needing a commit from IMF.  Remove pre-edit underline and reset flags which
1361        // converts the pre-edit word being displayed to a committed word.
1362        if ( !mUnderlinedPriorToPreEdit )
1363        {
1364          TextStyle style;
1365          style.SetUnderline( false );
1366          ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1367        }
1368        mPreEditFlag = false;
1369        mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1370        // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1371        PreEditReset( false );
1372      }
1373      mCursorPosition = 0;
1374
1375      mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1376      ReturnClosestIndex( tap.localPoint, mCursorPosition );
1377
1378      ImfManager imfManager = ImfManager::Get();
1379      if ( imfManager )
1380      {
1381        imfManager.SetCursorPosition ( mCursorPosition );
1382        imfManager.NotifyCursorPosition();
1383      }
1384
1385      std::size_t start = 0;
1386      std::size_t end = 0;
1387      Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1388
1389      SelectText( start, end );
1390    }
1391    // if no text but clipboard has content then show paste option
1392    if ( ( mClipboard && mClipboard.NumberOfItems() ) || !mStyledText.empty() )
1393    {
1394      ShowPopupCutCopyPaste();
1395    }
1396
1397    // If no text and clipboard empty then do nothing
1398 }
1399
1400 // TODO: Change the function name to be more general.
1401 void TextInput::OnTextTap(Dali::Actor actor, Dali::TapGesture tap)
1402 {
1403   DALI_LOG_INFO( gLogFilter, Debug::General, "OnTap mPreEditFlag[%s] mEditOnTouch[%s] mEditModeActive[%s] ", (mPreEditFlag)?"true":"false"
1404                                                                                                            , (mEditOnTouch)?"true":"false"
1405                                                                                                            , (mEditModeActive)?"true":"false");
1406
1407   if( mHandleOneGrabArea == actor || mHandleTwoGrabArea == actor )
1408   {
1409     return;
1410   }
1411
1412   if( mGrabArea == actor )
1413   {
1414     if( mPopUpPanel.GetState() == TextInputPopup::StateHidden || mPopUpPanel.GetState() == TextInputPopup::StateHiding )
1415     {
1416       SetUpPopUpSelection();
1417       ShowPopup();
1418     }
1419
1420     return;
1421   }
1422
1423   HidePopup();
1424   RemoveHighlight();
1425
1426   mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1427
1428   // Initially don't create the grab handle.
1429   bool createGrabHandle = false;
1430
1431   if ( !mEditModeActive )
1432   {
1433     // update line height before calculate the actual position.
1434     UpdateLineHeight();
1435
1436     // Only start edit mode if TextInput configured to edit on touch
1437     if ( mEditOnTouch )
1438     {
1439       // Set the initial cursor position in the tap point.
1440       ReturnClosestIndex(tap.localPoint, mCursorPosition );
1441
1442       // Create the grab handle.
1443       // TODO Make this a re-usable function.
1444       if ( IsGrabHandleEnabled() )
1445       {
1446         const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1447
1448         CreateGrabHandle();
1449
1450         mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1451         mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1452         mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1453         ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1454
1455       }
1456
1457       // Edit mode started after grab handle created to ensure the signal InputStarted is sent last.
1458       // This is used to ensure if selecting text hides the grab handle then this code is run after grab handle is created,
1459       // otherwise the Grab handle will be shown when selecting.
1460
1461       StartEditMode();
1462     }
1463   }
1464   else
1465   {
1466     // Show the keyboard if it was hidden.
1467     if (!VirtualKeyboard::IsVisible())
1468     {
1469       VirtualKeyboard::Show();
1470     }
1471
1472     // Reset keyboard as tap event has occurred.
1473     // Set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1474     PreEditReset( true );
1475
1476     GetTextLayoutInfo();
1477
1478     if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() ) // If string empty we do not need a grab handle.
1479     {
1480       // As already in edit mode, reposition cursor near tap and show grab handle for cursor, if grab handle not enabled then magnifier will be used instead.
1481
1482       ReturnClosestIndex(tap.localPoint, mCursorPosition );
1483
1484       DALI_LOG_INFO( gLogFilter, Debug::General, "mCursorPosition[%u]", mCursorPosition );
1485
1486       // Notify keyboard so it can 're-capture' word for predictive text.
1487       // As we have done a reset, is this required, expect IMF keyboard to request this information.
1488       ImfManager imfManager = ImfManager::Get();
1489       if ( imfManager )
1490       {
1491         imfManager.SetCursorPosition ( mCursorPosition );
1492         imfManager.NotifyCursorPosition();
1493       }
1494       const TextStyle oldInputStyle( mInputStyle );
1495
1496       mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1497
1498       DrawCursor();
1499
1500       // Create the grab handle.
1501       // Grab handle is created later.
1502       createGrabHandle = true;
1503
1504       if( oldInputStyle != mInputStyle )
1505       {
1506         // Updates the line height accordingly with the input style.
1507         UpdateLineHeight();
1508
1509         EmitStyleChangedSignal();
1510       }
1511     }
1512   }
1513
1514   if ( createGrabHandle && IsGrabHandleEnabled() )
1515   {
1516     const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1517
1518     CreateGrabHandle();
1519
1520     mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1521     mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1522     mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1523     ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1524
1525   }
1526 }
1527
1528 void TextInput::OnLongPress(Dali::Actor actor, Dali::LongPressGesture longPress)
1529 {
1530   DALI_LOG_INFO( gLogFilter, Debug::General, "OnLongPress\n" );
1531
1532   if(longPress.state == Dali::Gesture::Started)
1533   {
1534     // Start edit mode on long press
1535     if ( !mEditModeActive )
1536     {
1537       StartEditMode();
1538     }
1539
1540     // If text exists then select nearest word.
1541     if ( !mStyledText.empty())
1542     {
1543       HidePopup();
1544
1545       ShowGrabHandleAndSetVisibility( false );
1546
1547
1548       if ( mPreEditFlag )
1549       {
1550         // PreEdit will be committed here without needing a commit from IMF.  Remove pre-edit underline and reset flags which
1551         // converts the pre-edit word being displayed to a committed word.
1552         if ( !mUnderlinedPriorToPreEdit )
1553         {
1554           TextStyle style;
1555           style.SetUnderline( false );
1556           ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1557         }
1558         mPreEditFlag = false;
1559         mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1560         // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1561         PreEditReset( false );
1562       }
1563       mCursorPosition = 0;
1564
1565       mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1566       ReturnClosestIndex( longPress.localPoint, mCursorPosition );
1567
1568       ImfManager imfManager = ImfManager::Get();
1569       if ( imfManager )
1570       {
1571         imfManager.SetCursorPosition ( mCursorPosition );
1572         imfManager.NotifyCursorPosition();
1573       }
1574       std::size_t start = 0;
1575       std::size_t end = 0;
1576       Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1577
1578       SelectText( start, end );
1579     }
1580
1581     // if no text but clipboard has content then show paste option, if no text and clipboard empty then do nothing
1582     if ( ( mClipboard && mClipboard.NumberOfItems() ) || !mStyledText.empty() )
1583     {
1584       ShowPopupCutCopyPaste();
1585     }
1586   }
1587 }
1588
1589 void TextInput::OnClipboardTextSelected( ClipboardEventNotifier& notifier )
1590 {
1591   const Text clipboardText( notifier.GetContent() );
1592   PasteText( clipboardText );
1593
1594   SetCursorVisibility( true );
1595   StartCursorBlinkTimer();
1596
1597   ShowGrabHandleAndSetVisibility( false );
1598
1599
1600   HidePopup();
1601 }
1602
1603 bool TextInput::OnPopupButtonPressed( Toolkit::Button button )
1604 {
1605   mPopUpPanel.PressedSignal().Disconnect( this, &TextInput::OnPopupButtonPressed );
1606
1607   const std::string& name = button.GetName();
1608
1609   if(name == TextInputPopup::OPTION_SELECT_WORD)
1610   {
1611     std::size_t start = 0;
1612     std::size_t end = 0;
1613     Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1614
1615     SelectText( start, end );
1616   }
1617   else if(name == TextInputPopup::OPTION_SELECT_ALL)
1618   {
1619     SetCursorVisibility(false);
1620     StopCursorBlinkTimer();
1621
1622     std::size_t end = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
1623     std::size_t start = 0;
1624
1625     SelectText( start, end );
1626   }
1627   else if(name == TextInputPopup::OPTION_CUT)
1628   {
1629     bool ret = CopySelectedTextToClipboard();
1630
1631     if ( ret )
1632     {
1633       DeleteHighlightedText( true );
1634       CursorUpdate();
1635     }
1636
1637     SetCursorVisibility( true );
1638     StartCursorBlinkTimer();
1639
1640     HidePopup();
1641   }
1642   else if(name == TextInputPopup::OPTION_COPY)
1643   {
1644     CopySelectedTextToClipboard();
1645
1646     RemoveHighlight();
1647
1648     SetCursorVisibility( true );
1649     StartCursorBlinkTimer();
1650
1651     HidePopup();
1652   }
1653   else if(name == TextInputPopup::OPTION_PASTE)
1654   {
1655     const Text retrievedString( mClipboard.GetItem( 0 ) );  // currently can only get first item in clip board, index 0;
1656
1657     PasteText(retrievedString);
1658
1659     SetCursorVisibility( true );
1660     StartCursorBlinkTimer();
1661
1662     ShowGrabHandleAndSetVisibility( false );
1663
1664     HidePopup();
1665   }
1666   else if(name == TextInputPopup::OPTION_CLIPBOARD)
1667   {
1668     // In the case of clipboard being shown we do not want to show updated pop-up after hide animation completes
1669     // Hence pass the false parameter for signalFinished.
1670     HidePopup( true, false );
1671     mClipboard.ShowClipboard();
1672   }
1673
1674   return false;
1675 }
1676
1677 bool TextInput::OnCursorBlinkTimerTick()
1678 {
1679   // Cursor blinking
1680   mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1681   if ( mCursorRTLEnabled )
1682   {
1683     mCursorRTL.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1684   }
1685   mCursorBlinkStatus = !mCursorBlinkStatus;
1686
1687   return true;
1688 }
1689
1690 void TextInput::OnPopupHideFinished(TextInputPopup& popup)
1691 {
1692   popup.HideFinishedSignal().Disconnect( this, &TextInput::OnPopupHideFinished );
1693
1694   // Change Popup menu to Cut/Copy/Paste if text has been selected.
1695   if(mHighlightMeshActor && mState == StateEdit)
1696   {
1697     ShowPopupCutCopyPaste();
1698   }
1699 }
1700
1701 //FIXME this routine needs to be re-written as it contains too many branches.
1702 bool TextInput::OnKeyDownEvent(const KeyEvent& event)
1703 {
1704   std::string keyName = event.keyPressedName;
1705   std::string keyString = event.keyPressed;
1706
1707   DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyDownEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1708
1709   // Do not consume "Tab" and "Escape" keys.
1710   if(keyName == "Tab" || keyName == "Escape")
1711   {
1712     // Escape key to end the edit mode
1713     EndEditMode();
1714
1715     return false;
1716   }
1717
1718   HidePopup(); // If Pop-up shown then hides it as editing text.
1719
1720   // Update Flag, indicates whether to update the text-input contents or not.
1721   // Any key stroke that results in a visual change of the text-input should
1722   // set this flag to true.
1723   bool update(false);
1724
1725   // Whether to scroll text to cursor position.
1726   // Scroll is needed always the cursor is updated and after the pre-edit is received.
1727   bool scroll = false;
1728
1729   if (keyName == "Return")
1730   {
1731     if ( mNumberOflinesLimit > 1) // Prevents New line character / Return adding an extra line if limit set to 1
1732     {
1733       bool preEditFlagPreviouslySet( mPreEditFlag );
1734
1735       if (mHighlightMeshActor)
1736       {
1737         // replaces highlighted text with new line
1738         DeleteHighlightedText( false );
1739       }
1740       mCursorPosition = mCursorPosition + InsertAt( Text( NEWLINE ), mCursorPosition, 0 );
1741
1742       // If we are in pre-edit mode then pressing enter will cause a commit.  But the commit string does not include the
1743       // '\n' character so we need to ensure that the immediately following commit knows how it occurred.
1744       if ( mPreEditFlag )
1745       {
1746         mCommitByKeyInput = true;
1747       }
1748
1749       // If attempting to insert a new-line brings us out of PreEdit mode, then we should not ignore the next commit.
1750       if ( preEditFlagPreviouslySet && !mPreEditFlag )
1751       {
1752         mPreEditFlag = true;
1753         mIgnoreCommitFlag = false;
1754       }
1755       EmitTextModified();
1756       update = true;
1757     }
1758     else
1759     {
1760       RemoveHighlight();
1761     }
1762   } // Return
1763   else if ( keyName == "space" )
1764   {
1765     if ( mHighlightMeshActor )
1766     {
1767       // Some text is selected so erase it before adding space.
1768       DeleteHighlightedText( true );
1769       update = true;
1770     }
1771
1772     mCursorPosition = mCursorPosition + InsertAt(Text(keyString), mCursorPosition, 0);
1773
1774     // If we are in pre-edit mode then pressing the space-bar will cause a commit.  But the commit string does not include the
1775     // ' ' character so we need to ensure that the immediately following commit knows how it occurred.
1776     if ( mPreEditFlag )
1777     {
1778       mCommitByKeyInput = true;
1779     }
1780     EmitTextModified();
1781     update = true;
1782   } // space
1783   else if (keyName == "BackSpace")
1784   {
1785     if ( mHighlightMeshActor )
1786     {
1787       // Some text is selected so erase it
1788       DeleteHighlightedText( true );
1789       update = true;
1790     }
1791     else
1792     {
1793       if ( mCursorPosition > 0 )
1794       {
1795         DeleteCharacter( mCursorPosition );
1796         update = true;
1797       }
1798     }
1799     EmitTextModified();
1800   } // BackSpace
1801   else if (keyName == "Right")
1802   {
1803     AdvanceCursor();
1804     RemoveHighlight();
1805   }
1806   else if (keyName == "Left")
1807   {
1808     AdvanceCursor(true);
1809     RemoveHighlight();
1810   }
1811   else // event is a character
1812   {
1813     // Some text may be selected, hiding keyboard causes an empty keystring to be sent, we don't want to delete highlight in this case
1814     if ( !keyString.empty() )
1815     {
1816       if ( mHighlightMeshActor )
1817       {
1818         // replaces highlighted text with new character
1819         DeleteHighlightedText( false );
1820       }
1821
1822
1823       // Received key String
1824       mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, 0 );
1825       update = true;
1826       EmitTextModified();
1827     }
1828   }
1829
1830   // If key event has resulted in a change in the text/cursor, then trigger a relayout of text
1831   // as this is a costly operation.
1832   if(update)
1833   {
1834     CursorUpdate();
1835   }
1836
1837   if(update || scroll)
1838   {
1839     if( IsScrollEnabled() )
1840     {
1841       // Calculates the new cursor position (in actor coordinates)
1842       const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
1843
1844       ScrollTextViewToMakeCursorVisible( cursorPosition );
1845     }
1846   }
1847
1848   return true;
1849 }
1850
1851 bool TextInput::OnKeyUpEvent(const KeyEvent& event)
1852 {
1853   std::string keyName = event.keyPressedName;
1854   std::string keyString = event.keyPressed;
1855
1856   DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyUpEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1857
1858   // The selected text become deselected when the key code is DALI_KEY_BACK.
1859   if( IsTextSelected() && ( keyName == "XF86Stop" || keyName == "XF86Send") )
1860   {
1861     DeSelectText();
1862     return true;
1863   }
1864
1865   return false;
1866 }
1867
1868 void TextInput::OnTextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition )
1869 {
1870   // Updates the stored scroll position.
1871   mTextLayoutInfo.mScrollOffset = textView.GetScrollPosition();
1872
1873   const Vector3& controlSize = GetControlSize();
1874   Size cursorSize( CURSOR_THICKNESS, 0.f );
1875
1876   // Updates the cursor and grab handle position and visibility.
1877   if( mGrabHandle || mCursor )
1878   {
1879     cursorSize.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height;
1880     const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1881
1882     mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( cursorPosition, cursorSize, controlSize );
1883
1884     mActualGrabHandlePosition = cursorPosition.GetVectorXY();
1885
1886     if( mGrabHandle )
1887     {
1888       ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1889       mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1890     }
1891
1892     if( mCursor )
1893     {
1894       mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
1895       mCursor.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1896     }
1897   }
1898
1899   // Updates the selection handles and highlighted text position and visibility.
1900   if( mSelectionHandleOne && mSelectionHandleTwo )
1901   {
1902     const Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition(mSelectionHandleOnePosition);
1903     const Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition(mSelectionHandleTwoPosition);
1904     cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleOnePosition ) ).mSize.height;
1905     const bool isSelectionHandleOneVisible = IsPositionInsideBoundaries( cursorPositionOne, cursorSize, controlSize );
1906     cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleTwoPosition ) ).mSize.height;
1907     const bool isSelectionHandleTwoVisible = IsPositionInsideBoundaries( cursorPositionTwo, cursorSize, controlSize );
1908
1909     mSelectionHandleOneActualPosition = cursorPositionOne.GetVectorXY();
1910     mSelectionHandleTwoActualPosition = cursorPositionTwo.GetVectorXY();
1911
1912     mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
1913     mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
1914     mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
1915     mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
1916
1917     if( mHighlightMeshActor )
1918     {
1919       mHighlightMeshActor.SetVisible( true );
1920       UpdateHighlight();
1921     }
1922   }
1923 }
1924
1925 void TextInput::ScrollTextViewToMakeCursorVisible( const Vector3& cursorPosition )
1926 {
1927   // Scroll the text to make the cursor visible.
1928   const Size cursorSize( CURSOR_THICKNESS,
1929                          GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height );
1930
1931   // Need to scroll the text to make the cursor visible and to cover the whole text-input area.
1932
1933   const Vector3& controlSize = GetControlSize();
1934
1935   // Calculates the new scroll position.
1936   Vector2 scrollOffset = mTextLayoutInfo.mScrollOffset;
1937   if( ( cursorPosition.x < 0.f ) || ( cursorPosition.x > controlSize.width ) )
1938   {
1939     scrollOffset.x += cursorPosition.x;
1940   }
1941
1942   if( cursorPosition.y - cursorSize.height < 0.f )
1943   {
1944     scrollOffset.y += ( cursorPosition.y - cursorSize.height );
1945   }
1946   else if( cursorPosition.y > controlSize.height )
1947   {
1948     scrollOffset.y += cursorPosition.y;
1949   }
1950
1951   // Sets the new scroll position.
1952   SetScrollPosition( Vector2::ZERO ); // TODO: need to reset to the zero position in order to make the scroll trim to work.
1953   SetScrollPosition( scrollOffset );
1954 }
1955
1956 void TextInput::StartScrollTimer()
1957 {
1958   if( !mScrollTimer )
1959   {
1960     mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1961     mScrollTimer.TickSignal().Connect( this, &TextInput::OnScrollTimerTick );
1962   }
1963
1964   if( !mScrollTimer.IsRunning() )
1965   {
1966     mScrollTimer.Start();
1967   }
1968 }
1969
1970 void TextInput::StopScrollTimer()
1971 {
1972   if( mScrollTimer )
1973   {
1974     mScrollTimer.Stop();
1975   }
1976 }
1977
1978 bool TextInput::OnScrollTimerTick()
1979 {
1980   // TODO: need to set the new style accordingly the new handle position.
1981
1982   if( !( mGrabHandleVisibility && mGrabHandle ) && !( mSelectionHandleOne && mSelectionHandleTwo ) )
1983   {
1984     // nothing to do if all handles are invisible or doesn't exist.
1985     return true;
1986   }
1987
1988   // Text scrolling
1989
1990   // Choose between the grab handle or the selection handles.
1991   Vector3& actualHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mActualGrabHandlePosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
1992   std::size_t& handlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCursorPosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
1993   Vector3& currentHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCurrentHandlePosition : mCurrentSelectionHandlePosition;
1994
1995   std::size_t newCursorPosition = 0;
1996   ReturnClosestIndex( actualHandlePosition.GetVectorXY(), newCursorPosition );
1997
1998   // Whether the handle's position is different of the previous one and in the case of the selection handle,
1999   // the new selection handle's position needs to be different of the other one.
2000   const bool differentSelectionHandles = ( mGrabHandleVisibility && mGrabHandle ) ? newCursorPosition != handlePosition :
2001                                          ( mCurrentSelectionId == HandleOne ) ? ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleTwoPosition ) :
2002                                                                                 ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleOnePosition );
2003
2004   if( differentSelectionHandles )
2005   {
2006     handlePosition = newCursorPosition;
2007
2008     const Vector3 actualPosition = GetActualPositionFromCharacterPosition( newCursorPosition );
2009
2010     Vector2 scrollDelta = ( actualPosition - currentHandlePosition ).GetVectorXY();
2011
2012     Vector2 scrollPosition = mDisplayedTextView.GetScrollPosition();
2013     scrollPosition += scrollDelta;
2014     SetScrollPosition( scrollPosition );
2015
2016     if( mDisplayedTextView.IsScrollPositionTrimmed() )
2017     {
2018       StopScrollTimer();
2019     }
2020
2021     currentHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition ).GetVectorXY();
2022   }
2023
2024   actualHandlePosition.x += mScrollDisplacement.x;
2025   actualHandlePosition.y += mScrollDisplacement.y;
2026
2027   return true;
2028 }
2029
2030 // Public Internal Methods (public for testing purpose)
2031
2032 void TextInput::SetUpTouchEvents()
2033 {
2034   if ( !mTapDetector )
2035   {
2036     mTapDetector = TapGestureDetector::New();
2037     // Attach the actors and connect the signal
2038     mTapDetector.Attach(Self());
2039
2040     // As contains children which may register for tap the default control detector is not used.
2041     mTapDetector.DetectedSignal().Connect(this, &TextInput::OnTextTap);
2042   }
2043
2044   if ( !mDoubleTapDetector )
2045   {
2046     mDoubleTapDetector = TapGestureDetector::New();
2047     mDoubleTapDetector.SetTapsRequired( 2 );
2048     mDoubleTapDetector.DetectedSignal().Connect(this, &TextInput::OnDoubleTap);
2049
2050     // Only attach and detach the actor to the double tap detector when we enter/leave edit mode
2051     // so that we do not, unnecessarily, have a double tap request all the time
2052   }
2053
2054   if ( !mPanGestureDetector )
2055   {
2056     mPanGestureDetector = PanGestureDetector::New();
2057     mPanGestureDetector.DetectedSignal().Connect(this, &TextInput::OnHandlePan);
2058   }
2059
2060   if ( !mLongPressDetector )
2061   {
2062     mLongPressDetector = LongPressGestureDetector::New();
2063     mLongPressDetector.DetectedSignal().Connect(this, &TextInput::OnLongPress);
2064     mLongPressDetector.Attach(Self());
2065   }
2066 }
2067
2068 void TextInput::CreateTextViewActor()
2069 {
2070   mDisplayedTextView = Toolkit::TextView::New();
2071   mDisplayedTextView.SetMarkupProcessingEnabled( mMarkUpEnabled );
2072   mDisplayedTextView.SetParentOrigin(ParentOrigin::TOP_LEFT);
2073   mDisplayedTextView.SetAnchorPoint(AnchorPoint::TOP_LEFT);
2074   mDisplayedTextView.SetMultilinePolicy(Toolkit::TextView::SplitByWord);
2075   mDisplayedTextView.SetWidthExceedPolicy( Toolkit::TextView::Original );
2076   mDisplayedTextView.SetHeightExceedPolicy( Toolkit::TextView::Original );
2077   mDisplayedTextView.SetLineJustification( Toolkit::TextView::Left );
2078   mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>( Toolkit::Alignment::HorizontalLeft | Toolkit::Alignment::VerticalTop ) );
2079   mDisplayedTextView.SetPosition( Vector3( 0.0f, 0.0f, DISPLAYED_TEXT_VIEW_Z_OFFSET ) );
2080   mDisplayedTextView.SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
2081
2082   mDisplayedTextView.ScrolledSignal().Connect( this, &TextInput::OnTextViewScrolled );
2083
2084   Self().Add( mDisplayedTextView );
2085 }
2086
2087 // Start a timer to initiate, used by the cursor to blink.
2088 void TextInput::StartCursorBlinkTimer()
2089 {
2090   if ( !mCursorBlinkTimer )
2091   {
2092     mCursorBlinkTimer = Timer::New( CURSOR_BLINK_INTERVAL );
2093     mCursorBlinkTimer.TickSignal().Connect( this, &TextInput::OnCursorBlinkTimerTick );
2094   }
2095
2096   if ( !mCursorBlinkTimer.IsRunning() )
2097   {
2098     mCursorBlinkTimer.Start();
2099   }
2100 }
2101
2102 // Start a timer to initiate, used by the cursor to blink.
2103 void TextInput::StopCursorBlinkTimer()
2104 {
2105   if ( mCursorBlinkTimer )
2106   {
2107     mCursorBlinkTimer.Stop();
2108   }
2109 }
2110
2111 void TextInput::StartEditMode()
2112 {
2113   DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput StartEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2114
2115   if(!mEditModeActive)
2116   {
2117     SetKeyInputFocus();
2118   }
2119
2120   if ( mDoubleTapDetector )
2121   {
2122     mDoubleTapDetector.Attach( Self() );
2123   }
2124 }
2125
2126 void TextInput::EndEditMode()
2127 {
2128   DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput EndEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2129
2130   ClearKeyInputFocus();
2131
2132   if ( mDoubleTapDetector )
2133   {
2134     mDoubleTapDetector.Detach( Self() );
2135   }
2136 }
2137
2138 void TextInput::ApplyPreEditStyle( std::size_t preEditStartPosition, std::size_t preEditStringLength )
2139 {
2140   if ( mPreEditFlag && ( preEditStringLength > 0 ) )
2141   {
2142     mUnderlinedPriorToPreEdit = mInputStyle.IsUnderlineEnabled();
2143     TextStyle style;
2144     style.SetUnderline( true );
2145     ApplyStyleToRange( style, TextStyle::UNDERLINE , preEditStartPosition, preEditStartPosition + preEditStringLength -1 );
2146   }
2147 }
2148
2149 void TextInput::RemovePreEditStyle()
2150 {
2151   if ( !mUnderlinedPriorToPreEdit )
2152   {
2153     TextStyle style;
2154     style.SetUnderline( false );
2155     SetActiveStyle( style, TextStyle::UNDERLINE );
2156   }
2157 }
2158
2159 // IMF related methods
2160
2161
2162 ImfManager::ImfCallbackData TextInput::ImfEventReceived( Dali::ImfManager& imfManager, const ImfManager::ImfEventData&  imfEvent )
2163 {
2164   bool update( false );
2165   bool preeditResetRequired ( false );
2166
2167   if (imfEvent.eventName != ImfManager::GETSURROUNDING )
2168   {
2169     HidePopup(); // If Pop-up shown then hides it as editing text.
2170   }
2171
2172   switch ( imfEvent.eventName )
2173   {
2174     case ImfManager::PREEDIT:
2175     {
2176       mIgnoreFirstCommitFlag = false;
2177
2178       // Some text may be selected, hiding keyboard causes an empty predictive string to be sent, we don't want to delete highlight in this case
2179       if ( mHighlightMeshActor &&  (!imfEvent.predictiveString.empty()) )
2180       {
2181         // replaces highlighted text with new character
2182         DeleteHighlightedText( false );
2183       }
2184
2185       preeditResetRequired = PreEditReceived( imfEvent.predictiveString, imfEvent.cursorOffset );
2186
2187       if( IsScrollEnabled() )
2188       {
2189         // Calculates the new cursor position (in actor coordinates)
2190         const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2191         ScrollTextViewToMakeCursorVisible( cursorPosition );
2192       }
2193
2194       update = true;
2195
2196       break;
2197     }
2198     case ImfManager::COMMIT:
2199     {
2200       if( mIgnoreFirstCommitFlag )
2201       {
2202         // Do not commit in this case when keyboard sends a commit when shows for the first time (work-around for imf keyboard).
2203         mIgnoreFirstCommitFlag = false;
2204       }
2205       else
2206       {
2207         // A Commit message is a word that has been accepted, it may have been a pre-edit word previously but now commited.
2208
2209         // Some text may be selected, hiding keyboard causes an empty predictive string to be sent, we don't want to delete highlight in this case
2210         if ( mHighlightMeshActor &&  (!imfEvent.predictiveString.empty()) )
2211         {
2212           // replaces highlighted text with new character
2213           DeleteHighlightedText( false );
2214         }
2215
2216        // A PreEditReset can cause a commit message to be sent, the Ignore Commit flag is used in scenarios where the word is
2217        // not needed, one such scenario is when the pre-edit word is too long to fit.
2218        if ( !mIgnoreCommitFlag )
2219        {
2220          update = CommitReceived( imfEvent.predictiveString );
2221        }
2222        else
2223        {
2224          mIgnoreCommitFlag = false; // reset ignore flag so next commit is acted upon.
2225        }
2226       }
2227
2228       if( update )
2229       {
2230         if( IsScrollEnabled() )
2231         {
2232           // Calculates the new cursor position (in actor coordinates)
2233           const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2234
2235           ScrollTextViewToMakeCursorVisible( cursorPosition );
2236         }
2237       }
2238       break;
2239     }
2240     case ImfManager::DELETESURROUNDING:
2241     {
2242       DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - delete surrounding mPreEditFlag[%s] cursor offset[%d] characters to delete[%d] position to delete[%u] \n",
2243                      (mPreEditFlag)?"true":"false", imfEvent.cursorOffset, imfEvent.numberOfChars, static_cast<std::size_t>( mCursorPosition+imfEvent.cursorOffset) );
2244
2245       mPreEditFlag = false;
2246
2247       std::size_t toDelete = 0;
2248       std::size_t numberOfCharacters = 0;
2249
2250       if( mHighlightMeshActor )
2251       {
2252         // delete highlighted text.
2253         toDelete = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2254         numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - toDelete;
2255       }
2256       else
2257       {
2258         if( std::abs( imfEvent.cursorOffset ) < mCursorPosition )
2259         {
2260           toDelete = mCursorPosition + imfEvent.cursorOffset;
2261         }
2262         if( toDelete + imfEvent.numberOfChars > mStyledText.size() )
2263         {
2264           numberOfCharacters = mStyledText.size() - toDelete;
2265         }
2266         else
2267         {
2268           numberOfCharacters = imfEvent.numberOfChars;
2269         }
2270       }
2271       DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding pre-delete range mCursorPosition[%u] \n", mCursorPosition);
2272       DeleteRange( toDelete, numberOfCharacters );
2273
2274       mCursorPosition = toDelete;
2275       mNumberOfSurroundingCharactersDeleted = numberOfCharacters;
2276
2277       EmitTextModified();
2278
2279       DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding post-delete range mCursorPosition[%u] \n", mCursorPosition);
2280       break;
2281     }
2282     case ImfManager::GETSURROUNDING:
2283     {
2284       // If text is selected/highlighted and surrounding text received we do not want the keyboard to store the word at cursor and return it as a predictive word along with
2285       // the next key pressed.  Instead the Select function sets the cursor position and surrounding text.
2286       if (! ( mHighlightMeshActor || mSelectingText ) )
2287       {
2288         std::string text( GetText() );
2289         DALI_LOG_INFO( gLogFilter, Debug::General, "OnKey - surrounding text - set text [%s] and cursor[%u] \n", text.c_str(), mCursorPosition );
2290
2291         imfManager.SetCursorPosition( mCursorPosition );
2292         imfManager.SetSurroundingText( text );
2293       }
2294
2295       if( 0 != mNumberOfSurroundingCharactersDeleted )
2296       {
2297         mDisplayedTextView.RemoveTextFrom( mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2298         mNumberOfSurroundingCharactersDeleted = 0;
2299
2300         if( mStyledText.empty() )
2301         {
2302           // Styled text is empty, so set the placeholder text.
2303           mDisplayedTextView.SetText( mStyledPlaceHolderText );
2304           mPlaceHolderSet = true;
2305         }
2306       }
2307       break;
2308     }
2309     case ImfManager::VOID:
2310     {
2311       DALI_ASSERT_DEBUG( false );
2312     }
2313   } // end switch
2314
2315   ImfManager::ImfCallbackData callbackData( update, mCursorPosition, GetText(), preeditResetRequired );
2316
2317   return callbackData;
2318 }
2319
2320 bool TextInput::PreEditReceived(const std::string& keyString, std::size_t cursorOffset )
2321 {
2322   mPreserveCursorPosition = false;  // As in pre-edit state we should have the cursor at the end of the word displayed not last touch position.
2323
2324   DALI_LOG_INFO(gLogFilter, Debug::General, ">>PreEditReceived preserveCursorPos[%d] mCursorPos[%d] mPreEditFlag[%d]\n",
2325                 mPreserveCursorPosition, mCursorPosition, mPreEditFlag );
2326
2327   bool preeditResetRequest ( false );
2328
2329   if( mPreEditFlag ) // Already in pre-edit state.
2330   {
2331     if( mStyledText.size() >= mMaxStringLength )
2332     {
2333       DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived styledTextSize >= mMaxStringLength \n");
2334       // Cannot fit these characters into field, clear pre-edit.
2335       if ( !mUnderlinedPriorToPreEdit )
2336       {
2337         TextStyle style;
2338         style.SetUnderline( false );
2339         ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
2340       }
2341       mIgnoreCommitFlag = true;
2342       preeditResetRequest = false; // this will reset the keyboard's predictive suggestions.
2343       mPreEditFlag = false;
2344       EmitMaxInputCharactersReachedSignal();
2345     }
2346     else
2347     {
2348       // delete existing pre-edit string
2349       const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2350
2351       // Store new pre-edit string
2352       mPreEditString.SetText( keyString );
2353
2354       if ( keyString.empty() )
2355       {
2356         mPreEditFlag = false;
2357         mCursorPosition = mPreEditStartPosition;
2358
2359         if( mStyledText.empty() )
2360         {
2361           // Styled text is empty, so set the placeholder text.
2362           mDisplayedTextView.SetText( mStyledPlaceHolderText );
2363           mPlaceHolderSet = true;
2364         }
2365         else
2366         {
2367           mDisplayedTextView.RemoveTextFrom( mPreEditStartPosition, numberOfCharactersToReplace );
2368         }
2369         GetTextLayoutInfo();
2370         EmitTextModified();
2371       }
2372       else
2373       {
2374         // Insert new pre-edit string. InsertAt updates the size and position table.
2375         mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersToReplace );
2376         // If word was too long to be inserted then cursorOffset would be out of range as keyboard assumes there is not limit. Hence use of std::min.
2377         mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2378         ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2379         DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] \n", mCursorPosition);
2380         EmitTextModified();
2381       }
2382       // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2383       DrawCursor();
2384     }
2385   }
2386   else  // mPreEditFlag not set
2387   {
2388     if ( !keyString.empty() ) // Imf can send an empty pre-edit followed by Backspace instead of a commit.
2389     {
2390       DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived Initial Pre-Edit string \n");
2391       // new pre-edit so move into pre-edit state by setting flag
2392       mPreEditFlag = true;
2393       mPreEditString.SetText( keyString ); // store new pre-edit string
2394       mPreEditStartPosition = mCursorPosition; // store starting cursor position of pre-edit so know where to re-start from
2395       mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, 0 );
2396       // If word was too long to be inserted then cursorOffset would be out of range as keyboard assumes there is not limit. Hence use of std::min.
2397       mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2398       ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2399       DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] mPreEditStartPosition[%u]\n", mCursorPosition, mPreEditStartPosition);
2400       // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2401       DrawCursor();
2402       EmitTextModified();
2403     }
2404     else
2405     {
2406       DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived with empty keyString\n");
2407     }
2408   }
2409
2410   return preeditResetRequest;
2411 }
2412
2413 bool TextInput::CommitReceived(const std::string& keyString )
2414 {
2415   DALI_LOG_INFO(gLogFilter, Debug::General, ">>CommitReceived preserveCursorPos[%d] mPreEditStartPosition [%d] mCursorPos[%d] mPreEditFlag[%d] mIgnoreCommitFlag[%s]\n",
2416       mPreserveCursorPosition, mPreEditStartPosition, mCursorPosition, mPreEditFlag, (mIgnoreCommitFlag)?"true":"false" );
2417
2418   bool update( false );
2419
2420   RemovePreEditStyle();
2421
2422   const std::size_t styledTextSize( mStyledText.size() );
2423   if( styledTextSize >= mMaxStringLength )
2424   {
2425     // Cannot fit these characters into field, clear pre-edit.
2426     if ( mPreEditFlag )
2427     {
2428       mIgnoreCommitFlag = true;
2429       mPreEditFlag = false;
2430     }
2431     EmitMaxInputCharactersReachedSignal();
2432   }
2433   else
2434   {
2435     if( mPreEditFlag )
2436     {
2437       // delete existing pre-edit string
2438       const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2439       mPreEditFlag = false;
2440
2441       DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived mPreserveCursorPosition[%s] mPreEditStartPosition[%u]\n",
2442                     (mPreserveCursorPosition)?"true":"false", mPreEditStartPosition );
2443
2444       if ( mPreserveCursorPosition ) // PreEditReset has been called triggering this commit.
2445       {
2446         // No need to update cursor position as Cursor location given by touch.
2447         InsertAt( Text( keyString ), mPreEditStartPosition, numberOfCharactersToReplace );
2448         mPreserveCursorPosition = false;
2449       }
2450       else
2451       {
2452         // Cursor not set by touch so needs to be re-positioned to input more text
2453         mCursorPosition = mPreEditStartPosition + InsertAt( Text(keyString), mPreEditStartPosition, numberOfCharactersToReplace ); // update cursor position as InsertAt, re-draw cursor with this
2454
2455         // If a space or enter caused the commit then our string is one longer than the string given to us by the commit key.
2456         if ( mCommitByKeyInput )
2457         {
2458           mCursorPosition = std::min ( mCursorPosition + 1, mStyledText.size() );
2459           mCommitByKeyInput = false;
2460         }
2461       }
2462
2463       EmitTextModified();
2464
2465       if ( mSelectTextOnCommit )
2466       {
2467         SelectText(mRequestedSelection.mStartOfSelection, mRequestedSelection.mEndOfSelection );
2468       }
2469
2470       update = true;
2471     }
2472     else // mPreEditFlag not set
2473     {
2474       if ( !mIgnoreCommitFlag ) // Check if this commit should be ignored.
2475       {
2476         if( mStyledText.empty() && mPlaceHolderSet )
2477         {
2478           // If the styled text is empty and the placeholder text is set, it needs to be cleared.
2479           mDisplayedTextView.SetText( "" );
2480           mNumberOfSurroundingCharactersDeleted = 0;
2481           mPlaceHolderSet = false;
2482         }
2483         mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2484         update = true;
2485         mNumberOfSurroundingCharactersDeleted = 0;
2486         EmitTextModified();
2487       }
2488       else
2489       {
2490         mIgnoreCommitFlag = false; // Reset flag so future commits will not be ignored.
2491       }
2492     }
2493   }
2494
2495   mSelectTextOnCommit = false;
2496
2497   DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived << mCursorPos[%d] mPreEditFlag[%d] update[%s] \n",
2498                                              mCursorPosition, mPreEditFlag, (update)?"true":"false" );
2499
2500   return update;
2501 }
2502
2503 // End of IMF related methods
2504
2505 std::size_t TextInput::DeletePreEdit()
2506 {
2507   DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeletePreEdit mPreEditFlag[%s] \n", (mPreEditFlag)?"true":"false");
2508
2509   DALI_ASSERT_DEBUG( mPreEditFlag );
2510
2511   const std::size_t preEditStringLength = mPreEditString.GetLength();
2512   const std::size_t styledTextSize = mStyledText.size();
2513
2514   std::size_t endPosition = mPreEditStartPosition + preEditStringLength;
2515
2516   // Prevents erase items outside mStyledText bounds.
2517   if( mPreEditStartPosition > styledTextSize )
2518   {
2519     DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. mPreEditStartPosition > mStyledText.size()" );
2520     mPreEditStartPosition = styledTextSize;
2521   }
2522
2523   if( ( endPosition > styledTextSize ) || ( endPosition < mPreEditStartPosition ) )
2524   {
2525     DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. ( endPosition > mStyledText.size() ) || ( endPosition < mPreEditStartPosition )" );
2526     endPosition = styledTextSize;
2527   }
2528
2529   mStyledText.erase( mStyledText.begin() + mPreEditStartPosition, mStyledText.begin() + endPosition );
2530
2531   // DeletePreEdit() doesn't remove characters from the text-view because may be followed by an InsertAt() which inserts characters,
2532   // in that case, the Insert should use the returned number of deleted characters and replace the text which helps the text-view to
2533   // reuse glyphs.
2534   // In case DeletePreEdit() is not followed by an InsertAt() characters must be deleted after this call.
2535
2536   return preEditStringLength;
2537 }
2538
2539 void TextInput::PreEditReset( bool preserveCursorPosition )
2540 {
2541   DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReset preserveCursorPos[%d] mCursorPos[%d] \n",
2542                 preserveCursorPosition, mCursorPosition);
2543
2544   // Store flag to indicate that we do not want to lose the cursor position as the reset may have occurred due to touch event moving the cursor.
2545   mPreserveCursorPosition = preserveCursorPosition;
2546
2547   // Reset incase we are in a pre-edit state.
2548   ImfManager imfManager = ImfManager::Get();
2549   if ( imfManager )
2550   {
2551     imfManager.Reset(); // Will trigger a commit message
2552   }
2553 }
2554
2555 void TextInput::CursorUpdate()
2556 {
2557   DrawCursor();
2558
2559   ImfManager imfManager = ImfManager::Get();
2560   if ( imfManager )
2561   {
2562     std::string text( GetText() );
2563     imfManager.SetSurroundingText( text );  // Notifying IMF of a cursor change triggers a surrounding text request so updating it now.
2564     imfManager.SetCursorPosition ( mCursorPosition );
2565     imfManager.NotifyCursorPosition();
2566   }
2567 }
2568
2569 /* Delete highlighted characters redisplay*/
2570 void TextInput::DeleteHighlightedText( bool inheritStyle )
2571 {
2572   DALI_LOG_INFO( gLogFilter, Debug::General, "DeleteHighlightedText handlePosOne[%u] handlePosTwo[%u]\n", mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
2573
2574   if(mHighlightMeshActor)
2575   {
2576     mCursorPosition = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2577
2578     MarkupProcessor::StyledTextArray::iterator start = mStyledText.begin() + mCursorPosition;
2579     MarkupProcessor::StyledTextArray::iterator end =  mStyledText.begin() + std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2580
2581     // Get the styled text of the characters to be deleted as it may be needed if
2582     // the "exceed the text-input's boundaries" option is disabled.
2583     MarkupProcessor::StyledTextArray styledCharactersToDelete;
2584
2585     styledCharactersToDelete.insert( styledCharactersToDelete.begin(), start, end );
2586
2587     mStyledText.erase( start, end ); // erase range of characters
2588
2589     // Remove text from TextView.
2590
2591     if( mStyledText.empty() )
2592     {
2593       // Styled text is empty, so set the placeholder text.
2594       mDisplayedTextView.SetText( mStyledPlaceHolderText );
2595       mPlaceHolderSet = true;
2596     }
2597     else
2598     {
2599       const std::size_t numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - mCursorPosition;
2600
2601       mDisplayedTextView.RemoveTextFrom( mCursorPosition, numberOfCharacters );
2602
2603       // It may happen than after removing a white space or a new line character,
2604       // two words merge, this new word could be big enough to not fit in its
2605       // current line, so moved to the next one, and make some part of the text to
2606       // exceed the text-input's boundary.
2607       if( !mExceedEnabled )
2608       {
2609         // Get the new text layout after removing some characters.
2610         mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2611
2612         // Get text-input's size.
2613         const Vector3& size = GetControlSize();
2614
2615         if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2616             ( mTextLayoutInfo.mTextSize.height > size.height ) )
2617         {
2618           mDisplayedTextView.InsertTextAt( mCursorPosition, styledCharactersToDelete );
2619
2620           mStyledText.insert( mStyledText.begin() + mCursorPosition,
2621                               styledCharactersToDelete.begin(),
2622                               styledCharactersToDelete.end() );
2623         }
2624       }
2625     }
2626     GetTextLayoutInfo();
2627
2628     RemoveHighlight();
2629
2630     if( inheritStyle )
2631     {
2632       const TextStyle oldInputStyle( mInputStyle );
2633
2634       mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2635
2636       if( oldInputStyle != mInputStyle )
2637       {
2638         // Updates the line height accordingly with the input style.
2639         UpdateLineHeight();
2640
2641         EmitStyleChangedSignal();
2642       }
2643     }
2644   }
2645 }
2646
2647 void TextInput::DeleteRange( const std::size_t start, const std::size_t ncharacters )
2648 {
2649   DALI_ASSERT_DEBUG( start <= mStyledText.size() );
2650   DALI_ASSERT_DEBUG( !mStyledText.empty() );
2651
2652   DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeleteRange pre mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2653
2654
2655   if ( ( !mStyledText.empty()) && ( ( start + ncharacters ) <= mStyledText.size() ) )
2656   {
2657     MarkupProcessor::StyledTextArray::iterator itStart =  mStyledText.begin() + start;
2658     MarkupProcessor::StyledTextArray::iterator itEnd =  mStyledText.begin() + start + ncharacters;
2659
2660     mStyledText.erase(itStart, itEnd);
2661
2662     // update the selection handles if they are visible.
2663     if( mHighlightMeshActor )
2664     {
2665       std::size_t& minHandle = ( mSelectionHandleOnePosition <= mSelectionHandleTwoPosition ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition );
2666       std::size_t& maxHandle = ( mSelectionHandleTwoPosition > mSelectionHandleOnePosition ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition );
2667
2668       if( minHandle >= start + ncharacters )
2669       {
2670         minHandle -= ncharacters;
2671       }
2672       else if( ( minHandle > start ) && ( minHandle < start + ncharacters ) )
2673       {
2674         minHandle = start;
2675       }
2676
2677       if( maxHandle >= start + ncharacters )
2678       {
2679         maxHandle -= ncharacters;
2680       }
2681       else if( ( maxHandle > start ) && ( maxHandle < start + ncharacters ) )
2682       {
2683         maxHandle = start;
2684       }
2685     }
2686
2687     // Set text is not called here as currently it can not process the set text from deletion and then the set text from the in-coming pre-edit.
2688   }
2689
2690   DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteRange<< post mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2691
2692   // Although mStyledText has been set to a new text string we no longer re-draw the text or notify the cursor change.
2693   // This is a performance decision as the use of this function often means the text is being replaced or just deleted.
2694   // Mean we do not re-draw the text more than we have too.
2695 }
2696
2697 /* Delete character at current cursor position and redisplay*/
2698 void TextInput::DeleteCharacter( std::size_t positionToDelete )
2699 {
2700   // Ensure positionToDelete is not out of bounds.
2701   DALI_ASSERT_DEBUG( positionToDelete <= mStyledText.size() );
2702   DALI_ASSERT_DEBUG( !mStyledText.empty() );
2703   DALI_ASSERT_DEBUG( positionToDelete > 0 );
2704
2705   DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteCharacter positionToDelete[%u]", positionToDelete );
2706
2707
2708   if ( ( !mStyledText.empty()) && ( positionToDelete > 0 ) && positionToDelete <= mStyledText.size() )  // don't try to delete if no characters left of cursor
2709   {
2710     MarkupProcessor::StyledTextArray::iterator it =  mStyledText.begin() + positionToDelete - 1;
2711
2712     // Get the styled text of the character to be deleted as it may be needed if
2713     // the "exceed the text-input's boundaries" option is disabled.
2714     const MarkupProcessor::StyledText styledCharacterToDelete( *it );
2715
2716     mStyledText.erase(it);  // erase the character left of positionToDelete
2717
2718     if( mStyledText.empty() )
2719     {
2720       // Styled text is empty, so set the placeholder text.
2721       mDisplayedTextView.SetText( mStyledPlaceHolderText );
2722       mPlaceHolderSet = true;
2723     }
2724     else
2725     {
2726       mDisplayedTextView.RemoveTextFrom( positionToDelete - 1, 1 );
2727
2728       const Character characterToDelete = styledCharacterToDelete.mText[0];
2729
2730       // It may happen than after removing a white space or a new line character,
2731       // two words merge, this new word could be big enough to not fit in its
2732       // current line, so moved to the next one, and make some part of the text to
2733       // exceed the text-input's boundary.
2734       if( !mExceedEnabled )
2735       {
2736         if( characterToDelete.IsWhiteSpace() || characterToDelete.IsNewLine() )
2737         {
2738           // Get the new text layout after removing one character.
2739           mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2740
2741           // Get text-input's size.
2742           const Vector3& size = GetControlSize();
2743
2744           if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2745               ( mTextLayoutInfo.mTextSize.height > size.height ) )
2746           {
2747             MarkupProcessor::StyledTextArray array;
2748             array.push_back( styledCharacterToDelete );
2749             mDisplayedTextView.InsertTextAt( positionToDelete - 1, array );
2750
2751             mStyledText.insert( mStyledText.begin() + ( positionToDelete - 1 ), styledCharacterToDelete );
2752           }
2753         }
2754       }
2755     }
2756     GetTextLayoutInfo();
2757
2758     ShowGrabHandleAndSetVisibility( false );
2759
2760     mCursorPosition = positionToDelete -1;
2761
2762     const TextStyle oldInputStyle( mInputStyle );
2763
2764     mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2765
2766     if( oldInputStyle != mInputStyle )
2767     {
2768       // Updates the line height accordingly with the input style.
2769       UpdateLineHeight();
2770
2771       EmitStyleChangedSignal();
2772     }
2773   }
2774 }
2775
2776 /*Insert new character into the string and (optionally) redisplay text-input*/
2777 std::size_t TextInput::InsertAt( const Text& newText, const std::size_t insertionPosition, const std::size_t numberOfCharactersToReplace )
2778 {
2779   DALI_LOG_INFO(gLogFilter, Debug::General, "InsertAt insertionPosition[%u]\n", insertionPosition );
2780
2781   // Ensure insertionPosition is not out of bounds.
2782   DALI_ASSERT_ALWAYS( insertionPosition <= mStyledText.size() );
2783
2784   bool textExceedsMaximunNumberOfCharacters = false;
2785   bool textExceedsBoundary = false;
2786   std::size_t insertedStringLength = DoInsertAt( newText, insertionPosition, numberOfCharactersToReplace, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
2787
2788   ShowGrabHandleAndSetVisibility( false );
2789
2790   if( textExceedsMaximunNumberOfCharacters || textExceedsBoundary )
2791   {
2792     if( mPreEditFlag )
2793     {
2794       mIgnoreCommitFlag = true;
2795       mPreEditFlag = false;
2796       // A PreEditReset( false ) should be triggered from here if the keyboards predictive suggestions must be cleared.
2797       // Although can not directly call PreEditReset() as it will cause a recursive emit loop.
2798     }
2799
2800     if( textExceedsMaximunNumberOfCharacters )
2801     {
2802       EmitMaxInputCharactersReachedSignal();
2803     }
2804
2805     if( textExceedsBoundary )
2806     {
2807       EmitInputTextExceedsBoundariesSignal();
2808       PreEditReset( false );
2809     }
2810   }
2811
2812   return insertedStringLength;
2813 }
2814
2815 ImageActor TextInput::CreateCursor( Image cursorImage, const Vector4& border )
2816 {
2817   ImageActor cursor;
2818
2819   if ( cursorImage )
2820   {
2821     cursor = ImageActor::New( cursorImage );
2822   }
2823   else
2824   {
2825     cursor = ImageActor::New( Image::New( DEFAULT_CURSOR ) );
2826   }
2827
2828   cursor.SetStyle(ImageActor::STYLE_NINE_PATCH);
2829   cursor.SetNinePatchBorder( border );
2830
2831   cursor.SetParentOrigin(ParentOrigin::TOP_LEFT);
2832   cursor.SetAnchorPoint(AnchorPoint::BOTTOM_CENTER);
2833   cursor.SetVisible(false);
2834
2835   return cursor;
2836 }
2837
2838 void TextInput::AdvanceCursor(bool reverse, std::size_t places)
2839 {
2840   // As cursor is not moving due to grab handle, handle should be hidden.
2841   ShowGrabHandleAndSetVisibility( false );
2842
2843   bool cursorPositionChanged = false;
2844   if (reverse)
2845   {
2846     if ( mCursorPosition >= places )
2847     {
2848       mCursorPosition = mCursorPosition - places;
2849       cursorPositionChanged = true;
2850     }
2851   }
2852   else
2853   {
2854     if ((mCursorPosition + places) <= mStyledText.size())
2855     {
2856       mCursorPosition = mCursorPosition + places;
2857       cursorPositionChanged = true;
2858     }
2859   }
2860
2861   if( cursorPositionChanged )
2862   {
2863     const std::size_t cursorPositionForStyle = ( 0 == mCursorPosition ? 0 : mCursorPosition - 1 );
2864
2865     const TextStyle oldInputStyle( mInputStyle );
2866     mInputStyle = GetStyleAt( cursorPositionForStyle ); // Inherit style from selected position.
2867
2868     DrawCursor();
2869
2870     if( oldInputStyle != mInputStyle )
2871     {
2872       // Updates the line height accordingly with the input style.
2873       UpdateLineHeight();
2874
2875       EmitStyleChangedSignal();
2876     }
2877
2878     ImfManager imfManager = ImfManager::Get();
2879     if ( imfManager )
2880     {
2881       imfManager.SetCursorPosition ( mCursorPosition );
2882       imfManager.NotifyCursorPosition();
2883     }
2884   }
2885 }
2886
2887 void TextInput::DrawCursor(const std::size_t nthChar)
2888 {
2889   // Get height of cursor and set its size
2890   Size size( CURSOR_THICKNESS, 0.0f );
2891   if (!mTextLayoutInfo.mCharacterLayoutInfoTable.empty())
2892   {
2893     size.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height;
2894   }
2895   else
2896   {
2897     // Measure Font so know how big text will be if no initial text to measure.
2898     size.height = mLineHeight;
2899   }
2900
2901   mCursor.SetSize(size);
2902
2903   // If the character is italic then the cursor also tilts.
2904   mCursor.SetRotation( mInputStyle.IsItalicsEnabled() ? Degree( mInputStyle.GetItalicsAngle() - CURSOR_ANGLE_OFFSET ) : Degree( 0.f ), Vector3::ZAXIS );
2905
2906   DALI_ASSERT_DEBUG( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
2907
2908   if ( ( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) )
2909   {
2910     Vector3 altPosition;    // Alternate (i.e. opposite direction) cursor position.
2911     bool altPositionValid;  // Alternate cursor validity flag.
2912     bool directionRTL;      // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
2913     Vector3 position = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
2914
2915     SetAltCursorEnabled( altPositionValid );
2916
2917     if(!altPositionValid)
2918     {
2919       mCursor.SetPosition( position + UI_OFFSET );
2920     }
2921     else
2922     {
2923       size.height *= 0.5f;
2924       mCursor.SetSize(size);
2925       mCursor.SetPosition( position + UI_OFFSET - Vector3(0.0f, directionRTL ? 0.0f : size.height, 0.0f) );
2926
2927       // TODO: change this cursor pos, to be the one where the cursor is sourced from.
2928       Size rowSize = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) );
2929       size.height = rowSize.height * 0.5f;
2930       mCursorRTL.SetSize(size);
2931       mCursorRTL.SetPosition( altPosition + UI_OFFSET - Vector3(0.0f, directionRTL ? size.height : 0.0f, 0.0f) );
2932     }
2933
2934     if( IsScrollEnabled() )
2935     {
2936       // Whether cursor and grab handle are inside the boundaries of the text-input when text scroll is enabled.
2937       mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( position, size, GetControlSize() );
2938     }
2939   } // EditMode
2940 }
2941
2942 void TextInput::SetAltCursorEnabled( bool enabled )
2943 {
2944   mCursorRTLEnabled = enabled;
2945   mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2946 }
2947
2948 void TextInput::SetCursorVisibility( bool visible )
2949 {
2950   mCursorVisibility = visible;
2951   mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
2952   mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2953 }
2954
2955 void TextInput::CreateGrabHandle( Dali::Image image )
2956 {
2957   if ( !mGrabHandle )
2958   {
2959     if ( !image )
2960     {
2961       mGrabHandleImage = Image::New(DEFAULT_GRAB_HANDLE);
2962     }
2963     else
2964     {
2965       mGrabHandleImage = image;
2966     }
2967
2968     mGrabHandle = ImageActor::New(mGrabHandleImage);
2969     mGrabHandle.SetParentOrigin(ParentOrigin::TOP_LEFT);
2970     mGrabHandle.SetAnchorPoint(AnchorPoint::TOP_CENTER);
2971
2972     mGrabHandle.SetDrawMode(DrawMode::OVERLAY);
2973
2974     ShowGrabHandleAndSetVisibility( false );
2975
2976     CreateGrabArea( mGrabHandle );
2977
2978     mActiveLayer.Add(mGrabHandle);
2979   }
2980 }
2981
2982 void TextInput::CreateGrabArea( Actor& parent )
2983 {
2984   mGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
2985   mGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
2986   mGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE ) ) );  // grab area to be larger than text actor
2987   mGrabArea.TouchedSignal().Connect(this,&TextInput::OnPressDown);
2988   mTapDetector.Attach( mGrabArea );
2989   mPanGestureDetector.Attach( mGrabArea );
2990
2991   parent.Add(mGrabArea);
2992 }
2993
2994 Vector3 TextInput::MoveGrabHandle( const Vector2& displacement )
2995 {
2996   Vector3 actualHandlePosition;
2997
2998   if (mGrabHandle)
2999   {
3000     mActualGrabHandlePosition.x += displacement.x;
3001     mActualGrabHandlePosition.y += displacement.y;
3002
3003     // Grab handle should jump to the nearest character and take cursor with it
3004     std::size_t newCursorPosition = 0;
3005     ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY(), newCursorPosition );
3006
3007     actualHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition );
3008
3009     bool handleVisible = true;
3010
3011     if( IsScrollEnabled() )
3012     {
3013       const Vector3 controlSize = GetControlSize();
3014       const Size cursorSize = GetRowRectFromCharacterPosition( GetVisualPosition( newCursorPosition ) );
3015       // Scrolls the text if the handle is not in a visible position
3016       handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3017                                                   cursorSize,
3018                                                   controlSize );
3019
3020       if( handleVisible )
3021       {
3022         StopScrollTimer();
3023         mCurrentHandlePosition = actualHandlePosition;
3024         mScrollDisplacement = Vector2::ZERO;
3025       }
3026       else
3027       {
3028         if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3029         {
3030           mScrollDisplacement.x = -SCROLL_SPEED;
3031         }
3032         else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3033         {
3034           mScrollDisplacement.x = SCROLL_SPEED;
3035         }
3036         if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3037         {
3038           mScrollDisplacement.y = -SCROLL_SPEED;
3039         }
3040         else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3041         {
3042           mScrollDisplacement.y = SCROLL_SPEED;
3043         }
3044         StartScrollTimer();
3045       }
3046