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