TextInput Popup scrolling fixed after GUI changes. GUI settings changed.
[platform/core/uifw/dali-toolkit.git] / base / dali-toolkit / internal / controls / text-input / text-input-impl.cpp
1 /*
2  * Copyright (c) 2014 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 #include <dali/dali.h>
19
20 #include <dali-toolkit/internal/controls/text-input/text-input-impl.h>
21 #include <dali-toolkit/internal/controls/text-view/text-processor.h>
22 #include <dali-toolkit/public-api/controls/buttons/push-button.h>
23 #include <dali-toolkit/public-api/controls/alignment/alignment.h>
24
25 #include <dali/integration-api/debug.h>
26
27 #include <math.h>
28 #include <sstream>
29 #include <algorithm>
30
31 using namespace std;
32 using namespace Dali;
33
34 // Local Data
35 namespace
36 {
37
38 #if defined(DEBUG_ENABLED)
39 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_TEXT_INPUT");
40 #endif
41
42 const std::size_t DEFAULT_MAX_SIZE( std::numeric_limits<std::size_t>::max() ); // Max possible number
43 const std::size_t DEFAULT_NUMBER_OF_LINES_LIMIT( std::numeric_limits<std::size_t>::max() ); // Max possible number
44 const Vector3 DEFAULT_SELECTION_HANDLE_SIZE( 51.0f, 79.0f, 0.0f );  // Selection cursor image size
45 const Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.5f, 2.0f, 1.0f );
46 const Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.5f, 1.5f, 1.0f );
47 const Vector4 LIGHTBLUE( 0.07f, 0.41f, 0.59f, 1.0f );    // Used for Selection highlight
48
49 const char* DEFAULT_GRAB_HANDLE( DALI_IMAGE_DIR "insertpoint-icon.png" );
50 const char* DEFAULT_SELECTION_HANDLE_ONE( DALI_IMAGE_DIR "text-input-selection-handle-left.png" );
51 const char* DEFAULT_SELECTION_HANDLE_TWO( DALI_IMAGE_DIR "text-input-selection-handle-right.png" );
52 const char* DEFAULT_SELECTION_HANDLE_ONE_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-left-press.png" );
53 const char* DEFAULT_SELECTION_HANDLE_TWO_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-right-press.png" );
54 const char* DEFAULT_CURSOR( DALI_IMAGE_DIR "cursor.png" );
55
56 const Vector4 DEFAULT_CURSOR_IMAGE_9_BORDER( 2.0f, 2.0f, 2.0f, 2.0f );
57
58 const std::size_t CURSOR_BLINK_INTERVAL = 500;                              ///< Cursor blink interval
59 const float CHARACTER_THRESHOLD( 2.5f );                                    ///< the threshold of a line.
60 const float DISPLAYED_HIGHLIGHT_Z_OFFSET( 0.1f );                           ///< 1. Highlight rendered (z-offset).
61 const float DISPLAYED_TEXT_VIEW_Z_OFFSET( 0.2f );                           ///< 2. Text rendered (z-offset).
62 const float UI_Z_OFFSET( 0.2f );                                            ///< 3. Text Selection Handles/Cursor z-offset.
63
64 const Vector3 UI_OFFSET(0.0f, 0.0f, UI_Z_OFFSET);                           ///< Text Selection Handles/Cursor offset.
65 const Vector3 DEFAULT_HANDLE_ONE_OFFSET(0.0f, -5.0f, 0.0f);                 ///< Handle One's Offset
66 const Vector3 DEFAULT_HANDLE_TWO_OFFSET(0.0f, -5.0f, 0.0f);                 ///< Handle Two's Offset
67 const float TOP_HANDLE_TOP_OFFSET( 34.0f);                                   ///< Offset between top handle and cutCopyPaste pop-up
68 const float BOTTOM_HANDLE_BOTTOM_OFFSET(34.0f);                              ///< Offset between bottom handle and cutCopyPaste pop-up
69 const float CURSOR_THICKNESS(6.0f);
70 const Degree CURSOR_ANGLE_OFFSET(2.0f);                                     ///< Offset from the angle of italic angle.
71
72 const std::string NEWLINE( "\n" );
73
74 const TextStyle DEFAULT_TEXT_STYLE;
75
76 const unsigned int SCROLL_TICK_INTERVAL = 50u;
77 const float SCROLL_THRESHOLD = 10.f;
78 const float SCROLL_SPEED = 15.f;
79
80 /**
81  * Selection state enumeration (FSM)
82  */
83 enum SelectionState
84 {
85   SelectionNone,                            ///< Currently not encountered selected section.
86   SelectionStarted,                         ///< Encountered selected section
87   SelectionFinished                         ///< Finished selected section
88 };
89
90 /**
91  * Whether the given style is the default style or not.
92  * @param[in] style The given style.
93  * @return \e true if the given style is the default. Otherwise it returns \e false.
94  */
95 bool IsDefaultStyle( const TextStyle& style )
96 {
97   return DEFAULT_TEXT_STYLE == style;
98 }
99
100 /**
101  * Whether the given styled text is using the default style or not.
102  * @param[in] textArray The given text.
103  * @return \e true if the given styled text is using the default style. Otherwise it returns \e false.
104  */
105 bool IsTextDefaultStyle( const Toolkit::MarkupProcessor::StyledTextArray& textArray )
106 {
107   for( Toolkit::MarkupProcessor::StyledTextArray::const_iterator it = textArray.begin(), endIt = textArray.end(); it != endIt; ++it )
108   {
109     const TextStyle& style( (*it).mStyle );
110
111     if( !IsDefaultStyle( style ) )
112     {
113       return false;
114     }
115   }
116
117   return true;
118 }
119
120 std::size_t FindVisibleCharacterLeft( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable )
121 {
122   for( Toolkit::TextView::CharacterLayoutInfoContainer::const_reverse_iterator it = characterLayoutInfoTable.rbegin() + characterLayoutInfoTable.size() - cursorPosition, endIt = characterLayoutInfoTable.rend();
123        it != endIt;
124        ++it )
125   {
126     if( ( *it ).mIsVisible )
127     {
128       return --cursorPosition;
129     }
130
131     --cursorPosition;
132   }
133
134   return 0;
135 }
136
137 std::size_t FindVisibleCharacterRight( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable  )
138 {
139   for( Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = characterLayoutInfoTable.begin() + cursorPosition, endIt = characterLayoutInfoTable.end(); it < endIt; ++it )
140   {
141     if( ( *it ).mIsVisible )
142     {
143       return cursorPosition;
144     }
145
146     ++cursorPosition;
147   }
148
149   return cursorPosition;
150 }
151
152 /**
153  * Whether the given position plus the cursor size offset is inside the given boundary.
154  *
155  * @param[in] position The given position.
156  * @param[in] cursorSize The cursor size.
157  * @param[in] controlSize The given boundary.
158  *
159  * @return whether the given position is inside the given boundary.
160  */
161 bool IsPositionInsideBoundaries( const Vector3& position, const Size& cursorSize, const Vector3& controlSize )
162 {
163   return ( position.x >= -Math::MACHINE_EPSILON_1000 ) &&
164          ( position.x <= controlSize.width + Math::MACHINE_EPSILON_1000 ) &&
165          ( position.y - cursorSize.height >= -Math::MACHINE_EPSILON_1000 ) &&
166          ( position.y <= controlSize.height + Math::MACHINE_EPSILON_1000 );
167 }
168
169 /**
170  * Splits a text in two halves.
171  *
172  * If the text's number of characters is odd, firstHalf has one more character.
173  *
174  * @param[in] text The text to be split.
175  * @param[out] firstHalf The first half of the text.
176  * @param[out] secondHalf The second half of the text.
177  */
178 void SplitText( const Toolkit::MarkupProcessor::StyledTextArray& text,
179                       Toolkit::MarkupProcessor::StyledTextArray& firstHalf,
180                       Toolkit::MarkupProcessor::StyledTextArray& secondHalf )
181 {
182   firstHalf.clear();
183   secondHalf.clear();
184
185   const std::size_t textLength = text.size();
186   const std::size_t half = ( textLength / 2 ) + ( textLength % 2 );
187
188   firstHalf.insert( firstHalf.end(), text.begin(), text.begin() + half );
189   secondHalf.insert( secondHalf.end(), text.begin() + half, text.end() );
190 }
191
192 } // end of namespace
193
194 namespace Dali
195 {
196
197 namespace Toolkit
198 {
199 // Properties
200 const Property::Index TextInput::HIGHLIGHT_COLOR_PROPERTY                     = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX;
201 const Property::Index TextInput::CUT_AND_PASTE_COLOR_PROPERTY                 = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+1;
202 const Property::Index TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY         = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+2;
203 const Property::Index TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY          = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+3;
204 const Property::Index TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY            = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+4;
205 const Property::Index TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY    = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+5;
206 const Property::Index TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY            = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+6;
207 const Property::Index TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY    = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+7;
208 const Property::Index TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY        = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+8;
209 const Property::Index TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY       = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+9;
210 const Property::Index TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY      = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+10;
211 const Property::Index TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY     = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+11;
212 const Property::Index TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+12;
213 const Property::Index TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY  = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+13;
214
215 const Property::Index TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY             = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+14;
216
217 namespace Internal
218 {
219
220 namespace
221 {
222
223 BaseHandle Create()
224 {
225   return Toolkit::TextInput::New();
226 }
227
228 TypeRegistration typeRegistration( typeid(Toolkit::TextInput), typeid(Toolkit::Control), Create );
229
230 SignalConnectorType signalConnector1( typeRegistration, Toolkit::TextInput::SIGNAL_START_INPUT,                  &TextInput::DoConnectSignal );
231 SignalConnectorType signalConnector2( typeRegistration, Toolkit::TextInput::SIGNAL_END_INPUT,                    &TextInput::DoConnectSignal );
232 SignalConnectorType signalConnector3( typeRegistration, Toolkit::TextInput::SIGNAL_STYLE_CHANGED,                &TextInput::DoConnectSignal );
233 SignalConnectorType signalConnector4( typeRegistration, Toolkit::TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED, &TextInput::DoConnectSignal );
234 SignalConnectorType signalConnector5( typeRegistration, Toolkit::TextInput::SIGNAL_TOOLBAR_DISPLAYED,            &TextInput::DoConnectSignal );
235 SignalConnectorType signalConnector6( typeRegistration, Toolkit::TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES,       &TextInput::DoConnectSignal );
236
237 }
238
239 PropertyRegistration property1( typeRegistration, "highlight-color",  Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
240 PropertyRegistration property2( typeRegistration, "cut-and-paste-bg-color",  Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
241 PropertyRegistration property3( typeRegistration, "cut-and-paste-pressed-color",  Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
242 PropertyRegistration property4( typeRegistration, "cut-and-paste-icon-color",  Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
243 PropertyRegistration property5( typeRegistration, "cut-and-paste-icon-pressed-color",  Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
244 PropertyRegistration property6( typeRegistration, "cut-and-paste-text-color",  Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
245 PropertyRegistration property7( typeRegistration, "cut-and-paste-text-pressed-color",  Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
246 PropertyRegistration property8( typeRegistration, "cut-and-paste-border-color",  Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
247 PropertyRegistration property9( typeRegistration, "cut-button-position-priority",  Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
248 PropertyRegistration property10( typeRegistration, "copy-button-position-priority",  Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
249 PropertyRegistration property11( typeRegistration, "paste-button-position-priority",  Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
250 PropertyRegistration property12( typeRegistration, "select-button-position-priority",  Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
251 PropertyRegistration property13( typeRegistration, "select-all-button-position-priority",  Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
252 PropertyRegistration property14( typeRegistration, "clipboard-button-position-priority",  Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
253 PropertyRegistration property15( typeRegistration, "popup-offset-from-text", Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
254
255 // [TextInput::HighlightInfo] /////////////////////////////////////////////////
256
257 void TextInput::HighlightInfo::AddQuad( float x1, float y1, float x2, float y2 )
258 {
259   QuadCoordinates quad(x1, y1, x2, y2);
260   mQuadList.push_back( quad );
261 }
262
263 void TextInput::HighlightInfo::Clamp2D(const Vector2& min, const Vector2& max)
264 {
265   for(std::size_t i = 0;i < mQuadList.size(); i++)
266   {
267     QuadCoordinates& quad = mQuadList[i];
268
269     quad.min.Clamp(min, max);
270     quad.max.Clamp(min, max);
271   } // end for
272 }
273
274 // [TextInput] ////////////////////////////////////////////////////////////////
275
276 Dali::Toolkit::TextInput TextInput::New()
277 {
278   // Create the implementation
279   TextInputPtr textInput(new TextInput());
280   // Pass ownership to CustomActor via derived handle
281   Dali::Toolkit::TextInput handle(*textInput);
282
283   textInput->Initialize();
284
285   return handle;
286 }
287
288 TextInput::TextInput()
289 :Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS | REQUIRES_STYLE_CHANGE_SIGNALS ) ),
290  mState( StateEdit ),
291  mStyledText(),
292  mInputStyle(),
293  mLineHeight( 0.f ),
294  mDisplayedTextView(),
295  mStyledPlaceHolderText(),
296  mMaxStringLength( DEFAULT_MAX_SIZE ),
297  mNumberOflinesLimit( DEFAULT_NUMBER_OF_LINES_LIMIT ),
298  mCursorPosition( 0 ),
299  mActualGrabHandlePosition( 0.0f, 0.0f, 0.0f ),
300  mIsSelectionHandleOneFlipped( false ),
301  mIsSelectionHandleTwoFlipped( false ),
302  mSelectionHandleOneOffset( DEFAULT_HANDLE_ONE_OFFSET ),
303  mSelectionHandleTwoOffset( DEFAULT_HANDLE_TWO_OFFSET ),
304  mSelectionHandleOneActualPosition( 0.0f, 0.0f , 0.0f ),
305  mSelectionHandleTwoActualPosition( 0.0f, 0.0f , 0.0f ),
306  mSelectionHandleOnePosition( 0 ),
307  mSelectionHandleTwoPosition( 0 ),
308  mPreEditString(),
309  mPreEditStartPosition( 0 ),
310  mPreEditLength ( 0 ),
311  mNumberOfSurroundingCharactersDeleted( 0 ),
312  mTouchStartTime( 0 ),
313  mTextLayoutInfo(),
314  mCurrentCopySelecton(),
315  mPopupPanel(),
316  mScrollTimer(),
317  mScrollDisplacement(),
318  mCurrentHandlePosition(),
319  mCurrentSelectionId(),
320  mCurrentSelectionHandlePosition(),
321  mRequestedSelection( 0, 0 ),
322  mSelectionHandleFlipMargin( 0.0f, 0.0f, 0.0f, 0.0f ),
323  mBoundingRectangleWorldCoordinates( 0.0f, 0.0f, 0.0f, 0.0f ),
324  mClipboard(),
325  mMaterialColor( LIGHTBLUE ),
326  mPopupOffsetFromText ( Vector4( 0.0f, TOP_HANDLE_TOP_OFFSET, 0.0f, BOTTOM_HANDLE_BOTTOM_OFFSET ) ),
327  mOverrideAutomaticAlignment( false ),
328  mCursorRTLEnabled( false ),
329  mClosestCursorPositionEOL ( false ),
330  mCursorBlinkStatus( true ),
331  mCursorVisibility( false ),
332  mGrabHandleVisibility( false ),
333  mIsCursorInScrollArea( true ),
334  mIsGrabHandleInScrollArea( true ),
335  mEditModeActive( false ),
336  mEditOnTouch( true ),
337  mTextSelection( true ),
338  mExceedEnabled( true ),
339  mGrabHandleEnabled( true ),
340  mIsSelectionHandleFlipEnabled( true ),
341  mPreEditFlag( false ),
342  mIgnoreCommitFlag( false ),
343  mIgnoreFirstCommitFlag( false ),
344  mSelectingText( false ),
345  mPreserveCursorPosition( false ),
346  mSelectTextOnCommit( false ),
347  mUnderlinedPriorToPreEdit ( false ),
348  mCommitByKeyInput( false ),
349  mPlaceHolderSet( false ),
350  mMarkUpEnabled( false )
351 {
352   // Updates the line height accordingly with the input style.
353   UpdateLineHeight();
354 }
355
356 TextInput::~TextInput()
357 {
358   StopCursorBlinkTimer();
359 }
360
361 // Public
362
363 std::string TextInput::GetText() const
364 {
365   std::string text;
366
367   // Return text-view's text only if the text-input's text is not empty
368   // in order to not to return the placeholder text.
369   if( !mStyledText.empty() )
370   {
371     text = mDisplayedTextView.GetText();
372   }
373
374   return text;
375 }
376
377 std::string TextInput::GetMarkupText() const
378 {
379   std::string markupString;
380   MarkupProcessor::GetMarkupString( mStyledText, markupString );
381
382   return markupString;
383 }
384
385 void TextInput::SetPlaceholderText( const std::string& placeHolderText )
386 {
387   // Get the placeholder styled text array from the markup string.
388   MarkupProcessor::GetStyledTextArray( placeHolderText, mStyledPlaceHolderText, IsMarkupProcessingEnabled() );
389
390   if( mStyledText.empty() )
391   {
392     // Set the placeholder text only if the styled text is empty.
393     mDisplayedTextView.SetText( mStyledPlaceHolderText );
394     mPlaceHolderSet = true;
395   }
396 }
397
398 std::string TextInput::GetPlaceholderText()
399 {
400   // Traverses the styled placeholder array getting only the text.
401   //  Note that for some languages a 'character' could be represented by more than one 'char'
402
403   std::string placeholderText;
404   for( MarkupProcessor::StyledTextArray::const_iterator it = mStyledPlaceHolderText.begin(), endIt = mStyledPlaceHolderText.end(); it != endIt; ++it )
405   {
406     placeholderText.append( (*it).mText.GetText() );
407   }
408
409   return placeholderText ;
410 }
411
412 void TextInput::SetInitialText(const std::string& initialText)
413 {
414   DALI_LOG_INFO(gLogFilter, Debug::General, "SetInitialText string[%s]\n", initialText.c_str() );
415
416   if ( mPreEditFlag ) // If in the pre-edit state and text is being set then discard text being inserted.
417   {
418     mPreEditFlag = false;
419     mIgnoreCommitFlag = true;
420   }
421
422   SetText( initialText );
423   PreEditReset( false ); // Reset keyboard as text changed
424 }
425
426 void TextInput::SetText(const std::string& initialText)
427 {
428   DALI_LOG_INFO(gLogFilter, Debug::General, "SetText string[%s]\n", initialText.c_str() );
429
430   GetStyledTextArray( initialText, mStyledText, IsMarkupProcessingEnabled() );
431
432   if( mStyledText.empty() )
433   {
434     // If the initial text is empty, set the placeholder text.
435     mDisplayedTextView.SetText( mStyledPlaceHolderText );
436     mPlaceHolderSet = true;
437   }
438   else
439   {
440     mDisplayedTextView.SetText( mStyledText );
441     mPlaceHolderSet = false;
442   }
443
444   GetTextLayoutInfo();
445
446   mCursorPosition = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
447
448   ImfManager imfManager = ImfManager::Get();
449   if ( imfManager )
450   {
451     imfManager.SetCursorPosition( mCursorPosition );
452     imfManager.SetSurroundingText( initialText );
453     imfManager.NotifyCursorPosition();
454   }
455
456   if( IsScrollEnabled() )
457   {
458     ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
459   }
460
461   ShowGrabHandleAndSetVisibility( false );
462
463   RemoveHighlight();
464
465   DrawCursor();
466
467   EmitTextModified();
468 }
469
470 void TextInput::SetText( const MarkupProcessor::StyledTextArray& styleText )
471 {
472   DALI_LOG_INFO(gLogFilter, Debug::General, "SetText markup text\n" );
473
474   mDisplayedTextView.SetText( styleText );
475   mPlaceHolderSet = false;
476
477   // If text alignment hasn't been manually set by application developer, then we
478   // automatically determine the alignment based on the content of the text i.e. what
479   // language the text begins with.
480   // TODO: This should determine different alignments for each line (broken by '\n') of text.
481   if(!mOverrideAutomaticAlignment)
482   {
483     // Determine bidi direction of first character (skipping past whitespace, numbers, and symbols)
484     bool leftToRight(true);
485
486     if( !styleText.empty() )
487     {
488       bool breakOut(false);
489
490       for( MarkupProcessor::StyledTextArray::const_iterator textIter = styleText.begin(), textEndIter = styleText.end(); ( textIter != textEndIter ) && ( !breakOut ); ++textIter )
491       {
492         const Text& text = textIter->mText;
493
494         for( std::size_t i = 0; i < text.GetLength(); ++i )
495         {
496           Character character( text[i] );
497           if( character.GetCharacterDirection() != Character::Neutral )
498           {
499             leftToRight = ( character.GetCharacterDirection() == Character::LeftToRight );
500             breakOut = true;
501             break;
502           }
503         }
504       }
505     }
506
507     // Based on this direction, either left or right align text if not manually set by application developer.
508     mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(
509                                            ( leftToRight ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight) |
510                                              Toolkit::Alignment::VerticalTop ) );
511     mDisplayedTextView.SetLineJustification( leftToRight ? Toolkit::TextView::Left : Toolkit::TextView::Right);
512   }
513
514   EmitTextModified();
515 }
516
517 void TextInput::SetMaxCharacterLength(std::size_t maxChars)
518 {
519   mMaxStringLength = maxChars;
520 }
521
522 void TextInput::SetNumberOfLinesLimit(std::size_t maxLines)
523 {
524   DALI_ASSERT_DEBUG( maxLines > 0 )
525
526   if ( maxLines > 0)
527   {
528     mNumberOflinesLimit = maxLines;
529   }
530 }
531
532 std::size_t TextInput::GetNumberOfLinesLimit() const
533 {
534   return mNumberOflinesLimit;
535 }
536
537 std::size_t TextInput::GetNumberOfCharacters() const
538 {
539   return mStyledText.size();
540 }
541
542 // Styling
543 void TextInput::SetMaterialDiffuseColor( const Vector4& color )
544 {
545   mMaterialColor = color;
546   if ( mCustomMaterial )
547   {
548     mCustomMaterial.SetDiffuseColor( mMaterialColor );
549     mMeshData.SetMaterial( mCustomMaterial );
550   }
551 }
552
553 const Vector4& TextInput::GetMaterialDiffuseColor() const
554 {
555   return mMaterialColor;
556 }
557
558 // Signals
559
560 Toolkit::TextInput::InputSignalV2& TextInput::InputStartedSignal()
561 {
562   return mInputStartedSignalV2;
563 }
564
565 Toolkit::TextInput::InputSignalV2& TextInput::InputFinishedSignal()
566 {
567   return mInputFinishedSignalV2;
568 }
569
570 Toolkit::TextInput::InputSignalV2& TextInput::CutAndPasteToolBarDisplayedSignal()
571 {
572   return mCutAndPasteToolBarDisplayedV2;
573 }
574
575 Toolkit::TextInput::StyleChangedSignalV2& TextInput::StyleChangedSignal()
576 {
577   return mStyleChangedSignalV2;
578 }
579
580 Toolkit::TextInput::TextModifiedSignalType& TextInput::TextModifiedSignal()
581 {
582   return mTextModifiedSignal;
583 }
584
585 Toolkit::TextInput::MaxInputCharactersReachedSignalV2& TextInput::MaxInputCharactersReachedSignal()
586 {
587   return mMaxInputCharactersReachedSignalV2;
588 }
589
590 Toolkit::TextInput::InputTextExceedBoundariesSignalV2& TextInput::InputTextExceedBoundariesSignal()
591 {
592   return mInputTextExceedBoundariesSignalV2;
593 }
594
595 bool TextInput::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
596 {
597   Dali::BaseHandle handle( object );
598
599   bool connected( true );
600   Toolkit::TextInput textInput = Toolkit::TextInput::DownCast(handle);
601
602   if( Toolkit::TextInput::SIGNAL_START_INPUT == signalName )
603   {
604     textInput.InputStartedSignal().Connect( tracker, functor );
605   }
606   else if( Toolkit::TextInput::SIGNAL_END_INPUT == signalName )
607   {
608     textInput.InputFinishedSignal().Connect( tracker, functor );
609   }
610   else if( Toolkit::TextInput::SIGNAL_STYLE_CHANGED == signalName )
611   {
612     textInput.StyleChangedSignal().Connect( tracker, functor );
613   }
614   else if( Toolkit::TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED == signalName )
615   {
616     textInput.MaxInputCharactersReachedSignal().Connect( tracker, functor );
617   }
618   else if( Toolkit::TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES == signalName )
619   {
620     textInput.InputTextExceedBoundariesSignal().Connect( tracker, functor );
621   }
622   else
623   {
624     // signalName does not match any signal
625     connected = false;
626   }
627
628   return connected;
629 }
630
631 void TextInput::SetEditable(bool editMode, bool setCursorOnTouchPoint, const Vector2& touchPoint)
632 {
633   if(editMode)
634   {
635     // update line height before calculate the actual position.
636     UpdateLineHeight();
637
638     if(!mEditModeActive)
639     {
640       if( setCursorOnTouchPoint )
641       {
642         // Sets the cursor position for the given touch point.
643         ReturnClosestIndex( touchPoint, mCursorPosition );
644
645         // Creates the grab handle.
646         if( IsGrabHandleEnabled() )
647         {
648           const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
649
650           CreateGrabHandle();
651
652           mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
653           mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
654           mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
655           ShowGrabHandleAndSetVisibility( true );
656
657           // Scrolls the text-view if needed.
658           if( IsScrollEnabled() )
659           {
660             ScrollTextViewToMakeCursorVisible( cursorPosition );
661           }
662         }
663       }
664       else
665       {
666         mCursorPosition = mStyledText.size(); // Initially set cursor position to end of string.
667       }
668     }
669
670     StartEditMode();
671   }
672   else
673   {
674     EndEditMode();
675   }
676 }
677
678 bool TextInput::IsEditable() const
679 {
680   return mEditModeActive;
681 }
682
683 void TextInput::SetEditOnTouch( bool editOnTouch )
684 {
685   mEditOnTouch = editOnTouch;
686 }
687
688 bool TextInput::IsEditOnTouch() const
689 {
690   return mEditOnTouch;
691 }
692
693 void TextInput::SetTextSelectable( bool textSelectable )
694 {
695   mTextSelection = textSelectable;
696 }
697
698 bool TextInput::IsTextSelectable() const
699 {
700   return mTextSelection;
701 }
702
703 bool TextInput::IsTextSelected() const
704 {
705   return mHighlightMeshActor;
706 }
707
708 void TextInput::DeSelectText()
709 {
710   RemoveHighlight();
711   HidePopup();
712   CursorUpdate();
713 }
714
715 void TextInput::SetGrabHandleImage(Dali::Image image )
716 {
717   if (image)
718   {
719     CreateGrabHandle(image);
720   }
721 }
722
723 void TextInput::SetCursorImage(Dali::Image image, const Vector4& border )
724 {
725   DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
726
727   if ( image )
728   {
729     mCursor.SetImage( image );
730     mCursor.SetNinePatchBorder( border );
731   }
732 }
733
734 Vector3 TextInput::GetSelectionHandleSize()
735 {
736   return DEFAULT_SELECTION_HANDLE_SIZE;
737 }
738
739 void TextInput::SetRTLCursorImage(Dali::Image image, const Vector4& border )
740 {
741   DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
742
743   if ( image )
744   {
745     mCursorRTL.SetImage( image);
746     mCursorRTL.SetNinePatchBorder(  border );
747   }
748 }
749
750 void TextInput::EnableGrabHandle(bool toggle)
751 {
752   // enables grab handle with will in turn de-activate magnifier
753   mGrabHandleEnabled = toggle;
754 }
755
756 bool TextInput::IsGrabHandleEnabled()
757 {
758   // if false then magnifier will be shown instead.
759   return mGrabHandleEnabled;
760 }
761
762 void TextInput::EnableSelectionHandleFlip( bool toggle )
763 {
764   // Deprecated function.  To be removed.
765   mIsSelectionHandleFlipEnabled = toggle;
766 }
767
768 bool TextInput::IsSelectionHandleFlipEnabled()
769 {
770   // Deprecated function, To be removed. Returns true as handle flipping always enabled by default so handles do not exceed screen.
771   return true;
772 }
773
774 void TextInput::SetSelectionHandleFlipMargin( const Vector4& margin )
775 {
776   // Deprecated function, now just stores margin for retreival, remove completely once depricated Public API removed.
777   Vector3 textInputSize = mDisplayedTextView.GetCurrentSize();
778   const Vector4 flipBoundary( -margin.x, -margin.y, textInputSize.width + margin.z, textInputSize.height + margin.w );
779
780   mSelectionHandleFlipMargin = margin;
781 }
782
783 void TextInput::SetBoundingRectangle( const Rect<float>& boundingRectangle )
784 {
785   // Convert to world coordinates and store as a Vector4 to be compatiable with Property Notifications.
786   Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
787
788   const float originX = boundingRectangle.x - 0.5f * stageSize.width;
789   const float originY = boundingRectangle.y - 0.5f * stageSize.height;
790
791   const Vector4 boundary( originX,
792                           originY,
793                           originX + boundingRectangle.width,
794                           originY + boundingRectangle.height );
795
796   mBoundingRectangleWorldCoordinates = boundary;
797 }
798
799 const Rect<float> TextInput::GetBoundingRectangle() const
800 {
801   Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
802
803   const float originX = mBoundingRectangleWorldCoordinates.x + 0.5f * stageSize.width;
804   const float originY = mBoundingRectangleWorldCoordinates.y + 0.5f * stageSize.height;
805
806   Rect<float>boundingRect( originX, originY, mBoundingRectangleWorldCoordinates.z - mBoundingRectangleWorldCoordinates.x, mBoundingRectangleWorldCoordinates.w - mBoundingRectangleWorldCoordinates.y);
807
808   return boundingRect;
809 }
810
811 const Vector4& TextInput::GetSelectionHandleFlipMargin()
812 {
813   return mSelectionHandleFlipMargin;
814 }
815
816 void TextInput::SetTextColor( const Vector4& color )
817 {
818   mDisplayedTextView.SetColor( color );
819 }
820
821 void TextInput::SetActiveStyle( const TextStyle& style, const TextStyle::Mask mask )
822 {
823   if( style != mInputStyle )
824   {
825     // different style.
826     bool emitSignal = false;
827
828     // mask: modify style according to mask, if different emit signal.
829     const TextStyle oldInputStyle( mInputStyle );
830
831     // Copy the new style.
832     mInputStyle.Copy( style, mask );
833
834     // if style has changed, emit signal.
835     if( oldInputStyle != mInputStyle )
836     {
837       emitSignal = true;
838     }
839
840     // Updates the line height accordingly with the input style.
841     UpdateLineHeight();
842
843     // Changing font point size will require the cursor to be re-sized
844     DrawCursor();
845
846     if( emitSignal )
847     {
848       EmitStyleChangedSignal();
849     }
850   }
851 }
852
853 void TextInput::ApplyStyle( const TextStyle& style, const TextStyle::Mask mask )
854 {
855   if ( IsTextSelected() )
856   {
857     const std::size_t begin = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
858     const std::size_t end = std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition) - 1;
859
860     if( !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
861     {
862       ApplyStyleToRange(style, mask, mTextLayoutInfo.mCharacterLogicalToVisualMap[begin], mTextLayoutInfo.mCharacterLogicalToVisualMap[end]);
863     }
864
865     // Keeps the old style to be compared with the new one.
866     const TextStyle oldInputStyle( mInputStyle );
867
868     // Copy only those parameters from the style which are set in the mask.
869     mInputStyle.Copy( style, mask );
870
871     if( mInputStyle != oldInputStyle )
872     {
873       // Updates the line height accordingly with the input style.
874       UpdateLineHeight();
875
876       EmitStyleChangedSignal();
877     }
878   }
879 }
880
881 void TextInput::ApplyStyleToAll( const TextStyle& style, const TextStyle::Mask mask )
882 {
883   if( !mStyledText.empty() )
884   {
885     ApplyStyleToRange( style, mask, 0, mStyledText.size() - 1 );
886   }
887 }
888
889 TextStyle TextInput::GetStyleAtCursor() const
890 {
891   TextStyle style;
892
893   if ( !mStyledText.empty() && ( mCursorPosition > 0 ) )
894   {
895     DALI_ASSERT_DEBUG( ( 0 <= mCursorPosition-1 ) && ( mCursorPosition-1 < mStyledText.size() ) );
896     style = mStyledText.at( mCursorPosition-1 ).mStyle;
897   }
898   else // No text.
899   {
900     style = mInputStyle;
901
902     if ( mInputStyle.GetFontPointSize() <  Math::MACHINE_EPSILON_1000 )
903     {
904       Dali::Font defaultFont = Dali::Font::New();
905       style.SetFontPointSize( PointSize( defaultFont.GetPointSize()) );
906     }
907   }
908
909   return style;
910 }
911
912 TextStyle TextInput::GetStyleAt( std::size_t position ) const
913 {
914   DALI_ASSERT_DEBUG( ( 0 <= position ) && ( position <= mStyledText.size() ) );
915
916   if( position >= mStyledText.size() )
917   {
918     position = mStyledText.size() - 1;
919   }
920
921   return mStyledText.at( position ).mStyle;
922 }
923
924 void TextInput::SetTextAlignment( Toolkit::Alignment::Type align )
925 {
926   mDisplayedTextView.SetTextAlignment( align );
927   mOverrideAutomaticAlignment = true;
928 }
929
930 void TextInput::SetTextLineJustification( Toolkit::TextView::LineJustification justification )
931 {
932   mDisplayedTextView.SetLineJustification( justification );
933   mOverrideAutomaticAlignment = true;
934 }
935
936 void TextInput::SetFadeBoundary( const Toolkit::TextView::FadeBoundary& fadeBoundary )
937 {
938   mDisplayedTextView.SetFadeBoundary( fadeBoundary );
939 }
940
941 const Toolkit::TextView::FadeBoundary& TextInput::GetFadeBoundary() const
942 {
943   return mDisplayedTextView.GetFadeBoundary();
944 }
945
946 Toolkit::Alignment::Type TextInput::GetTextAlignment() const
947 {
948   return mDisplayedTextView.GetTextAlignment();
949 }
950
951 void TextInput::SetMultilinePolicy( Toolkit::TextView::MultilinePolicy policy )
952 {
953   mDisplayedTextView.SetMultilinePolicy( policy );
954 }
955
956 Toolkit::TextView::MultilinePolicy TextInput::GetMultilinePolicy() const
957 {
958   return mDisplayedTextView.GetMultilinePolicy();
959 }
960
961 void TextInput::SetWidthExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
962 {
963   mDisplayedTextView.SetWidthExceedPolicy( policy );
964 }
965
966 Toolkit::TextView::ExceedPolicy TextInput::GetWidthExceedPolicy() const
967 {
968   return mDisplayedTextView.GetWidthExceedPolicy();
969 }
970
971 void TextInput::SetHeightExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
972 {
973   mDisplayedTextView.SetHeightExceedPolicy( policy );
974 }
975
976 Toolkit::TextView::ExceedPolicy TextInput::GetHeightExceedPolicy() const
977 {
978   return mDisplayedTextView.GetHeightExceedPolicy();
979 }
980
981 void TextInput::SetExceedEnabled( bool enable )
982 {
983   mExceedEnabled = enable;
984 }
985
986 bool TextInput::GetExceedEnabled() const
987 {
988   return mExceedEnabled;
989 }
990
991 void TextInput::SetBackground(Dali::Image image )
992 {
993   // TODO Should add this function and add public api to match.
994 }
995
996 bool TextInput::OnTouchEvent(const TouchEvent& event)
997 {
998   return false;
999 }
1000
1001 bool TextInput::OnKeyEvent(const KeyEvent& event)
1002 {
1003   switch( event.state )
1004   {
1005     case KeyEvent::Down:
1006     {
1007       return OnKeyDownEvent(event);
1008     }
1009     break;
1010
1011     case KeyEvent::Up:
1012     {
1013       return OnKeyUpEvent(event);
1014     }
1015     break;
1016
1017     default:
1018     {
1019       return false;
1020     }
1021     break;
1022   }
1023 }
1024
1025 void TextInput::OnKeyInputFocusGained()
1026 {
1027   DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusGained\n" );
1028
1029   mEditModeActive = true;
1030
1031   mActiveLayer.RaiseToTop(); // Ensure layer holding handles is on top
1032
1033   mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1034
1035   // Updates the line height accordingly with the input style.
1036   UpdateLineHeight();
1037
1038   // Connect the signals to use in text input.
1039   VirtualKeyboard::StatusChangedSignal().Connect( this, &TextInput::KeyboardStatusChanged );
1040   VirtualKeyboard::LanguageChangedSignal().Connect( this, &TextInput::SetTextDirection );
1041
1042   // Set the text direction if empty and connect to the signal to ensure we change direction when the language changes.
1043   SetTextDirection();
1044
1045   GetTextLayoutInfo();
1046
1047   DrawCursor();
1048   SetCursorVisibility( true );
1049   StartCursorBlinkTimer();
1050
1051   Toolkit::TextInput handle( GetOwner() );
1052   mInputStartedSignalV2.Emit( handle );
1053
1054   ImfManager imfManager = ImfManager::Get();
1055
1056   if ( imfManager )
1057   {
1058     imfManager.EventReceivedSignal().Connect(this, &TextInput::ImfEventReceived);
1059
1060     // Notify that the text editing start.
1061     imfManager.Activate();
1062
1063     // When window gain lost focus, the imf manager is deactivated. Thus when window gain focus again, the imf manager must be activated.
1064     imfManager.SetRestoreAferFocusLost( true );
1065
1066     imfManager.SetCursorPosition( mCursorPosition );
1067     imfManager.NotifyCursorPosition();
1068   }
1069
1070   mClipboard = Clipboard::Get(); // Store handle to clipboard
1071
1072   // Now in edit mode we can accept string to paste from clipboard
1073   if( Adaptor::IsAvailable() )
1074   {
1075     ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1076     if ( notifier )
1077     {
1078       notifier.ContentSelectedSignal().Connect( this, &TextInput::OnClipboardTextSelected );
1079     }
1080   }
1081 }
1082
1083 void TextInput::OnKeyInputFocusLost()
1084 {
1085   DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusLost\n" );
1086
1087   if( mPreEditFlag )
1088   {
1089     // If key input focus is lost, it removes the
1090     // underline from the last pre-edit text.
1091     RemovePreEditStyle();
1092     const std::size_t numberOfCharactersDeleted = DeletePreEdit();
1093     InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersDeleted );
1094     EmitTextModified();
1095   }
1096
1097   ImfManager imfManager = ImfManager::Get();
1098   if ( imfManager )
1099   {
1100     // The text editing is finished. Therefore the imf manager don't have restore activation.
1101     imfManager.SetRestoreAferFocusLost( false );
1102
1103     // Notify that the text editing finish.
1104     imfManager.Deactivate();
1105
1106     imfManager.EventReceivedSignal().Disconnect(this, &TextInput::ImfEventReceived);
1107   }
1108   // Disconnect signal used the text input.
1109   VirtualKeyboard::LanguageChangedSignal().Disconnect( this, &TextInput::SetTextDirection );
1110
1111   Toolkit::TextInput handle( GetOwner() );
1112   mInputFinishedSignalV2.Emit( handle );
1113   mEditModeActive = false;
1114   mPreEditFlag = false;
1115   RemoveHighlight();
1116   SetCursorVisibility( false );
1117   StopCursorBlinkTimer();
1118
1119   ShowGrabHandleAndSetVisibility( false );
1120
1121   mClipboard.Reset();
1122   // No longer in edit mode so do not want to receive string from clipboard
1123   if( Adaptor::IsAvailable() )
1124   {
1125     ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1126     if ( notifier )
1127     {
1128       notifier.ContentSelectedSignal().Disconnect( this, &TextInput::OnClipboardTextSelected );
1129     }
1130     Clipboard clipboard = Clipboard::Get();
1131
1132     if ( clipboard )
1133     {
1134       clipboard.HideClipboard();
1135     }
1136   }
1137 }
1138
1139 void TextInput::OnControlStageConnection()
1140 {
1141   Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
1142
1143   if ( mBoundingRectangleWorldCoordinates == Vector4::ZERO )
1144   {
1145     SetBoundingRectangle( Rect<float>( 0.0f, 0.0f, stageSize.width, stageSize.height ));
1146   }
1147 }
1148
1149 void TextInput::CreateActiveLayer()
1150 {
1151   Actor self = Self();
1152   mActiveLayer = Layer::New();
1153
1154   mActiveLayer.SetAnchorPoint( AnchorPoint::CENTER);
1155   mActiveLayer.SetParentOrigin( ParentOrigin::CENTER);
1156   mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
1157
1158   self.Add( mActiveLayer );
1159   mActiveLayer.RaiseToTop();
1160 }
1161
1162 void TextInput::OnInitialize()
1163 {
1164   CreateTextViewActor();
1165
1166   SetUpTouchEvents();
1167
1168   // Create 2 cursors (standard LTR and RTL cursor for when text can be added at
1169   // different positions depending on language)
1170   Image mCursorImage = Image::New( DEFAULT_CURSOR );
1171   mCursor = CreateCursor( mCursorImage, DEFAULT_CURSOR_IMAGE_9_BORDER );
1172   mCursorRTL = CreateCursor( mCursorImage, DEFAULT_CURSOR_IMAGE_9_BORDER );
1173
1174   Actor self = Self();
1175   self.Add( mCursor );
1176   self.Add( mCursorRTL );
1177
1178   mCursorVisibility = false;
1179
1180   CreateActiveLayer(); // todo move this so layer only created when needed.
1181
1182   // Assign names to image actors
1183   mCursor.SetName("mainCursor");
1184   mCursorRTL.SetName("rtlCursor");
1185 }
1186
1187 void TextInput::OnControlSizeSet(const Vector3& targetSize)
1188 {
1189   mDisplayedTextView.SetSize( targetSize );
1190   GetTextLayoutInfo();
1191   mActiveLayer.SetSize(targetSize);
1192 }
1193
1194 void TextInput::OnRelaidOut( Vector2 size, ActorSizeContainer& container )
1195 {
1196   Relayout( mDisplayedTextView, size, container );
1197   Relayout( mPopupPanel.GetRootActor(), size, container );
1198
1199   GetTextLayoutInfo();
1200
1201   DrawCursor();
1202 }
1203
1204 Vector3 TextInput::GetNaturalSize()
1205 {
1206   Vector3 naturalSize = mDisplayedTextView.GetNaturalSize();
1207
1208   if( mEditModeActive && ( Vector3::ZERO == naturalSize ) )
1209   {
1210     // If the natural is zero, it means there is no text. Let's return the cursor height as the natural height.
1211     naturalSize.height = mLineHeight;
1212   }
1213
1214   return naturalSize;
1215 }
1216
1217 float TextInput::GetHeightForWidth( float width )
1218 {
1219   float height = mDisplayedTextView.GetHeightForWidth( width );
1220
1221   if( mEditModeActive && ( fabsf( height ) < Math::MACHINE_EPSILON_1000 ) )
1222   {
1223     // If the height is zero, it means there is no text. Let's return the cursor height.
1224     height = mLineHeight;
1225   }
1226
1227   return height;
1228 }
1229
1230 /*end of Virtual methods from parent*/
1231
1232 // Private Internal methods
1233
1234 void TextInput::OnHandlePan(Actor actor, PanGesture gesture)
1235 {
1236   switch (gesture.state)
1237   {
1238     case Gesture::Started:
1239     // fall through so code not duplicated
1240     case Gesture::Continuing:
1241     {
1242       if (actor == mGrabArea)
1243       {
1244         SetCursorVisibility( true );
1245         ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1246         MoveGrabHandle( gesture.displacement );
1247         HidePopup(); // Do not show popup whilst handle is moving
1248       }
1249       else if (actor == mHandleOneGrabArea)
1250       {
1251         // the displacement in PanGesture is affected by the actor's rotation.
1252         mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1253         mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1254
1255         MoveSelectionHandle( HandleOne, gesture.displacement );
1256
1257         mState = StateDraggingHandle;
1258         HidePopup();
1259       }
1260       else if (actor == mHandleTwoGrabArea)
1261       {
1262         // the displacement in PanGesture is affected by the actor's rotation.
1263         mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1264         mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1265
1266         MoveSelectionHandle( HandleTwo, gesture.displacement );
1267
1268         mState = StateDraggingHandle;
1269         HidePopup();
1270       }
1271     }
1272     break;
1273
1274     case Gesture::Finished:
1275     {
1276       // Revert back to non-pressed selection handle images
1277       if (actor == mGrabArea)
1278       {
1279         mActualGrabHandlePosition = MoveGrabHandle( gesture.displacement );
1280         SetCursorVisibility( true );
1281         SetUpPopupSelection();
1282         ShowPopup();
1283       }
1284       if (actor == mHandleOneGrabArea)
1285       {
1286         // the displacement in PanGesture is affected by the actor's rotation.
1287         mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1288         mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1289
1290         mSelectionHandleOneActualPosition = MoveSelectionHandle( HandleOne, gesture.displacement );
1291
1292         mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1293         mState = StateEdit;
1294         ShowPopupCutCopyPaste();
1295       }
1296       if (actor == mHandleTwoGrabArea)
1297       {
1298         // the displacement in PanGesture is affected by the actor's rotation.
1299         mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1300         mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1301
1302         mSelectionHandleTwoActualPosition = MoveSelectionHandle( HandleTwo, gesture.displacement );
1303
1304         mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1305         mState = StateEdit;
1306         ShowPopupCutCopyPaste();
1307       }
1308     }
1309     break;
1310     default:
1311       break;
1312   }
1313 }
1314
1315 // Stop the flashing animation so easy to see when moved.
1316 bool TextInput::OnPressDown(Dali::Actor actor, const TouchEvent& touch)
1317 {
1318   if (touch.GetPoint(0).state == TouchPoint::Down)
1319   {
1320     SetCursorVisibility( true );
1321     StopCursorBlinkTimer();
1322   }
1323   else if (touch.GetPoint(0).state == TouchPoint::Up)
1324   {
1325     SetCursorVisibility( true );
1326     StartCursorBlinkTimer();
1327   }
1328   return false;
1329 }
1330
1331 // selection handle one
1332 bool TextInput::OnHandleOneTouched(Dali::Actor actor, const TouchEvent& touch)
1333 {
1334   if (touch.GetPoint(0).state == TouchPoint::Down)
1335   {
1336     mSelectionHandleOne.SetImage( mSelectionHandleOneImagePressed );
1337   }
1338   else if (touch.GetPoint(0).state == TouchPoint::Up)
1339   {
1340     mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1341   }
1342   return false;
1343 }
1344
1345 // selection handle two
1346 bool TextInput::OnHandleTwoTouched(Dali::Actor actor, const TouchEvent& touch)
1347 {
1348   if (touch.GetPoint(0).state == TouchPoint::Down)
1349   {
1350     mSelectionHandleTwo.SetImage( mSelectionHandleTwoImagePressed );
1351   }
1352   else if (touch.GetPoint(0).state == TouchPoint::Up)
1353   {
1354     mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1355   }
1356   return false;
1357 }
1358
1359 void TextInput::OnDoubleTap(Dali::Actor actor, Dali::TapGesture tap)
1360 {
1361    // If text exists then select nearest word.
1362    if ( !mStyledText.empty())
1363    {
1364      HidePopup();
1365
1366      ShowGrabHandleAndSetVisibility( false );
1367
1368
1369      if ( mPreEditFlag )
1370      {
1371        // PreEdit will be committed here without needing a commit from IMF.  Remove pre-edit underline and reset flags which
1372        // converts the pre-edit word being displayed to a committed word.
1373        if ( !mUnderlinedPriorToPreEdit )
1374        {
1375          TextStyle style;
1376          style.SetUnderline( false );
1377          ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1378        }
1379        mPreEditFlag = false;
1380        mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1381        // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1382        PreEditReset( false );
1383      }
1384      mCursorPosition = 0;
1385
1386      mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1387      ReturnClosestIndex( tap.localPoint, mCursorPosition );
1388
1389      ImfManager imfManager = ImfManager::Get();
1390      if ( imfManager )
1391      {
1392        imfManager.SetCursorPosition ( mCursorPosition );
1393        imfManager.NotifyCursorPosition();
1394      }
1395
1396      std::size_t start = 0;
1397      std::size_t end = 0;
1398      Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1399
1400      SelectText( start, end );
1401    }
1402    // if no text but clipboard has content then show paste option
1403    if ( ( mClipboard && mClipboard.NumberOfItems() ) || !mStyledText.empty() )
1404    {
1405      ShowPopupCutCopyPaste();
1406    }
1407
1408    // If no text and clipboard empty then do nothing
1409 }
1410
1411 // TODO: Change the function name to be more general.
1412 void TextInput::OnTextTap(Dali::Actor actor, Dali::TapGesture tap)
1413 {
1414   DALI_LOG_INFO( gLogFilter, Debug::General, "OnTap mPreEditFlag[%s] mEditOnTouch[%s] mEditModeActive[%s] ", (mPreEditFlag)?"true":"false"
1415                                                                                                            , (mEditOnTouch)?"true":"false"
1416                                                                                                            , (mEditModeActive)?"true":"false");
1417
1418   if( mHandleOneGrabArea == actor || mHandleTwoGrabArea == actor )
1419   {
1420     return;
1421   }
1422
1423   if( mGrabArea == actor )
1424   {
1425     if( mPopupPanel.GetState() == TextInputPopup::StateHidden || mPopupPanel.GetState() == TextInputPopup::StateHiding )
1426     {
1427       SetUpPopupSelection();
1428       ShowPopup();
1429     }
1430
1431     return;
1432   }
1433
1434   HidePopup();
1435   RemoveHighlight();
1436
1437   mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1438
1439   // Initially don't create the grab handle.
1440   bool createGrabHandle = false;
1441
1442   if ( !mEditModeActive )
1443   {
1444     // update line height before calculate the actual position.
1445     UpdateLineHeight();
1446
1447     // Only start edit mode if TextInput configured to edit on touch
1448     if ( mEditOnTouch )
1449     {
1450       // Set the initial cursor position in the tap point.
1451       ReturnClosestIndex(tap.localPoint, mCursorPosition );
1452
1453       // Create the grab handle.
1454       // TODO Make this a re-usable function.
1455       if ( IsGrabHandleEnabled() )
1456       {
1457         const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1458
1459         CreateGrabHandle();
1460
1461         mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1462         mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1463         mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1464         ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1465
1466       }
1467
1468       // Edit mode started after grab handle created to ensure the signal InputStarted is sent last.
1469       // This is used to ensure if selecting text hides the grab handle then this code is run after grab handle is created,
1470       // otherwise the Grab handle will be shown when selecting.
1471
1472       StartEditMode();
1473     }
1474   }
1475   else
1476   {
1477     // Show the keyboard if it was hidden.
1478     if (!VirtualKeyboard::IsVisible())
1479     {
1480       VirtualKeyboard::Show();
1481     }
1482
1483     // Reset keyboard as tap event has occurred.
1484     // Set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1485     PreEditReset( true );
1486
1487     GetTextLayoutInfo();
1488
1489     if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() ) // If string empty we do not need a grab handle.
1490     {
1491       // 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.
1492
1493       ReturnClosestIndex(tap.localPoint, mCursorPosition );
1494
1495       DALI_LOG_INFO( gLogFilter, Debug::General, "mCursorPosition[%u]", mCursorPosition );
1496
1497       // Notify keyboard so it can 're-capture' word for predictive text.
1498       // As we have done a reset, is this required, expect IMF keyboard to request this information.
1499       ImfManager imfManager = ImfManager::Get();
1500       if ( imfManager )
1501       {
1502         imfManager.SetCursorPosition ( mCursorPosition );
1503         imfManager.NotifyCursorPosition();
1504       }
1505       const TextStyle oldInputStyle( mInputStyle );
1506
1507       mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1508
1509       DrawCursor();
1510
1511       // Create the grab handle.
1512       // Grab handle is created later.
1513       createGrabHandle = true;
1514
1515       if( oldInputStyle != mInputStyle )
1516       {
1517         // Updates the line height accordingly with the input style.
1518         UpdateLineHeight();
1519
1520         EmitStyleChangedSignal();
1521       }
1522     }
1523   }
1524
1525   if ( createGrabHandle && IsGrabHandleEnabled() )
1526   {
1527     const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1528
1529     CreateGrabHandle();
1530
1531     mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1532     mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1533     mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1534     ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1535
1536   }
1537 }
1538
1539 void TextInput::OnLongPress(Dali::Actor actor, Dali::LongPressGesture longPress)
1540 {
1541   DALI_LOG_INFO( gLogFilter, Debug::General, "OnLongPress\n" );
1542
1543   if(longPress.state == Dali::Gesture::Started)
1544   {
1545     // Start edit mode on long press
1546     if ( !mEditModeActive )
1547     {
1548       StartEditMode();
1549     }
1550
1551     // If text exists then select nearest word.
1552     if ( !mStyledText.empty())
1553     {
1554       HidePopup();
1555
1556       ShowGrabHandleAndSetVisibility( false );
1557
1558
1559       if ( mPreEditFlag )
1560       {
1561         // PreEdit will be committed here without needing a commit from IMF.  Remove pre-edit underline and reset flags which
1562         // converts the pre-edit word being displayed to a committed word.
1563         if ( !mUnderlinedPriorToPreEdit )
1564         {
1565           TextStyle style;
1566           style.SetUnderline( false );
1567           ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1568         }
1569         mPreEditFlag = false;
1570         mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1571         // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1572         PreEditReset( false );
1573       }
1574       mCursorPosition = 0;
1575
1576       mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1577       ReturnClosestIndex( longPress.localPoint, mCursorPosition );
1578
1579       ImfManager imfManager = ImfManager::Get();
1580       if ( imfManager )
1581       {
1582         imfManager.SetCursorPosition ( mCursorPosition );
1583         imfManager.NotifyCursorPosition();
1584       }
1585       std::size_t start = 0;
1586       std::size_t end = 0;
1587       Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1588
1589       SelectText( start, end );
1590     }
1591
1592     // if no text but clipboard has content then show paste option, if no text and clipboard empty then do nothing
1593     if ( ( mClipboard && mClipboard.NumberOfItems() ) || !mStyledText.empty() )
1594     {
1595       ShowPopupCutCopyPaste();
1596     }
1597   }
1598 }
1599
1600 void TextInput::OnClipboardTextSelected( ClipboardEventNotifier& notifier )
1601 {
1602   const Text clipboardText( notifier.GetContent() );
1603   PasteText( clipboardText );
1604
1605   SetCursorVisibility( true );
1606   StartCursorBlinkTimer();
1607
1608   ShowGrabHandleAndSetVisibility( false );
1609
1610
1611   HidePopup();
1612 }
1613
1614 bool TextInput::OnPopupButtonPressed( Toolkit::Button button )
1615 {
1616   mPopupPanel.PressedSignal().Disconnect( this, &TextInput::OnPopupButtonPressed );
1617
1618   const std::string& name = button.GetName();
1619
1620   if(name == TextInputPopup::OPTION_SELECT_WORD)
1621   {
1622     std::size_t start = 0;
1623     std::size_t end = 0;
1624     Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1625
1626     SelectText( start, end );
1627   }
1628   else if(name == TextInputPopup::OPTION_SELECT_ALL)
1629   {
1630     SetCursorVisibility(false);
1631     StopCursorBlinkTimer();
1632
1633     std::size_t end = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
1634     std::size_t start = 0;
1635
1636     SelectText( start, end );
1637   }
1638   else if(name == TextInputPopup::OPTION_CUT)
1639   {
1640     bool ret = CopySelectedTextToClipboard();
1641
1642     if ( ret )
1643     {
1644       DeleteHighlightedText( true );
1645       CursorUpdate();
1646     }
1647
1648     SetCursorVisibility( true );
1649     StartCursorBlinkTimer();
1650
1651     HidePopup();
1652   }
1653   else if(name == TextInputPopup::OPTION_COPY)
1654   {
1655     CopySelectedTextToClipboard();
1656
1657     RemoveHighlight();
1658
1659     SetCursorVisibility( true );
1660     StartCursorBlinkTimer();
1661
1662     HidePopup();
1663   }
1664   else if(name == TextInputPopup::OPTION_PASTE)
1665   {
1666     const Text retrievedString( mClipboard.GetItem( 0 ) );  // currently can only get first item in clip board, index 0;
1667
1668     PasteText(retrievedString);
1669
1670     SetCursorVisibility( true );
1671     StartCursorBlinkTimer();
1672
1673     ShowGrabHandleAndSetVisibility( false );
1674
1675     HidePopup();
1676   }
1677   else if(name == TextInputPopup::OPTION_CLIPBOARD)
1678   {
1679     // In the case of clipboard being shown we do not want to show updated pop-up after hide animation completes
1680     // Hence pass the false parameter for signalFinished.
1681     HidePopup( true, false );
1682     mClipboard.ShowClipboard();
1683   }
1684
1685   return false;
1686 }
1687
1688 bool TextInput::OnCursorBlinkTimerTick()
1689 {
1690   // Cursor blinking
1691   mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1692   if ( mCursorRTLEnabled )
1693   {
1694     mCursorRTL.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1695   }
1696   mCursorBlinkStatus = !mCursorBlinkStatus;
1697
1698   return true;
1699 }
1700
1701 void TextInput::OnPopupHideFinished(TextInputPopup& popup)
1702 {
1703   popup.HideFinishedSignal().Disconnect( this, &TextInput::OnPopupHideFinished );
1704
1705   // Change Popup menu to Cut/Copy/Paste if text has been selected.
1706   if(mHighlightMeshActor && mState == StateEdit)
1707   {
1708     ShowPopupCutCopyPaste();
1709   }
1710 }
1711
1712 //FIXME this routine needs to be re-written as it contains too many branches.
1713 bool TextInput::OnKeyDownEvent(const KeyEvent& event)
1714 {
1715   std::string keyName = event.keyPressedName;
1716   std::string keyString = event.keyPressed;
1717
1718   DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyDownEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1719
1720   // Do not consume "Tab" and "Escape" keys.
1721   if(keyName == "Tab" || keyName == "Escape")
1722   {
1723     // Escape key to end the edit mode
1724     EndEditMode();
1725
1726     return false;
1727   }
1728
1729   HidePopup(); // If Pop-up shown then hides it as editing text.
1730
1731   // Update Flag, indicates whether to update the text-input contents or not.
1732   // Any key stroke that results in a visual change of the text-input should
1733   // set this flag to true.
1734   bool update(false);
1735
1736   // Whether to scroll text to cursor position.
1737   // Scroll is needed always the cursor is updated and after the pre-edit is received.
1738   bool scroll = false;
1739
1740   if (keyName == "Return")
1741   {
1742     if ( mNumberOflinesLimit > 1) // Prevents New line character / Return adding an extra line if limit set to 1
1743     {
1744       bool preEditFlagPreviouslySet( mPreEditFlag );
1745
1746       if (mHighlightMeshActor)
1747       {
1748         // replaces highlighted text with new line
1749         DeleteHighlightedText( false );
1750       }
1751       mCursorPosition = mCursorPosition + InsertAt( Text( NEWLINE ), mCursorPosition, 0 );
1752
1753       // If we are in pre-edit mode then pressing enter will cause a commit.  But the commit string does not include the
1754       // '\n' character so we need to ensure that the immediately following commit knows how it occurred.
1755       if ( mPreEditFlag )
1756       {
1757         mCommitByKeyInput = true;
1758       }
1759
1760       // If attempting to insert a new-line brings us out of PreEdit mode, then we should not ignore the next commit.
1761       if ( preEditFlagPreviouslySet && !mPreEditFlag )
1762       {
1763         mPreEditFlag = true;
1764         mIgnoreCommitFlag = false;
1765       }
1766       EmitTextModified();
1767       update = true;
1768     }
1769     else
1770     {
1771       RemoveHighlight();
1772     }
1773   } // Return
1774   else if ( keyName == "space" )
1775   {
1776     if ( mHighlightMeshActor )
1777     {
1778       // Some text is selected so erase it before adding space.
1779       DeleteHighlightedText( true );
1780       update = true;
1781     }
1782
1783     mCursorPosition = mCursorPosition + InsertAt(Text(keyString), mCursorPosition, 0);
1784
1785     // If we are in pre-edit mode then pressing the space-bar will cause a commit.  But the commit string does not include the
1786     // ' ' character so we need to ensure that the immediately following commit knows how it occurred.
1787     if ( mPreEditFlag )
1788     {
1789       mCommitByKeyInput = true;
1790     }
1791     EmitTextModified();
1792     update = true;
1793   } // space
1794   else if (keyName == "BackSpace")
1795   {
1796     if ( mHighlightMeshActor )
1797     {
1798       // Some text is selected so erase it
1799       DeleteHighlightedText( true );
1800       update = true;
1801     }
1802     else
1803     {
1804       if ( mCursorPosition > 0 )
1805       {
1806         DeleteCharacter( mCursorPosition );
1807         update = true;
1808       }
1809     }
1810     EmitTextModified();
1811   } // BackSpace
1812   else if (keyName == "Right")
1813   {
1814     AdvanceCursor();
1815     RemoveHighlight();
1816   }
1817   else if (keyName == "Left")
1818   {
1819     AdvanceCursor(true);
1820     RemoveHighlight();
1821   }
1822   else // event is a character
1823   {
1824     // Some text may be selected, hiding keyboard causes an empty keystring to be sent, we don't want to delete highlight in this case
1825     if ( !keyString.empty() )
1826     {
1827       if ( mHighlightMeshActor )
1828       {
1829         // replaces highlighted text with new character
1830         DeleteHighlightedText( false );
1831       }
1832
1833
1834       // Received key String
1835       mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, 0 );
1836       update = true;
1837       EmitTextModified();
1838     }
1839   }
1840
1841   // If key event has resulted in a change in the text/cursor, then trigger a relayout of text
1842   // as this is a costly operation.
1843   if(update)
1844   {
1845     CursorUpdate();
1846   }
1847
1848   if(update || scroll)
1849   {
1850     if( IsScrollEnabled() )
1851     {
1852       // Calculates the new cursor position (in actor coordinates)
1853       const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
1854
1855       ScrollTextViewToMakeCursorVisible( cursorPosition );
1856     }
1857   }
1858
1859   return true;
1860 }
1861
1862 bool TextInput::OnKeyUpEvent(const KeyEvent& event)
1863 {
1864   std::string keyName = event.keyPressedName;
1865   std::string keyString = event.keyPressed;
1866
1867   DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyUpEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1868
1869   // The selected text become deselected when the key code is DALI_KEY_BACK.
1870   if( IsTextSelected() && ( keyName == "XF86Stop" || keyName == "XF86Send") )
1871   {
1872     DeSelectText();
1873     return true;
1874   }
1875
1876   return false;
1877 }
1878
1879 void TextInput::OnTextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition )
1880 {
1881   // Updates the stored scroll position.
1882   mTextLayoutInfo.mScrollOffset = textView.GetScrollPosition();
1883
1884   const Vector3& controlSize = GetControlSize();
1885   Size cursorSize( CURSOR_THICKNESS, 0.f );
1886
1887   // Updates the cursor and grab handle position and visibility.
1888   if( mGrabHandle || mCursor )
1889   {
1890     cursorSize.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height;
1891     const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1892
1893     mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( cursorPosition, cursorSize, controlSize );
1894
1895     mActualGrabHandlePosition = cursorPosition.GetVectorXY();
1896
1897     if( mGrabHandle )
1898     {
1899       ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1900       mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1901     }
1902
1903     if( mCursor )
1904     {
1905       mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
1906       mCursor.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1907     }
1908   }
1909
1910   // Updates the selection handles and highlighted text position and visibility.
1911   if( mSelectionHandleOne && mSelectionHandleTwo )
1912   {
1913     const Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition(mSelectionHandleOnePosition);
1914     const Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition(mSelectionHandleTwoPosition);
1915     cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleOnePosition ) ).mSize.height;
1916     const bool isSelectionHandleOneVisible = IsPositionInsideBoundaries( cursorPositionOne, cursorSize, controlSize );
1917     cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleTwoPosition ) ).mSize.height;
1918     const bool isSelectionHandleTwoVisible = IsPositionInsideBoundaries( cursorPositionTwo, cursorSize, controlSize );
1919
1920     mSelectionHandleOneActualPosition = cursorPositionOne.GetVectorXY();
1921     mSelectionHandleTwoActualPosition = cursorPositionTwo.GetVectorXY();
1922
1923     mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
1924     mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
1925     mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
1926     mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
1927
1928     if( mHighlightMeshActor )
1929     {
1930       mHighlightMeshActor.SetVisible( true );
1931       UpdateHighlight();
1932     }
1933   }
1934 }
1935
1936 void TextInput::ScrollTextViewToMakeCursorVisible( const Vector3& cursorPosition )
1937 {
1938   // Scroll the text to make the cursor visible.
1939   const Size cursorSize( CURSOR_THICKNESS,
1940                          GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height );
1941
1942   // Need to scroll the text to make the cursor visible and to cover the whole text-input area.
1943
1944   const Vector3& controlSize = GetControlSize();
1945
1946   // Calculates the new scroll position.
1947   Vector2 scrollOffset = mTextLayoutInfo.mScrollOffset;
1948   if( ( cursorPosition.x < 0.f ) || ( cursorPosition.x > controlSize.width ) )
1949   {
1950     scrollOffset.x += cursorPosition.x;
1951   }
1952
1953   if( cursorPosition.y - cursorSize.height < 0.f )
1954   {
1955     scrollOffset.y += ( cursorPosition.y - cursorSize.height );
1956   }
1957   else if( cursorPosition.y > controlSize.height )
1958   {
1959     scrollOffset.y += cursorPosition.y;
1960   }
1961
1962   // Sets the new scroll position.
1963   SetScrollPosition( Vector2::ZERO ); // TODO: need to reset to the zero position in order to make the scroll trim to work.
1964   SetScrollPosition( scrollOffset );
1965 }
1966
1967 void TextInput::StartScrollTimer()
1968 {
1969   if( !mScrollTimer )
1970   {
1971     mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1972     mScrollTimer.TickSignal().Connect( this, &TextInput::OnScrollTimerTick );
1973   }
1974
1975   if( !mScrollTimer.IsRunning() )
1976   {
1977     mScrollTimer.Start();
1978   }
1979 }
1980
1981 void TextInput::StopScrollTimer()
1982 {
1983   if( mScrollTimer )
1984   {
1985     mScrollTimer.Stop();
1986   }
1987 }
1988
1989 bool TextInput::OnScrollTimerTick()
1990 {
1991   // TODO: need to set the new style accordingly the new handle position.
1992
1993   if( !( mGrabHandleVisibility && mGrabHandle ) && !( mSelectionHandleOne && mSelectionHandleTwo ) )
1994   {
1995     // nothing to do if all handles are invisible or doesn't exist.
1996     return true;
1997   }
1998
1999   // Text scrolling
2000
2001   // Choose between the grab handle or the selection handles.
2002   Vector3& actualHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mActualGrabHandlePosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
2003   std::size_t& handlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCursorPosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
2004   Vector3& currentHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCurrentHandlePosition : mCurrentSelectionHandlePosition;
2005
2006   std::size_t newCursorPosition = 0;
2007   ReturnClosestIndex( actualHandlePosition.GetVectorXY(), newCursorPosition );
2008
2009   // Whether the handle's position is different of the previous one and in the case of the selection handle,
2010   // the new selection handle's position needs to be different of the other one.
2011   const bool differentSelectionHandles = ( mGrabHandleVisibility && mGrabHandle ) ? newCursorPosition != handlePosition :
2012                                          ( mCurrentSelectionId == HandleOne ) ? ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleTwoPosition ) :
2013                                                                                 ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleOnePosition );
2014
2015   if( differentSelectionHandles )
2016   {
2017     handlePosition = newCursorPosition;
2018
2019     const Vector3 actualPosition = GetActualPositionFromCharacterPosition( newCursorPosition );
2020
2021     Vector2 scrollDelta = ( actualPosition - currentHandlePosition ).GetVectorXY();
2022
2023     Vector2 scrollPosition = mDisplayedTextView.GetScrollPosition();
2024     scrollPosition += scrollDelta;
2025     SetScrollPosition( scrollPosition );
2026
2027     if( mDisplayedTextView.IsScrollPositionTrimmed() )
2028     {
2029       StopScrollTimer();
2030     }
2031
2032     currentHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition ).GetVectorXY();
2033   }
2034
2035   actualHandlePosition.x += mScrollDisplacement.x;
2036   actualHandlePosition.y += mScrollDisplacement.y;
2037
2038   return true;
2039 }
2040
2041 // Public Internal Methods (public for testing purpose)
2042
2043 void TextInput::SetUpTouchEvents()
2044 {
2045   if ( !mTapDetector )
2046   {
2047     mTapDetector = TapGestureDetector::New();
2048     // Attach the actors and connect the signal
2049     mTapDetector.Attach(Self());
2050
2051     // As contains children which may register for tap the default control detector is not used.
2052     mTapDetector.DetectedSignal().Connect(this, &TextInput::OnTextTap);
2053   }
2054
2055   if ( !mDoubleTapDetector )
2056   {
2057     mDoubleTapDetector = TapGestureDetector::New();
2058     mDoubleTapDetector.SetTapsRequired( 2 );
2059     mDoubleTapDetector.DetectedSignal().Connect(this, &TextInput::OnDoubleTap);
2060
2061     // Only attach and detach the actor to the double tap detector when we enter/leave edit mode
2062     // so that we do not, unnecessarily, have a double tap request all the time
2063   }
2064
2065   if ( !mPanGestureDetector )
2066   {
2067     mPanGestureDetector = PanGestureDetector::New();
2068     mPanGestureDetector.DetectedSignal().Connect(this, &TextInput::OnHandlePan);
2069   }
2070
2071   if ( !mLongPressDetector )
2072   {
2073     mLongPressDetector = LongPressGestureDetector::New();
2074     mLongPressDetector.DetectedSignal().Connect(this, &TextInput::OnLongPress);
2075     mLongPressDetector.Attach(Self());
2076   }
2077 }
2078
2079 void TextInput::CreateTextViewActor()
2080 {
2081   mDisplayedTextView = Toolkit::TextView::New();
2082   mDisplayedTextView.SetMarkupProcessingEnabled( mMarkUpEnabled );
2083   mDisplayedTextView.SetParentOrigin(ParentOrigin::TOP_LEFT);
2084   mDisplayedTextView.SetAnchorPoint(AnchorPoint::TOP_LEFT);
2085   mDisplayedTextView.SetMultilinePolicy(Toolkit::TextView::SplitByWord);
2086   mDisplayedTextView.SetWidthExceedPolicy( Toolkit::TextView::Original );
2087   mDisplayedTextView.SetHeightExceedPolicy( Toolkit::TextView::Original );
2088   mDisplayedTextView.SetLineJustification( Toolkit::TextView::Left );
2089   mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>( Toolkit::Alignment::HorizontalLeft | Toolkit::Alignment::VerticalTop ) );
2090   mDisplayedTextView.SetPosition( Vector3( 0.0f, 0.0f, DISPLAYED_TEXT_VIEW_Z_OFFSET ) );
2091   mDisplayedTextView.SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
2092
2093   mDisplayedTextView.ScrolledSignal().Connect( this, &TextInput::OnTextViewScrolled );
2094
2095   Self().Add( mDisplayedTextView );
2096 }
2097
2098 // Start a timer to initiate, used by the cursor to blink.
2099 void TextInput::StartCursorBlinkTimer()
2100 {
2101   if ( !mCursorBlinkTimer )
2102   {
2103     mCursorBlinkTimer = Timer::New( CURSOR_BLINK_INTERVAL );
2104     mCursorBlinkTimer.TickSignal().Connect( this, &TextInput::OnCursorBlinkTimerTick );
2105   }
2106
2107   if ( !mCursorBlinkTimer.IsRunning() )
2108   {
2109     mCursorBlinkTimer.Start();
2110   }
2111 }
2112
2113 // Start a timer to initiate, used by the cursor to blink.
2114 void TextInput::StopCursorBlinkTimer()
2115 {
2116   if ( mCursorBlinkTimer )
2117   {
2118     mCursorBlinkTimer.Stop();
2119   }
2120 }
2121
2122 void TextInput::StartEditMode()
2123 {
2124   DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput StartEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2125
2126   if(!mEditModeActive)
2127   {
2128     SetKeyInputFocus();
2129   }
2130
2131   if ( mDoubleTapDetector )
2132   {
2133     mDoubleTapDetector.Attach( Self() );
2134   }
2135 }
2136
2137 void TextInput::EndEditMode()
2138 {
2139   DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput EndEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2140
2141   ClearKeyInputFocus();
2142
2143   if ( mDoubleTapDetector )
2144   {
2145     mDoubleTapDetector.Detach( Self() );
2146   }
2147 }
2148
2149 void TextInput::ApplyPreEditStyle( std::size_t preEditStartPosition, std::size_t preEditStringLength )
2150 {
2151   if ( mPreEditFlag && ( preEditStringLength > 0 ) )
2152   {
2153     mUnderlinedPriorToPreEdit = mInputStyle.IsUnderlineEnabled();
2154     TextStyle style;
2155     style.SetUnderline( true );
2156     ApplyStyleToRange( style, TextStyle::UNDERLINE , preEditStartPosition, preEditStartPosition + preEditStringLength -1 );
2157   }
2158 }
2159
2160 void TextInput::RemovePreEditStyle()
2161 {
2162   if ( !mUnderlinedPriorToPreEdit )
2163   {
2164     TextStyle style;
2165     style.SetUnderline( false );
2166     SetActiveStyle( style, TextStyle::UNDERLINE );
2167   }
2168 }
2169
2170 // IMF related methods
2171
2172
2173 ImfManager::ImfCallbackData TextInput::ImfEventReceived( Dali::ImfManager& imfManager, const ImfManager::ImfEventData&  imfEvent )
2174 {
2175   bool update( false );
2176   bool preeditResetRequired ( false );
2177
2178   if (imfEvent.eventName != ImfManager::GETSURROUNDING )
2179   {
2180     HidePopup(); // If Pop-up shown then hides it as editing text.
2181   }
2182
2183   switch ( imfEvent.eventName )
2184   {
2185     case ImfManager::PREEDIT:
2186     {
2187       mIgnoreFirstCommitFlag = false;
2188
2189       // 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
2190       if ( mHighlightMeshActor &&  (!imfEvent.predictiveString.empty()) )
2191       {
2192         // replaces highlighted text with new character
2193         DeleteHighlightedText( false );
2194       }
2195
2196       preeditResetRequired = PreEditReceived( imfEvent.predictiveString, imfEvent.cursorOffset );
2197
2198       if( IsScrollEnabled() )
2199       {
2200         // Calculates the new cursor position (in actor coordinates)
2201         const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2202         ScrollTextViewToMakeCursorVisible( cursorPosition );
2203       }
2204
2205       update = true;
2206
2207       break;
2208     }
2209     case ImfManager::COMMIT:
2210     {
2211       if( mIgnoreFirstCommitFlag )
2212       {
2213         // Do not commit in this case when keyboard sends a commit when shows for the first time (work-around for imf keyboard).
2214         mIgnoreFirstCommitFlag = false;
2215       }
2216       else
2217       {
2218         // A Commit message is a word that has been accepted, it may have been a pre-edit word previously but now commited.
2219
2220         // 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
2221         if ( mHighlightMeshActor &&  (!imfEvent.predictiveString.empty()) )
2222         {
2223           // replaces highlighted text with new character
2224           DeleteHighlightedText( false );
2225         }
2226
2227        // A PreEditReset can cause a commit message to be sent, the Ignore Commit flag is used in scenarios where the word is
2228        // not needed, one such scenario is when the pre-edit word is too long to fit.
2229        if ( !mIgnoreCommitFlag )
2230        {
2231          update = CommitReceived( imfEvent.predictiveString );
2232        }
2233        else
2234        {
2235          mIgnoreCommitFlag = false; // reset ignore flag so next commit is acted upon.
2236        }
2237       }
2238
2239       if( update )
2240       {
2241         if( IsScrollEnabled() )
2242         {
2243           // Calculates the new cursor position (in actor coordinates)
2244           const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2245
2246           ScrollTextViewToMakeCursorVisible( cursorPosition );
2247         }
2248       }
2249       break;
2250     }
2251     case ImfManager::DELETESURROUNDING:
2252     {
2253       DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - delete surrounding mPreEditFlag[%s] cursor offset[%d] characters to delete[%d] position to delete[%u] \n",
2254                      (mPreEditFlag)?"true":"false", imfEvent.cursorOffset, imfEvent.numberOfChars, static_cast<std::size_t>( mCursorPosition+imfEvent.cursorOffset) );
2255
2256       mPreEditFlag = false;
2257
2258       std::size_t toDelete = 0;
2259       std::size_t numberOfCharacters = 0;
2260
2261       if( mHighlightMeshActor )
2262       {
2263         // delete highlighted text.
2264         toDelete = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2265         numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - toDelete;
2266       }
2267       else
2268       {
2269         if( std::abs( imfEvent.cursorOffset ) < mCursorPosition )
2270         {
2271           toDelete = mCursorPosition + imfEvent.cursorOffset;
2272         }
2273         if( toDelete + imfEvent.numberOfChars > mStyledText.size() )
2274         {
2275           numberOfCharacters = mStyledText.size() - toDelete;
2276         }
2277         else
2278         {
2279           numberOfCharacters = imfEvent.numberOfChars;
2280         }
2281       }
2282       DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding pre-delete range mCursorPosition[%u] \n", mCursorPosition);
2283       DeleteRange( toDelete, numberOfCharacters );
2284
2285       mCursorPosition = toDelete;
2286       mNumberOfSurroundingCharactersDeleted = numberOfCharacters;
2287
2288       EmitTextModified();
2289
2290       DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding post-delete range mCursorPosition[%u] \n", mCursorPosition);
2291       break;
2292     }
2293     case ImfManager::GETSURROUNDING:
2294     {
2295       // 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
2296       // the next key pressed.  Instead the Select function sets the cursor position and surrounding text.
2297       if (! ( mHighlightMeshActor || mSelectingText ) )
2298       {
2299         std::string text( GetText() );
2300         DALI_LOG_INFO( gLogFilter, Debug::General, "OnKey - surrounding text - set text [%s] and cursor[%u] \n", text.c_str(), mCursorPosition );
2301
2302         imfManager.SetCursorPosition( mCursorPosition );
2303         imfManager.SetSurroundingText( text );
2304       }
2305
2306       if( 0 != mNumberOfSurroundingCharactersDeleted )
2307       {
2308         mDisplayedTextView.RemoveTextFrom( mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2309         mNumberOfSurroundingCharactersDeleted = 0;
2310
2311         if( mStyledText.empty() )
2312         {
2313           // Styled text is empty, so set the placeholder text.
2314           mDisplayedTextView.SetText( mStyledPlaceHolderText );
2315           mPlaceHolderSet = true;
2316         }
2317       }
2318       break;
2319     }
2320     case ImfManager::VOID:
2321     {
2322       DALI_ASSERT_DEBUG( false );
2323     }
2324   } // end switch
2325
2326   ImfManager::ImfCallbackData callbackData( update, mCursorPosition, GetText(), preeditResetRequired );
2327
2328   return callbackData;
2329 }
2330
2331 bool TextInput::PreEditReceived(const std::string& keyString, std::size_t cursorOffset )
2332 {
2333   mPreserveCursorPosition = false;  // As in pre-edit state we should have the cursor at the end of the word displayed not last touch position.
2334
2335   DALI_LOG_INFO(gLogFilter, Debug::General, ">>PreEditReceived preserveCursorPos[%d] mCursorPos[%d] mPreEditFlag[%d]\n",
2336                 mPreserveCursorPosition, mCursorPosition, mPreEditFlag );
2337
2338   bool preeditResetRequest ( false );
2339
2340   if( mPreEditFlag ) // Already in pre-edit state.
2341   {
2342     if( mStyledText.size() >= mMaxStringLength )
2343     {
2344       DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived styledTextSize >= mMaxStringLength \n");
2345       // Cannot fit these characters into field, clear pre-edit.
2346       if ( !mUnderlinedPriorToPreEdit )
2347       {
2348         TextStyle style;
2349         style.SetUnderline( false );
2350         ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
2351       }
2352       mIgnoreCommitFlag = true;
2353       preeditResetRequest = false; // this will reset the keyboard's predictive suggestions.
2354       mPreEditFlag = false;
2355       EmitMaxInputCharactersReachedSignal();
2356     }
2357     else
2358     {
2359       // delete existing pre-edit string
2360       const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2361
2362       // Store new pre-edit string
2363       mPreEditString.SetText( keyString );
2364
2365       if ( keyString.empty() )
2366       {
2367         mPreEditFlag = false;
2368         mCursorPosition = mPreEditStartPosition;
2369
2370         if( mStyledText.empty() )
2371         {
2372           // Styled text is empty, so set the placeholder text.
2373           mDisplayedTextView.SetText( mStyledPlaceHolderText );
2374           mPlaceHolderSet = true;
2375         }
2376         else
2377         {
2378           mDisplayedTextView.RemoveTextFrom( mPreEditStartPosition, numberOfCharactersToReplace );
2379         }
2380         GetTextLayoutInfo();
2381         EmitTextModified();
2382       }
2383       else
2384       {
2385         // Insert new pre-edit string. InsertAt updates the size and position table.
2386         mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersToReplace );
2387         // 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.
2388         mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2389         ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2390         DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] \n", mCursorPosition);
2391         EmitTextModified();
2392       }
2393       // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2394       DrawCursor();
2395     }
2396   }
2397   else  // mPreEditFlag not set
2398   {
2399     if ( !keyString.empty() ) // Imf can send an empty pre-edit followed by Backspace instead of a commit.
2400     {
2401       DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived Initial Pre-Edit string \n");
2402       // new pre-edit so move into pre-edit state by setting flag
2403       mPreEditFlag = true;
2404       mPreEditString.SetText( keyString ); // store new pre-edit string
2405       mPreEditStartPosition = mCursorPosition; // store starting cursor position of pre-edit so know where to re-start from
2406       mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, 0 );
2407       // 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.
2408       mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2409       ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2410       DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] mPreEditStartPosition[%u]\n", mCursorPosition, mPreEditStartPosition);
2411       // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2412       DrawCursor();
2413       EmitTextModified();
2414     }
2415     else
2416     {
2417       DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived with empty keyString\n");
2418     }
2419   }
2420
2421   return preeditResetRequest;
2422 }
2423
2424 bool TextInput::CommitReceived(const std::string& keyString )
2425 {
2426   DALI_LOG_INFO(gLogFilter, Debug::General, ">>CommitReceived preserveCursorPos[%d] mPreEditStartPosition [%d] mCursorPos[%d] mPreEditFlag[%d] mIgnoreCommitFlag[%s]\n",
2427       mPreserveCursorPosition, mPreEditStartPosition, mCursorPosition, mPreEditFlag, (mIgnoreCommitFlag)?"true":"false" );
2428
2429   bool update( false );
2430
2431   RemovePreEditStyle();
2432
2433   const std::size_t styledTextSize( mStyledText.size() );
2434   if( styledTextSize >= mMaxStringLength )
2435   {
2436     // Cannot fit these characters into field, clear pre-edit.
2437     if ( mPreEditFlag )
2438     {
2439       mIgnoreCommitFlag = true;
2440       mPreEditFlag = false;
2441     }
2442     EmitMaxInputCharactersReachedSignal();
2443   }
2444   else
2445   {
2446     if( mPreEditFlag )
2447     {
2448       // delete existing pre-edit string
2449       const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2450       mPreEditFlag = false;
2451
2452       DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived mPreserveCursorPosition[%s] mPreEditStartPosition[%u]\n",
2453                     (mPreserveCursorPosition)?"true":"false", mPreEditStartPosition );
2454
2455       if ( mPreserveCursorPosition ) // PreEditReset has been called triggering this commit.
2456       {
2457         // No need to update cursor position as Cursor location given by touch.
2458         InsertAt( Text( keyString ), mPreEditStartPosition, numberOfCharactersToReplace );
2459         mPreserveCursorPosition = false;
2460       }
2461       else
2462       {
2463         // Cursor not set by touch so needs to be re-positioned to input more text
2464         mCursorPosition = mPreEditStartPosition + InsertAt( Text(keyString), mPreEditStartPosition, numberOfCharactersToReplace ); // update cursor position as InsertAt, re-draw cursor with this
2465
2466         // If a space or enter caused the commit then our string is one longer than the string given to us by the commit key.
2467         if ( mCommitByKeyInput )
2468         {
2469           mCursorPosition = std::min ( mCursorPosition + 1, mStyledText.size() );
2470           mCommitByKeyInput = false;
2471         }
2472       }
2473
2474       EmitTextModified();
2475
2476       if ( mSelectTextOnCommit )
2477       {
2478         SelectText(mRequestedSelection.mStartOfSelection, mRequestedSelection.mEndOfSelection );
2479       }
2480
2481       update = true;
2482     }
2483     else // mPreEditFlag not set
2484     {
2485       if ( !mIgnoreCommitFlag ) // Check if this commit should be ignored.
2486       {
2487         if( mStyledText.empty() && mPlaceHolderSet )
2488         {
2489           // If the styled text is empty and the placeholder text is set, it needs to be cleared.
2490           mDisplayedTextView.SetText( "" );
2491           mNumberOfSurroundingCharactersDeleted = 0;
2492           mPlaceHolderSet = false;
2493         }
2494         mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2495         update = true;
2496         mNumberOfSurroundingCharactersDeleted = 0;
2497         EmitTextModified();
2498       }
2499       else
2500       {
2501         mIgnoreCommitFlag = false; // Reset flag so future commits will not be ignored.
2502       }
2503     }
2504   }
2505
2506   mSelectTextOnCommit = false;
2507
2508   DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived << mCursorPos[%d] mPreEditFlag[%d] update[%s] \n",
2509                                              mCursorPosition, mPreEditFlag, (update)?"true":"false" );
2510
2511   return update;
2512 }
2513
2514 // End of IMF related methods
2515
2516 std::size_t TextInput::DeletePreEdit()
2517 {
2518   DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeletePreEdit mPreEditFlag[%s] \n", (mPreEditFlag)?"true":"false");
2519
2520   DALI_ASSERT_DEBUG( mPreEditFlag );
2521
2522   const std::size_t preEditStringLength = mPreEditString.GetLength();
2523   const std::size_t styledTextSize = mStyledText.size();
2524
2525   std::size_t endPosition = mPreEditStartPosition + preEditStringLength;
2526
2527   // Prevents erase items outside mStyledText bounds.
2528   if( mPreEditStartPosition > styledTextSize )
2529   {
2530     DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. mPreEditStartPosition > mStyledText.size()" );
2531     mPreEditStartPosition = styledTextSize;
2532   }
2533
2534   if( ( endPosition > styledTextSize ) || ( endPosition < mPreEditStartPosition ) )
2535   {
2536     DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. ( endPosition > mStyledText.size() ) || ( endPosition < mPreEditStartPosition )" );
2537     endPosition = styledTextSize;
2538   }
2539
2540   mStyledText.erase( mStyledText.begin() + mPreEditStartPosition, mStyledText.begin() + endPosition );
2541
2542   // DeletePreEdit() doesn't remove characters from the text-view because may be followed by an InsertAt() which inserts characters,
2543   // in that case, the Insert should use the returned number of deleted characters and replace the text which helps the text-view to
2544   // reuse glyphs.
2545   // In case DeletePreEdit() is not followed by an InsertAt() characters must be deleted after this call.
2546
2547   return preEditStringLength;
2548 }
2549
2550 void TextInput::PreEditReset( bool preserveCursorPosition )
2551 {
2552   DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReset preserveCursorPos[%d] mCursorPos[%d] \n",
2553                 preserveCursorPosition, mCursorPosition);
2554
2555   // 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.
2556   mPreserveCursorPosition = preserveCursorPosition;
2557
2558   // Reset incase we are in a pre-edit state.
2559   ImfManager imfManager = ImfManager::Get();
2560   if ( imfManager )
2561   {
2562     imfManager.Reset(); // Will trigger a commit message
2563   }
2564 }
2565
2566 void TextInput::CursorUpdate()
2567 {
2568   DrawCursor();
2569
2570   ImfManager imfManager = ImfManager::Get();
2571   if ( imfManager )
2572   {
2573     std::string text( GetText() );
2574     imfManager.SetSurroundingText( text );  // Notifying IMF of a cursor change triggers a surrounding text request so updating it now.
2575     imfManager.SetCursorPosition ( mCursorPosition );
2576     imfManager.NotifyCursorPosition();
2577   }
2578 }
2579
2580 /* Delete highlighted characters redisplay*/
2581 void TextInput::DeleteHighlightedText( bool inheritStyle )
2582 {
2583   DALI_LOG_INFO( gLogFilter, Debug::General, "DeleteHighlightedText handlePosOne[%u] handlePosTwo[%u]\n", mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
2584
2585   if(mHighlightMeshActor)
2586   {
2587     mCursorPosition = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2588
2589     MarkupProcessor::StyledTextArray::iterator start = mStyledText.begin() + mCursorPosition;
2590     MarkupProcessor::StyledTextArray::iterator end =  mStyledText.begin() + std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2591
2592     // Get the styled text of the characters to be deleted as it may be needed if
2593     // the "exceed the text-input's boundaries" option is disabled.
2594     MarkupProcessor::StyledTextArray styledCharactersToDelete;
2595
2596     styledCharactersToDelete.insert( styledCharactersToDelete.begin(), start, end );
2597
2598     mStyledText.erase( start, end ); // erase range of characters
2599
2600     // Remove text from TextView.
2601
2602     if( mStyledText.empty() )
2603     {
2604       // Styled text is empty, so set the placeholder text.
2605       mDisplayedTextView.SetText( mStyledPlaceHolderText );
2606       mPlaceHolderSet = true;
2607     }
2608     else
2609     {
2610       const std::size_t numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - mCursorPosition;
2611
2612       mDisplayedTextView.RemoveTextFrom( mCursorPosition, numberOfCharacters );
2613
2614       // It may happen than after removing a white space or a new line character,
2615       // two words merge, this new word could be big enough to not fit in its
2616       // current line, so moved to the next one, and make some part of the text to
2617       // exceed the text-input's boundary.
2618       if( !mExceedEnabled )
2619       {
2620         // Get the new text layout after removing some characters.
2621         mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2622
2623         // Get text-input's size.
2624         const Vector3& size = GetControlSize();
2625
2626         if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2627             ( mTextLayoutInfo.mTextSize.height > size.height ) )
2628         {
2629           mDisplayedTextView.InsertTextAt( mCursorPosition, styledCharactersToDelete );
2630
2631           mStyledText.insert( mStyledText.begin() + mCursorPosition,
2632                               styledCharactersToDelete.begin(),
2633                               styledCharactersToDelete.end() );
2634         }
2635       }
2636     }
2637     GetTextLayoutInfo();
2638
2639     RemoveHighlight();
2640
2641     if( inheritStyle )
2642     {
2643       const TextStyle oldInputStyle( mInputStyle );
2644
2645       mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2646
2647       if( oldInputStyle != mInputStyle )
2648       {
2649         // Updates the line height accordingly with the input style.
2650         UpdateLineHeight();
2651
2652         EmitStyleChangedSignal();
2653       }
2654     }
2655   }
2656 }
2657
2658 void TextInput::DeleteRange( const std::size_t start, const std::size_t ncharacters )
2659 {
2660   DALI_ASSERT_DEBUG( start <= mStyledText.size() );
2661   DALI_ASSERT_DEBUG( !mStyledText.empty() );
2662
2663   DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeleteRange pre mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2664
2665
2666   if ( ( !mStyledText.empty()) && ( ( start + ncharacters ) <= mStyledText.size() ) )
2667   {
2668     MarkupProcessor::StyledTextArray::iterator itStart =  mStyledText.begin() + start;
2669     MarkupProcessor::StyledTextArray::iterator itEnd =  mStyledText.begin() + start + ncharacters;
2670
2671     mStyledText.erase(itStart, itEnd);
2672
2673     // update the selection handles if they are visible.
2674     if( mHighlightMeshActor )
2675     {
2676       std::size_t& minHandle = ( mSelectionHandleOnePosition <= mSelectionHandleTwoPosition ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition );
2677       std::size_t& maxHandle = ( mSelectionHandleTwoPosition > mSelectionHandleOnePosition ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition );
2678
2679       if( minHandle >= start + ncharacters )
2680       {
2681         minHandle -= ncharacters;
2682       }
2683       else if( ( minHandle > start ) && ( minHandle < start + ncharacters ) )
2684       {
2685         minHandle = start;
2686       }
2687
2688       if( maxHandle >= start + ncharacters )
2689       {
2690         maxHandle -= ncharacters;
2691       }
2692       else if( ( maxHandle > start ) && ( maxHandle < start + ncharacters ) )
2693       {
2694         maxHandle = start;
2695       }
2696     }
2697
2698     // 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.
2699   }
2700
2701   DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteRange<< post mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2702
2703   // Although mStyledText has been set to a new text string we no longer re-draw the text or notify the cursor change.
2704   // This is a performance decision as the use of this function often means the text is being replaced or just deleted.
2705   // Mean we do not re-draw the text more than we have too.
2706 }
2707
2708 /* Delete character at current cursor position and redisplay*/
2709 void TextInput::DeleteCharacter( std::size_t positionToDelete )
2710 {
2711   // Ensure positionToDelete is not out of bounds.
2712   DALI_ASSERT_DEBUG( positionToDelete <= mStyledText.size() );
2713   DALI_ASSERT_DEBUG( !mStyledText.empty() );
2714   DALI_ASSERT_DEBUG( positionToDelete > 0 );
2715
2716   DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteCharacter positionToDelete[%u]", positionToDelete );
2717
2718
2719   if ( ( !mStyledText.empty()) && ( positionToDelete > 0 ) && positionToDelete <= mStyledText.size() )  // don't try to delete if no characters left of cursor
2720   {
2721     MarkupProcessor::StyledTextArray::iterator it =  mStyledText.begin() + positionToDelete - 1;
2722
2723     // Get the styled text of the character to be deleted as it may be needed if
2724     // the "exceed the text-input's boundaries" option is disabled.
2725     const MarkupProcessor::StyledText styledCharacterToDelete( *it );
2726
2727     mStyledText.erase(it);  // erase the character left of positionToDelete
2728
2729     if( mStyledText.empty() )
2730     {
2731       // Styled text is empty, so set the placeholder text.
2732       mDisplayedTextView.SetText( mStyledPlaceHolderText );
2733       mPlaceHolderSet = true;
2734     }
2735     else
2736     {
2737       mDisplayedTextView.RemoveTextFrom( positionToDelete - 1, 1 );
2738
2739       const Character characterToDelete = styledCharacterToDelete.mText[0];
2740
2741       // It may happen than after removing a white space or a new line character,
2742       // two words merge, this new word could be big enough to not fit in its
2743       // current line, so moved to the next one, and make some part of the text to
2744       // exceed the text-input's boundary.
2745       if( !mExceedEnabled )
2746       {
2747         if( characterToDelete.IsWhiteSpace() || characterToDelete.IsNewLine() )
2748         {
2749           // Get the new text layout after removing one character.
2750           mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2751
2752           // Get text-input's size.
2753           const Vector3& size = GetControlSize();
2754
2755           if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2756               ( mTextLayoutInfo.mTextSize.height > size.height ) )
2757           {
2758             MarkupProcessor::StyledTextArray array;
2759             array.push_back( styledCharacterToDelete );
2760             mDisplayedTextView.InsertTextAt( positionToDelete - 1, array );
2761
2762             mStyledText.insert( mStyledText.begin() + ( positionToDelete - 1 ), styledCharacterToDelete );
2763           }
2764         }
2765       }
2766     }
2767     GetTextLayoutInfo();
2768
2769     ShowGrabHandleAndSetVisibility( false );
2770
2771     mCursorPosition = positionToDelete -1;
2772
2773     const TextStyle oldInputStyle( mInputStyle );
2774
2775     mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2776
2777     if( oldInputStyle != mInputStyle )
2778     {
2779       // Updates the line height accordingly with the input style.
2780       UpdateLineHeight();
2781
2782       EmitStyleChangedSignal();
2783     }
2784   }
2785 }
2786
2787 /*Insert new character into the string and (optionally) redisplay text-input*/
2788 std::size_t TextInput::InsertAt( const Text& newText, const std::size_t insertionPosition, const std::size_t numberOfCharactersToReplace )
2789 {
2790   DALI_LOG_INFO(gLogFilter, Debug::General, "InsertAt insertionPosition[%u]\n", insertionPosition );
2791
2792   // Ensure insertionPosition is not out of bounds.
2793   DALI_ASSERT_ALWAYS( insertionPosition <= mStyledText.size() );
2794
2795   bool textExceedsMaximunNumberOfCharacters = false;
2796   bool textExceedsBoundary = false;
2797   std::size_t insertedStringLength = DoInsertAt( newText, insertionPosition, numberOfCharactersToReplace, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
2798
2799   ShowGrabHandleAndSetVisibility( false );
2800
2801   if( textExceedsMaximunNumberOfCharacters || textExceedsBoundary )
2802   {
2803     if( mPreEditFlag )
2804     {
2805       mIgnoreCommitFlag = true;
2806       mPreEditFlag = false;
2807       // A PreEditReset( false ) should be triggered from here if the keyboards predictive suggestions must be cleared.
2808       // Although can not directly call PreEditReset() as it will cause a recursive emit loop.
2809     }
2810
2811     if( textExceedsMaximunNumberOfCharacters )
2812     {
2813       EmitMaxInputCharactersReachedSignal();
2814     }
2815
2816     if( textExceedsBoundary )
2817     {
2818       EmitInputTextExceedsBoundariesSignal();
2819       PreEditReset( false );
2820     }
2821   }
2822
2823   return insertedStringLength;
2824 }
2825
2826 ImageActor TextInput::CreateCursor( Image cursorImage, const Vector4& border )
2827 {
2828   ImageActor cursor;
2829
2830   if ( cursorImage )
2831   {
2832     cursor = ImageActor::New( cursorImage );
2833   }
2834   else
2835   {
2836     cursor = ImageActor::New( Image::New( DEFAULT_CURSOR ) );
2837   }
2838
2839   cursor.SetStyle(ImageActor::STYLE_NINE_PATCH);
2840   cursor.SetNinePatchBorder( border );
2841
2842   cursor.SetParentOrigin(ParentOrigin::TOP_LEFT);
2843   cursor.SetAnchorPoint(AnchorPoint::BOTTOM_CENTER);
2844   cursor.SetVisible(false);
2845
2846   return cursor;
2847 }
2848
2849 void TextInput::AdvanceCursor(bool reverse, std::size_t places)
2850 {
2851   // As cursor is not moving due to grab handle, handle should be hidden.
2852   ShowGrabHandleAndSetVisibility( false );
2853
2854   bool cursorPositionChanged = false;
2855   if (reverse)
2856   {
2857     if ( mCursorPosition >= places )
2858     {
2859       mCursorPosition = mCursorPosition - places;
2860       cursorPositionChanged = true;
2861     }
2862   }
2863   else
2864   {
2865     if ((mCursorPosition + places) <= mStyledText.size())
2866     {
2867       mCursorPosition = mCursorPosition + places;
2868       cursorPositionChanged = true;
2869     }
2870   }
2871
2872   if( cursorPositionChanged )
2873   {
2874     const std::size_t cursorPositionForStyle = ( 0 == mCursorPosition ? 0 : mCursorPosition - 1 );
2875
2876     const TextStyle oldInputStyle( mInputStyle );
2877     mInputStyle = GetStyleAt( cursorPositionForStyle ); // Inherit style from selected position.
2878
2879     DrawCursor();
2880
2881     if( oldInputStyle != mInputStyle )
2882     {
2883       // Updates the line height accordingly with the input style.
2884       UpdateLineHeight();
2885
2886       EmitStyleChangedSignal();
2887     }
2888
2889     ImfManager imfManager = ImfManager::Get();
2890     if ( imfManager )
2891     {
2892       imfManager.SetCursorPosition ( mCursorPosition );
2893       imfManager.NotifyCursorPosition();
2894     }
2895   }
2896 }
2897
2898 void TextInput::DrawCursor(const std::size_t nthChar)
2899 {
2900   // Get height of cursor and set its size
2901   Size size( CURSOR_THICKNESS, 0.0f );
2902   if (!mTextLayoutInfo.mCharacterLayoutInfoTable.empty())
2903   {
2904     size.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height;
2905   }
2906   else
2907   {
2908     // Measure Font so know how big text will be if no initial text to measure.
2909     size.height = mLineHeight;
2910   }
2911
2912   mCursor.SetSize(size);
2913
2914   // If the character is italic then the cursor also tilts.
2915   mCursor.SetRotation( mInputStyle.IsItalicsEnabled() ? Degree( mInputStyle.GetItalicsAngle() - CURSOR_ANGLE_OFFSET ) : Degree( 0.f ), Vector3::ZAXIS );
2916
2917   DALI_ASSERT_DEBUG( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
2918
2919   if ( ( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) )
2920   {
2921     Vector3 altPosition;    // Alternate (i.e. opposite direction) cursor position.
2922     bool altPositionValid;  // Alternate cursor validity flag.
2923     bool directionRTL;      // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
2924     Vector3 position = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
2925
2926     SetAltCursorEnabled( altPositionValid );
2927
2928     if(!altPositionValid)
2929     {
2930       mCursor.SetPosition( position + UI_OFFSET );
2931     }
2932     else
2933     {
2934       size.height *= 0.5f;
2935       mCursor.SetSize(size);
2936       mCursor.SetPosition( position + UI_OFFSET - Vector3(0.0f, directionRTL ? 0.0f : size.height, 0.0f) );
2937
2938       // TODO: change this cursor pos, to be the one where the cursor is sourced from.
2939       Size rowSize = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) );
2940       size.height = rowSize.height * 0.5f;
2941       mCursorRTL.SetSize(size);
2942       mCursorRTL.SetPosition( altPosition + UI_OFFSET - Vector3(0.0f, directionRTL ? size.height : 0.0f, 0.0f) );
2943     }
2944
2945     if( IsScrollEnabled() )
2946     {
2947       // Whether cursor and grab handle are inside the boundaries of the text-input when text scroll is enabled.
2948       mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( position, size, GetControlSize() );
2949     }
2950   } // EditMode
2951 }
2952
2953 void TextInput::SetAltCursorEnabled( bool enabled )
2954 {
2955   mCursorRTLEnabled = enabled;
2956   mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2957 }
2958
2959 void TextInput::SetCursorVisibility( bool visible )
2960 {
2961   mCursorVisibility = visible;
2962   mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
2963   mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2964 }
2965
2966 void TextInput::CreateGrabHandle( Dali::Image image )
2967 {
2968   if ( !mGrabHandle )
2969   {
2970     if ( !image )
2971     {
2972       mGrabHandleImage = Image::New(DEFAULT_GRAB_HANDLE);
2973     }
2974     else
2975     {
2976       mGrabHandleImage = image;
2977     }
2978
2979     mGrabHandle = ImageActor::New(mGrabHandleImage);
2980     mGrabHandle.SetParentOrigin(ParentOrigin::TOP_LEFT);
2981     mGrabHandle.SetAnchorPoint(AnchorPoint::TOP_CENTER);
2982
2983     mGrabHandle.SetDrawMode(DrawMode::OVERLAY);
2984
2985     ShowGrabHandleAndSetVisibility( false );
2986
2987     CreateGrabArea( mGrabHandle );
2988
2989     mActiveLayer.Add(mGrabHandle);
2990   }
2991 }
2992
2993 void TextInput::CreateGrabArea( Actor& parent )
2994 {
2995   mGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
2996   mGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
2997   mGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE ) ) );  // grab area to be larger than text actor
2998   mGrabArea.TouchedSignal().Connect(this,&TextInput::OnPressDown);
2999   mTapDetector.Attach( mGrabArea );
3000   mPanGestureDetector.Attach( mGrabArea );
3001
3002   parent.Add(mGrabArea);
3003 }
3004
3005 Vector3 TextInput::MoveGrabHandle( const Vector2& displacement )
3006 {
3007   Vector3 actualHandlePosition;
3008
3009   if (mGrabHandle)
3010   {
3011     mActualGrabHandlePosition.x += displacement.x;
3012     mActualGrabHandlePosition.y += displacement.y;
3013
3014     // Grab handle should jump to the nearest character and take cursor with it
3015     std::size_t newCursorPosition = 0;
3016     ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY(), newCursorPosition );
3017
3018     actualHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition );
3019
3020     bool handleVisible = true;
3021
3022     if( IsScrollEnabled() )
3023     {
3024       const Vector3 controlSize = GetControlSize();
3025       const Size cursorSize = GetRowRectFromCharacterPosition( GetVisualPosition( newCursorPosition ) );
3026       // Scrolls the text if the handle is not in a visible position
3027       handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3028                                                   cursorSize,
3029                                                   controlSize );
3030
3031       if( handleVisible )
3032       {
3033         StopScrollTimer();
3034         mCurrentHandlePosition = actualHandlePosition;
3035         mScrollDisplacement = Vector2::ZERO;
3036       }
3037       else
3038       {
3039         if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3040         {
3041           mScrollDisplacement.x = -SCROLL_SPEED;
3042         }
3043         else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3044         {
3045           mScrollDisplacement.x = SCROLL_SPEED;
3046         }
3047         if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3048         {
3049           mScrollDisplacement.y = -SCROLL_SPEED;
3050         }
3051         else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3052         {
3053           mScrollDisplacement.y = SCROLL_SPEED;
3054         }
3055         StartScrollTimer();
3056       }
3057     }
3058
3059     if( handleVisible &&                           // Only redraw cursor and do updates if position changed
3060         ( newCursorPosition != mCursorPosition ) ) // and the new position is visible (if scroll is not enabled, it's always true).
3061     {
3062       mCursorPosition = newCursorPosition;
3063
3064       mGrabHandle.SetPosition( actualHandlePosition + UI_OFFSET );
3065
3066       const TextStyle oldInputStyle( mInputStyle );
3067
3068       mInputStyle = GetStyleAtCursor(); //Inherit style from cursor position
3069
3070       CursorUpdate();  // Let keyboard know the new cursor position so can 're-capture' for prediction.
3071
3072       if( oldInputStyle != mInputStyle )
3073       {
3074         // Updates the line height accordingly with the input style.
3075         UpdateLineHeight();
3076
3077         EmitStyleChangedSignal();
3078       }
3079     }
3080   }
3081
3082   return actualHandlePosition;
3083 }
3084
3085 void TextInput::ShowGrabHandle( bool visible )
3086 {
3087   if ( IsGrabHandleEnabled() )
3088   {
3089     if( mGrabHandle )
3090     {
3091       mGrabHandle.SetVisible( mGrabHandleVisibility );
3092     }
3093     StartMonitoringStageForTouch();
3094   }
3095 }
3096
3097 void TextInput::ShowGrabHandleAndSetVisibility( bool visible )
3098 {
3099   mGrabHandleVisibility = visible;
3100   ShowGrabHandle( visible );
3101 }
3102
3103 // Callbacks connected to be Property notifications for Boundary checking.
3104
3105 void TextInput::OnLeftBoundaryExceeded(PropertyNotification& source)
3106 {
3107   mIsSelectionHandleOneFlipped = true;
3108   mSelectionHandleOne.SetScale( -1.0f, 1.0f, 1.0f );
3109   mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3110 }
3111
3112 void TextInput::OnReturnToLeftBoundary(PropertyNotification& source)
3113 {
3114   mIsSelectionHandleOneFlipped = false;
3115   mSelectionHandleOne.SetScale( 1.0f, 1.0f, 1.0f );
3116   mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3117 }
3118
3119 void TextInput::OnRightBoundaryExceeded(PropertyNotification& source)
3120 {
3121   mIsSelectionHandleTwoFlipped = true;
3122   mSelectionHandleTwo.SetScale( -1.0f, 1.0f, 1.0f );
3123   mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3124 }
3125
3126 void TextInput::OnReturnToRightBoundary(PropertyNotification& source)
3127 {
3128   mIsSelectionHandleTwoFlipped = false;
3129   mSelectionHandleTwo.SetScale( 1.0f, 1.0f, 1.0f );
3130   mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3131 }
3132
3133 // todo change PropertyNotification signal definition to include Actor. Hence won't need duplicate functions.
3134 void TextInput::OnHandleOneLeavesBoundary( PropertyNotification& source)
3135 {
3136   mSelectionHandleOne.SetOpacity(0.0f);
3137 }
3138
3139 void TextInput::OnHandleOneWithinBoundary(PropertyNotification& source)
3140 {
3141   mSelectionHandleOne.SetOpacity(1.0f);
3142 }
3143
3144 void TextInput::OnHandleTwoLeavesBoundary( PropertyNotification& source)
3145 {
3146   mSelectionHandleTwo.SetOpacity(0.0f);
3147 }
3148
3149 void TextInput::OnHandleTwoWithinBoundary(PropertyNotification& source)
3150 {
3151   mSelectionHandleTwo.SetOpacity(1.0f);
3152 }
3153
3154 // End of Callbacks connected to be Property notifications for Boundary checking.
3155
3156 void TextInput::SetUpHandlePropertyNotifications()
3157 {
3158   /* Property notifications for handles exceeding the boundary and returning back within boundary */
3159
3160   Vector3 handlesize = GetSelectionHandleSize();
3161
3162   // Exceeding horizontal boundary
3163   PropertyNotification leftNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.x + handlesize.x) );
3164   leftNotification.NotifySignal().Connect( this, &TextInput::OnLeftBoundaryExceeded );
3165
3166   PropertyNotification rightNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.z - handlesize.x ) );
3167   rightNotification.NotifySignal().Connect( this, &TextInput::OnRightBoundaryExceeded );
3168
3169   // Within horizontal boundary
3170   PropertyNotification leftLeaveNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.x + 2*handlesize.x ) );
3171   leftLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToLeftBoundary );
3172
3173   PropertyNotification rightLeaveNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.z - 2*handlesize.x ) );
3174   rightLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToRightBoundary );
3175
3176   // Exceeding vertical boundary
3177   PropertyNotification verticalExceedNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3178                                                        OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3179                                                                          mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3180   verticalExceedNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneLeavesBoundary );
3181
3182   PropertyNotification verticalExceedNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3183                                                        OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3184                                                                          mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3185   verticalExceedNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoLeavesBoundary );
3186
3187   // Within vertical boundary
3188   PropertyNotification verticalWithinNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3189                                                        InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3190                                                                         mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3191   verticalWithinNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneWithinBoundary );
3192
3193   PropertyNotification verticalWithinNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3194                                                        InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3195                                                                         mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3196   verticalWithinNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoWithinBoundary );
3197 }
3198
3199 void TextInput::CreateSelectionHandles( std::size_t start, std::size_t end, Dali::Image handleOneImage,  Dali::Image handleTwoImage )
3200 {
3201   mSelectionHandleOnePosition = start;
3202   mSelectionHandleTwoPosition = end;
3203
3204   if ( !mSelectionHandleOne )
3205   {
3206     // create normal and pressed images
3207     mSelectionHandleOneImage = Image::New( DEFAULT_SELECTION_HANDLE_ONE );
3208     mSelectionHandleOneImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_ONE_PRESSED );
3209
3210     mSelectionHandleOne = ImageActor::New( mSelectionHandleOneImage );
3211     mSelectionHandleOne.SetName("SelectionHandleOne");
3212     mSelectionHandleOne.SetParentOrigin( ParentOrigin::TOP_LEFT );
3213     mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
3214     mIsSelectionHandleOneFlipped = false;
3215     mSelectionHandleOne.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text
3216
3217     mHandleOneGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3218     mHandleOneGrabArea.SetName("SelectionHandleOneGrabArea");
3219
3220     mHandleOneGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) );  // grab area to be larger than text actor
3221     mHandleOneGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3222
3223     mTapDetector.Attach( mHandleOneGrabArea );
3224     mPanGestureDetector.Attach( mHandleOneGrabArea );
3225
3226     mHandleOneGrabArea.TouchedSignal().Connect(this,&TextInput::OnHandleOneTouched);
3227
3228     mSelectionHandleOne.Add( mHandleOneGrabArea );
3229     mActiveLayer.Add( mSelectionHandleOne );
3230   }
3231
3232   if ( !mSelectionHandleTwo )
3233   {
3234     // create normal and pressed images
3235     mSelectionHandleTwoImage = Image::New( DEFAULT_SELECTION_HANDLE_TWO );
3236     mSelectionHandleTwoImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_TWO_PRESSED );
3237
3238     mSelectionHandleTwo = ImageActor::New( mSelectionHandleTwoImage );
3239     mSelectionHandleTwo.SetName("SelectionHandleTwo");
3240     mSelectionHandleTwo.SetParentOrigin( ParentOrigin::TOP_LEFT );
3241     mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT );
3242     mIsSelectionHandleTwoFlipped = false;
3243     mSelectionHandleTwo.SetDrawMode(DrawMode::OVERLAY); // ensure grab handle above text
3244
3245     mHandleTwoGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3246     mHandleTwoGrabArea.SetName("SelectionHandleTwoGrabArea");
3247     mHandleTwoGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) );  // grab area to be larger than text actor
3248     mHandleTwoGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3249
3250     mTapDetector.Attach( mHandleTwoGrabArea );
3251     mPanGestureDetector.Attach( mHandleTwoGrabArea );
3252
3253     mHandleTwoGrabArea.TouchedSignal().Connect(this, &TextInput::OnHandleTwoTouched);
3254
3255     mSelectionHandleTwo.Add( mHandleTwoGrabArea );
3256
3257     mActiveLayer.Add( mSelectionHandleTwo );
3258   }
3259
3260   SetUpHandlePropertyNotifications();
3261
3262   // update table as text may have changed.
3263   GetTextLayoutInfo();
3264
3265   mSelectionHandleOneActualPosition = GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition );
3266   mSelectionHandleTwoActualPosition = GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition );
3267
3268   mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
3269   mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
3270
3271   // Calculates and set the visibility if the scroll mode is enabled.
3272   bool isSelectionHandleOneVisible = true;
3273   bool isSelectionHandleTwoVisible = true;
3274   if( IsScrollEnabled() )
3275   {
3276     const Vector3& controlSize( GetControlSize() );
3277     isSelectionHandleOneVisible = IsPositionInsideBoundaries( mSelectionHandleOneActualPosition, Size::ZERO, controlSize );
3278     isSelectionHandleTwoVisible = IsPositionInsideBoundaries( mSelectionHandleTwoActualPosition, Size::ZERO, controlSize );
3279     mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
3280     mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
3281   }
3282
3283   CreateHighlight();  // function will only create highlight if not already created.
3284 }
3285
3286 Vector3 TextInput::MoveSelectionHandle( SelectionHandleId handleId, const Vector2& displacement )
3287 {
3288   Vector3 actualHandlePosition;
3289
3290   if ( mSelectionHandleOne && mSelectionHandleTwo )
3291   {
3292     const Vector3& controlSize = GetControlSize();
3293
3294     Size cursorSize( CURSOR_THICKNESS, 0.f );
3295
3296     // Get a reference of the wanted selection handle (handle one or two).
3297     Vector3& actualSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
3298
3299     // Get a reference for the current position of the handle and a copy of its pair
3300     std::size_t& currentSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3301     const std::size_t pairSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition;
3302
3303     // Get a handle of the selection handle actor
3304     ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3305
3306     // Selection handles should jump to the nearest character
3307     std::size_t newHandlePosition = 0;
3308     ReturnClosestIndex( actualSelectionHandlePosition.GetVectorXY(), newHandlePosition );
3309
3310     actualHandlePosition = GetActualPositionFromCharacterPosition( newHandlePosition );
3311
3312     bool handleVisible = true;
3313
3314     if( IsScrollEnabled() )
3315     {
3316       mCurrentSelectionId = handleId;
3317
3318       cursorSize.height = GetRowRectFromCharacterPosition( GetVisualPosition( newHandlePosition ) ).height;
3319       // Restricts the movement of the grab handle inside the boundaries of the text-input.
3320       handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3321                                                   cursorSize,
3322                                                   controlSize );
3323
3324       if( handleVisible )
3325       {
3326         StopScrollTimer();
3327         mCurrentSelectionHandlePosition = actualHandlePosition;
3328         mScrollDisplacement = Vector2::ZERO;
3329       }
3330       else
3331       {
3332         if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3333         {
3334           mScrollDisplacement.x = -SCROLL_SPEED;
3335         }
3336         else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3337         {
3338           mScrollDisplacement.x = SCROLL_SPEED;
3339         }
3340         if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3341         {
3342           mScrollDisplacement.y = -SCROLL_SPEED;
3343         }
3344         else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3345         {
3346           mScrollDisplacement.y = SCROLL_SPEED;
3347         }
3348         StartScrollTimer();
3349       }
3350     }
3351
3352     if ( handleVisible &&                                          // Ensure the handle is visible.
3353          ( newHandlePosition != pairSelectionHandlePosition ) &&   // Ensure handle one is not the same position as handle two.
3354          ( newHandlePosition != currentSelectionHandlePosition ) ) // Ensure the handle has moved.
3355     {
3356       currentSelectionHandlePosition = newHandlePosition;
3357
3358       Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3359       selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3360
3361       UpdateHighlight();
3362
3363       if ( handleId == HandleOne )
3364       {
3365         const TextStyle oldInputStyle( mInputStyle );
3366
3367         // Set Active Style to that of first character in selection
3368         if( mSelectionHandleOnePosition < mStyledText.size() )
3369         {
3370           mInputStyle = ( mStyledText.at( mSelectionHandleOnePosition ) ).mStyle;
3371         }
3372
3373         if( oldInputStyle != mInputStyle )
3374         {
3375           // Updates the line height accordingly with the input style.
3376           UpdateLineHeight();
3377
3378           EmitStyleChangedSignal();
3379         }
3380       }
3381     }
3382   }
3383
3384   return actualHandlePosition; // Returns Handle position passed in if new value not assigned.
3385 }
3386
3387 void TextInput::SetSelectionHandlePosition(SelectionHandleId handleId)
3388 {
3389
3390   const std::size_t selectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3391   ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3392
3393   if ( selectionHandleActor )
3394   {
3395     const Vector3 actualHandlePosition = GetActualPositionFromCharacterPosition( selectionHandlePosition );
3396     Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3397     selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3398
3399     if( IsScrollEnabled() )
3400     {
3401       const Size cursorSize( CURSOR_THICKNESS,
3402                              GetRowRectFromCharacterPosition( GetVisualPosition( selectionHandlePosition ) ).height );
3403       selectionHandleActor.SetVisible( IsPositionInsideBoundaries( actualHandlePosition,
3404                                                                    cursorSize,
3405                                                                    GetControlSize() ) );
3406     }
3407   }
3408 }
3409
3410 std::size_t TextInput::GetVisualPosition(std::size_t logicalPosition) const
3411 {
3412   // Note: we're allowing caller to request a logical position of size (i.e. end of string)
3413   // For now the visual position of end of logical string will be end of visual string.
3414   DALI_ASSERT_DEBUG( logicalPosition <= mTextLayoutInfo.mCharacterLogicalToVisualMap.size() );
3415
3416   return logicalPosition != mTextLayoutInfo.mCharacterLogicalToVisualMap.size() ? mTextLayoutInfo.mCharacterLogicalToVisualMap[logicalPosition] : mTextLayoutInfo.mCharacterLogicalToVisualMap.size();
3417 }
3418
3419 void TextInput::GetVisualTextSelection(std::vector<bool>& selectedVisualText, std::size_t startSelection, std::size_t endSelection)
3420 {
3421   std::vector<int>::iterator it = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin();
3422   std::vector<int>::iterator startSelectionIt = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::min(startSelection, endSelection);
3423   std::vector<int>::iterator endSelectionIt = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::max(startSelection, endSelection);
3424   std::vector<int>::iterator end = mTextLayoutInfo.mCharacterLogicalToVisualMap.end();
3425
3426   selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size() );
3427
3428   // Deselect text prior to startSelectionIt
3429   for(;it!=startSelectionIt;++it)
3430   {
3431     selectedVisualText[*it] = false;
3432   }
3433
3434   // Select text from startSelectionIt -> endSelectionIt
3435   for(;it!=endSelectionIt;++it)
3436   {
3437     selectedVisualText[*it] = true;
3438   }
3439
3440   // Deselect text after endSelection
3441   for(;it!=end;++it)
3442   {
3443     selectedVisualText[*it] = false;
3444   }
3445
3446   selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size(), false );
3447 }
3448
3449 // Calculate the dimensions of the quads they will make the highlight mesh
3450 TextInput::HighlightInfo TextInput::CalculateHighlightInfo()
3451 {
3452   // At the moment there is no public API to modify the block alignment option.
3453   const bool blockAlignEnabled = true;
3454
3455   mNewHighlightInfo.mQuadList.clear(); // clear last quad information.
3456
3457   if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
3458   {
3459     Toolkit::TextView::CharacterLayoutInfoContainer::iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3460     Toolkit::TextView::CharacterLayoutInfoContainer::iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
3461
3462     // Get vector of flags representing characters that are selected (true) vs unselected (false).
3463     std::vector<bool> selectedVisualText;
3464     GetVisualTextSelection(selectedVisualText, mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
3465     std::vector<bool>::iterator selectedIt(selectedVisualText.begin());
3466     std::vector<bool>::iterator selectedEndIt(selectedVisualText.end());
3467
3468     SelectionState selectionState = SelectionNone;          ///< Current selection status of cursor over entire text.
3469     float rowLeft = 0.0f;
3470     float rowRight = 0.0f;
3471     // Keep track of the TextView's min/max extents. Should be able to query this from TextView.
3472     float maxRowLeft = std::numeric_limits<float>::max();
3473     float maxRowRight = 0.0f;
3474
3475     Toolkit::TextView::CharacterLayoutInfoContainer::iterator lastIt = it;
3476
3477     // Scan through entire text.
3478     while(it != end)
3479     {
3480       // selectionState: None when not in selection, Started when in selection, and Ended when reached end of selection.
3481
3482       Toolkit::TextView::CharacterLayoutInfo& charInfo(*it);
3483       bool charSelected( false );
3484       if( selectedIt != selectedEndIt )
3485       {
3486         charSelected = *selectedIt++;
3487       }
3488
3489       if(selectionState == SelectionNone)
3490       {
3491         if(charSelected)
3492         {
3493           selectionState = SelectionStarted;
3494           rowLeft = charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3495           rowRight = rowLeft + charInfo.mSize.width;
3496         }
3497       }
3498       else if(selectionState == SelectionStarted)
3499       {
3500         // break selection on:
3501         // 1. new line causing selection break. (\n or wordwrap)
3502         // 2. character not selected.
3503         if(charInfo.mPosition.y - lastIt->mPosition.y > CHARACTER_THRESHOLD ||
3504            !charSelected)
3505         {
3506           // finished selection.
3507           // TODO: TextView should have a table of visual rows, and each character a reference to the row
3508           // that it resides on. That way this enumeration is not necessary.
3509           Vector2 min, max;
3510           if(lastIt->mIsNewLineChar)
3511           {
3512             // If the last character is a new line, then to get the row rect, we need to scan from the character before the new line.
3513             lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3514           }
3515           const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3516           maxRowLeft = std::min(maxRowLeft, min.x);
3517           maxRowRight = std::max(maxRowRight, max.x);
3518           float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3519           float rowTop = rowBottom - rowSize.height;
3520
3521           // Still selected, and block-align mode then set rowRight to max, so it can be clamped afterwards
3522           if(charSelected && blockAlignEnabled)
3523           {
3524             rowRight = std::numeric_limits<float>::max();
3525           }
3526           mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3527
3528           selectionState = SelectionNone;
3529
3530           // Still selected? start a new selection
3531           if( charSelected )
3532           {
3533             // if block-align mode then set rowLeft to min, so it can be clamped afterwards
3534             rowLeft = blockAlignEnabled ? 0.0f : charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3535             rowRight = ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width;
3536             selectionState = SelectionStarted;
3537           }
3538         }
3539         else
3540         {
3541           // build up highlight(s) with this selection data.
3542           rowLeft = std::min( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x, rowLeft );
3543           rowRight = std::max( ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width, rowRight );
3544         }
3545       }
3546
3547       lastIt = it++;
3548     }
3549
3550     // If reached end, and still on selection, then close selection.
3551     if(it == end)
3552     {
3553       if(selectionState == SelectionStarted)
3554       {
3555         // finished selection.
3556         Vector2 min, max;
3557         if(lastIt->mIsNewLineChar)
3558         {
3559           lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3560         }
3561         const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3562         maxRowLeft = std::min(maxRowLeft, min.x);
3563         maxRowRight = std::max(maxRowRight, max.x);
3564         float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3565         float rowTop = rowBottom - rowSize.height;
3566         mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3567       }
3568     }
3569
3570     // Get the top left and bottom right corners.
3571     const Toolkit::TextView::CharacterLayoutInfo& firstCharacter( *mTextLayoutInfo.mCharacterLayoutInfoTable.begin() );
3572     const Vector2 topLeft( maxRowLeft, firstCharacter.mPosition.y - firstCharacter.mSize.height );
3573     const Vector2 bottomRight( topLeft.x + mTextLayoutInfo.mTextSize.width, topLeft.y + mTextLayoutInfo.mTextSize.height );
3574
3575     // Clamp quads so they appear to clip to borders of the whole text.
3576     mNewHighlightInfo.Clamp2D( topLeft, bottomRight );
3577
3578     // For block-align align Further Clamp quads to max left and right extents
3579     if(blockAlignEnabled)
3580     {
3581       // BlockAlign: Will adjust highlight to block:
3582       // i.e.
3583       //   H[ello] (top row right = max of all rows right)
3584       // [--this-] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3585       // [is some] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3586       // [text] (bottom row left = min of all rows left)
3587       // (common in SMS messaging selection)
3588       //
3589       // As opposed to the default which is tight text highlighting.
3590       //   H[ello]
3591       //   [this]
3592       // [is some]
3593       // [text]
3594       // (common in regular text editors/web browser selection)
3595
3596       mNewHighlightInfo.Clamp2D( Vector2(maxRowLeft, topLeft.y), Vector2(maxRowRight, bottomRight.y ) );
3597     }
3598
3599     // Finally clamp quads again so they don't exceed the boundry of the control.
3600     const Vector3& controlSize = GetControlSize();
3601     mNewHighlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
3602   } // end if
3603
3604   return mNewHighlightInfo;
3605 }
3606
3607 void TextInput::UpdateHighlight()
3608 {
3609 //  Construct a Mesh with a texture to be used as the highlight 'box' for selected text
3610 //
3611 //  Example scenarios where mesh is made from 3, 1, 2, 2 ,3 or 3 quads.
3612 //
3613 //   [ TOP   ]  [ TOP ]      [TOP ]  [ TOP    ]      [ TOP  ]      [ TOP  ]
3614 //  [ MIDDLE ]             [BOTTOM]  [BOTTOM]      [ MIDDLE ]   [ MIDDLE  ]
3615 //  [ BOTTOM]                                      [ MIDDLE ]   [ MIDDLE  ]
3616 //                                                 [BOTTOM]     [ MIDDLE  ]
3617 //                                                              [BOTTOM]
3618 //
3619 //  Each quad is created as 2 triangles.
3620 //  Middle is just 1 quad regardless of its size.
3621 //
3622 //  (0,0)         (0,0)
3623 //     0*    *2     0*       *2
3624 //     TOP          TOP
3625 //     3*    *1     3*       *1
3626 //  4*       *1     4*     *6
3627 //     MIDDLE         BOTTOM
3628 //  6*       *5     7*     *5
3629 //  6*    *8
3630 //   BOTTOM
3631 //  9*    *7
3632 //
3633
3634   if ( mHighlightMeshActor )
3635   {
3636     // vertex and triangle buffers should always be present if MeshActor is alive.
3637     HighlightInfo newHighlightInfo = CalculateHighlightInfo();
3638     MeshData::VertexContainer vertices;
3639     Dali::MeshData::FaceIndices faceIndices;
3640
3641     if( !newHighlightInfo.mQuadList.empty() )
3642     {
3643       std::vector<QuadCoordinates>::iterator iter = newHighlightInfo.mQuadList.begin();
3644       std::vector<QuadCoordinates>::iterator endIter = newHighlightInfo.mQuadList.end();
3645
3646       // vertex position defaults to (0 0 0)
3647       MeshData::Vertex vertex;
3648       // set normal for all vertices as (0 0 1) pointing outward from TextInput Actor.
3649       vertex.nZ = 1.0f;
3650
3651       for(std::size_t v = 0; iter != endIter; ++iter,v+=4 )
3652       {
3653         // Add each quad geometry (a sub-selection) to the mesh data.
3654
3655         // 0-----1
3656         // |\    |
3657         // | \ A |
3658         // |  \  |
3659         // | B \ |
3660         // |    \|
3661         // 2-----3
3662
3663         QuadCoordinates& quad = *iter;
3664         // top-left (v+0)
3665         vertex.x = quad.min.x;
3666         vertex.y = quad.min.y;
3667         vertices.push_back( vertex );
3668
3669         // top-right (v+1)
3670         vertex.x = quad.max.x;
3671         vertex.y = quad.min.y;
3672         vertices.push_back( vertex );
3673
3674         // bottom-left (v+2)
3675         vertex.x = quad.min.x;
3676         vertex.y = quad.max.y;
3677         vertices.push_back( vertex );
3678
3679         // bottom-right (v+3)
3680         vertex.x = quad.max.x;
3681         vertex.y = quad.max.y;
3682         vertices.push_back( vertex );
3683
3684         // triangle A (3, 1, 0)
3685         faceIndices.push_back( v + 3 );
3686         faceIndices.push_back( v + 1 );
3687         faceIndices.push_back( v );
3688
3689         // triangle B (0, 2, 3)
3690         faceIndices.push_back( v );
3691         faceIndices.push_back( v + 2 );
3692         faceIndices.push_back( v + 3 );
3693
3694         mMeshData.SetFaceIndices( faceIndices );
3695       }
3696
3697       BoneContainer bones(0); // passed empty as bones not required
3698       mMeshData.SetData( vertices, faceIndices, bones, mCustomMaterial );
3699       mHighlightMesh.UpdateMeshData(mMeshData);
3700     }
3701   }
3702 }
3703
3704 void TextInput::ClearPopup()
3705 {
3706   mPopupPanel.Clear();
3707 }
3708
3709 void TextInput::AddPopupOptions()
3710 {
3711   mPopupPanel.AddPopupOptions();
3712 }
3713
3714 void TextInput::SetPopupPosition( const Vector3& position, const Vector2& alternativePosition )
3715 {
3716   const Vector3& visiblePopUpSize = mPopupPanel.GetVisibileSize();
3717
3718   Vector3 clampedPosition ( position );
3719   Vector3 tailOffsetPosition ( position );
3720
3721   float xOffSet( 0.0f );
3722
3723   Actor self = Self();
3724   const Vector3 textViewTopLeftWorldPosition = self.GetCurrentWorldPosition() - self.GetCurrentSize()*0.5f;
3725
3726   const float popUpLeft = textViewTopLeftWorldPosition.x + position.x - visiblePopUpSize.width*0.5f;
3727   const float popUpTop = textViewTopLeftWorldPosition.y + position.y - visiblePopUpSize.height;
3728
3729   // Clamp to left or right or of boundary
3730   if( popUpLeft < mBoundingRectangleWorldCoordinates.x )
3731   {
3732     xOffSet = mBoundingRectangleWorldCoordinates.x - popUpLeft ;
3733   }
3734   else if ( popUpLeft + visiblePopUpSize.width > mBoundingRectangleWorldCoordinates.z )
3735   {
3736     xOffSet = mBoundingRectangleWorldCoordinates.z - ( popUpLeft + visiblePopUpSize.width );
3737   }
3738
3739   clampedPosition.x = position.x + xOffSet;
3740   tailOffsetPosition.x = -xOffSet;
3741
3742   // Check if top left of PopUp outside of top bounding rectangle, if so then flip to lower position.
3743   bool flipTail( false );
3744
3745   if ( popUpTop < mBoundingRectangleWorldCoordinates.y )
3746   {
3747     clampedPosition.y = alternativePosition.y + visiblePopUpSize.height;
3748     flipTail = true;
3749   }
3750
3751   mPopupPanel.GetRootActor().SetPosition( clampedPosition );
3752   mPopupPanel.SetTailPosition( tailOffsetPosition, flipTail );
3753 }
3754
3755 void TextInput::HidePopup(bool animate, bool signalFinished )
3756 {
3757   if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown )  )
3758   {
3759     mPopupPanel.Hide( animate );
3760
3761     if( animate && signalFinished )
3762     {
3763       mPopupPanel.HideFinishedSignal().Connect( this, &TextInput::OnPopupHideFinished );
3764     }
3765   }
3766 }
3767
3768 void TextInput::ShowPopup( bool animate )
3769 {
3770   Vector3 position;
3771   Vector2 alternativePopupPosition;
3772
3773   if(mHighlightMeshActor && mState == StateEdit)
3774   {
3775     Vector3 topHandle;
3776     Vector3 bottomHandle; // referring to the bottom most point of the handle or the bottom line of selection.
3777     Size rowSize;
3778     // When text is selected, show popup above top handle (and text), or below bottom handle.
3779     // topHandle: referring to the top most point of the handle or the top line of selection.
3780     if ( mSelectionHandleTwoActualPosition.y > mSelectionHandleOneActualPosition.y )
3781     {
3782       topHandle = mSelectionHandleOneActualPosition;
3783       bottomHandle = mSelectionHandleTwoActualPosition;
3784       rowSize= GetRowRectFromCharacterPosition( mSelectionHandleOnePosition );
3785     }
3786     else
3787     {
3788       topHandle = mSelectionHandleTwoActualPosition;
3789       bottomHandle = mSelectionHandleOneActualPosition;
3790       rowSize = GetRowRectFromCharacterPosition( mSelectionHandleTwoPosition );
3791     }
3792     topHandle.y += -mPopupOffsetFromText.y - rowSize.height;
3793     position = Vector3(topHandle.x, topHandle.y, 0.0f);
3794
3795     float xPosition = ( fabsf( topHandle.x - bottomHandle.x ) )*0.5f + std::min( mSelectionHandleOneActualPosition.x , mSelectionHandleTwoActualPosition.x );
3796
3797     position.x = xPosition;
3798
3799     // Alternative position if no upper space
3800     bottomHandle.y += GetSelectionHandleSize().y + mPopupOffsetFromText.w;
3801     alternativePopupPosition = Vector2 ( position.x, bottomHandle.y );
3802   }
3803   else
3804   {
3805     // When no text is selected, show popup at world position of grab handle or cursor
3806     position = GetActualPositionFromCharacterPosition( mCursorPosition );
3807     const Size rowSize = GetRowRectFromCharacterPosition( mCursorPosition );
3808     position.y -= ( mPopupOffsetFromText.y + rowSize.height );
3809     // if can't be positioned above, then position below row.
3810     alternativePopupPosition = Vector2( position.x, position.y ); // default if no grab handle
3811     if ( mGrabHandle )
3812     {
3813       // If grab handle enabled then position pop-up below the grab handle.
3814       alternativePopupPosition.y = rowSize.height + mGrabHandle.GetCurrentSize().height + mPopupOffsetFromText.w +50.0f;
3815     }
3816   }
3817
3818   SetPopupPosition( position, alternativePopupPosition );
3819
3820   // Show popup
3821   mPopupPanel.Show( Self(), animate );
3822   StartMonitoringStageForTouch();
3823
3824   mPopupPanel.PressedSignal().Connect( this, &TextInput::OnPopupButtonPressed );
3825 }
3826
3827 void TextInput::ShowPopupCutCopyPaste()
3828 {
3829   ClearPopup();
3830
3831   mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3832   // Check the selected text is whole text or not.
3833   if( IsTextSelected() && ( mStyledText.size() != GetSelectedText().size() ) )
3834   {
3835     mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3836   }
3837
3838   if ( !mStyledText.empty() )
3839   {
3840     mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCopy, true );
3841     mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, true );
3842   }
3843
3844   if( mClipboard && mClipboard.NumberOfItems() )
3845   {
3846     mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
3847     mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
3848   }
3849
3850   AddPopupOptions();
3851
3852   mPopupPanel.Hide(false);
3853   ShowPopup();
3854 }
3855
3856 void TextInput::SetUpPopupSelection()
3857 {
3858   ClearPopup();
3859   mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3860   // If no text exists then don't offer to select
3861   if ( !mStyledText.empty() )
3862   {
3863     mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3864     mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelect, true );
3865     mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, true );
3866   }
3867   // if clipboard has valid contents then offer paste option
3868   if( mClipboard && mClipboard.NumberOfItems() )
3869   {
3870     mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
3871     mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
3872   }
3873
3874   AddPopupOptions();
3875
3876   mPopupPanel.Hide(false);
3877 }
3878
3879 bool TextInput::ReturnClosestIndex(const Vector2& source, std::size_t& closestIndex )
3880 {
3881   bool found = false;
3882   closestIndex = 0;
3883
3884   std::vector<Toolkit::TextView::CharacterLayoutInfo> matchedCharacters;
3885   bool lastRightToLeftChar(false);          /// RTL state of previous character encountered (character on the left of touch point)
3886   bool rightToLeftChar(false);              /// RTL state of current character encountered (character on the right of touch point)
3887   float glyphIntersection(0.0f);            /// Glyph intersection, the point between the two nearest characters touched.
3888
3889   const Vector2 sourceScrollOffset( source + mTextLayoutInfo.mScrollOffset );
3890
3891   if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
3892   {
3893     float closestYdifference = std::numeric_limits<float>::max();
3894     std::size_t lineOffset = 0;                  /// Keep track of position of the first character on the matched line of interest.
3895     std::size_t numberOfMatchedCharacters = 0;
3896
3897     // 1. Find closest character line to y part of source, create vector of all entries in that Y position
3898     // TODO: There should be an easy call to enumerate through each visual line, instead of each character on all visual lines.
3899
3900     for( std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), endIt = mTextLayoutInfo.mCharacterLayoutInfoTable.end(); it != endIt; ++it )
3901     {
3902       const Toolkit::TextView::CharacterLayoutInfo& info( *it );
3903       float baselinePosition = info.mPosition.y - info.mDescender;
3904
3905       if( info.mIsVisible )
3906       {
3907         // store difference between source y point and the y position of the current character
3908         float currentYdifference = fabsf( sourceScrollOffset.y - ( baselinePosition ) );
3909
3910         if(  currentYdifference < closestYdifference  )
3911         {
3912           // closest so far; store this difference and clear previous matchedCharacters as no longer closest
3913           lineOffset = it - mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3914           closestYdifference = currentYdifference;
3915           matchedCharacters.clear();
3916           numberOfMatchedCharacters = 0; // reset count
3917         }
3918
3919         // add all characters that are on the same Y axis (within the CHARACTER_THRESHOLD) to the matched array.
3920         if( fabsf( closestYdifference - currentYdifference )  < CHARACTER_THRESHOLD )
3921         {
3922           // ignore new line character.
3923           if( !info.mIsNewLineChar )
3924           {
3925             matchedCharacters.push_back( info );
3926             numberOfMatchedCharacters++;
3927           }
3928         }
3929       }
3930     } // End of loop checking each character's y position in the character layout table
3931
3932     // Check if last character is a newline, if it is
3933     // then need pretend there is an imaginary line afterwards,
3934     // and check if user is touching below previous line.
3935     const Toolkit::TextView::CharacterLayoutInfo& lastInfo( mTextLayoutInfo.mCharacterLayoutInfoTable[mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1] );
3936
3937     if( ( lastInfo.mIsVisible ) && ( lastInfo.mIsNewLineChar ) && ( sourceScrollOffset.y > lastInfo.mPosition.y ) )
3938     {
3939       closestIndex = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
3940     }
3941     else
3942     {
3943       std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator it = matchedCharacters.begin();
3944       std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator endIt = matchedCharacters.end();
3945
3946       bool matched( false );
3947
3948       // 2 Iterate through matching list of y positions and find closest matching X position.
3949       for( ; it != endIt; ++it )
3950       {
3951         const Toolkit::TextView::CharacterLayoutInfo& info( *it );
3952
3953         if( info.mIsVisible )
3954         {
3955           // stop when on left side of character's center.
3956           const float characterMidPointPosition = info.mPosition.x  + ( info.mSize.width * 0.5f ) ;
3957           if( sourceScrollOffset.x < characterMidPointPosition )
3958           {
3959             if(info.mIsRightToLeftCharacter)
3960             {
3961               rightToLeftChar = true;
3962             }
3963             glyphIntersection = info.mPosition.x;
3964             matched = true;
3965             break;
3966           }
3967
3968           lastRightToLeftChar = info.mIsRightToLeftCharacter;
3969         }
3970       }
3971
3972       if( it == endIt )
3973       {
3974         rightToLeftChar = lastRightToLeftChar;
3975       }
3976
3977       std::size_t matchCharacterIndex = it - matchedCharacters.begin();
3978       closestIndex = lineOffset + matchCharacterIndex;
3979
3980       mClosestCursorPositionEOL = false; // reset
3981       if ( it == endIt && !matched )
3982       {
3983         mClosestCursorPositionEOL = true; // Reached end of matched characters in closest line but no match so cursor should be after last character.
3984       }
3985
3986       // For RTL characters, need to adjust closestIndex by 1 (as the inequality above would be reverse)
3987       if( rightToLeftChar && lastRightToLeftChar )
3988       {
3989         --closestIndex; // (-1 = numeric_limits<std::size_t>::max())
3990       }
3991     }
3992   }
3993
3994   // closestIndex is the visual index, need to convert it to the logical index
3995   if( !mTextLayoutInfo.mCharacterVisualToLogicalMap.empty() )
3996   {
3997     if( closestIndex < mTextLayoutInfo.mCharacterVisualToLogicalMap.size() )
3998     {
3999       // Checks for situations where user is touching between LTR and RTL
4000       // characters. To identify if the user means the end of a LTR string
4001       // or the beginning of an RTL string, and vice versa.
4002       if( closestIndex > 0 )
4003       {
4004         if( rightToLeftChar && !lastRightToLeftChar )
4005         {
4006           // [LTR] [RTL]
4007           //   |..|..|
4008           //   AAA BBB
4009           // A: In this touch range, the user is indicating that they wish to place
4010           // the cursor at the end of the LTR text.
4011           // B: In this touch range, the user is indicating that they wish to place
4012           // the cursor at the end of the RTL text.
4013
4014           // Result of touching A area:
4015           // [.....LTR]|[RTL......]+
4016           //
4017           // |: primary cursor (for typing LTR chars)
4018           // +: secondary cursor (for typing RTL chars)
4019
4020           // Result of touching B area:
4021           // [.....LTR]+[RTL......]|
4022           //
4023           // |: primary cursor (for typing RTL chars)
4024           // +: secondary cursor (for typing LTR chars)
4025
4026           if( sourceScrollOffset.x < glyphIntersection )
4027           {
4028             --closestIndex;
4029           }
4030         }
4031         else if( !rightToLeftChar && lastRightToLeftChar )
4032         {
4033           if( sourceScrollOffset.x < glyphIntersection )
4034           {
4035             --closestIndex;
4036           }
4037         }
4038       }
4039
4040       closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap[closestIndex];
4041       // If user touched a left-side of RTL char, and the character on the left was an LTR then position logical cursor
4042       // one further ahead
4043       if( rightToLeftChar && !lastRightToLeftChar )
4044       {
4045         ++closestIndex;
4046       }
4047     }
4048     else if( closestIndex == numeric_limits<std::size_t>::max() ) // -1 RTL (after last arabic character on line)
4049     {
4050       closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap.size();
4051     }
4052     else if( mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterVisualToLogicalMap[ closestIndex - 1 ] ].mIsRightToLeftCharacter ) // size() LTR (after last european character on line)
4053     {
4054       closestIndex = 0;
4055     }
4056   }
4057
4058   return found;
4059 }
4060
4061 float TextInput::GetLineJustificationPosition() const
4062 {
4063   const Vector3& size = mDisplayedTextView.GetCurrentSize();
4064   Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4065   float alignmentOffset = 0.f;
4066
4067   // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4068   if( alignment & Toolkit::Alignment::HorizontalLeft )
4069   {
4070     alignmentOffset = 0.f;
4071   }
4072   else if( alignment & Toolkit::Alignment::HorizontalCenter )
4073   {
4074     alignmentOffset = 0.5f * ( size.width - mTextLayoutInfo.mTextSize.width );
4075   }
4076   else if( alignment & Toolkit::Alignment::HorizontalRight )
4077   {
4078     alignmentOffset = size.width - mTextLayoutInfo.mTextSize.width;
4079   }
4080
4081   Toolkit::TextView::LineJustification justification = mDisplayedTextView.GetLineJustification();
4082   float justificationOffset = 0.f;
4083
4084   switch( justification )
4085   {
4086     case Toolkit::TextView::Left:
4087     {
4088       justificationOffset = 0.f;
4089       break;
4090     }
4091     case Toolkit::TextView::Center:
4092     {
4093       justificationOffset = 0.5f * mTextLayoutInfo.mTextSize.width;
4094       break;
4095     }
4096     case Toolkit::TextView::Right:
4097     {
4098       justificationOffset = mTextLayoutInfo.mTextSize.width;
4099       break;
4100     }
4101     case Toolkit::TextView::Justified:
4102     {
4103       justificationOffset = 0.f;
4104       break;
4105     }
4106     default:
4107     {
4108       DALI_ASSERT_ALWAYS( false );
4109     }
4110   } // end switch
4111
4112   return alignmentOffset + justificationOffset;
4113 }
4114
4115 Vector3 TextInput::PositionCursorAfterWordWrap( std::size_t characterPosition ) const
4116 {
4117   /* Word wrap occurs automatically in TextView when the exceed policy moves a word to the next line when not enough space on current.
4118      A newline character is not inserted in this case */
4119
4120   DALI_ASSERT_DEBUG( !(characterPosition <= 0 ));
4121
4122   Vector3 cursorPosition;
4123
4124   Toolkit::TextView::CharacterLayoutInfo currentCharInfo;
4125
4126   if ( characterPosition == mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4127   {
4128     // end character so use
4129     currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1 ];
4130     cursorPosition = Vector3(currentCharInfo.mPosition.x + currentCharInfo.mSize.width, currentCharInfo.mPosition.y, currentCharInfo.mPosition.z) ;
4131   }
4132   else
4133   {
4134     currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4135   }
4136
4137   Toolkit::TextView::CharacterLayoutInfo previousCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1];
4138
4139   // If previous character on a different line then use current characters position
4140   if ( fabsf( (currentCharInfo.mPosition.y - currentCharInfo.mDescender )  - ( previousCharInfo.mPosition.y - previousCharInfo.mDescender) ) > Math::MACHINE_EPSILON_1000 )
4141   {
4142     if ( mClosestCursorPositionEOL )
4143     {
4144       cursorPosition = Vector3(previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z) ;
4145     }
4146     else
4147     {
4148       cursorPosition = Vector3(currentCharInfo.mPosition);
4149     }
4150   }
4151   else
4152   {
4153     // Previous character is on same line so use position of previous character plus it's width.
4154     cursorPosition = Vector3(previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z) ;
4155   }
4156
4157   return cursorPosition;
4158 }
4159
4160 Vector3 TextInput::GetActualPositionFromCharacterPosition(std::size_t characterPosition) const
4161 {
4162   bool direction(false);
4163   Vector3 alternatePosition;
4164   bool alternatePositionValid(false);
4165
4166   return GetActualPositionFromCharacterPosition( characterPosition, direction, alternatePosition, alternatePositionValid );
4167 }
4168
4169 Vector3 TextInput::GetActualPositionFromCharacterPosition(std::size_t characterPosition, bool& directionRTL, Vector3& alternatePosition, bool& alternatePositionValid ) const
4170 {
4171   Vector3 cursorPosition( 0.f, 0.f, 0.f );
4172
4173   alternatePositionValid = false;
4174   directionRTL = false;
4175
4176   if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
4177   {
4178     std::size_t visualCharacterPosition;
4179
4180     // When cursor is not at beginning, consider possibility of
4181     // showing 2 cursors. (whereas at beginning we only ever show one cursor)
4182     if(characterPosition > 0)
4183     {
4184       // Cursor position should be the end of the last character.
4185       // If the last character is LTR, then the end is on the right side of the glyph.
4186       // If the last character is RTL, then the end is on the left side of the glyph.
4187       visualCharacterPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition - 1 ];
4188
4189       if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + visualCharacterPosition ) ).mIsVisible )
4190       {
4191         visualCharacterPosition = FindVisibleCharacter( Left, visualCharacterPosition );
4192       }
4193
4194       Toolkit::TextView::CharacterLayoutInfo info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4195       if( ( visualCharacterPosition > 0 ) && info.mIsNewLineChar && !IsScrollEnabled() )
4196       {
4197         // Prevents the cursor to exceed the boundary if the last visible character is a 'new line character' and the scroll is not enabled.
4198         const Vector3& size = GetControlSize();
4199
4200         if( info.mPosition.y + info.mSize.height - mDisplayedTextView.GetLineHeightOffset() > size.height )
4201         {
4202           --visualCharacterPosition;
4203         }
4204         info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4205       }
4206
4207       if(!info.mIsNewLineChar)
4208       {
4209         cursorPosition = PositionCursorAfterWordWrap( characterPosition ); // Get position of cursor/handles taking in account auto word wrap.
4210       }
4211       else
4212       {
4213         // When cursor points to first character on new line, position cursor at the start of this glyph.
4214         if(characterPosition < mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4215         {
4216           std::size_t visualCharacterNextPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition ];
4217           const Toolkit::TextView::CharacterLayoutInfo& infoNext = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterNextPosition ];
4218           const float start( infoNext.mIsRightToLeftCharacter ? infoNext.mSize.width : 0.0f );
4219
4220           cursorPosition.x = infoNext.mPosition.x + start;
4221           cursorPosition.y = infoNext.mPosition.y;
4222         }
4223         else
4224         {
4225           // If cursor points to the end of text, then can only position
4226           // cursor where the new line starts based on the line-justification position.
4227           cursorPosition.x = GetLineJustificationPosition();
4228
4229           if(characterPosition == mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4230           {
4231             // If this is after the last character, then we can assume that the new cursor
4232             // should be exactly one row below the current row.
4233
4234             const Size rowRect(GetRowRectFromCharacterPosition(characterPosition - 1));
4235             cursorPosition.y = info.mPosition.y + rowRect.height;
4236           }
4237           else
4238           {
4239             // If this is not after last character, then we can use this row's height.
4240             // should be exactly one row below the current row.
4241
4242             const Size rowRect(GetRowRectFromCharacterPosition(characterPosition));
4243             cursorPosition.y = info.mPosition.y + rowRect.height;
4244           }
4245         }
4246       }
4247
4248       directionRTL = info.mIsRightToLeftCharacter;
4249
4250       // 1. When the cursor is neither at the beginning or the end,
4251       // we can show multiple cursors under situations when the cursor is
4252       // between RTL and LTR text...
4253       if(characterPosition != mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4254       {
4255         std::size_t visualCharacterAltPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[characterPosition] - 1;
4256
4257         DALI_ASSERT_ALWAYS(visualCharacterAltPosition < mTextLayoutInfo.mCharacterLayoutInfoTable.size());
4258         const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterAltPosition ];
4259
4260         if(!info.mIsRightToLeftCharacter && infoAlt.mIsRightToLeftCharacter)
4261         {
4262           // Stuation occurs when cursor is at the end of English text (LTR) and beginning of Arabic (RTL)
4263           // Text:     [...LTR...]|[...RTL...]
4264           // Cursor pos:          ^
4265           // Alternate cursor pos:            ^
4266           // In which case we need to display an alternate cursor for the RTL text.
4267
4268           alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width;
4269           alternatePosition.y = infoAlt.mPosition.y;
4270           alternatePositionValid = true;
4271         }
4272         else if(info.mIsRightToLeftCharacter && !infoAlt.mIsRightToLeftCharacter)
4273         {
4274           // Situation occurs when cursor is at end of the Arabic text (LTR) and beginning of English (RTL)
4275           // Text:           |[...RTL...] [...LTR....]
4276           // Cursor pos:     ^
4277           // Alternate cursor pos:       ^
4278           // In which case we need to display an alternate cursor for the RTL text.
4279
4280           alternatePosition.x = infoAlt.mPosition.x;
4281           alternatePosition.y = infoAlt.mPosition.y;
4282           alternatePositionValid = true;
4283         }
4284       }
4285       else
4286       {
4287         // 2. When the cursor is at the end of the text,
4288         // and we have multi-directional text,
4289         // we can also consider showing mulitple cursors.
4290         // The rule here is:
4291         // If first and last characters on row are different
4292         // Directions, then two cursors need to be displayed.
4293
4294         // Get first logical glyph on row
4295         std::size_t startCharacterPosition = GetRowStartFromCharacterPosition( characterPosition - 1 );
4296
4297         std::size_t visualCharacterStartPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ startCharacterPosition ];
4298         const Toolkit::TextView::CharacterLayoutInfo& infoStart= mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterStartPosition ];
4299
4300         if(info.mIsRightToLeftCharacter && !infoStart.mIsRightToLeftCharacter)
4301         {
4302           // For text Starting as LTR and ending as RTL. End cursor position is as follows:
4303           // Text:     [...LTR...]|[...RTL...]
4304           // Cursor pos:          ^
4305           // Alternate cursor pos:            ^
4306           // In which case we need to display an alternate cursor for the RTL text, this cursor
4307           // should be at the end of the given line.
4308
4309           const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1 ];
4310           alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width;
4311           alternatePosition.y = infoAlt.mPosition.y;
4312           alternatePositionValid = true;
4313         }
4314         else if(!info.mIsRightToLeftCharacter && infoStart.mIsRightToLeftCharacter) // starting RTL
4315         {
4316           // For text Starting as RTL and ending as LTR. End cursor position is as follows:
4317           // Text:           |[...RTL...] [...LTR....]
4318           // Cursor pos:     ^
4319           // Alternate cursor pos:       ^
4320           // In which case we need to display an alternate cursor for the RTL text.
4321
4322           const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ startCharacterPosition ];
4323           alternatePosition.x = infoAlt.mPosition.x;
4324           alternatePosition.y = infoAlt.mPosition.y;
4325           alternatePositionValid = true;
4326         }
4327       }
4328     } // characterPosition > 0
4329     else if(characterPosition == 0)
4330     {
4331       // When the cursor position is at the beginning, it should be at the start of the current character.
4332       // If the current character is LTR, then the start is on the right side of the glyph.
4333       // If the current character is RTL, then the start is on the left side of the glyph.
4334       visualCharacterPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition ];
4335
4336       if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + visualCharacterPosition ) ).mIsVisible )
4337       {
4338          visualCharacterPosition = FindVisibleCharacter( Right, visualCharacterPosition );
4339       }
4340
4341       const Toolkit::TextView::CharacterLayoutInfo& info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4342       const float start(info.mIsRightToLeftCharacter ? info.mSize.width : 0.0f);
4343
4344       cursorPosition.x = info.mPosition.x + start;
4345       cursorPosition.y = info.mPosition.y;
4346       directionRTL = info.mIsRightToLeftCharacter;
4347     }
4348   }
4349   else
4350   {
4351     // If the character table is void, place the cursor accordingly the text alignment.
4352     const Vector3& size = GetControlSize();
4353
4354     Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4355     float alignmentOffset = 0.f;
4356
4357     // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4358     if( alignment & Toolkit::Alignment::HorizontalLeft )
4359     {
4360       alignmentOffset = 0.f;
4361     }
4362     else if( alignment & Toolkit::Alignment::HorizontalCenter )
4363     {
4364       alignmentOffset = 0.5f * ( size.width );
4365     }
4366     else if( alignment & Toolkit::Alignment::HorizontalRight )
4367     {
4368       alignmentOffset = size.width;
4369     }
4370
4371     // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4372     cursorPosition.x = alignmentOffset;
4373
4374     // Work out cursor 'y' position when there are any character accordingly with the text view alignment settings.
4375     if( alignment & Toolkit::Alignment::VerticalTop )
4376     {
4377       cursorPosition.y = mLineHeight;
4378     }
4379     else if( alignment & Toolkit::Alignment::VerticalCenter )
4380     {
4381       cursorPosition.y = 0.5f * ( size.height + mLineHeight );
4382     }
4383     else if( alignment & Toolkit::Alignment::VerticalBottom )
4384     {
4385       cursorPosition.y = size.height;
4386     }
4387   }
4388
4389   cursorPosition.x -= mTextLayoutInfo.mScrollOffset.x;
4390   cursorPosition.y -= mTextLayoutInfo.mScrollOffset.y;
4391   if( alternatePositionValid )
4392   {
4393     alternatePosition.x -= mTextLayoutInfo.mScrollOffset.x;
4394     alternatePosition.y -= mTextLayoutInfo.mScrollOffset.y;
4395   }
4396
4397   return cursorPosition;
4398 }
4399
4400 std::size_t TextInput::GetRowStartFromCharacterPosition(std::size_t logicalPosition) const
4401 {
4402   // scan string from current position to beginning of current line to note direction of line
4403   while(logicalPosition)
4404   {
4405     logicalPosition--;
4406     std::size_t visualPosition = GetVisualPosition(logicalPosition);
4407     if(mTextLayoutInfo.mCharacterLayoutInfoTable[visualPosition].mIsNewLineChar)
4408     {
4409       logicalPosition++;
4410       break;
4411     }
4412   }
4413
4414   return logicalPosition;
4415 }
4416
4417 Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition) const
4418 {
4419   Vector2 min, max;
4420
4421   return GetRowRectFromCharacterPosition( characterPosition, min, max );
4422 }
4423
4424 Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition, Vector2& min, Vector2& max) const
4425 {
4426   // if we have no text content, then return position 0,0 with width 0, and height the same as cursor height.
4427   if( mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4428   {
4429     min = Vector2::ZERO;
4430     max = Vector2(0.0f, mLineHeight);
4431     return max;
4432   }
4433
4434   // TODO: This info should be readily available from text-view, we should not have to search hard for it.
4435   Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator begin = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
4436   Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
4437
4438   // If cursor is pointing to end of line, then start from last character.
4439   characterPosition = std::min( characterPosition, static_cast<std::size_t>(mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1) );
4440
4441   Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4442
4443   // 0. Find first a visible character. Draw a cursor beyound text-input bounds is not wanted.
4444   if( !it->mIsVisible )
4445   {
4446     characterPosition = FindVisibleCharacter( Left, characterPosition );
4447     it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4448   }
4449
4450   // Scan characters left and right of cursor, stopping when end of line/string reached or
4451   // y position greater than threshold of reference line.
4452
4453   // 1. scan left until we reach the beginning or a different line.
4454   Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator validCharIt = it;
4455   float referenceLine = it->mPosition.y - CHARACTER_THRESHOLD;
4456   // min-x position is the left-most char's left (x)
4457   // max-x position is the right-most char's right (x)
4458   // min-y position is the minimum of all character's top (y)
4459   // max-y position is the maximum of all character's bottom (y+height)
4460   min.y = validCharIt->mPosition.y;
4461   max.y = validCharIt->mPosition.y + validCharIt->mSize.y;
4462
4463   while(true)
4464   {
4465     validCharIt = it;
4466     min.y = std::min(min.y, validCharIt->mPosition.y);
4467     max.y = std::max(max.y, validCharIt->mPosition.y + validCharIt->mSize.y);
4468
4469     if(it == begin)
4470     {
4471       break;
4472     }
4473
4474     --it;
4475
4476     if( (it->mPosition.y < referenceLine) ||
4477         (it->mIsNewLineChar) ||
4478         (!it->mIsVisible) )
4479     {
4480       break;
4481     }
4482   }
4483
4484   // info refers to the first character on this line.
4485   min.x = validCharIt->mPosition.x;
4486
4487   // 2. scan right until we reach end or a different line
4488   it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4489   referenceLine = it->mPosition.y + CHARACTER_THRESHOLD;
4490
4491   while(it != end)
4492   {
4493     if( (it->mPosition.y > referenceLine) ||
4494         (it->mIsNewLineChar) ||
4495         (!it->mIsVisible) )
4496     {
4497       break;
4498     }
4499
4500     validCharIt = it;
4501     min.y = std::min(min.y, validCharIt->mPosition.y);
4502     max.y = std::max(max.y, validCharIt->mPosition.y + validCharIt->mSize.y);
4503
4504     ++it;
4505   }
4506
4507   DALI_ASSERT_DEBUG ( validCharIt != end  && "validCharIt invalid")
4508
4509   if ( validCharIt != end )
4510   {
4511     // info refers to the last character on this line.
4512     max.x = validCharIt->mPosition.x + validCharIt->mSize.x;
4513   }
4514
4515   return Size( max.x - min.x, max.y - min.y );
4516 }
4517
4518 bool TextInput::WasTouchedCheck( const Actor& touchedActor ) const
4519 {
4520   Actor popUpPanel = mPopupPanel.GetRootActor();
4521
4522   if ( ( touchedActor == Self() ) || ( touchedActor == popUpPanel ) )
4523   {
4524     return true;
4525   }
4526   else
4527   {
4528     Dali::Actor parent( touchedActor.GetParent() );
4529
4530     if ( parent )
4531     {
4532       return WasTouchedCheck( parent );
4533     }
4534   }
4535
4536   return false;
4537 }
4538
4539 void TextInput::StartMonitoringStageForTouch()
4540 {
4541   Stage stage = Stage::GetCurrent();
4542   stage.TouchedSignal().Connect( this, &TextInput::OnStageTouched );
4543 }
4544
4545 void TextInput::EndMonitoringStageForTouch()
4546 {
4547   Stage stage = Stage::GetCurrent();
4548   stage.TouchedSignal().Disconnect( this, &TextInput::OnStageTouched );
4549 }
4550
4551 void TextInput::OnStageTouched(const TouchEvent& event)
4552 {
4553   if( event.GetPointCount() > 0 )
4554   {
4555     if ( TouchPoint::Down == event.GetPoint(0).state )
4556     {
4557       const Actor touchedActor(event.GetPoint(0).hitActor);
4558
4559       bool popUpShown( false );
4560
4561       if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown ) )
4562       {
4563         popUpShown = true;
4564       }
4565
4566       bool textInputTouched = (touchedActor && WasTouchedCheck( touchedActor ));
4567
4568       if ( ( mHighlightMeshActor || popUpShown ) && !textInputTouched )
4569       {
4570         EndMonitoringStageForTouch();
4571         HidePopup( true, false );
4572       }
4573
4574       if ( ( IsGrabHandleEnabled() && mGrabHandle  ) && !textInputTouched )
4575       {
4576         EndMonitoringStageForTouch();
4577         ShowGrabHandleAndSetVisibility( false );
4578       }
4579     }
4580   }
4581 }
4582
4583 void TextInput::SelectText(std::size_t start, std::size_t end)
4584 {
4585   DALI_LOG_INFO(gLogFilter, Debug::General, "SelectText mEditModeActive[%s] grabHandle[%s] start[%u] end[%u] size[%u]\n", mEditModeActive?"true":"false",
4586                                                                                                                           IsGrabHandleEnabled()?"true":"false",
4587                                                                                                                           start, end, mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
4588   DALI_ASSERT_ALWAYS( start <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText start out of max range" );
4589   DALI_ASSERT_ALWAYS( end <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText end out of max range" );
4590
4591   StartMonitoringStageForTouch();
4592
4593   if ( mEditModeActive ) // Only allow text selection when in edit mode
4594   {
4595     // When replacing highlighted text keyboard should ignore current word at cursor hence notify keyboard that the cursor is at the start of the highlight.
4596     mSelectingText = true;
4597
4598     mCursorPosition = std::min( start, end ); // Set cursor position to start of highlighted text.
4599
4600     ImfManager imfManager = ImfManager::Get();
4601     if ( imfManager )
4602     {
4603       imfManager.SetCursorPosition ( mCursorPosition );
4604       imfManager.SetSurroundingText( GetText() );
4605       imfManager.NotifyCursorPosition();
4606     }
4607     // As the imfManager has been notified of the new cursor position we do not need to reset the pre-edit as it will be updated instead.
4608
4609     // Hide grab handle when selecting.
4610     ShowGrabHandleAndSetVisibility( false );
4611
4612     if( start != end )  // something to select
4613     {
4614       SetCursorVisibility( false );
4615       StopCursorBlinkTimer();
4616
4617       CreateSelectionHandles(start, end);
4618       UpdateHighlight();
4619
4620       const TextStyle oldInputStyle( mInputStyle );
4621       mInputStyle = GetStyleAt( mCursorPosition ); // Inherit style from selected position.
4622
4623       if( oldInputStyle != mInputStyle )
4624       {
4625         // Updates the line height accordingly with the input style.
4626         UpdateLineHeight();
4627
4628         EmitStyleChangedSignal();
4629       }
4630
4631       HidePopup();
4632     }
4633
4634     mSelectingText = false;
4635   }
4636 }
4637
4638 MarkupProcessor::StyledTextArray TextInput::GetSelectedText()
4639 {
4640   MarkupProcessor::StyledTextArray currentSelectedText;
4641
4642   if ( IsTextSelected() )
4643   {
4644     MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4645     MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4646
4647     for(; it != end; ++it)
4648     {
4649       MarkupProcessor::StyledText& styledText( *it );
4650       currentSelectedText.push_back( styledText );
4651     }
4652   }
4653   return currentSelectedText;
4654 }
4655
4656 void TextInput::ApplyStyleToRange(const TextStyle& style, const TextStyle::Mask mask, const std::size_t begin, const std::size_t end)
4657 {
4658   const std::size_t beginIndex = std::min( begin, end );
4659   const std::size_t endIndex = std::max( begin, end );
4660
4661   // Apply the style
4662   MarkupProcessor::SetTextStyleToRange( mStyledText, style, mask, beginIndex, endIndex );
4663
4664   // Create a styled text array used to replace the text into the text-view.
4665   MarkupProcessor::StyledTextArray text;
4666   text.insert( text.begin(), mStyledText.begin() + beginIndex, mStyledText.begin() + endIndex + 1 );
4667
4668   mDisplayedTextView.ReplaceTextFromTo( beginIndex, ( endIndex - beginIndex ) + 1, text );
4669   GetTextLayoutInfo();
4670
4671   if( IsScrollEnabled() )
4672   {
4673     // Need to set the scroll position as the text's size may have changed.
4674     ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
4675   }
4676
4677   ShowGrabHandleAndSetVisibility( false );
4678
4679   DrawCursor();
4680
4681   UpdateHighlight();
4682
4683   // Set Handle positioning as the new style may have repositioned the characters.
4684   SetSelectionHandlePosition(HandleOne);
4685   SetSelectionHandlePosition(HandleTwo);
4686 }
4687
4688 void TextInput::KeyboardStatusChanged(bool keyboardShown)
4689 {
4690   // Just hide the grab handle when keyboard is hidden.
4691   if (!keyboardShown )
4692   {
4693     ShowGrabHandleAndSetVisibility( false );
4694
4695     // If the keyboard is not now being shown, then hide the popup panel
4696     mPopupPanel.Hide( true );
4697   }
4698 }
4699
4700 // Removes highlight and resumes edit mode state
4701 void TextInput::RemoveHighlight()
4702 {
4703   DALI_LOG_INFO(gLogFilter, Debug::General, "RemoveHighlight\n");
4704
4705   if ( mHighlightMeshActor )
4706   {
4707     if ( mSelectionHandleOne )
4708     {
4709       mActiveLayer.Remove( mSelectionHandleOne );
4710       mSelectionHandleOne.Reset();
4711       mSelectionHandleOneOffset.x = 0.0f;
4712     }
4713     if ( mSelectionHandleTwo )
4714     {
4715       mActiveLayer.Remove( mSelectionHandleTwo );
4716       mSelectionHandleTwo.Reset();
4717       mSelectionHandleTwoOffset.x = 0.0f;
4718     }
4719
4720     mNewHighlightInfo.mQuadList.clear();
4721
4722     Self().Remove( mHighlightMeshActor );
4723
4724     SetCursorVisibility( true );
4725     StartCursorBlinkTimer();
4726
4727     mHighlightMeshActor.Reset();
4728     // NOTE: We cannot dereference mHighlightMesh, due
4729     // to a bug in how the scene-graph MeshRenderer uses the Mesh data incorrectly.
4730
4731     HidePopup();
4732   }
4733
4734   mSelectionHandleOnePosition = 0;
4735   mSelectionHandleTwoPosition = 0;
4736 }
4737
4738 void TextInput::CreateHighlight()
4739 {
4740   if ( !mHighlightMeshActor )
4741   {
4742     mMeshData = MeshData( );
4743     mMeshData.SetHasNormals( true );
4744
4745     mCustomMaterial = Material::New("CustomMaterial");
4746     mCustomMaterial.SetDiffuseColor( mMaterialColor );
4747
4748     mMeshData.SetMaterial( mCustomMaterial );
4749
4750     mHighlightMesh = Mesh::New( mMeshData );
4751
4752     mHighlightMeshActor = MeshActor::New( mHighlightMesh );
4753     mHighlightMeshActor.SetName( "HighlightMeshActor" );
4754     mHighlightMeshActor.SetInheritShaderEffect( false );
4755     mHighlightMeshActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
4756     mHighlightMeshActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
4757     mHighlightMeshActor.SetPosition( 0.0f, 0.0f, DISPLAYED_HIGHLIGHT_Z_OFFSET );
4758     mHighlightMeshActor.SetAffectedByLighting(false);
4759
4760     Self().Add(mHighlightMeshActor);
4761   }
4762 }
4763
4764
4765 bool TextInput::CopySelectedTextToClipboard()
4766 {
4767   mCurrentCopySelecton.clear();
4768
4769   mCurrentCopySelecton = GetSelectedText();
4770
4771   std::string stringToStore;
4772
4773   /* Create a StyledTextArray from the selected region so can use the MarkUpProcessor to produce
4774    * a marked up string.
4775    */
4776   MarkupProcessor::StyledTextArray selectedText(mCurrentCopySelecton.begin(),mCurrentCopySelecton.end());
4777   MarkupProcessor::GetPlainString( selectedText, stringToStore );
4778   bool success = mClipboard.SetItem( stringToStore );
4779   return success;
4780 }
4781
4782 void TextInput::PasteText( const Text& text )
4783 {
4784   // Update Flag, indicates whether to update the text-input contents or not.
4785   // Any key stroke that results in a visual change of the text-input should
4786   // set this flag to true.
4787   bool update = false;
4788   if( mHighlightMeshActor )
4789   {
4790     /* if highlighted, delete entire text, and position cursor at start of deleted text. */
4791     mCursorPosition = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4792
4793     ImfManager imfManager = ImfManager::Get();
4794     if ( imfManager )
4795     {
4796       imfManager.SetCursorPosition( mCursorPosition );
4797       imfManager.NotifyCursorPosition();
4798     }
4799     DeleteHighlightedText( true );
4800     update = true;
4801   }
4802
4803   bool textExceedsMaximunNumberOfCharacters = false;
4804   bool textExceedsBoundary = false;
4805
4806   std::size_t insertedStringLength = DoInsertAt( text, mCursorPosition, 0, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
4807
4808   mCursorPosition += insertedStringLength;
4809   ImfManager imfManager = ImfManager::Get();
4810   if ( imfManager )
4811   {
4812     imfManager.SetCursorPosition ( mCursorPosition );
4813     imfManager.NotifyCursorPosition();
4814   }
4815
4816   update = update || ( insertedStringLength > 0 );
4817   if( update )
4818   {
4819     CursorUpdate();
4820   }
4821
4822   if( insertedStringLength < text.GetLength() )
4823   {
4824     EmitMaxInputCharactersReachedSignal();
4825   }
4826
4827   if( textExceedsBoundary )
4828   {
4829     EmitInputTextExceedsBoundariesSignal();
4830   }
4831 }
4832
4833 void TextInput::SetTextDirection()
4834 {
4835   // Put the cursor to the right if we are empty and an RTL language is being used.
4836   if ( mStyledText.empty() )
4837   {
4838     VirtualKeyboard::TextDirection direction( VirtualKeyboard::GetTextDirection() );
4839
4840     // Get the current text alignment preserving the vertical alignment. Also preserve the horizontal center
4841     // alignment as we do not want to set the text direction if we've been asked to be in the center.
4842     //
4843     // TODO: Should split SetTextAlignment into two APIs to better handle this (sometimes apps just want to
4844     //       set vertical alignment but are being forced to set the horizontal alignment as well with the
4845     //       current API.
4846     int alignment( mDisplayedTextView.GetTextAlignment() &
4847                   ( Toolkit::Alignment::VerticalTop |
4848                     Toolkit::Alignment::VerticalCenter |
4849                     Toolkit::Alignment::VerticalBottom |
4850                     Toolkit::Alignment::HorizontalCenter ) );
4851     Toolkit::TextView::LineJustification justification( mDisplayedTextView.GetLineJustification() );
4852
4853     // If our alignment is in the center, then do not change.
4854     if ( !( alignment & Toolkit::Alignment::HorizontalCenter ) )
4855     {
4856       alignment |= ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight;
4857     }
4858
4859     // If our justification is in the center, then do not change.
4860     if ( justification != Toolkit::TextView::Center )
4861     {
4862       justification = ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::TextView::Left : Toolkit::TextView::Right;
4863     }
4864
4865     mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(alignment) );
4866     mDisplayedTextView.SetLineJustification( justification );
4867   }
4868 }
4869
4870 void TextInput::UpdateLineHeight()
4871 {
4872   Dali::Font font = Dali::Font::New( FontParameters( mInputStyle.GetFontName(), mInputStyle.GetFontStyle(), mInputStyle.GetFontPointSize() ) );
4873   mLineHeight = font.GetLineHeight();
4874
4875   // If the height exceed policy is shrink or exceed the boundaries of the text-input is not allowed, then modify the line height is needed.
4876
4877   const bool shrink = mDisplayedTextView && ( Toolkit::TextView::ShrinkToFit == mDisplayedTextView.GetHeightExceedPolicy() ) && mStyledText.empty();
4878
4879   if( !mExceedEnabled || shrink )
4880   {
4881     mLineHeight = std::min( mLineHeight, GetControlSize().height );
4882   }
4883 }
4884
4885 std::size_t TextInput::FindVisibleCharacter( const FindVisibleCharacterDirection direction , const std::size_t cursorPosition ) const
4886 {
4887   std::size_t position = 0;
4888
4889   const std::size_t tableSize = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
4890
4891   switch( direction )
4892   {
4893     case Left:
4894     {
4895       position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4896
4897       if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1 : position ) ) ).mIsVisible )
4898       {
4899         position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4900       }
4901       break;
4902     }
4903     case Right:
4904     {
4905       position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4906       if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1 : position ) ) ).mIsVisible )
4907       {
4908         position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4909       }
4910       break;
4911     }
4912     case ByEnd:
4913     {
4914       position = FindVisibleCharacterLeft( 0, mTextLayoutInfo.mCharacterLayoutInfoTable );
4915       break;
4916     }
4917     default:
4918     {
4919       DALI_ASSERT_ALWAYS( !"TextInput::FindVisibleCharacter() Unknown direction." );
4920     }
4921   }
4922
4923   return position;
4924 }
4925
4926 void TextInput::SetSortModifier( float depthOffset )
4927 {
4928   if(mDisplayedTextView)
4929   {
4930     mDisplayedTextView.SetSortModifier(depthOffset);
4931   }
4932 }
4933
4934 void TextInput::SetSnapshotModeEnabled( bool enable )
4935 {
4936   if(mDisplayedTextView)
4937   {
4938     mDisplayedTextView.SetSnapshotModeEnabled( enable );
4939   }
4940 }
4941
4942 bool TextInput::IsSnapshotModeEnabled() const
4943 {
4944   bool snapshotEnabled = false;
4945
4946   if(mDisplayedTextView)
4947   {
4948     snapshotEnabled = mDisplayedTextView.IsSnapshotModeEnabled();
4949   }
4950
4951   return snapshotEnabled;
4952 }
4953
4954 void TextInput::SetMarkupProcessingEnabled( bool enable )
4955 {
4956   mMarkUpEnabled = enable;
4957 }
4958
4959 bool TextInput::IsMarkupProcessingEnabled() const
4960 {
4961   return mMarkUpEnabled;
4962 }
4963
4964 void TextInput::SetScrollEnabled( bool enable )
4965 {
4966   if( mDisplayedTextView )
4967   {
4968     mDisplayedTextView.SetScrollEnabled( enable );
4969   }
4970
4971   if( !enable )
4972   {
4973     // Don't set cursor's and handle's visibility to false if they are outside the
4974     // boundaries of the text-input.
4975     mIsCursorInScrollArea = true;
4976     mIsGrabHandleInScrollArea = true;
4977     if( mSelectionHandleOne && mSelectionHandleTwo )
4978     {
4979       mSelectionHandleOne.SetVisible( true );
4980       mSelectionHandleTwo.SetVisible( true );
4981
4982       if( mHighlightMeshActor )
4983       {
4984         mHighlightMeshActor.SetVisible( true );
4985       }
4986     }
4987   }
4988 }
4989
4990 bool TextInput::IsScrollEnabled() const
4991 {
4992   bool scrollEnabled = false;
4993
4994   if( mDisplayedTextView )
4995   {
4996     scrollEnabled = mDisplayedTextView.IsScrollEnabled();
4997   }
4998
4999   return scrollEnabled;
5000 }
5001
5002 void TextInput::SetScrollPosition( const Vector2& position )
5003 {
5004   if( mDisplayedTextView )
5005   {
5006     mDisplayedTextView.SetScrollPosition( position );
5007   }
5008 }
5009
5010 Vector2 TextInput::GetScrollPosition() const
5011 {
5012   Vector2 scrollPosition;
5013
5014   if( mDisplayedTextView )
5015   {
5016     scrollPosition = mDisplayedTextView.GetScrollPosition();
5017   }
5018
5019   return scrollPosition;
5020 }
5021
5022 std::size_t TextInput::DoInsertAt( const Text& text, const std::size_t position, const std::size_t numberOfCharactersToReplace, bool& textExceedsMaximunNumberOfCharacters, bool& textExceedsBoundary )
5023 {
5024   // determine number of characters that we can write to style text buffer, this is the insertStringLength
5025   std::size_t insertedStringLength = std::min( text.GetLength(), mMaxStringLength - mStyledText.size() );
5026   textExceedsMaximunNumberOfCharacters = insertedStringLength < text.GetLength();
5027
5028   // Add style to the new input text.
5029   MarkupProcessor::StyledTextArray textToInsert;
5030   for( std::size_t i = 0; i < insertedStringLength; ++i )
5031   {
5032     const MarkupProcessor::StyledText newStyledCharacter( text[i], mInputStyle );
5033     textToInsert.push_back( newStyledCharacter );
5034   }
5035
5036   //Insert text to the TextView.
5037   const bool emptyTextView = mStyledText.empty();
5038   if( emptyTextView && mPlaceHolderSet )
5039   {
5040     // There is no text set so call to TextView::SetText() is needed in order to clear the placeholder text.
5041     mDisplayedTextView.SetText( textToInsert );
5042   }
5043   else
5044   {
5045     if( 0 == numberOfCharactersToReplace )
5046     {
5047       mDisplayedTextView.InsertTextAt( position, textToInsert );
5048     }
5049     else
5050     {
5051       mDisplayedTextView.ReplaceTextFromTo( position, numberOfCharactersToReplace, textToInsert );
5052     }
5053   }
5054   mPlaceHolderSet = false;
5055
5056   if( textToInsert.empty() )
5057   {
5058     // If no text has been inserted, GetTextLayoutInfo() need to be called to check whether mStyledText has some text.
5059     GetTextLayoutInfo();
5060   }
5061   else
5062   {
5063     // GetTextLayoutInfo() can't be used here as mStyledText is not updated yet.
5064     mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5065   }
5066
5067   textExceedsBoundary = false;
5068
5069   if( !mExceedEnabled )
5070   {
5071     const Vector3& size = GetControlSize();
5072
5073     if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5074     {
5075       // If new text does not fit within TextView
5076       mDisplayedTextView.RemoveTextFrom( position, insertedStringLength );
5077       // previously inserted text has been removed. Call GetTextLayoutInfo() to check whether mStyledText has some text.
5078       GetTextLayoutInfo();
5079       textExceedsBoundary = true;
5080       insertedStringLength = 0;
5081     }
5082
5083     if( textExceedsBoundary )
5084     {
5085       // Add the part of the text which fits on the text-input.
5086
5087       // Split the text which doesn't fit in two halves.
5088       MarkupProcessor::StyledTextArray firstHalf;
5089       MarkupProcessor::StyledTextArray secondHalf;
5090       SplitText( textToInsert, firstHalf, secondHalf );
5091
5092       // Clear text. This text will be filled with the text inserted.
5093       textToInsert.clear();
5094
5095       // Where to insert the text.
5096       std::size_t positionToInsert = position;
5097
5098       bool end = text.GetLength() <= 1;
5099       while( !end )
5100       {
5101         // Insert text and check ...
5102         const std::size_t textLength = firstHalf.size();
5103         mDisplayedTextView.InsertTextAt( positionToInsert, firstHalf );
5104         mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5105
5106         if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5107         {
5108           // Inserted text doesn't fit.
5109
5110           // Remove inserted text
5111           mDisplayedTextView.RemoveTextFrom( positionToInsert, textLength );
5112           mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5113
5114           // The iteration finishes when only one character doesn't fit.
5115           end = textLength <= 1;
5116
5117           if( !end )
5118           {
5119             // Prepare next two halves for next iteration.
5120             MarkupProcessor::StyledTextArray copyText = firstHalf;
5121             SplitText( copyText, firstHalf, secondHalf );
5122           }
5123         }
5124         else
5125         {
5126           // Text fits.
5127
5128           // store text to be inserted in mStyledText.
5129           textToInsert.insert( textToInsert.end(), firstHalf.begin(), firstHalf.end() );
5130
5131           // Increase the inserted characters counter.
5132           insertedStringLength += textLength;
5133
5134           // Prepare next two halves for next iteration.
5135           MarkupProcessor::StyledTextArray copyText = secondHalf;
5136           SplitText( copyText, firstHalf, secondHalf );
5137
5138           // Update where next text has to be inserted
5139           positionToInsert += textLength;
5140         }
5141       }
5142     }
5143   }
5144
5145   if( textToInsert.empty() && emptyTextView )
5146   {
5147     // No character has been added and the text-view was empty.
5148     // Set the placeholder text.
5149     mDisplayedTextView.SetText( mStyledPlaceHolderText );
5150     mPlaceHolderSet = true;
5151   }
5152   else
5153   {
5154     MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + position;
5155     mStyledText.insert( it, textToInsert.begin(), textToInsert.end() );
5156     mPlaceHolderSet = false;
5157   }
5158
5159   return insertedStringLength;
5160 }
5161
5162 void TextInput::GetTextLayoutInfo()
5163 {
5164   if( mStyledText.empty() )
5165   {
5166     // The text-input has no text, clear the text-view's layout info.
5167     mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5168   }
5169   else
5170   {
5171     if( mDisplayedTextView )
5172     {
5173       mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5174     }
5175     else
5176     {
5177       // There is no text-view.
5178       mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5179     }
5180   }
5181 }
5182
5183 void TextInput::SetOffsetFromText( const Vector4& offset )
5184 {
5185   mPopupOffsetFromText = offset;
5186 }
5187
5188 const Vector4& TextInput::GetOffsetFromText() const
5189 {
5190   return mPopupOffsetFromText;
5191 }
5192
5193 void TextInput::SetProperty( BaseObject* object, Property::Index propertyIndex, const Property::Value& value )
5194 {
5195   Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5196
5197   if ( textInput )
5198   {
5199     TextInput& textInputImpl( GetImpl( textInput ) );
5200
5201     switch ( propertyIndex )
5202     {
5203       case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5204       {
5205         textInputImpl.SetMaterialDiffuseColor( value.Get< Vector4 >() );
5206         break;
5207       }
5208       case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5209       {
5210         textInputImpl.mPopupPanel.SetCutPastePopupColor( value.Get< Vector4 >() );
5211         break;
5212       }
5213       case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5214       {
5215         textInputImpl.mPopupPanel.SetCutPastePopupPressedColor( value.Get< Vector4 >() );
5216         break;
5217       }
5218       case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY:
5219       {
5220         textInputImpl.mPopupPanel.SetCutPastePopupBorderColor( value.Get< Vector4 >() );
5221         break;
5222       }
5223       case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5224       {
5225         textInputImpl.mPopupPanel.SetCutPastePopupIconColor( value.Get< Vector4 >() );
5226         break;
5227       }
5228       case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5229       {
5230         textInputImpl.mPopupPanel.SetCutPastePopupIconPressedColor( value.Get< Vector4 >() );
5231         break;
5232       }
5233       case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5234       {
5235         textInputImpl.mPopupPanel.SetCutPastePopupTextColor( value.Get< Vector4 >() );
5236         break;
5237       }
5238       case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5239       {
5240         textInputImpl.mPopupPanel.SetCutPastePopupTextPressedColor( value.Get< Vector4 >() );
5241         break;
5242       }
5243       case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5244       {
5245         textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCut, value.Get<unsigned int>() );
5246         break;
5247       }
5248       case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5249       {
5250         textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCopy, value.Get<unsigned int>() );
5251         break;
5252       }
5253       case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5254       {
5255         textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsPaste, value.Get<unsigned int>() );
5256         break;
5257       }
5258       case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5259       {
5260         textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelect, value.Get<unsigned int>() );
5261         break;
5262       }
5263       case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5264       {
5265         textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll, value.Get<unsigned int>() );
5266         break;
5267       }
5268       case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5269       {
5270         textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsClipboard, value.Get<unsigned int>() );
5271         break;
5272       }
5273       case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5274       {
5275         textInputImpl.SetOffsetFromText( value.Get< Vector4 >() );
5276         break;
5277       }
5278     }
5279   }
5280 }
5281
5282 Property::Value TextInput::GetProperty( BaseObject* object, Property::Index propertyIndex )
5283 {
5284   Property::Value value;
5285
5286   Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5287
5288   if ( textInput )
5289   {
5290     TextInput& textInputImpl( GetImpl( textInput ) );
5291
5292     switch ( propertyIndex )
5293     {
5294       case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5295       {
5296         value = textInputImpl.GetMaterialDiffuseColor();
5297         break;
5298       }
5299       case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5300       {
5301         value = textInputImpl.mPopupPanel.GetCutPastePopupColor();
5302         break;
5303       }
5304       case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5305       {
5306         value = textInputImpl.mPopupPanel.GetCutPastePopupPressedColor();
5307         break;
5308       }
5309       case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY :
5310       {
5311         value = textInputImpl.mPopupPanel.GetCutPastePopupBorderColor();
5312         break;
5313       }
5314       case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5315       {
5316         value = textInputImpl.mPopupPanel.GetCutPastePopupIconColor();
5317         break;
5318       }
5319       case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5320       {
5321         value = textInputImpl.mPopupPanel.GetCutPastePopupIconPressedColor();
5322         break;
5323       }
5324       case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5325       {
5326         value = textInputImpl.mPopupPanel.GetCutPastePopupTextColor();
5327         break;
5328       }
5329       case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5330       {
5331         value = textInputImpl.mPopupPanel.GetCutPastePopupTextPressedColor();
5332         break;
5333       }
5334       case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5335       {
5336         value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCut );
5337         break;
5338       }
5339       case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5340       {
5341         value =  textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCopy );
5342         break;
5343       }
5344       case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5345       {
5346         value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsPaste );
5347         break;
5348       }
5349       case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5350       {
5351         value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelect );
5352         break;
5353       }
5354       case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5355       {
5356         value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll );
5357         break;
5358       }
5359       case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5360       {
5361         value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsClipboard );
5362         break;
5363       }
5364       case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5365       {
5366         value = textInputImpl.GetOffsetFromText();
5367         break;
5368       }
5369     }
5370   }
5371   return value;
5372 }
5373
5374 void TextInput::EmitStyleChangedSignal()
5375 {
5376   // emit signal if input style changes.
5377   Toolkit::TextInput handle( GetOwner() );
5378   mStyleChangedSignalV2.Emit( handle, mInputStyle );
5379 }
5380
5381 void TextInput::EmitTextModified()
5382 {
5383   // emit signal when text changes.
5384   Toolkit::TextInput handle( GetOwner() );
5385   mTextModifiedSignal.Emit( handle );
5386 }
5387
5388
5389 void TextInput::EmitMaxInputCharactersReachedSignal()
5390 {
5391   // emit signal if max characters is reached during text input.
5392   DALI_LOG_INFO(gLogFilter, Debug::General, "EmitMaxInputCharactersReachedSignal \n");
5393
5394   Toolkit::TextInput handle( GetOwner() );
5395   mMaxInputCharactersReachedSignalV2.Emit( handle );
5396 }
5397
5398 void TextInput::EmitInputTextExceedsBoundariesSignal()
5399 {
5400   // Emit a signal when the input text exceeds the boundaries of the text input.
5401
5402   Toolkit::TextInput handle( GetOwner() );
5403   mInputTextExceedBoundariesSignalV2.Emit( handle );
5404 }
5405
5406 } // namespace Internal
5407
5408 } // namespace Toolkit
5409
5410 } // namespace Dali