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