d2c6c863123d4b4f2fab508fc23c139ea9fc58a3
[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::SetPlaceholderText( const std::string& placeHolderText )
358 {
359   // Get the placeholder styled text array from the markup string.
360   MarkupProcessor::GetStyledTextArray( placeHolderText, mStyledPlaceHolderText, IsMarkupProcessingEnabled() );
361
362   if( mStyledText.empty() )
363   {
364     // Set the placeholder text only if the styled text is empty.
365     mDisplayedTextView.SetText( mStyledPlaceHolderText );
366     mPlaceHolderSet = true;
367   }
368 }
369
370 std::string TextInput::GetPlaceholderText()
371 {
372   // Traverses the styled placeholder array getting only the text.
373   //  Note that for some languages a 'character' could be represented by more than one 'char'
374
375   std::string placeholderText;
376   for( MarkupProcessor::StyledTextArray::const_iterator it = mStyledPlaceHolderText.begin(), endIt = mStyledPlaceHolderText.end(); it != endIt; ++it )
377   {
378     placeholderText.append( (*it).mText.GetText() );
379   }
380
381   return placeholderText ;
382 }
383
384 void TextInput::SetInitialText(const std::string& initialText)
385 {
386   DALI_LOG_INFO(gLogFilter, Debug::General, "SetInitialText string[%s]\n", initialText.c_str() );
387
388   if ( mPreEditFlag ) // If in the pre-edit state and text is being set then discard text being inserted.
389   {
390     mPreEditFlag = false;
391     mIgnoreCommitFlag = true;
392   }
393
394   SetText( initialText );
395   PreEditReset( false ); // Reset keyboard as text changed
396 }
397
398 void TextInput::SetText(const std::string& initialText)
399 {
400   DALI_LOG_INFO(gLogFilter, Debug::General, "SetText string[%s]\n", initialText.c_str() );
401
402   GetStyledTextArray( initialText, mStyledText, IsMarkupProcessingEnabled() );
403
404   if( mStyledText.empty() )
405   {
406     // If the initial text is empty, set the placeholder text.
407     mDisplayedTextView.SetText( mStyledPlaceHolderText );
408     mPlaceHolderSet = true;
409   }
410   else
411   {
412     mDisplayedTextView.SetText( mStyledText );
413     mPlaceHolderSet = false;
414   }
415
416   GetTextLayoutInfo();
417
418   mCursorPosition = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
419
420   ImfManager imfManager = ImfManager::Get();
421   if ( imfManager )
422   {
423     imfManager.SetCursorPosition( mCursorPosition );
424     imfManager.SetSurroundingText( initialText );
425     imfManager.NotifyCursorPosition();
426   }
427
428   if( IsScrollEnabled() )
429   {
430     ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
431   }
432
433   ShowGrabHandleAndSetVisibility( false );
434
435   RemoveHighlight();
436
437   DrawCursor();
438
439   EmitTextModified();
440 }
441
442 void TextInput::SetText( const MarkupProcessor::StyledTextArray& styleText )
443 {
444   DALI_LOG_INFO(gLogFilter, Debug::General, "SetText markup text\n" );
445
446   mDisplayedTextView.SetText( styleText );
447   mPlaceHolderSet = false;
448
449   // If text alignment hasn't been manually set by application developer, then we
450   // automatically determine the alignment based on the content of the text i.e. what
451   // language the text begins with.
452   // TODO: This should determine different alignments for each line (broken by '\n') of text.
453   if(!mOverrideAutomaticAlignment)
454   {
455     // Determine bidi direction of first character (skipping past whitespace, numbers, and symbols)
456     bool leftToRight(true);
457
458     if( !styleText.empty() )
459     {
460       bool breakOut(false);
461
462       for( MarkupProcessor::StyledTextArray::const_iterator textIter = styleText.begin(), textEndIter = styleText.end(); ( textIter != textEndIter ) && ( !breakOut ); ++textIter )
463       {
464         const Text& text = textIter->mText;
465
466         for( std::size_t i = 0; i < text.GetLength(); ++i )
467         {
468           Character character( text[i] );
469           if( character.GetCharacterDirection() != Character::Neutral )
470           {
471             leftToRight = ( character.GetCharacterDirection() == Character::LeftToRight );
472             breakOut = true;
473             break;
474           }
475         }
476       }
477     }
478
479     // Based on this direction, either left or right align text if not manually set by application developer.
480     mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(
481                                            ( leftToRight ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight) |
482                                              Toolkit::Alignment::VerticalTop ) );
483     mDisplayedTextView.SetLineJustification( leftToRight ? Toolkit::TextView::Left : Toolkit::TextView::Right);
484   }
485
486   EmitTextModified();
487 }
488
489 void TextInput::SetMaxCharacterLength(std::size_t maxChars)
490 {
491   mMaxStringLength = maxChars;
492 }
493
494 void TextInput::SetNumberOfLinesLimit(std::size_t maxLines)
495 {
496   DALI_ASSERT_DEBUG( maxLines > 0 )
497
498   if ( maxLines > 0)
499   {
500     mNumberOflinesLimit = maxLines;
501   }
502 }
503
504 std::size_t TextInput::GetNumberOfLinesLimit() const
505 {
506   return mNumberOflinesLimit;
507 }
508
509 std::size_t TextInput::GetNumberOfCharacters() const
510 {
511   return mStyledText.size();
512 }
513
514 // Styling
515 void TextInput::SetMaterialDiffuseColor( const Vector4& color )
516 {
517   mMaterialColor = color;
518   if ( mCustomMaterial )
519   {
520     mCustomMaterial.SetDiffuseColor( mMaterialColor );
521     mMeshData.SetMaterial( mCustomMaterial );
522   }
523 }
524
525 const Vector4& TextInput::GetMaterialDiffuseColor() const
526 {
527   return mMaterialColor;
528 }
529
530 // Signals
531
532 Toolkit::TextInput::InputSignalV2& TextInput::InputStartedSignal()
533 {
534   return mInputStartedSignalV2;
535 }
536
537 Toolkit::TextInput::InputSignalV2& TextInput::InputFinishedSignal()
538 {
539   return mInputFinishedSignalV2;
540 }
541
542 Toolkit::TextInput::InputSignalV2& TextInput::CutAndPasteToolBarDisplayedSignal()
543 {
544   return mCutAndPasteToolBarDisplayedV2;
545 }
546
547 Toolkit::TextInput::StyleChangedSignalV2& TextInput::StyleChangedSignal()
548 {
549   return mStyleChangedSignalV2;
550 }
551
552 Toolkit::TextInput::TextModifiedSignalType& TextInput::TextModifiedSignal()
553 {
554   return mTextModifiedSignal;
555 }
556
557 Toolkit::TextInput::MaxInputCharactersReachedSignalV2& TextInput::MaxInputCharactersReachedSignal()
558 {
559   return mMaxInputCharactersReachedSignalV2;
560 }
561
562 Toolkit::TextInput::InputTextExceedBoundariesSignalV2& TextInput::InputTextExceedBoundariesSignal()
563 {
564   return mInputTextExceedBoundariesSignalV2;
565 }
566
567 bool TextInput::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
568 {
569   Dali::BaseHandle handle( object );
570
571   bool connected( true );
572   Toolkit::TextInput textInput = Toolkit::TextInput::DownCast(handle);
573
574   if( Toolkit::TextInput::SIGNAL_START_INPUT == signalName )
575   {
576     textInput.InputStartedSignal().Connect( tracker, functor );
577   }
578   else if( Toolkit::TextInput::SIGNAL_END_INPUT == signalName )
579   {
580     textInput.InputFinishedSignal().Connect( tracker, functor );
581   }
582   else if( Toolkit::TextInput::SIGNAL_STYLE_CHANGED == signalName )
583   {
584     textInput.StyleChangedSignal().Connect( tracker, functor );
585   }
586   else if( Toolkit::TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED == signalName )
587   {
588     textInput.MaxInputCharactersReachedSignal().Connect( tracker, functor );
589   }
590   else if( Toolkit::TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES == signalName )
591   {
592     textInput.InputTextExceedBoundariesSignal().Connect( tracker, functor );
593   }
594   else
595   {
596     // signalName does not match any signal
597     connected = false;
598   }
599
600   return connected;
601 }
602
603 void TextInput::SetEditable(bool editMode, bool setCursorOnTouchPoint, const Vector2& touchPoint)
604 {
605   if(editMode)
606   {
607     // update line height before calculate the actual position.
608     UpdateLineHeight();
609
610     if(!mEditModeActive)
611     {
612       if( setCursorOnTouchPoint )
613       {
614         // Sets the cursor position for the given touch point.
615         ReturnClosestIndex( touchPoint, mCursorPosition );
616
617         // Creates the grab handle.
618         if( IsGrabHandleEnabled() )
619         {
620           const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
621
622           CreateGrabHandle();
623
624           mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
625           mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
626           mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
627           ShowGrabHandleAndSetVisibility( true );
628
629           // Scrolls the text-view if needed.
630           if( IsScrollEnabled() )
631           {
632             ScrollTextViewToMakeCursorVisible( cursorPosition );
633           }
634         }
635       }
636       else
637       {
638         mCursorPosition = mStyledText.size(); // Initially set cursor position to end of string.
639       }
640     }
641
642     StartEditMode();
643   }
644   else
645   {
646     EndEditMode();
647   }
648 }
649
650 bool TextInput::IsEditable() const
651 {
652   return mEditModeActive;
653 }
654
655 void TextInput::SetEditOnTouch( bool editOnTouch )
656 {
657   mEditOnTouch = editOnTouch;
658 }
659
660 bool TextInput::IsEditOnTouch() const
661 {
662   return mEditOnTouch;
663 }
664
665 void TextInput::SetTextSelectable( bool textSelectable )
666 {
667   mTextSelection = textSelectable;
668 }
669
670 bool TextInput::IsTextSelectable() const
671 {
672   return mTextSelection;
673 }
674
675 bool TextInput::IsTextSelected() const
676 {
677   return mHighlightMeshActor;
678 }
679
680 void TextInput::DeSelectText()
681 {
682   RemoveHighlight();
683   HidePopup();
684   CursorUpdate();
685 }
686
687 void TextInput::SetGrabHandleImage(Dali::Image image )
688 {
689   if (image)
690   {
691     CreateGrabHandle(image);
692   }
693 }
694
695 void TextInput::SetCursorImage(Dali::Image image, const Vector4& border )
696 {
697   DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
698
699   if ( image )
700   {
701     mCursor.SetImage( image );
702     mCursor.SetNinePatchBorder( border );
703   }
704 }
705
706 Vector3 TextInput::GetSelectionHandleSize()
707 {
708   return DEFAULT_SELECTION_HANDLE_SIZE;
709 }
710
711 void TextInput::SetRTLCursorImage(Dali::Image image, const Vector4& border )
712 {
713   DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
714
715   if ( image )
716   {
717     mCursorRTL.SetImage( image);
718     mCursorRTL.SetNinePatchBorder(  border );
719   }
720 }
721
722 void TextInput::EnableGrabHandle(bool toggle)
723 {
724   // enables grab handle with will in turn de-activate magnifier
725   mGrabHandleEnabled = toggle;
726 }
727
728 bool TextInput::IsGrabHandleEnabled()
729 {
730   // if false then magnifier will be shown instead.
731   return mGrabHandleEnabled;
732 }
733
734 void TextInput::EnableSelectionHandleFlip( bool toggle )
735 {
736   // Deprecated function.  To be removed.
737   mIsSelectionHandleFlipEnabled = toggle;
738 }
739
740 bool TextInput::IsSelectionHandleFlipEnabled()
741 {
742   // Deprecated function, To be removed. Returns true as handle flipping always enabled by default so handles do not exceed screen.
743   return true;
744 }
745
746 void TextInput::SetSelectionHandleFlipMargin( const Vector4& margin )
747 {
748   // Deprecated function, now just stores margin for retreival, remove completely once depricated Public API removed.
749   Vector3 textInputSize = mDisplayedTextView.GetCurrentSize();
750   const Vector4 flipBoundary( -margin.x, -margin.y, textInputSize.width + margin.z, textInputSize.height + margin.w );
751
752   mSelectionHandleFlipMargin = margin;
753 }
754
755 void TextInput::SetBoundingRectangle( const Rect<float>& boundingRectangle )
756 {
757   // Convert to world coordinates and store as a Vector4 to be compatiable with Property Notifications.
758   Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
759
760   const float originX = boundingRectangle.x - 0.5f * stageSize.width;
761   const float originY = boundingRectangle.y - 0.5f * stageSize.height;
762
763   const Vector4 boundary( originX,
764                           originY,
765                           originX + boundingRectangle.width,
766                           originY + boundingRectangle.height );
767
768   mBoundingRectangleWorldCoordinates = boundary;
769 }
770
771 const Rect<float> TextInput::GetBoundingRectangle() const
772 {
773   Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
774
775   const float originX = mBoundingRectangleWorldCoordinates.x + 0.5f * stageSize.width;
776   const float originY = mBoundingRectangleWorldCoordinates.y + 0.5f * stageSize.height;
777
778   Rect<float>boundingRect( originX, originY, mBoundingRectangleWorldCoordinates.z - mBoundingRectangleWorldCoordinates.x, mBoundingRectangleWorldCoordinates.w - mBoundingRectangleWorldCoordinates.y);
779
780   return boundingRect;
781 }
782
783 const Vector4& TextInput::GetSelectionHandleFlipMargin()
784 {
785   return mSelectionHandleFlipMargin;
786 }
787
788 void TextInput::SetTextColor( const Vector4& color )
789 {
790   mDisplayedTextView.SetColor( color );
791 }
792
793 void TextInput::SetActiveStyle( const TextStyle& style, const TextStyle::Mask mask )
794 {
795   if( style != mInputStyle )
796   {
797     // different style.
798     bool emitSignal = false;
799
800     // mask: modify style according to mask, if different emit signal.
801     const TextStyle oldInputStyle( mInputStyle );
802
803     // Copy the new style.
804     mInputStyle.Copy( style, mask );
805
806     // if style has changed, emit signal.
807     if( oldInputStyle != mInputStyle )
808     {
809       emitSignal = true;
810     }
811
812     // Updates the line height accordingly with the input style.
813     UpdateLineHeight();
814
815     // Changing font point size will require the cursor to be re-sized
816     DrawCursor();
817
818     if( emitSignal )
819     {
820       EmitStyleChangedSignal();
821     }
822   }
823 }
824
825 void TextInput::ApplyStyle( const TextStyle& style, const TextStyle::Mask mask )
826 {
827   if ( IsTextSelected() )
828   {
829     const std::size_t begin = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
830     const std::size_t end = std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition) - 1;
831
832     if( !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
833     {
834       ApplyStyleToRange(style, mask, mTextLayoutInfo.mCharacterLogicalToVisualMap[begin], mTextLayoutInfo.mCharacterLogicalToVisualMap[end]);
835     }
836
837     // Keeps the old style to be compared with the new one.
838     const TextStyle oldInputStyle( mInputStyle );
839
840     // Copy only those parameters from the style which are set in the mask.
841     mInputStyle.Copy( style, mask );
842
843     if( mInputStyle != oldInputStyle )
844     {
845       // Updates the line height accordingly with the input style.
846       UpdateLineHeight();
847
848       EmitStyleChangedSignal();
849     }
850   }
851 }
852
853 void TextInput::ApplyStyleToAll( const TextStyle& style, const TextStyle::Mask mask )
854 {
855   if( !mStyledText.empty() )
856   {
857     ApplyStyleToRange( style, mask, 0, mStyledText.size() - 1 );
858   }
859 }
860
861 TextStyle TextInput::GetStyleAtCursor() const
862 {
863   TextStyle style;
864
865   if ( !mStyledText.empty() && ( mCursorPosition > 0 ) )
866   {
867     DALI_ASSERT_DEBUG( ( 0 <= mCursorPosition-1 ) && ( mCursorPosition-1 < mStyledText.size() ) );
868     style = mStyledText.at( mCursorPosition-1 ).mStyle;
869   }
870   else // No text.
871   {
872     style = mInputStyle;
873
874     if ( mInputStyle.GetFontPointSize() <  Math::MACHINE_EPSILON_1000 )
875     {
876       Dali::Font defaultFont = Dali::Font::New();
877       style.SetFontPointSize( PointSize( defaultFont.GetPointSize()) );
878     }
879   }
880
881   return style;
882 }
883
884 TextStyle TextInput::GetStyleAt( std::size_t position ) const
885 {
886   DALI_ASSERT_DEBUG( ( 0 <= position ) && ( position <= mStyledText.size() ) );
887
888   if( position >= mStyledText.size() )
889   {
890     position = mStyledText.size() - 1;
891   }
892
893   return mStyledText.at( position ).mStyle;
894 }
895
896 void TextInput::SetTextAlignment( Toolkit::Alignment::Type align )
897 {
898   mDisplayedTextView.SetTextAlignment( align );
899   mOverrideAutomaticAlignment = true;
900 }
901
902 void TextInput::SetTextLineJustification( Toolkit::TextView::LineJustification justification )
903 {
904   mDisplayedTextView.SetLineJustification( justification );
905   mOverrideAutomaticAlignment = true;
906 }
907
908 void TextInput::SetFadeBoundary( const Toolkit::TextView::FadeBoundary& fadeBoundary )
909 {
910   mDisplayedTextView.SetFadeBoundary( fadeBoundary );
911 }
912
913 const Toolkit::TextView::FadeBoundary& TextInput::GetFadeBoundary() const
914 {
915   return mDisplayedTextView.GetFadeBoundary();
916 }
917
918 Toolkit::Alignment::Type TextInput::GetTextAlignment() const
919 {
920   return mDisplayedTextView.GetTextAlignment();
921 }
922
923 void TextInput::SetMultilinePolicy( Toolkit::TextView::MultilinePolicy policy )
924 {
925   mDisplayedTextView.SetMultilinePolicy( policy );
926 }
927
928 Toolkit::TextView::MultilinePolicy TextInput::GetMultilinePolicy() const
929 {
930   return mDisplayedTextView.GetMultilinePolicy();
931 }
932
933 void TextInput::SetWidthExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
934 {
935   mDisplayedTextView.SetWidthExceedPolicy( policy );
936 }
937
938 Toolkit::TextView::ExceedPolicy TextInput::GetWidthExceedPolicy() const
939 {
940   return mDisplayedTextView.GetWidthExceedPolicy();
941 }
942
943 void TextInput::SetHeightExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
944 {
945   mDisplayedTextView.SetHeightExceedPolicy( policy );
946 }
947
948 Toolkit::TextView::ExceedPolicy TextInput::GetHeightExceedPolicy() const
949 {
950   return mDisplayedTextView.GetHeightExceedPolicy();
951 }
952
953 void TextInput::SetExceedEnabled( bool enable )
954 {
955   mExceedEnabled = enable;
956 }
957
958 bool TextInput::GetExceedEnabled() const
959 {
960   return mExceedEnabled;
961 }
962
963 void TextInput::SetBackground(Dali::Image image )
964 {
965   // TODO Should add this function and add public api to match.
966 }
967
968 bool TextInput::OnTouchEvent(const TouchEvent& event)
969 {
970   return false;
971 }
972
973 bool TextInput::OnKeyEvent(const KeyEvent& event)
974 {
975   switch( event.state )
976   {
977     case KeyEvent::Down:
978     {
979       return OnKeyDownEvent(event);
980     }
981     break;
982
983     case KeyEvent::Up:
984     {
985       return OnKeyUpEvent(event);
986     }
987     break;
988
989     default:
990     {
991       return false;
992     }
993     break;
994   }
995 }
996
997 void TextInput::OnKeyInputFocusGained()
998 {
999   DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusGained\n" );
1000
1001   mEditModeActive = true;
1002
1003   mActiveLayer.RaiseToTop(); // Ensure layer holding handles is on top
1004
1005   mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1006
1007   // Updates the line height accordingly with the input style.
1008   UpdateLineHeight();
1009
1010   // Connect the signals to use in text input.
1011   VirtualKeyboard::StatusChangedSignal().Connect( this, &TextInput::KeyboardStatusChanged );
1012   VirtualKeyboard::LanguageChangedSignal().Connect( this, &TextInput::SetTextDirection );
1013
1014   // Set the text direction if empty and connect to the signal to ensure we change direction when the language changes.
1015   SetTextDirection();
1016
1017   GetTextLayoutInfo();
1018
1019   DrawCursor();
1020   SetCursorVisibility( true );
1021   StartCursorBlinkTimer();
1022
1023   Toolkit::TextInput handle( GetOwner() );
1024   mInputStartedSignalV2.Emit( handle );
1025
1026   ImfManager imfManager = ImfManager::Get();
1027
1028   if ( imfManager )
1029   {
1030     imfManager.EventReceivedSignal().Connect(this, &TextInput::ImfEventReceived);
1031
1032     // Notify that the text editing start.
1033     imfManager.Activate();
1034
1035     // When window gain lost focus, the imf manager is deactivated. Thus when window gain focus again, the imf manager must be activated.
1036     imfManager.SetRestoreAferFocusLost( true );
1037
1038     imfManager.SetCursorPosition( mCursorPosition );
1039     imfManager.NotifyCursorPosition();
1040   }
1041
1042   mClipboard = Clipboard::Get(); // Store handle to clipboard
1043
1044   // Now in edit mode we can accept string to paste from clipboard
1045   ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1046   if ( notifier )
1047   {
1048     notifier.ContentSelectedSignal().Connect( this, &TextInput::OnClipboardTextSelected );
1049   }
1050 }
1051
1052 void TextInput::OnKeyInputFocusLost()
1053 {
1054   DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusLost\n" );
1055
1056   if( mPreEditFlag )
1057   {
1058     // If key input focus is lost, it removes the
1059     // underline from the last pre-edit text.
1060     RemovePreEditStyle();
1061     const std::size_t numberOfCharactersDeleted = DeletePreEdit();
1062     InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersDeleted );
1063     EmitTextModified();
1064   }
1065
1066   ImfManager imfManager = ImfManager::Get();
1067   if ( imfManager )
1068   {
1069     // The text editing is finished. Therefore the imf manager don't have restore activation.
1070     imfManager.SetRestoreAferFocusLost( false );
1071
1072     // Notify that the text editing finish.
1073     imfManager.Deactivate();
1074
1075     imfManager.EventReceivedSignal().Disconnect(this, &TextInput::ImfEventReceived);
1076   }
1077   // Disconnect signal used the text input.
1078   VirtualKeyboard::LanguageChangedSignal().Disconnect( this, &TextInput::SetTextDirection );
1079
1080   Toolkit::TextInput handle( GetOwner() );
1081   mInputFinishedSignalV2.Emit( handle );
1082   mEditModeActive = false;
1083   mPreEditFlag = false;
1084   RemoveHighlight();
1085   SetCursorVisibility( false );
1086   StopCursorBlinkTimer();
1087
1088   ShowGrabHandleAndSetVisibility( false );
1089
1090   mClipboard.Reset();
1091   // No longer in edit mode so do not want to receive string from clipboard
1092   ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1093   if ( notifier )
1094   {
1095     notifier.ContentSelectedSignal().Disconnect( this, &TextInput::OnClipboardTextSelected );
1096   }
1097
1098   Clipboard clipboard = Clipboard::Get();
1099   if ( clipboard )
1100   {
1101     clipboard.HideClipboard();
1102   }
1103 }
1104
1105 void TextInput::OnControlStageConnection()
1106 {
1107   Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
1108
1109   if ( mBoundingRectangleWorldCoordinates == Vector4::ZERO )
1110   {
1111     SetBoundingRectangle( Rect<float>( 0.0f, 0.0f, stageSize.width, stageSize.height ));
1112   }
1113 }
1114
1115 void TextInput::CreateActiveLayer()
1116 {
1117   Actor self = Self();
1118   mActiveLayer = Layer::New();
1119   mActiveLayer.SetName ( "ActiveLayerActor" );
1120
1121   mActiveLayer.SetAnchorPoint( AnchorPoint::CENTER);
1122   mActiveLayer.SetParentOrigin( ParentOrigin::CENTER);
1123   mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
1124
1125   self.Add( mActiveLayer );
1126   mActiveLayer.RaiseToTop();
1127 }
1128
1129 void TextInput::OnInitialize()
1130 {
1131   CreateTextViewActor();
1132
1133   SetUpTouchEvents();
1134
1135   // Create 2 cursors (standard LTR and RTL cursor for when text can be added at
1136   // different positions depending on language)
1137   mCursor = CreateCursor(DEFAULT_CURSOR_COLOR);
1138   mCursorRTL = CreateCursor(DEFAULT_CURSOR_COLOR);
1139
1140   Actor self = Self();
1141   self.Add( mCursor );
1142   self.Add( mCursorRTL );
1143
1144   mCursorVisibility = false;
1145
1146   CreateActiveLayer(); // todo move this so layer only created when needed.
1147
1148   // Assign names to image actors
1149   mCursor.SetName("mainCursor");
1150   mCursorRTL.SetName("rtlCursor");
1151 }
1152
1153 void TextInput::OnControlSizeSet(const Vector3& targetSize)
1154 {
1155   mDisplayedTextView.SetSize( targetSize );
1156   GetTextLayoutInfo();
1157   mActiveLayer.SetSize(targetSize);
1158 }
1159
1160 void TextInput::OnRelaidOut( Vector2 size, ActorSizeContainer& container )
1161 {
1162   Relayout( mDisplayedTextView, size, container );
1163   Relayout( mPopupPanel.GetRootActor(), size, container );
1164
1165   GetTextLayoutInfo();
1166
1167   DrawCursor();
1168 }
1169
1170 Vector3 TextInput::GetNaturalSize()
1171 {
1172   Vector3 naturalSize = mDisplayedTextView.GetNaturalSize();
1173
1174   if( mEditModeActive && ( Vector3::ZERO == naturalSize ) )
1175   {
1176     // If the natural is zero, it means there is no text. Let's return the cursor height as the natural height.
1177     naturalSize.height = mLineHeight;
1178   }
1179
1180   return naturalSize;
1181 }
1182
1183 float TextInput::GetHeightForWidth( float width )
1184 {
1185   float height = mDisplayedTextView.GetHeightForWidth( width );
1186
1187   if( mEditModeActive && ( fabsf( height ) < Math::MACHINE_EPSILON_1000 ) )
1188   {
1189     // If the height is zero, it means there is no text. Let's return the cursor height.
1190     height = mLineHeight;
1191   }
1192
1193   return height;
1194 }
1195
1196 /*end of Virtual methods from parent*/
1197
1198 // Private Internal methods
1199
1200 void TextInput::OnHandlePan(Actor actor, PanGesture gesture)
1201 {
1202   switch (gesture.state)
1203   {
1204     case Gesture::Started:
1205     // fall through so code not duplicated
1206     case Gesture::Continuing:
1207     {
1208       if (actor == mGrabArea)
1209       {
1210         SetCursorVisibility( true );
1211         ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1212         MoveGrabHandle( gesture.displacement );
1213         HidePopup(); // Do not show popup whilst handle is moving
1214       }
1215       else if (actor == mHandleOneGrabArea)
1216       {
1217         // the displacement in PanGesture is affected by the actor's rotation.
1218         mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1219         mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1220
1221         MoveSelectionHandle( HandleOne, gesture.displacement );
1222
1223         mState = StateDraggingHandle;
1224         HidePopup();
1225       }
1226       else if (actor == mHandleTwoGrabArea)
1227       {
1228         // the displacement in PanGesture is affected by the actor's rotation.
1229         mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1230         mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1231
1232         MoveSelectionHandle( HandleTwo, gesture.displacement );
1233
1234         mState = StateDraggingHandle;
1235         HidePopup();
1236       }
1237     }
1238     break;
1239
1240     case Gesture::Finished:
1241     {
1242       // Revert back to non-pressed selection handle images
1243       if (actor == mGrabArea)
1244       {
1245         mActualGrabHandlePosition = MoveGrabHandle( gesture.displacement );
1246         SetCursorVisibility( true );
1247         SetUpPopupSelection();
1248         ShowPopup();
1249       }
1250       if (actor == mHandleOneGrabArea)
1251       {
1252         // the displacement in PanGesture is affected by the actor's rotation.
1253         mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1254         mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1255
1256         mSelectionHandleOneActualPosition = MoveSelectionHandle( HandleOne, gesture.displacement );
1257
1258         mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1259         mState = StateEdit;
1260         ShowPopupCutCopyPaste();
1261       }
1262       if (actor == mHandleTwoGrabArea)
1263       {
1264         // the displacement in PanGesture is affected by the actor's rotation.
1265         mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1266         mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1267
1268         mSelectionHandleTwoActualPosition = MoveSelectionHandle( HandleTwo, gesture.displacement );
1269
1270         mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1271         mState = StateEdit;
1272         ShowPopupCutCopyPaste();
1273       }
1274     }
1275     break;
1276     default:
1277       break;
1278   }
1279 }
1280
1281 // Stop the flashing animation so easy to see when moved.
1282 bool TextInput::OnPressDown(Dali::Actor actor, const TouchEvent& touch)
1283 {
1284   if (touch.GetPoint(0).state == TouchPoint::Down)
1285   {
1286     SetCursorVisibility( true );
1287     StopCursorBlinkTimer();
1288   }
1289   else if (touch.GetPoint(0).state == TouchPoint::Up)
1290   {
1291     SetCursorVisibility( true );
1292     StartCursorBlinkTimer();
1293   }
1294   return false;
1295 }
1296
1297 // selection handle one
1298 bool TextInput::OnHandleOneTouched(Dali::Actor actor, const TouchEvent& touch)
1299 {
1300   if (touch.GetPoint(0).state == TouchPoint::Down)
1301   {
1302     mSelectionHandleOne.SetImage( mSelectionHandleOneImagePressed );
1303   }
1304   else if (touch.GetPoint(0).state == TouchPoint::Up)
1305   {
1306     mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1307   }
1308   return false;
1309 }
1310
1311 // selection handle two
1312 bool TextInput::OnHandleTwoTouched(Dali::Actor actor, const TouchEvent& touch)
1313 {
1314   if (touch.GetPoint(0).state == TouchPoint::Down)
1315   {
1316     mSelectionHandleTwo.SetImage( mSelectionHandleTwoImagePressed );
1317   }
1318   else if (touch.GetPoint(0).state == TouchPoint::Up)
1319   {
1320     mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1321   }
1322   return false;
1323 }
1324
1325 void TextInput::OnDoubleTap(Dali::Actor actor, Dali::TapGesture tap)
1326 {
1327    // If text exists then select nearest word.
1328    if ( !mStyledText.empty())
1329    {
1330      HidePopup();
1331
1332      ShowGrabHandleAndSetVisibility( false );
1333
1334
1335      if ( mPreEditFlag )
1336      {
1337        // PreEdit will be committed here without needing a commit from IMF.  Remove pre-edit underline and reset flags which
1338        // converts the pre-edit word being displayed to a committed word.
1339        if ( !mUnderlinedPriorToPreEdit )
1340        {
1341          TextStyle style;
1342          style.SetUnderline( false );
1343          ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1344        }
1345        mPreEditFlag = false;
1346        mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1347        // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1348        PreEditReset( false );
1349      }
1350      mCursorPosition = 0;
1351
1352      mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1353      ReturnClosestIndex( tap.localPoint, mCursorPosition );
1354
1355      std::size_t start = 0;
1356      std::size_t end = 0;
1357      Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1358
1359      mCursorPosition = end; // Ensure cursor is positioned at end of selected word
1360
1361      ImfManager imfManager = ImfManager::Get();
1362      if ( imfManager )
1363      {
1364        imfManager.SetCursorPosition ( mCursorPosition );
1365        imfManager.NotifyCursorPosition();
1366      }
1367
1368      if ( !mStyledText.at(end-1).mText[0].IsWhiteSpace() )
1369      {
1370        SelectText( start, end );
1371        ShowPopupCutCopyPaste();
1372      }
1373      else
1374      {
1375        RemoveHighlight( false ); // Remove highlight but do not auto hide popup
1376        HidePopup( false ); // Hide popup with setting to do auto show.
1377        SetUpPopupSelection( false ); // Set to false so if nearest word is whitespace it will not show cut button.
1378        ShowPopup();
1379      }
1380    }
1381    else if ( mClipboard && mClipboard.NumberOfItems() )
1382    {
1383      ShowPopupCutCopyPaste();
1384    }
1385
1386    // If no text and clipboard empty then do nothing
1387 }
1388
1389 // TODO: Change the function name to be more general.
1390 void TextInput::OnTextTap(Dali::Actor actor, Dali::TapGesture tap)
1391 {
1392   DALI_LOG_INFO( gLogFilter, Debug::General, "OnTap mPreEditFlag[%s] mEditOnTouch[%s] mEditModeActive[%s] ", (mPreEditFlag)?"true":"false"
1393                                                                                                            , (mEditOnTouch)?"true":"false"
1394                                                                                                            , (mEditModeActive)?"true":"false");
1395
1396   if( mHandleOneGrabArea == actor || mHandleTwoGrabArea == actor )
1397   {
1398     return;
1399   }
1400
1401   if( mGrabArea == actor )
1402   {
1403     if( mPopupPanel.GetState() == TextInputPopup::StateHidden || mPopupPanel.GetState() == TextInputPopup::StateHiding )
1404     {
1405       SetUpPopupSelection();
1406       ShowPopup();
1407     }
1408
1409     return;
1410   }
1411
1412   HidePopup();
1413   RemoveHighlight();
1414
1415   mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1416
1417   // Initially don't create the grab handle.
1418   bool createGrabHandle = false;
1419
1420   if ( !mEditModeActive )
1421   {
1422     // update line height before calculate the actual position.
1423     UpdateLineHeight();
1424
1425     // Only start edit mode if TextInput configured to edit on touch
1426     if ( mEditOnTouch )
1427     {
1428       // Set the initial cursor position in the tap point.
1429       ReturnClosestIndex(tap.localPoint, mCursorPosition );
1430       StartEditMode();
1431     }
1432   }
1433   else
1434   {
1435     // Show the keyboard if it was hidden.
1436     if (!VirtualKeyboard::IsVisible())
1437     {
1438       VirtualKeyboard::Show();
1439     }
1440
1441     // Reset keyboard as tap event has occurred.
1442     // Set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1443     PreEditReset( true );
1444
1445     GetTextLayoutInfo();
1446
1447     if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() ) // If string empty we do not need a grab handle.
1448     {
1449       // 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.
1450
1451       ReturnClosestIndex(tap.localPoint, mCursorPosition );
1452
1453       DALI_LOG_INFO( gLogFilter, Debug::General, "mCursorPosition[%u]", mCursorPosition );
1454
1455       // Notify keyboard so it can 're-capture' word for predictive text.
1456       // As we have done a reset, is this required, expect IMF keyboard to request this information.
1457       ImfManager imfManager = ImfManager::Get();
1458       if ( imfManager )
1459       {
1460         imfManager.SetCursorPosition ( mCursorPosition );
1461         imfManager.NotifyCursorPosition();
1462       }
1463       const TextStyle oldInputStyle( mInputStyle );
1464
1465       mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1466
1467       DrawCursor();
1468
1469       // Create the grab handle.
1470       // Grab handle is created later.
1471       createGrabHandle = true;
1472
1473       if( oldInputStyle != mInputStyle )
1474       {
1475         // Updates the line height accordingly with the input style.
1476         UpdateLineHeight();
1477
1478         EmitStyleChangedSignal();
1479       }
1480     }
1481   }
1482
1483   // Edit mode started after grab handle created to ensure the signal InputStarted is sent last.
1484   // This is used to ensure if selecting text hides the grab handle then this code is run after grab handle is created,
1485   // otherwise the Grab handle will be shown when selecting.
1486   if ( createGrabHandle && IsGrabHandleEnabled() )
1487   {
1488     const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1489
1490     CreateGrabHandle();
1491
1492     mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1493     mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1494     mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1495     ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1496
1497   }
1498 }
1499
1500 void TextInput::OnLongPress(Dali::Actor actor, Dali::LongPressGesture longPress)
1501 {
1502   DALI_LOG_INFO( gLogFilter, Debug::General, "OnLongPress\n" );
1503
1504   // Ignore longpress if in selection mode already
1505   if( mHighlightMeshActor )
1506   {
1507     return;
1508   }
1509
1510   if(longPress.state == Dali::Gesture::Started)
1511   {
1512     // Start edit mode on long press
1513     if ( !mEditModeActive )
1514     {
1515       StartEditMode();
1516     }
1517
1518     // If text exists then select nearest word.
1519     if ( !mStyledText.empty())
1520     {
1521       HidePopup();
1522
1523       ShowGrabHandleAndSetVisibility( false );
1524
1525
1526       if ( mPreEditFlag )
1527       {
1528         // PreEdit will be committed here without needing a commit from IMF.  Remove pre-edit underline and reset flags which
1529         // converts the pre-edit word being displayed to a committed word.
1530         if ( !mUnderlinedPriorToPreEdit )
1531         {
1532           TextStyle style;
1533           style.SetUnderline( false );
1534           ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1535         }
1536         mPreEditFlag = false;
1537         mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1538         // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1539         PreEditReset( false );
1540       }
1541       mCursorPosition = 0;
1542
1543       mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1544       ReturnClosestIndex( longPress.localPoint, mCursorPosition );
1545
1546       std::size_t start = 0;
1547       std::size_t end = 0;
1548       Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1549
1550       mCursorPosition  = end; // Ensure cursor is positioned at end of selected word
1551
1552       ImfManager imfManager = ImfManager::Get();
1553       if ( imfManager )
1554       {
1555         imfManager.SetCursorPosition ( mCursorPosition );
1556         imfManager.NotifyCursorPosition();
1557       }
1558
1559       SelectText( start, end );
1560     }
1561
1562     // if no text but clipboard has content then show paste option, if no text and clipboard empty then do nothing
1563     if ( ( mClipboard && mClipboard.NumberOfItems() ) || !mStyledText.empty() )
1564     {
1565       ShowPopupCutCopyPaste();
1566     }
1567   }
1568 }
1569
1570 void TextInput::OnClipboardTextSelected( ClipboardEventNotifier& notifier )
1571 {
1572   const Text clipboardText( notifier.GetContent() );
1573   PasteText( clipboardText );
1574
1575   SetCursorVisibility( true );
1576   StartCursorBlinkTimer();
1577
1578   ShowGrabHandleAndSetVisibility( false );
1579
1580
1581   HidePopup();
1582 }
1583
1584 bool TextInput::OnPopupButtonPressed( Toolkit::Button button )
1585 {
1586   mPopupPanel.PressedSignal().Disconnect( this, &TextInput::OnPopupButtonPressed );
1587
1588   const std::string& name = button.GetName();
1589
1590   if(name == TextInputPopup::OPTION_SELECT_WORD)
1591   {
1592     std::size_t start = 0;
1593     std::size_t end = 0;
1594     Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1595
1596     SelectText( start, end );
1597   }
1598   else if(name == TextInputPopup::OPTION_SELECT_ALL)
1599   {
1600     SetCursorVisibility(false);
1601     StopCursorBlinkTimer();
1602
1603     std::size_t end = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
1604     std::size_t start = 0;
1605
1606     SelectText( start, end );
1607   }
1608   else if(name == TextInputPopup::OPTION_CUT)
1609   {
1610     bool ret = CopySelectedTextToClipboard();
1611
1612     if ( ret )
1613     {
1614       DeleteHighlightedText( true );
1615       CursorUpdate();
1616     }
1617
1618     SetCursorVisibility( true );
1619     StartCursorBlinkTimer();
1620
1621     HidePopup();
1622   }
1623   else if(name == TextInputPopup::OPTION_COPY)
1624   {
1625     CopySelectedTextToClipboard();
1626
1627     RemoveHighlight();
1628
1629     SetCursorVisibility( true );
1630     StartCursorBlinkTimer();
1631
1632     HidePopup();
1633   }
1634   else if(name == TextInputPopup::OPTION_PASTE)
1635   {
1636     const Text retrievedString( mClipboard.GetItem( 0 ) );  // currently can only get first item in clip board, index 0;
1637
1638     PasteText(retrievedString);
1639
1640     SetCursorVisibility( true );
1641     StartCursorBlinkTimer();
1642
1643     ShowGrabHandleAndSetVisibility( false );
1644
1645     HidePopup();
1646   }
1647   else if(name == TextInputPopup::OPTION_CLIPBOARD)
1648   {
1649     // In the case of clipboard being shown we do not want to show updated pop-up after hide animation completes
1650     // Hence pass the false parameter for signalFinished.
1651     HidePopup( true, false );
1652     mClipboard.ShowClipboard();
1653   }
1654
1655   return false;
1656 }
1657
1658 bool TextInput::OnCursorBlinkTimerTick()
1659 {
1660   // Cursor blinking
1661   mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1662   if ( mCursorRTLEnabled )
1663   {
1664     mCursorRTL.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1665   }
1666   mCursorBlinkStatus = !mCursorBlinkStatus;
1667
1668   return true;
1669 }
1670
1671 void TextInput::OnPopupHideFinished(TextInputPopup& popup)
1672 {
1673   popup.HideFinishedSignal().Disconnect( this, &TextInput::OnPopupHideFinished );
1674
1675   // Change Popup menu to Cut/Copy/Paste if text has been selected.
1676   if(mHighlightMeshActor && mState == StateEdit)
1677   {
1678     ShowPopupCutCopyPaste();
1679   }
1680 }
1681
1682 //FIXME this routine needs to be re-written as it contains too many branches.
1683 bool TextInput::OnKeyDownEvent(const KeyEvent& event)
1684 {
1685   std::string keyName = event.keyPressedName;
1686   std::string keyString = event.keyPressed;
1687
1688   DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyDownEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1689
1690   // Do not consume "Tab" and "Escape" keys.
1691   if(keyName == "Tab" || keyName == "Escape")
1692   {
1693     // Escape key to end the edit mode
1694     EndEditMode();
1695
1696     return false;
1697   }
1698
1699   HidePopup(); // If Pop-up shown then hides it as editing text.
1700
1701   // Update Flag, indicates whether to update the text-input contents or not.
1702   // Any key stroke that results in a visual change of the text-input should
1703   // set this flag to true.
1704   bool update(false);
1705
1706   // Whether to scroll text to cursor position.
1707   // Scroll is needed always the cursor is updated and after the pre-edit is received.
1708   bool scroll = false;
1709
1710   if (keyName == "Return")
1711   {
1712     if ( mNumberOflinesLimit > 1) // Prevents New line character / Return adding an extra line if limit set to 1
1713     {
1714       bool preEditFlagPreviouslySet( mPreEditFlag );
1715
1716       // replaces highlighted text with new line
1717       DeleteHighlightedText( false );
1718
1719       mCursorPosition = mCursorPosition + InsertAt( Text( NEWLINE ), mCursorPosition, 0 );
1720
1721       // If we are in pre-edit mode then pressing enter will cause a commit.  But the commit string does not include the
1722       // '\n' character so we need to ensure that the immediately following commit knows how it occurred.
1723       if ( mPreEditFlag )
1724       {
1725         mCommitByKeyInput = true;
1726       }
1727
1728       // If attempting to insert a new-line brings us out of PreEdit mode, then we should not ignore the next commit.
1729       if ( preEditFlagPreviouslySet && !mPreEditFlag )
1730       {
1731         mPreEditFlag = true;
1732         mIgnoreCommitFlag = false;
1733       }
1734       EmitTextModified();
1735       update = true;
1736     }
1737     else
1738     {
1739       RemoveHighlight();
1740     }
1741   } // Return
1742   else if ( keyName == "space" )
1743   {
1744     if ( mHighlightMeshActor )
1745     {
1746       // Some text is selected so erase it before adding space.
1747       DeleteHighlightedText( true );
1748       update = true;
1749     }
1750
1751     mCursorPosition = mCursorPosition + InsertAt(Text(keyString), mCursorPosition, 0);
1752
1753     // If we are in pre-edit mode then pressing the space-bar will cause a commit.  But the commit string does not include the
1754     // ' ' character so we need to ensure that the immediately following commit knows how it occurred.
1755     if ( mPreEditFlag )
1756     {
1757       mCommitByKeyInput = true;
1758     }
1759     EmitTextModified();
1760     update = true;
1761   } // space
1762   else if (keyName == "BackSpace")
1763   {
1764     if ( mHighlightMeshActor )
1765     {
1766       // Some text is selected so erase it
1767       DeleteHighlightedText( true );
1768       update = true;
1769     }
1770     else
1771     {
1772       if ( mCursorPosition > 0 )
1773       {
1774         DeleteCharacter( mCursorPosition );
1775         update = true;
1776       }
1777     }
1778     EmitTextModified();
1779   } // BackSpace
1780   else if (keyName == "Right")
1781   {
1782     AdvanceCursor();
1783     RemoveHighlight();
1784   }
1785   else if (keyName == "Left")
1786   {
1787     AdvanceCursor(true);
1788     RemoveHighlight();
1789   }
1790   else // event is a character
1791   {
1792     // Some text may be selected, hiding keyboard causes an empty keystring to be sent, we don't want to delete highlight in this case
1793     if ( !keyString.empty() )
1794     {
1795       // replaces highlighted text with new character
1796       DeleteHighlightedText( false );
1797
1798       // Received key String
1799       mCursorPosition += InsertAt( Text( keyString ), mCursorPosition, 0 );
1800       update = true;
1801       EmitTextModified();
1802     }
1803   }
1804
1805   // If key event has resulted in a change in the text/cursor, then trigger a relayout of text
1806   // as this is a costly operation.
1807   if(update)
1808   {
1809     CursorUpdate();
1810   }
1811
1812   if(update || scroll)
1813   {
1814     if( IsScrollEnabled() )
1815     {
1816       // Calculates the new cursor position (in actor coordinates)
1817       const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
1818
1819       ScrollTextViewToMakeCursorVisible( cursorPosition );
1820     }
1821   }
1822
1823   return true;
1824 }
1825
1826 bool TextInput::OnKeyUpEvent(const KeyEvent& event)
1827 {
1828   std::string keyName = event.keyPressedName;
1829   std::string keyString = event.keyPressed;
1830
1831   DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyUpEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1832
1833   // The selected text become deselected when the key code is DALI_KEY_BACK.
1834   if( IsTextSelected() && ( keyName == "XF86Stop" || keyName == "XF86Send") )
1835   {
1836     DeSelectText();
1837     return true;
1838   }
1839
1840   return false;
1841 }
1842
1843 void TextInput::OnTextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition )
1844 {
1845   // Updates the stored scroll position.
1846   mTextLayoutInfo.mScrollOffset = textView.GetScrollPosition();
1847
1848   const Vector3& controlSize = GetControlSize();
1849   Size cursorSize( CURSOR_THICKNESS, 0.f );
1850
1851   // Updates the cursor and grab handle position and visibility.
1852   if( mGrabHandle || mCursor )
1853   {
1854     cursorSize.height = GetRowRectFromCharacterPosition( mCursorPosition ).height;
1855     const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1856
1857     mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( cursorPosition, cursorSize, controlSize );
1858
1859     mActualGrabHandlePosition = cursorPosition.GetVectorXY();
1860
1861     if( mGrabHandle )
1862     {
1863       ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1864       mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1865     }
1866
1867     if( mCursor )
1868     {
1869       mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
1870       mCursor.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1871     }
1872   }
1873
1874   // Updates the selection handles and highlighted text position and visibility.
1875   if( mSelectionHandleOne && mSelectionHandleTwo )
1876   {
1877     const Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition(mSelectionHandleOnePosition);
1878     const Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition(mSelectionHandleTwoPosition);
1879     cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleOnePosition ) ).mSize.height;
1880     const bool isSelectionHandleOneVisible = IsPositionInsideBoundaries( cursorPositionOne, cursorSize, controlSize );
1881     cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleTwoPosition ) ).mSize.height;
1882     const bool isSelectionHandleTwoVisible = IsPositionInsideBoundaries( cursorPositionTwo, cursorSize, controlSize );
1883
1884     mSelectionHandleOneActualPosition = cursorPositionOne.GetVectorXY();
1885     mSelectionHandleTwoActualPosition = cursorPositionTwo.GetVectorXY();
1886
1887     mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
1888     mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
1889     mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
1890     mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
1891
1892     if( mHighlightMeshActor )
1893     {
1894       mHighlightMeshActor.SetVisible( true );
1895       UpdateHighlight();
1896     }
1897   }
1898 }
1899
1900 void TextInput::ScrollTextViewToMakeCursorVisible( const Vector3& cursorPosition )
1901 {
1902   // Scroll the text to make the cursor visible.
1903   const Size cursorSize( CURSOR_THICKNESS,
1904                          GetRowRectFromCharacterPosition( mCursorPosition ).height );
1905
1906   // Need to scroll the text to make the cursor visible and to cover the whole text-input area.
1907
1908   const Vector3& controlSize = GetControlSize();
1909
1910   // Calculates the new scroll position.
1911   Vector2 scrollOffset = mTextLayoutInfo.mScrollOffset;
1912   if( ( cursorPosition.x < 0.f ) || ( cursorPosition.x > controlSize.width ) )
1913   {
1914     scrollOffset.x += cursorPosition.x;
1915   }
1916
1917   if( cursorPosition.y - cursorSize.height < 0.f || cursorPosition.y > controlSize.height )
1918   {
1919     scrollOffset.y += cursorPosition.y;
1920   }
1921
1922   // Sets the new scroll position.
1923   SetScrollPosition( Vector2::ZERO ); // TODO: need to reset to the zero position in order to make the scroll trim to work.
1924   SetScrollPosition( scrollOffset );
1925 }
1926
1927 void TextInput::StartScrollTimer()
1928 {
1929   if( !mScrollTimer )
1930   {
1931     mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1932     mScrollTimer.TickSignal().Connect( this, &TextInput::OnScrollTimerTick );
1933   }
1934
1935   if( !mScrollTimer.IsRunning() )
1936   {
1937     mScrollTimer.Start();
1938   }
1939 }
1940
1941 void TextInput::StopScrollTimer()
1942 {
1943   if( mScrollTimer )
1944   {
1945     mScrollTimer.Stop();
1946   }
1947 }
1948
1949 bool TextInput::OnScrollTimerTick()
1950 {
1951   // TODO: need to set the new style accordingly the new handle position.
1952
1953   if( !( mGrabHandleVisibility && mGrabHandle ) && !( mSelectionHandleOne && mSelectionHandleTwo ) )
1954   {
1955     // nothing to do if all handles are invisible or doesn't exist.
1956     return true;
1957   }
1958
1959   // Text scrolling
1960
1961   // Choose between the grab handle or the selection handles.
1962   Vector3& actualHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mActualGrabHandlePosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
1963   std::size_t& handlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCursorPosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
1964   Vector3& currentHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCurrentHandlePosition : mCurrentSelectionHandlePosition;
1965
1966   std::size_t newCursorPosition = 0;
1967   ReturnClosestIndex( actualHandlePosition.GetVectorXY(), newCursorPosition );
1968
1969   // Whether the handle's position is different of the previous one and in the case of the selection handle,
1970   // the new selection handle's position needs to be different of the other one.
1971   const bool differentSelectionHandles = ( mGrabHandleVisibility && mGrabHandle ) ? newCursorPosition != handlePosition :
1972                                          ( mCurrentSelectionId == HandleOne ) ? ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleTwoPosition ) :
1973                                                                                 ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleOnePosition );
1974
1975   if( differentSelectionHandles )
1976   {
1977     handlePosition = newCursorPosition;
1978
1979     const Vector3 actualPosition = GetActualPositionFromCharacterPosition( newCursorPosition );
1980
1981     Vector2 scrollDelta = ( actualPosition - currentHandlePosition ).GetVectorXY();
1982
1983     Vector2 scrollPosition = mDisplayedTextView.GetScrollPosition();
1984     scrollPosition += scrollDelta;
1985     SetScrollPosition( scrollPosition );
1986
1987     if( mDisplayedTextView.IsScrollPositionTrimmed() )
1988     {
1989       StopScrollTimer();
1990     }
1991
1992     currentHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition ).GetVectorXY();
1993   }
1994
1995   actualHandlePosition.x += mScrollDisplacement.x;
1996   actualHandlePosition.y += mScrollDisplacement.y;
1997
1998   return true;
1999 }
2000
2001 // Public Internal Methods (public for testing purpose)
2002
2003 void TextInput::SetUpTouchEvents()
2004 {
2005   if ( !mTapDetector )
2006   {
2007     mTapDetector = TapGestureDetector::New();
2008     // Attach the actors and connect the signal
2009     mTapDetector.Attach(Self());
2010
2011     // As contains children which may register for tap the default control detector is not used.
2012     mTapDetector.DetectedSignal().Connect(this, &TextInput::OnTextTap);
2013   }
2014
2015   if ( !mDoubleTapDetector )
2016   {
2017     mDoubleTapDetector = TapGestureDetector::New();
2018     mDoubleTapDetector.SetTapsRequired( 2 );
2019     mDoubleTapDetector.DetectedSignal().Connect(this, &TextInput::OnDoubleTap);
2020
2021     // Only attach and detach the actor to the double tap detector when we enter/leave edit mode
2022     // so that we do not, unnecessarily, have a double tap request all the time
2023   }
2024
2025   if ( !mPanGestureDetector )
2026   {
2027     mPanGestureDetector = PanGestureDetector::New();
2028     mPanGestureDetector.DetectedSignal().Connect(this, &TextInput::OnHandlePan);
2029   }
2030
2031   if ( !mLongPressDetector )
2032   {
2033     mLongPressDetector = LongPressGestureDetector::New();
2034     mLongPressDetector.DetectedSignal().Connect(this, &TextInput::OnLongPress);
2035     mLongPressDetector.Attach(Self());
2036   }
2037 }
2038
2039 void TextInput::CreateTextViewActor()
2040 {
2041   mDisplayedTextView = Toolkit::TextView::New();
2042   mDisplayedTextView.SetName( "DisplayedTextView ");
2043   mDisplayedTextView.SetMarkupProcessingEnabled( mMarkUpEnabled );
2044   mDisplayedTextView.SetParentOrigin(ParentOrigin::TOP_LEFT);
2045   mDisplayedTextView.SetAnchorPoint(AnchorPoint::TOP_LEFT);
2046   mDisplayedTextView.SetMultilinePolicy(Toolkit::TextView::SplitByWord);
2047   mDisplayedTextView.SetWidthExceedPolicy( Toolkit::TextView::Original );
2048   mDisplayedTextView.SetHeightExceedPolicy( Toolkit::TextView::Original );
2049   mDisplayedTextView.SetLineJustification( Toolkit::TextView::Left );
2050   mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>( Toolkit::Alignment::HorizontalLeft | Toolkit::Alignment::VerticalTop ) );
2051   mDisplayedTextView.SetPosition( Vector3( 0.0f, 0.0f, DISPLAYED_TEXT_VIEW_Z_OFFSET ) );
2052   mDisplayedTextView.SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
2053
2054   mDisplayedTextView.ScrolledSignal().Connect( this, &TextInput::OnTextViewScrolled );
2055
2056   Self().Add( mDisplayedTextView );
2057 }
2058
2059 // Start a timer to initiate, used by the cursor to blink.
2060 void TextInput::StartCursorBlinkTimer()
2061 {
2062   if ( !mCursorBlinkTimer )
2063   {
2064     mCursorBlinkTimer = Timer::New( CURSOR_BLINK_INTERVAL );
2065     mCursorBlinkTimer.TickSignal().Connect( this, &TextInput::OnCursorBlinkTimerTick );
2066   }
2067
2068   if ( !mCursorBlinkTimer.IsRunning() )
2069   {
2070     mCursorBlinkTimer.Start();
2071   }
2072 }
2073
2074 // Start a timer to initiate, used by the cursor to blink.
2075 void TextInput::StopCursorBlinkTimer()
2076 {
2077   if ( mCursorBlinkTimer )
2078   {
2079     mCursorBlinkTimer.Stop();
2080   }
2081 }
2082
2083 void TextInput::StartEditMode()
2084 {
2085   DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput StartEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2086
2087   if(!mEditModeActive)
2088   {
2089     SetKeyInputFocus();
2090   }
2091
2092   if ( mDoubleTapDetector )
2093   {
2094     mDoubleTapDetector.Attach( Self() );
2095   }
2096 }
2097
2098 void TextInput::EndEditMode()
2099 {
2100   DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput EndEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2101
2102   ClearKeyInputFocus();
2103
2104   if ( mDoubleTapDetector )
2105   {
2106     mDoubleTapDetector.Detach( Self() );
2107   }
2108 }
2109
2110 void TextInput::ApplyPreEditStyle( std::size_t preEditStartPosition, std::size_t preEditStringLength )
2111 {
2112   if ( mPreEditFlag && ( preEditStringLength > 0 ) )
2113   {
2114     mUnderlinedPriorToPreEdit = mInputStyle.IsUnderlineEnabled();
2115     TextStyle style;
2116     style.SetUnderline( true );
2117     ApplyStyleToRange( style, TextStyle::UNDERLINE , preEditStartPosition, preEditStartPosition + preEditStringLength -1 );
2118   }
2119 }
2120
2121 void TextInput::RemovePreEditStyle()
2122 {
2123   if ( !mUnderlinedPriorToPreEdit )
2124   {
2125     TextStyle style;
2126     style.SetUnderline( false );
2127     SetActiveStyle( style, TextStyle::UNDERLINE );
2128   }
2129 }
2130
2131 // IMF related methods
2132
2133
2134 ImfManager::ImfCallbackData TextInput::ImfEventReceived( Dali::ImfManager& imfManager, const ImfManager::ImfEventData&  imfEvent )
2135 {
2136   bool update( false );
2137   bool preeditResetRequired ( false );
2138
2139   if (imfEvent.eventName != ImfManager::GETSURROUNDING )
2140   {
2141     HidePopup(); // If Pop-up shown then hides it as editing text.
2142   }
2143
2144   switch ( imfEvent.eventName )
2145   {
2146     case ImfManager::PREEDIT:
2147     {
2148       mIgnoreFirstCommitFlag = false;
2149
2150       // 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
2151       if ( mHighlightMeshActor &&  (!imfEvent.predictiveString.empty()) )
2152       {
2153         // replaces highlighted text with new character
2154         DeleteHighlightedText( false );
2155       }
2156
2157       preeditResetRequired = PreEditReceived( imfEvent.predictiveString, imfEvent.cursorOffset );
2158
2159       if( IsScrollEnabled() )
2160       {
2161         // Calculates the new cursor position (in actor coordinates)
2162         const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2163         ScrollTextViewToMakeCursorVisible( cursorPosition );
2164       }
2165
2166       update = true;
2167
2168       break;
2169     }
2170     case ImfManager::COMMIT:
2171     {
2172       if( mIgnoreFirstCommitFlag )
2173       {
2174         // Do not commit in this case when keyboard sends a commit when shows for the first time (work-around for imf keyboard).
2175         mIgnoreFirstCommitFlag = false;
2176       }
2177       else
2178       {
2179         // A Commit message is a word that has been accepted, it may have been a pre-edit word previously but now commited.
2180
2181         // 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
2182         if ( mHighlightMeshActor &&  (!imfEvent.predictiveString.empty()) )
2183         {
2184           // replaces highlighted text with new character
2185           DeleteHighlightedText( false );
2186         }
2187
2188        // A PreEditReset can cause a commit message to be sent, the Ignore Commit flag is used in scenarios where the word is
2189        // not needed, one such scenario is when the pre-edit word is too long to fit.
2190        if ( !mIgnoreCommitFlag )
2191        {
2192          update = CommitReceived( imfEvent.predictiveString );
2193        }
2194        else
2195        {
2196          mIgnoreCommitFlag = false; // reset ignore flag so next commit is acted upon.
2197        }
2198       }
2199
2200       if( update )
2201       {
2202         if( IsScrollEnabled() )
2203         {
2204           // Calculates the new cursor position (in actor coordinates)
2205           const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2206
2207           ScrollTextViewToMakeCursorVisible( cursorPosition );
2208         }
2209       }
2210       break;
2211     }
2212     case ImfManager::DELETESURROUNDING:
2213     {
2214       DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - delete surrounding mPreEditFlag[%s] cursor offset[%d] characters to delete[%d] position to delete[%u] \n",
2215                      (mPreEditFlag)?"true":"false", imfEvent.cursorOffset, imfEvent.numberOfChars, static_cast<std::size_t>( mCursorPosition+imfEvent.cursorOffset) );
2216
2217       mPreEditFlag = false;
2218
2219       std::size_t toDelete = 0;
2220       std::size_t numberOfCharacters = 0;
2221
2222       if( mHighlightMeshActor )
2223       {
2224         // delete highlighted text.
2225         toDelete = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2226         numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - toDelete;
2227       }
2228       else
2229       {
2230         if( static_cast<std::size_t>(std::abs( imfEvent.cursorOffset )) < mCursorPosition )
2231         {
2232           toDelete = mCursorPosition + imfEvent.cursorOffset;
2233         }
2234         if( toDelete + imfEvent.numberOfChars > mStyledText.size() )
2235         {
2236           numberOfCharacters = mStyledText.size() - toDelete;
2237         }
2238         else
2239         {
2240           numberOfCharacters = imfEvent.numberOfChars;
2241         }
2242       }
2243       DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding pre-delete range mCursorPosition[%u] \n", mCursorPosition);
2244       DeleteRange( toDelete, numberOfCharacters );
2245
2246       mCursorPosition = toDelete;
2247       mNumberOfSurroundingCharactersDeleted = numberOfCharacters;
2248
2249       EmitTextModified();
2250
2251       DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding post-delete range mCursorPosition[%u] \n", mCursorPosition);
2252       break;
2253     }
2254     case ImfManager::GETSURROUNDING:
2255     {
2256       // 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
2257       // the next key pressed.  Instead the Select function sets the cursor position and surrounding text.
2258       if (! ( mHighlightMeshActor || mSelectingText ) )
2259       {
2260         std::string text( GetText() );
2261         DALI_LOG_INFO( gLogFilter, Debug::General, "OnKey - surrounding text - set text [%s] and cursor[%u] \n", text.c_str(), mCursorPosition );
2262
2263         imfManager.SetCursorPosition( mCursorPosition );
2264         imfManager.SetSurroundingText( text );
2265       }
2266
2267       if( 0 != mNumberOfSurroundingCharactersDeleted )
2268       {
2269         mDisplayedTextView.RemoveTextFrom( mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2270         mNumberOfSurroundingCharactersDeleted = 0;
2271
2272         if( mStyledText.empty() )
2273         {
2274           // Styled text is empty, so set the placeholder text.
2275           mDisplayedTextView.SetText( mStyledPlaceHolderText );
2276           mPlaceHolderSet = true;
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           // Styled text is empty, so set the placeholder text.
2334           mDisplayedTextView.SetText( mStyledPlaceHolderText );
2335           mPlaceHolderSet = true;
2336         }
2337         else
2338         {
2339           mDisplayedTextView.RemoveTextFrom( mPreEditStartPosition, numberOfCharactersToReplace );
2340         }
2341         GetTextLayoutInfo();
2342         EmitTextModified();
2343       }
2344       else
2345       {
2346         // Insert new pre-edit string. InsertAt updates the size and position table.
2347         mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersToReplace );
2348         // 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.
2349         mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2350         ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2351         DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] \n", mCursorPosition);
2352         EmitTextModified();
2353       }
2354       // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2355       DrawCursor();
2356     }
2357   }
2358   else  // mPreEditFlag not set
2359   {
2360     if ( !keyString.empty() ) // Imf can send an empty pre-edit followed by Backspace instead of a commit.
2361     {
2362       DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived Initial Pre-Edit string \n");
2363       // new pre-edit so move into pre-edit state by setting flag
2364       mPreEditFlag = true;
2365       mPreEditString.SetText( keyString ); // store new pre-edit string
2366       mPreEditStartPosition = mCursorPosition; // store starting cursor position of pre-edit so know where to re-start from
2367       mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, 0 );
2368       // 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.
2369       mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2370       ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2371       DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] mPreEditStartPosition[%u]\n", mCursorPosition, mPreEditStartPosition);
2372       // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2373       DrawCursor();
2374       EmitTextModified();
2375     }
2376     else
2377     {
2378       DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived with empty keyString\n");
2379     }
2380   }
2381
2382   return preeditResetRequest;
2383 }
2384
2385 bool TextInput::CommitReceived(const std::string& keyString )
2386 {
2387   DALI_LOG_INFO(gLogFilter, Debug::General, ">>CommitReceived preserveCursorPos[%d] mPreEditStartPosition [%d] mCursorPos[%d] mPreEditFlag[%d] mIgnoreCommitFlag[%s]\n",
2388       mPreserveCursorPosition, mPreEditStartPosition, mCursorPosition, mPreEditFlag, (mIgnoreCommitFlag)?"true":"false" );
2389
2390   bool update( false );
2391
2392   RemovePreEditStyle();
2393
2394   const std::size_t styledTextSize( mStyledText.size() );
2395   if( styledTextSize >= mMaxStringLength )
2396   {
2397     // Cannot fit these characters into field, clear pre-edit.
2398     if ( mPreEditFlag )
2399     {
2400       mIgnoreCommitFlag = true;
2401       mPreEditFlag = false;
2402     }
2403     EmitMaxInputCharactersReachedSignal();
2404   }
2405   else
2406   {
2407     if( mPreEditFlag )
2408     {
2409       // delete existing pre-edit string
2410       const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2411       mPreEditFlag = false;
2412
2413       DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived mPreserveCursorPosition[%s] mPreEditStartPosition[%u]\n",
2414                     (mPreserveCursorPosition)?"true":"false", mPreEditStartPosition );
2415
2416       if ( mPreserveCursorPosition ) // PreEditReset has been called triggering this commit.
2417       {
2418         // No need to update cursor position as Cursor location given by touch.
2419         InsertAt( Text( keyString ), mPreEditStartPosition, numberOfCharactersToReplace );
2420         mPreserveCursorPosition = false;
2421       }
2422       else
2423       {
2424         // Cursor not set by touch so needs to be re-positioned to input more text
2425         mCursorPosition = mPreEditStartPosition + InsertAt( Text(keyString), mPreEditStartPosition, numberOfCharactersToReplace ); // update cursor position as InsertAt, re-draw cursor with this
2426
2427         // If a space or enter caused the commit then our string is one longer than the string given to us by the commit key.
2428         if ( mCommitByKeyInput )
2429         {
2430           mCursorPosition = std::min ( mCursorPosition + 1, mStyledText.size() );
2431           mCommitByKeyInput = false;
2432         }
2433       }
2434
2435       EmitTextModified();
2436
2437       if ( mSelectTextOnCommit )
2438       {
2439         SelectText(mRequestedSelection.mStartOfSelection, mRequestedSelection.mEndOfSelection );
2440       }
2441
2442       update = true;
2443     }
2444     else // mPreEditFlag not set
2445     {
2446       if ( !mIgnoreCommitFlag ) // Check if this commit should be ignored.
2447       {
2448         if( mStyledText.empty() && mPlaceHolderSet )
2449         {
2450           // If the styled text is empty and the placeholder text is set, it needs to be cleared.
2451           mDisplayedTextView.SetText( "" );
2452           mNumberOfSurroundingCharactersDeleted = 0;
2453           mPlaceHolderSet = false;
2454         }
2455         mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2456         update = true;
2457         mNumberOfSurroundingCharactersDeleted = 0;
2458         EmitTextModified();
2459       }
2460       else
2461       {
2462         mIgnoreCommitFlag = false; // Reset flag so future commits will not be ignored.
2463       }
2464     }
2465   }
2466
2467   mSelectTextOnCommit = false;
2468
2469   DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived << mCursorPos[%d] mPreEditFlag[%d] update[%s] \n",
2470                                              mCursorPosition, mPreEditFlag, (update)?"true":"false" );
2471
2472   return update;
2473 }
2474
2475 // End of IMF related methods
2476
2477 std::size_t TextInput::DeletePreEdit()
2478 {
2479   DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeletePreEdit mPreEditFlag[%s] \n", (mPreEditFlag)?"true":"false");
2480
2481   DALI_ASSERT_DEBUG( mPreEditFlag );
2482
2483   const std::size_t preEditStringLength = mPreEditString.GetLength();
2484   const std::size_t styledTextSize = mStyledText.size();
2485
2486   std::size_t endPosition = mPreEditStartPosition + preEditStringLength;
2487
2488   // Prevents erase items outside mStyledText bounds.
2489   if( mPreEditStartPosition > styledTextSize )
2490   {
2491     DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. mPreEditStartPosition > mStyledText.size()" );
2492     mPreEditStartPosition = styledTextSize;
2493   }
2494
2495   if( ( endPosition > styledTextSize ) || ( endPosition < mPreEditStartPosition ) )
2496   {
2497     DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. ( endPosition > mStyledText.size() ) || ( endPosition < mPreEditStartPosition )" );
2498     endPosition = styledTextSize;
2499   }
2500
2501   mStyledText.erase( mStyledText.begin() + mPreEditStartPosition, mStyledText.begin() + endPosition );
2502
2503   // DeletePreEdit() doesn't remove characters from the text-view because may be followed by an InsertAt() which inserts characters,
2504   // in that case, the Insert should use the returned number of deleted characters and replace the text which helps the text-view to
2505   // reuse glyphs.
2506   // In case DeletePreEdit() is not followed by an InsertAt() characters must be deleted after this call.
2507
2508   return preEditStringLength;
2509 }
2510
2511 void TextInput::PreEditReset( bool preserveCursorPosition )
2512 {
2513   DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReset preserveCursorPos[%d] mCursorPos[%d] \n",
2514                 preserveCursorPosition, mCursorPosition);
2515
2516   // 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.
2517   mPreserveCursorPosition = preserveCursorPosition;
2518
2519   // Reset incase we are in a pre-edit state.
2520   ImfManager imfManager = ImfManager::Get();
2521   if ( imfManager )
2522   {
2523     imfManager.Reset(); // Will trigger a commit message
2524   }
2525 }
2526
2527 void TextInput::CursorUpdate()
2528 {
2529   DrawCursor();
2530
2531   ImfManager imfManager = ImfManager::Get();
2532   if ( imfManager )
2533   {
2534     std::string text( GetText() );
2535     imfManager.SetSurroundingText( text );  // Notifying IMF of a cursor change triggers a surrounding text request so updating it now.
2536     imfManager.SetCursorPosition ( mCursorPosition );
2537     imfManager.NotifyCursorPosition();
2538   }
2539 }
2540
2541 /* Delete highlighted characters redisplay*/
2542 void TextInput::DeleteHighlightedText( bool inheritStyle )
2543 {
2544   DALI_LOG_INFO( gLogFilter, Debug::General, "DeleteHighlightedText handlePosOne[%u] handlePosTwo[%u]\n", mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
2545
2546   if( mHighlightMeshActor )
2547   {
2548     mCursorPosition = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2549
2550     MarkupProcessor::StyledTextArray::iterator start = mStyledText.begin() + mCursorPosition;
2551     MarkupProcessor::StyledTextArray::iterator end =  mStyledText.begin() + std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2552
2553     // Get the styled text of the characters to be deleted as it may be needed if
2554     // the "exceed the text-input's boundaries" option is disabled.
2555     MarkupProcessor::StyledTextArray styledCharactersToDelete;
2556
2557     styledCharactersToDelete.insert( styledCharactersToDelete.begin(), start, end );
2558
2559     mStyledText.erase( start, end ); // erase range of characters
2560
2561     // Remove text from TextView.
2562
2563     if( mStyledText.empty() )
2564     {
2565       // Styled text is empty, so set the placeholder text.
2566       mDisplayedTextView.SetText( mStyledPlaceHolderText );
2567       mPlaceHolderSet = true;
2568     }
2569     else
2570     {
2571       const std::size_t numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - mCursorPosition;
2572
2573       mDisplayedTextView.RemoveTextFrom( mCursorPosition, numberOfCharacters );
2574
2575       // It may happen than after removing a white space or a new line character,
2576       // two words merge, this new word could be big enough to not fit in its
2577       // current line, so moved to the next one, and make some part of the text to
2578       // exceed the text-input's boundary.
2579       if( !mExceedEnabled )
2580       {
2581         // Get the new text layout after removing some characters.
2582         mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2583
2584         // Get text-input's size.
2585         const Vector3& size = GetControlSize();
2586
2587         if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2588             ( mTextLayoutInfo.mTextSize.height > size.height ) )
2589         {
2590           mDisplayedTextView.InsertTextAt( mCursorPosition, styledCharactersToDelete );
2591
2592           mStyledText.insert( mStyledText.begin() + mCursorPosition,
2593                               styledCharactersToDelete.begin(),
2594                               styledCharactersToDelete.end() );
2595         }
2596       }
2597     }
2598     GetTextLayoutInfo();
2599
2600     RemoveHighlight();
2601
2602     EmitTextModified();
2603
2604     if( inheritStyle )
2605     {
2606       const TextStyle oldInputStyle( mInputStyle );
2607
2608       mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2609
2610       if( oldInputStyle != mInputStyle )
2611       {
2612         // Updates the line height accordingly with the input style.
2613         UpdateLineHeight();
2614
2615         EmitStyleChangedSignal();
2616       }
2617     }
2618   }
2619 }
2620
2621 void TextInput::DeleteRange( const std::size_t start, const std::size_t ncharacters )
2622 {
2623   DALI_ASSERT_DEBUG( start <= mStyledText.size() );
2624   DALI_ASSERT_DEBUG( !mStyledText.empty() );
2625
2626   DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeleteRange pre mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2627
2628
2629   if ( ( !mStyledText.empty()) && ( ( start + ncharacters ) <= mStyledText.size() ) )
2630   {
2631     MarkupProcessor::StyledTextArray::iterator itStart =  mStyledText.begin() + start;
2632     MarkupProcessor::StyledTextArray::iterator itEnd =  mStyledText.begin() + start + ncharacters;
2633
2634     mStyledText.erase(itStart, itEnd);
2635
2636     // update the selection handles if they are visible.
2637     if( mHighlightMeshActor )
2638     {
2639       std::size_t& minHandle = ( mSelectionHandleOnePosition <= mSelectionHandleTwoPosition ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition );
2640       std::size_t& maxHandle = ( mSelectionHandleTwoPosition > mSelectionHandleOnePosition ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition );
2641
2642       if( minHandle >= start + ncharacters )
2643       {
2644         minHandle -= ncharacters;
2645       }
2646       else if( ( minHandle > start ) && ( minHandle < start + ncharacters ) )
2647       {
2648         minHandle = start;
2649       }
2650
2651       if( maxHandle >= start + ncharacters )
2652       {
2653         maxHandle -= ncharacters;
2654       }
2655       else if( ( maxHandle > start ) && ( maxHandle < start + ncharacters ) )
2656       {
2657         maxHandle = start;
2658       }
2659     }
2660
2661     // 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.
2662   }
2663
2664   DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteRange<< post mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2665
2666   // Although mStyledText has been set to a new text string we no longer re-draw the text or notify the cursor change.
2667   // This is a performance decision as the use of this function often means the text is being replaced or just deleted.
2668   // Mean we do not re-draw the text more than we have too.
2669 }
2670
2671 /* Delete character at current cursor position and redisplay*/
2672 void TextInput::DeleteCharacter( std::size_t positionToDelete )
2673 {
2674   // Ensure positionToDelete is not out of bounds.
2675   DALI_ASSERT_DEBUG( positionToDelete <= mStyledText.size() );
2676   DALI_ASSERT_DEBUG( !mStyledText.empty() );
2677   DALI_ASSERT_DEBUG( positionToDelete > 0 );
2678
2679   DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteCharacter positionToDelete[%u]", positionToDelete );
2680
2681
2682   if ( ( !mStyledText.empty()) && ( positionToDelete > 0 ) && positionToDelete <= mStyledText.size() )  // don't try to delete if no characters left of cursor
2683   {
2684     MarkupProcessor::StyledTextArray::iterator it =  mStyledText.begin() + positionToDelete - 1;
2685
2686     // Get the styled text of the character to be deleted as it may be needed if
2687     // the "exceed the text-input's boundaries" option is disabled.
2688     const MarkupProcessor::StyledText styledCharacterToDelete( *it );
2689
2690     mStyledText.erase(it);  // erase the character left of positionToDelete
2691
2692     if( mStyledText.empty() )
2693     {
2694       // Styled text is empty, so set the placeholder text.
2695       mDisplayedTextView.SetText( mStyledPlaceHolderText );
2696       mPlaceHolderSet = true;
2697     }
2698     else
2699     {
2700       mDisplayedTextView.RemoveTextFrom( positionToDelete - 1, 1 );
2701
2702       const Character characterToDelete = styledCharacterToDelete.mText[0];
2703
2704       // It may happen than after removing a white space or a new line character,
2705       // two words merge, this new word could be big enough to not fit in its
2706       // current line, so moved to the next one, and make some part of the text to
2707       // exceed the text-input's boundary.
2708       if( !mExceedEnabled )
2709       {
2710         if( characterToDelete.IsWhiteSpace() || characterToDelete.IsNewLine() )
2711         {
2712           // Get the new text layout after removing one character.
2713           mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2714
2715           // Get text-input's size.
2716           const Vector3& size = GetControlSize();
2717
2718           if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2719               ( mTextLayoutInfo.mTextSize.height > size.height ) )
2720           {
2721             MarkupProcessor::StyledTextArray array;
2722             array.push_back( styledCharacterToDelete );
2723             mDisplayedTextView.InsertTextAt( positionToDelete - 1, array );
2724
2725             mStyledText.insert( mStyledText.begin() + ( positionToDelete - 1 ), styledCharacterToDelete );
2726           }
2727         }
2728       }
2729     }
2730     GetTextLayoutInfo();
2731
2732     ShowGrabHandleAndSetVisibility( false );
2733
2734     mCursorPosition = positionToDelete -1;
2735
2736     const TextStyle oldInputStyle( mInputStyle );
2737
2738     mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2739
2740     if( oldInputStyle != mInputStyle )
2741     {
2742       // Updates the line height accordingly with the input style.
2743       UpdateLineHeight();
2744
2745       EmitStyleChangedSignal();
2746     }
2747   }
2748 }
2749
2750 /*Insert new character into the string and (optionally) redisplay text-input*/
2751 std::size_t TextInput::InsertAt( const Text& newText, const std::size_t insertionPosition, const std::size_t numberOfCharactersToReplace )
2752 {
2753   DALI_LOG_INFO(gLogFilter, Debug::General, "InsertAt insertionPosition[%u]\n", insertionPosition );
2754
2755   // Ensure insertionPosition is not out of bounds.
2756   DALI_ASSERT_ALWAYS( insertionPosition <= mStyledText.size() );
2757
2758   bool textExceedsMaximunNumberOfCharacters = false;
2759   bool textExceedsBoundary = false;
2760   std::size_t insertedStringLength = DoInsertAt( newText, insertionPosition, numberOfCharactersToReplace, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
2761
2762   ShowGrabHandleAndSetVisibility( false );
2763
2764   if( textExceedsMaximunNumberOfCharacters || textExceedsBoundary )
2765   {
2766     if( mPreEditFlag )
2767     {
2768       mIgnoreCommitFlag = true;
2769       mPreEditFlag = false;
2770       // A PreEditReset( false ) should be triggered from here if the keyboards predictive suggestions must be cleared.
2771       // Although can not directly call PreEditReset() as it will cause a recursive emit loop.
2772     }
2773
2774     if( textExceedsMaximunNumberOfCharacters )
2775     {
2776       EmitMaxInputCharactersReachedSignal();
2777     }
2778
2779     if( textExceedsBoundary )
2780     {
2781       EmitInputTextExceedsBoundariesSignal();
2782       PreEditReset( false );
2783     }
2784   }
2785
2786   return insertedStringLength;
2787 }
2788
2789 ImageActor TextInput::CreateCursor( const Vector4& color)
2790 {
2791   ImageActor cursor;
2792   cursor = CreateSolidColorActor(color);
2793   cursor.SetName( "Cursor" );
2794
2795   cursor.SetParentOrigin(ParentOrigin::TOP_LEFT);
2796   cursor.SetAnchorPoint(AnchorPoint::BOTTOM_LEFT);
2797   cursor.SetVisible(false);
2798
2799   return cursor;
2800 }
2801
2802 void TextInput::AdvanceCursor(bool reverse, std::size_t places)
2803 {
2804   // As cursor is not moving due to grab handle, handle should be hidden.
2805   ShowGrabHandleAndSetVisibility( false );
2806
2807   bool cursorPositionChanged = false;
2808   if (reverse)
2809   {
2810     if ( mCursorPosition >= places )
2811     {
2812       mCursorPosition = mCursorPosition - places;
2813       cursorPositionChanged = true;
2814     }
2815   }
2816   else
2817   {
2818     if ((mCursorPosition + places) <= mStyledText.size())
2819     {
2820       mCursorPosition = mCursorPosition + places;
2821       cursorPositionChanged = true;
2822     }
2823   }
2824
2825   if( cursorPositionChanged )
2826   {
2827     const std::size_t cursorPositionForStyle = ( 0 == mCursorPosition ? 0 : mCursorPosition - 1 );
2828
2829     const TextStyle oldInputStyle( mInputStyle );
2830     mInputStyle = GetStyleAt( cursorPositionForStyle ); // Inherit style from selected position.
2831
2832     DrawCursor();
2833
2834     if( oldInputStyle != mInputStyle )
2835     {
2836       // Updates the line height accordingly with the input style.
2837       UpdateLineHeight();
2838
2839       EmitStyleChangedSignal();
2840     }
2841
2842     ImfManager imfManager = ImfManager::Get();
2843     if ( imfManager )
2844     {
2845       imfManager.SetCursorPosition ( mCursorPosition );
2846       imfManager.NotifyCursorPosition();
2847     }
2848   }
2849 }
2850
2851 void TextInput::DrawCursor()
2852 {
2853   const Size rowRect = GetRowRectFromCharacterPosition( mCursorPosition );
2854
2855   // Get height of cursor and set its size
2856   Size size( CURSOR_THICKNESS, 0.0f );
2857   if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
2858   {
2859     size.height = rowRect.height;
2860   }
2861   else
2862   {
2863     // Measure Font so know how big text will be if no initial text to measure.
2864     size.height = mLineHeight;
2865   }
2866
2867   mCursor.SetSize(size);
2868
2869   // If the character is italic then the cursor also tilts.
2870   mCursor.SetRotation( mInputStyle.IsItalicsEnabled() ? Degree( mInputStyle.GetItalicsAngle() - CURSOR_ANGLE_OFFSET ) : Degree( 0.f ), Vector3::ZAXIS );
2871
2872   DALI_ASSERT_DEBUG( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
2873
2874   if( ( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) )
2875   {
2876     Vector3 altPosition;    // Alternate (i.e. opposite direction) cursor position.
2877     bool altPositionValid;  // Alternate cursor validity flag.
2878     bool directionRTL;      // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
2879     Vector3 position = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
2880
2881     SetAltCursorEnabled( altPositionValid );
2882
2883     if( !altPositionValid )
2884     {
2885       mCursor.SetPosition( position + UI_OFFSET );
2886     }
2887     else
2888     {
2889       size.height *= 0.5f;
2890       mCursor.SetSize(size);
2891       mCursor.SetPosition( position + UI_OFFSET - Vector3( 0.0f, directionRTL ? 0.0f : size.height, 0.0f ) );
2892
2893       // TODO: change this cursor pos, to be the one where the cursor is sourced from.
2894       size.height = rowRect.height * 0.5f;
2895       mCursorRTL.SetSize(size);
2896       mCursorRTL.SetPosition( altPosition + UI_OFFSET - Vector3( 0.0f, directionRTL ? size.height : 0.0f, 0.0f ) );
2897     }
2898
2899     if( IsScrollEnabled() )
2900     {
2901       // Whether cursor and grab handle are inside the boundaries of the text-input when text scroll is enabled.
2902       mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( position, size, GetControlSize() );
2903     }
2904   } // EditMode
2905 }
2906
2907 void TextInput::SetAltCursorEnabled( bool enabled )
2908 {
2909   mCursorRTLEnabled = enabled;
2910   mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2911 }
2912
2913 void TextInput::SetCursorVisibility( bool visible )
2914 {
2915   mCursorVisibility = visible;
2916   mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
2917   mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2918 }
2919
2920 void TextInput::CreateGrabHandle( Dali::Image image )
2921 {
2922   if ( !mGrabHandle )
2923   {
2924     if ( !image )
2925     {
2926       mGrabHandleImage = Image::New(DEFAULT_GRAB_HANDLE);
2927     }
2928     else
2929     {
2930       mGrabHandleImage = image;
2931     }
2932
2933     mGrabHandle = ImageActor::New(mGrabHandleImage);
2934     mGrabHandle.SetParentOrigin(ParentOrigin::TOP_LEFT);
2935     mGrabHandle.SetAnchorPoint(AnchorPoint::TOP_CENTER);
2936
2937     mGrabHandle.SetDrawMode(DrawMode::OVERLAY);
2938
2939     ShowGrabHandleAndSetVisibility( false );
2940
2941     CreateGrabArea( mGrabHandle );
2942
2943     mActiveLayer.Add(mGrabHandle);
2944   }
2945 }
2946
2947 void TextInput::CreateGrabArea( Actor& parent )
2948 {
2949   mGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
2950   mGrabArea.SetName( "GrabArea" );
2951   mGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
2952   mGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE ) ) );  // grab area to be larger than text actor
2953   mGrabArea.TouchedSignal().Connect(this,&TextInput::OnPressDown);
2954   mTapDetector.Attach( mGrabArea );
2955   mPanGestureDetector.Attach( mGrabArea );
2956   mLongPressDetector.Attach( mGrabArea );
2957
2958   parent.Add(mGrabArea);
2959 }
2960
2961 Vector3 TextInput::MoveGrabHandle( const Vector2& displacement )
2962 {
2963   Vector3 actualHandlePosition;
2964
2965   if (mGrabHandle)
2966   {
2967     mActualGrabHandlePosition.x += displacement.x;
2968     mActualGrabHandlePosition.y += displacement.y;
2969
2970     // Grab handle should jump to the nearest character and take cursor with it
2971     std::size_t newCursorPosition = 0;
2972     ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY(), newCursorPosition );
2973
2974     Vector3 altPosition;    // Alternate (i.e. opposite direction) cursor position.
2975     bool altPositionValid;  // Alternate cursor validity flag.
2976     bool directionRTL;      // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
2977     actualHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition, directionRTL, altPosition, altPositionValid );
2978
2979     if( altPositionValid )
2980     {
2981       // Check which of the positions is the closest.
2982       if( fabsf( altPosition.x - mActualGrabHandlePosition.x ) < fabsf( actualHandlePosition.x - mActualGrabHandlePosition.x ) )
2983       {
2984         actualHandlePosition = altPosition;
2985       }
2986     }
2987
2988     bool handleVisible = true;
2989
2990     if( IsScrollEnabled() )
2991     {
2992       const Vector3 controlSize = GetControlSize();
2993       const Size cursorSize = GetRowRectFromCharacterPosition( newCursorPosition );
2994       // Scrolls the text if the handle is not in a visible position
2995       handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
2996                                                   cursorSize,
2997                                                   controlSize );
2998
2999       if( handleVisible )
3000       {
3001         StopScrollTimer();
3002         mCurrentHandlePosition = actualHandlePosition;
3003         mScrollDisplacement = Vector2::ZERO;
3004       }
3005       else
3006       {
3007         if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3008         {
3009           mScrollDisplacement.x = -SCROLL_SPEED;
3010         }
3011         else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3012         {
3013           mScrollDisplacement.x = SCROLL_SPEED;
3014         }
3015         if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3016         {
3017           mScrollDisplacement.y = -SCROLL_SPEED;
3018         }
3019         else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3020         {
3021           mScrollDisplacement.y = SCROLL_SPEED;
3022         }
3023         StartScrollTimer();
3024       }
3025     }
3026
3027     if( handleVisible &&                           // Only redraw cursor and do updates if position changed
3028         ( newCursorPosition != mCursorPosition ) ) // and the new position is visible (if scroll is not enabled, it's always true).
3029     {
3030       mCursorPosition = newCursorPosition;
3031
3032       mGrabHandle.SetPosition( actualHandlePosition + UI_OFFSET );
3033
3034       const TextStyle oldInputStyle( mInputStyle );
3035
3036       mInputStyle = GetStyleAtCursor(); //Inherit style from cursor position
3037
3038       CursorUpdate();  // Let keyboard know the new cursor position so can 're-capture' for prediction.
3039
3040       if( oldInputStyle != mInputStyle )
3041       {
3042         // Updates the line height accordingly with the input style.
3043         UpdateLineHeight();
3044
3045         EmitStyleChangedSignal();
3046       }
3047     }
3048   }
3049
3050   return actualHandlePosition;
3051 }
3052
3053 void TextInput::ShowGrabHandle( bool visible )
3054 {
3055   if ( IsGrabHandleEnabled() )
3056   {
3057     if( mGrabHandle )
3058     {
3059       mGrabHandle.SetVisible( mGrabHandleVisibility );
3060     }
3061     StartMonitoringStageForTouch();
3062   }
3063 }
3064
3065 void TextInput::ShowGrabHandleAndSetVisibility( bool visible )
3066 {
3067   mGrabHandleVisibility = visible;
3068   ShowGrabHandle( visible );
3069 }
3070
3071 // Callbacks connected to be Property notifications for Boundary checking.
3072
3073 void TextInput::OnLeftBoundaryExceeded(PropertyNotification& source)
3074 {
3075   mIsSelectionHandleOneFlipped = true;
3076   mSelectionHandleOne.SetScale( -1.0f, 1.0f, 1.0f );
3077   mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3078 }
3079
3080 void TextInput::OnReturnToLeftBoundary(PropertyNotification& source)
3081 {
3082   mIsSelectionHandleOneFlipped = false;
3083   mSelectionHandleOne.SetScale( 1.0f, 1.0f, 1.0f );
3084   mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3085 }
3086
3087 void TextInput::OnRightBoundaryExceeded(PropertyNotification& source)
3088 {
3089   mIsSelectionHandleTwoFlipped = true;
3090   mSelectionHandleTwo.SetScale( -1.0f, 1.0f, 1.0f );
3091   mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3092 }
3093
3094 void TextInput::OnReturnToRightBoundary(PropertyNotification& source)
3095 {
3096   mIsSelectionHandleTwoFlipped = false;
3097   mSelectionHandleTwo.SetScale( 1.0f, 1.0f, 1.0f );
3098   mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3099 }
3100
3101 // todo change PropertyNotification signal definition to include Actor. Hence won't need duplicate functions.
3102 void TextInput::OnHandleOneLeavesBoundary( PropertyNotification& source)
3103 {
3104   mSelectionHandleOne.SetOpacity(0.0f);
3105 }
3106
3107 void TextInput::OnHandleOneWithinBoundary(PropertyNotification& source)
3108 {
3109   mSelectionHandleOne.SetOpacity(1.0f);
3110 }
3111
3112 void TextInput::OnHandleTwoLeavesBoundary( PropertyNotification& source)
3113 {
3114   mSelectionHandleTwo.SetOpacity(0.0f);
3115 }
3116
3117 void TextInput::OnHandleTwoWithinBoundary(PropertyNotification& source)
3118 {
3119   mSelectionHandleTwo.SetOpacity(1.0f);
3120 }
3121
3122 // End of Callbacks connected to be Property notifications for Boundary checking.
3123
3124 void TextInput::SetUpHandlePropertyNotifications()
3125 {
3126   /* Property notifications for handles exceeding the boundary and returning back within boundary */
3127
3128   Vector3 handlesize = GetSelectionHandleSize();
3129
3130   // Exceeding horizontal boundary
3131   PropertyNotification leftNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.x + handlesize.x) );
3132   leftNotification.NotifySignal().Connect( this, &TextInput::OnLeftBoundaryExceeded );
3133
3134   PropertyNotification rightNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.z - handlesize.x ) );
3135   rightNotification.NotifySignal().Connect( this, &TextInput::OnRightBoundaryExceeded );
3136
3137   // Within horizontal boundary
3138   PropertyNotification leftLeaveNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.x + 2*handlesize.x ) );
3139   leftLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToLeftBoundary );
3140
3141   PropertyNotification rightLeaveNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.z - 2*handlesize.x ) );
3142   rightLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToRightBoundary );
3143
3144   // Exceeding vertical boundary
3145   PropertyNotification verticalExceedNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3146                                                        OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3147                                                                          mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3148   verticalExceedNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneLeavesBoundary );
3149
3150   PropertyNotification verticalExceedNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3151                                                        OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3152                                                                          mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3153   verticalExceedNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoLeavesBoundary );
3154
3155   // Within vertical boundary
3156   PropertyNotification verticalWithinNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3157                                                        InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3158                                                                         mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3159   verticalWithinNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneWithinBoundary );
3160
3161   PropertyNotification verticalWithinNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3162                                                        InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3163                                                                         mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3164   verticalWithinNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoWithinBoundary );
3165 }
3166
3167 void TextInput::CreateSelectionHandles( std::size_t start, std::size_t end, Dali::Image handleOneImage,  Dali::Image handleTwoImage )
3168 {
3169   mSelectionHandleOnePosition = start;
3170   mSelectionHandleTwoPosition = end;
3171
3172   if ( !mSelectionHandleOne )
3173   {
3174     // create normal and pressed images
3175     mSelectionHandleOneImage = Image::New( DEFAULT_SELECTION_HANDLE_ONE );
3176     mSelectionHandleOneImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_ONE_PRESSED );
3177
3178     mSelectionHandleOne = ImageActor::New( mSelectionHandleOneImage );
3179     mSelectionHandleOne.SetName("SelectionHandleOne");
3180     mSelectionHandleOne.SetParentOrigin( ParentOrigin::TOP_LEFT );
3181     mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
3182     mIsSelectionHandleOneFlipped = false;
3183     mSelectionHandleOne.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text
3184
3185     mHandleOneGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3186     mHandleOneGrabArea.SetName("SelectionHandleOneGrabArea");
3187
3188     mHandleOneGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) );  // grab area to be larger than text actor
3189     mHandleOneGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3190
3191     mTapDetector.Attach( mHandleOneGrabArea );
3192     mPanGestureDetector.Attach( mHandleOneGrabArea );
3193
3194     mHandleOneGrabArea.TouchedSignal().Connect(this,&TextInput::OnHandleOneTouched);
3195
3196     mSelectionHandleOne.Add( mHandleOneGrabArea );
3197     mActiveLayer.Add( mSelectionHandleOne );
3198   }
3199
3200   if ( !mSelectionHandleTwo )
3201   {
3202     // create normal and pressed images
3203     mSelectionHandleTwoImage = Image::New( DEFAULT_SELECTION_HANDLE_TWO );
3204     mSelectionHandleTwoImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_TWO_PRESSED );
3205
3206     mSelectionHandleTwo = ImageActor::New( mSelectionHandleTwoImage );
3207     mSelectionHandleTwo.SetName("SelectionHandleTwo");
3208     mSelectionHandleTwo.SetParentOrigin( ParentOrigin::TOP_LEFT );
3209     mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT );
3210     mIsSelectionHandleTwoFlipped = false;
3211     mSelectionHandleTwo.SetDrawMode(DrawMode::OVERLAY); // ensure grab handle above text
3212
3213     mHandleTwoGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3214     mHandleTwoGrabArea.SetName("SelectionHandleTwoGrabArea");
3215     mHandleTwoGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) );  // grab area to be larger than text actor
3216     mHandleTwoGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3217
3218     mTapDetector.Attach( mHandleTwoGrabArea );
3219     mPanGestureDetector.Attach( mHandleTwoGrabArea );
3220
3221     mHandleTwoGrabArea.TouchedSignal().Connect(this, &TextInput::OnHandleTwoTouched);
3222
3223     mSelectionHandleTwo.Add( mHandleTwoGrabArea );
3224
3225     mActiveLayer.Add( mSelectionHandleTwo );
3226   }
3227
3228   SetUpHandlePropertyNotifications();
3229
3230   // update table as text may have changed.
3231   GetTextLayoutInfo();
3232
3233   mSelectionHandleOneActualPosition = GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition );
3234   mSelectionHandleTwoActualPosition = GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition );
3235
3236   mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
3237   mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
3238
3239   // Calculates and set the visibility if the scroll mode is enabled.
3240   bool isSelectionHandleOneVisible = true;
3241   bool isSelectionHandleTwoVisible = true;
3242   if( IsScrollEnabled() )
3243   {
3244     const Vector3& controlSize( GetControlSize() );
3245     isSelectionHandleOneVisible = IsPositionInsideBoundaries( mSelectionHandleOneActualPosition, Size::ZERO, controlSize );
3246     isSelectionHandleTwoVisible = IsPositionInsideBoundaries( mSelectionHandleTwoActualPosition, Size::ZERO, controlSize );
3247     mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
3248     mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
3249   }
3250
3251   CreateHighlight();  // function will only create highlight if not already created.
3252 }
3253
3254 Vector3 TextInput::MoveSelectionHandle( SelectionHandleId handleId, const Vector2& displacement )
3255 {
3256   Vector3 actualHandlePosition;
3257
3258   if ( mSelectionHandleOne && mSelectionHandleTwo )
3259   {
3260     const Vector3& controlSize = GetControlSize();
3261
3262     Size cursorSize( CURSOR_THICKNESS, 0.f );
3263
3264     // Get a reference of the wanted selection handle (handle one or two).
3265     Vector3& actualSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
3266
3267     // Get a reference for the current position of the handle and a copy of its pair
3268     std::size_t& currentSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3269     const std::size_t pairSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition;
3270
3271     // Get a handle of the selection handle actor
3272     ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3273
3274     // Selection handles should jump to the nearest character
3275     std::size_t newHandlePosition = 0;
3276     ReturnClosestIndex( actualSelectionHandlePosition.GetVectorXY(), newHandlePosition );
3277
3278     actualHandlePosition = GetActualPositionFromCharacterPosition( newHandlePosition );
3279
3280     bool handleVisible = true;
3281
3282     if( IsScrollEnabled() )
3283     {
3284       mCurrentSelectionId = handleId;
3285
3286       cursorSize.height = GetRowRectFromCharacterPosition( newHandlePosition ).height;
3287       // Restricts the movement of the grab handle inside the boundaries of the text-input.
3288       handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3289                                                   cursorSize,
3290                                                   controlSize );
3291
3292       if( handleVisible )
3293       {
3294         StopScrollTimer();
3295         mCurrentSelectionHandlePosition = actualHandlePosition;
3296         mScrollDisplacement = Vector2::ZERO;
3297       }
3298       else
3299       {
3300         if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3301         {
3302           mScrollDisplacement.x = -SCROLL_SPEED;
3303         }
3304         else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3305         {
3306           mScrollDisplacement.x = SCROLL_SPEED;
3307         }
3308         if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3309         {
3310           mScrollDisplacement.y = -SCROLL_SPEED;
3311         }
3312         else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3313         {
3314           mScrollDisplacement.y = SCROLL_SPEED;
3315         }
3316         StartScrollTimer();
3317       }
3318     }
3319
3320     if ( handleVisible &&                                          // Ensure the handle is visible.
3321          ( newHandlePosition != pairSelectionHandlePosition ) &&   // Ensure handle one is not the same position as handle two.
3322          ( newHandlePosition != currentSelectionHandlePosition ) ) // Ensure the handle has moved.
3323     {
3324       currentSelectionHandlePosition = newHandlePosition;
3325
3326       Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3327       selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3328
3329       UpdateHighlight();
3330
3331       if ( handleId == HandleOne )
3332       {
3333         const TextStyle oldInputStyle( mInputStyle );
3334
3335         // Set Active Style to that of first character in selection
3336         if( mSelectionHandleOnePosition < mStyledText.size() )
3337         {
3338           mInputStyle = ( mStyledText.at( mSelectionHandleOnePosition ) ).mStyle;
3339         }
3340
3341         if( oldInputStyle != mInputStyle )
3342         {
3343           // Updates the line height accordingly with the input style.
3344           UpdateLineHeight();
3345
3346           EmitStyleChangedSignal();
3347         }
3348       }
3349     }
3350   }
3351
3352   return actualHandlePosition; // Returns Handle position passed in if new value not assigned.
3353 }
3354
3355 void TextInput::SetSelectionHandlePosition(SelectionHandleId handleId)
3356 {
3357   const std::size_t selectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3358   ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3359
3360   if ( selectionHandleActor )
3361   {
3362     const Vector3 actualHandlePosition = GetActualPositionFromCharacterPosition( selectionHandlePosition );
3363     Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3364     selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3365
3366     if( IsScrollEnabled() )
3367     {
3368       const Size cursorSize( CURSOR_THICKNESS,
3369                              GetRowRectFromCharacterPosition( selectionHandlePosition ).height );
3370       selectionHandleActor.SetVisible( IsPositionInsideBoundaries( actualHandlePosition,
3371                                                                    cursorSize,
3372                                                                    GetControlSize() ) );
3373     }
3374   }
3375 }
3376
3377 void TextInput::GetVisualTextSelection(std::vector<bool>& selectedVisualText, std::size_t startSelection, std::size_t endSelection)
3378 {
3379   std::vector<int>::iterator it = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin();
3380   std::vector<int>::iterator startSelectionIt = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::min(startSelection, endSelection);
3381   std::vector<int>::iterator endSelectionIt = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::max(startSelection, endSelection);
3382   std::vector<int>::iterator end = mTextLayoutInfo.mCharacterLogicalToVisualMap.end();
3383
3384   selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size() );
3385
3386   // Deselect text prior to startSelectionIt
3387   for(;it!=startSelectionIt;++it)
3388   {
3389     selectedVisualText[*it] = false;
3390   }
3391
3392   // Select text from startSelectionIt -> endSelectionIt
3393   for(;it!=endSelectionIt;++it)
3394   {
3395     selectedVisualText[*it] = true;
3396   }
3397
3398   // Deselect text after endSelection
3399   for(;it!=end;++it)
3400   {
3401     selectedVisualText[*it] = false;
3402   }
3403
3404   selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size(), false );
3405 }
3406
3407 // Calculate the dimensions of the quads they will make the highlight mesh
3408 TextInput::HighlightInfo TextInput::CalculateHighlightInfo()
3409 {
3410   // At the moment there is no public API to modify the block alignment option.
3411   const bool blockAlignEnabled = true;
3412
3413   mNewHighlightInfo.mQuadList.clear(); // clear last quad information.
3414
3415   if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
3416   {
3417     Toolkit::TextView::CharacterLayoutInfoContainer::iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3418     Toolkit::TextView::CharacterLayoutInfoContainer::iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
3419
3420     // Get vector of flags representing characters that are selected (true) vs unselected (false).
3421     std::vector<bool> selectedVisualText;
3422     GetVisualTextSelection(selectedVisualText, mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
3423     std::vector<bool>::iterator selectedIt(selectedVisualText.begin());
3424     std::vector<bool>::iterator selectedEndIt(selectedVisualText.end());
3425
3426     SelectionState selectionState = SelectionNone;          ///< Current selection status of cursor over entire text.
3427     float rowLeft = 0.0f;
3428     float rowRight = 0.0f;
3429     // Keep track of the TextView's min/max extents. Should be able to query this from TextView.
3430     float maxRowLeft = std::numeric_limits<float>::max();
3431     float maxRowRight = 0.0f;
3432
3433     Toolkit::TextView::CharacterLayoutInfoContainer::iterator lastIt = it;
3434
3435     // Scan through entire text.
3436     while(it != end)
3437     {
3438       // selectionState: None when not in selection, Started when in selection, and Ended when reached end of selection.
3439
3440       Toolkit::TextView::CharacterLayoutInfo& charInfo(*it);
3441       bool charSelected( false );
3442       if( selectedIt != selectedEndIt )
3443       {
3444         charSelected = *selectedIt++;
3445       }
3446
3447       if(selectionState == SelectionNone)
3448       {
3449         if(charSelected)
3450         {
3451           selectionState = SelectionStarted;
3452           rowLeft = charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3453           rowRight = rowLeft + charInfo.mSize.width;
3454         }
3455       }
3456       else if(selectionState == SelectionStarted)
3457       {
3458         // break selection on:
3459         // 1. new line causing selection break. (\n or wordwrap)
3460<