(TextInput) Double tap on whitespace gives selection options not cut or copy
[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      std::size_t start = 0;
1392      std::size_t end = 0;
1393      Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1394
1395      mCursorPosition = end; // Ensure cursor is positioned at end of selected word
1396
1397      ImfManager imfManager = ImfManager::Get();
1398      if ( imfManager )
1399      {
1400        imfManager.SetCursorPosition ( mCursorPosition );
1401        imfManager.NotifyCursorPosition();
1402      }
1403
1404      if ( !mStyledText.at(end-1).mText[0].IsWhiteSpace() )
1405      {
1406        SelectText( start, end );
1407        ShowPopupCutCopyPaste();
1408      }
1409      else
1410      {
1411        RemoveHighlight( false ); // Remove highlight but do not auto hide popup
1412        HidePopup( false ); // Hide popup with setting to do auto show.
1413        SetUpPopupSelection( false ); // Set to false so if nearest word is whitespace it will not show cut button.
1414        ShowPopup();
1415      }
1416    }
1417    else if ( mClipboard && mClipboard.NumberOfItems() )
1418    {
1419      ShowPopupCutCopyPaste();
1420    }
1421
1422    // If no text and clipboard empty then do nothing
1423 }
1424
1425 // TODO: Change the function name to be more general.
1426 void TextInput::OnTextTap(Dali::Actor actor, Dali::TapGesture tap)
1427 {
1428   DALI_LOG_INFO( gLogFilter, Debug::General, "OnTap mPreEditFlag[%s] mEditOnTouch[%s] mEditModeActive[%s] ", (mPreEditFlag)?"true":"false"
1429                                                                                                            , (mEditOnTouch)?"true":"false"
1430                                                                                                            , (mEditModeActive)?"true":"false");
1431
1432   if( mHandleOneGrabArea == actor || mHandleTwoGrabArea == actor )
1433   {
1434     return;
1435   }
1436
1437   if( mGrabArea == actor )
1438   {
1439     if( mPopupPanel.GetState() == TextInputPopup::StateHidden || mPopupPanel.GetState() == TextInputPopup::StateHiding )
1440     {
1441       SetUpPopupSelection();
1442       ShowPopup();
1443     }
1444
1445     return;
1446   }
1447
1448   HidePopup();
1449   RemoveHighlight();
1450
1451   mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1452
1453   // Initially don't create the grab handle.
1454   bool createGrabHandle = false;
1455
1456   if ( !mEditModeActive )
1457   {
1458     // update line height before calculate the actual position.
1459     UpdateLineHeight();
1460
1461     // Only start edit mode if TextInput configured to edit on touch
1462     if ( mEditOnTouch )
1463     {
1464       // Set the initial cursor position in the tap point.
1465       ReturnClosestIndex(tap.localPoint, mCursorPosition );
1466
1467       // Create the grab handle.
1468       // TODO Make this a re-usable function.
1469       if ( IsGrabHandleEnabled() )
1470       {
1471         const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1472
1473         CreateGrabHandle();
1474
1475         mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1476         mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1477         mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1478         ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1479
1480       }
1481
1482       // Edit mode started after grab handle created to ensure the signal InputStarted is sent last.
1483       // This is used to ensure if selecting text hides the grab handle then this code is run after grab handle is created,
1484       // otherwise the Grab handle will be shown when selecting.
1485
1486       StartEditMode();
1487     }
1488   }
1489   else
1490   {
1491     // Show the keyboard if it was hidden.
1492     if (!VirtualKeyboard::IsVisible())
1493     {
1494       VirtualKeyboard::Show();
1495     }
1496
1497     // Reset keyboard as tap event has occurred.
1498     // Set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1499     PreEditReset( true );
1500
1501     GetTextLayoutInfo();
1502
1503     if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() ) // If string empty we do not need a grab handle.
1504     {
1505       // 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.
1506
1507       ReturnClosestIndex(tap.localPoint, mCursorPosition );
1508
1509       DALI_LOG_INFO( gLogFilter, Debug::General, "mCursorPosition[%u]", mCursorPosition );
1510
1511       // Notify keyboard so it can 're-capture' word for predictive text.
1512       // As we have done a reset, is this required, expect IMF keyboard to request this information.
1513       ImfManager imfManager = ImfManager::Get();
1514       if ( imfManager )
1515       {
1516         imfManager.SetCursorPosition ( mCursorPosition );
1517         imfManager.NotifyCursorPosition();
1518       }
1519       const TextStyle oldInputStyle( mInputStyle );
1520
1521       mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1522
1523       DrawCursor();
1524
1525       // Create the grab handle.
1526       // Grab handle is created later.
1527       createGrabHandle = true;
1528
1529       if( oldInputStyle != mInputStyle )
1530       {
1531         // Updates the line height accordingly with the input style.
1532         UpdateLineHeight();
1533
1534         EmitStyleChangedSignal();
1535       }
1536     }
1537   }
1538
1539   if ( createGrabHandle && IsGrabHandleEnabled() )
1540   {
1541     const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1542
1543     CreateGrabHandle();
1544
1545     mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1546     mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1547     mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1548     ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1549
1550   }
1551 }
1552
1553 void TextInput::OnLongPress(Dali::Actor actor, Dali::LongPressGesture longPress)
1554 {
1555   DALI_LOG_INFO( gLogFilter, Debug::General, "OnLongPress\n" );
1556
1557   // Ignore longpress if in selection mode already
1558   if( mHighlightMeshActor )
1559   {
1560     return;
1561   }
1562
1563   if(longPress.state == Dali::Gesture::Started)
1564   {
1565     // Start edit mode on long press
1566     if ( !mEditModeActive )
1567     {
1568       StartEditMode();
1569     }
1570
1571     // If text exists then select nearest word.
1572     if ( !mStyledText.empty())
1573     {
1574       HidePopup();
1575
1576       ShowGrabHandleAndSetVisibility( false );
1577
1578
1579       if ( mPreEditFlag )
1580       {
1581         // PreEdit will be committed here without needing a commit from IMF.  Remove pre-edit underline and reset flags which
1582         // converts the pre-edit word being displayed to a committed word.
1583         if ( !mUnderlinedPriorToPreEdit )
1584         {
1585           TextStyle style;
1586           style.SetUnderline( false );
1587           ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1588         }
1589         mPreEditFlag = false;
1590         mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1591         // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1592         PreEditReset( false );
1593       }
1594       mCursorPosition = 0;
1595
1596       mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1597       ReturnClosestIndex( longPress.localPoint, mCursorPosition );
1598
1599       std::size_t start = 0;
1600       std::size_t end = 0;
1601       Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1602
1603       mCursorPosition  = end; // Ensure cursor is positioned at end of selected word
1604
1605       ImfManager imfManager = ImfManager::Get();
1606       if ( imfManager )
1607       {
1608         imfManager.SetCursorPosition ( mCursorPosition );
1609         imfManager.NotifyCursorPosition();
1610       }
1611
1612       SelectText( start, end );
1613     }
1614
1615     // if no text but clipboard has content then show paste option, if no text and clipboard empty then do nothing
1616     if ( ( mClipboard && mClipboard.NumberOfItems() ) || !mStyledText.empty() )
1617     {
1618       ShowPopupCutCopyPaste();
1619     }
1620   }
1621 }
1622
1623 void TextInput::OnClipboardTextSelected( ClipboardEventNotifier& notifier )
1624 {
1625   const Text clipboardText( notifier.GetContent() );
1626   PasteText( clipboardText );
1627
1628   SetCursorVisibility( true );
1629   StartCursorBlinkTimer();
1630
1631   ShowGrabHandleAndSetVisibility( false );
1632
1633
1634   HidePopup();
1635 }
1636
1637 bool TextInput::OnPopupButtonPressed( Toolkit::Button button )
1638 {
1639   mPopupPanel.PressedSignal().Disconnect( this, &TextInput::OnPopupButtonPressed );
1640
1641   const std::string& name = button.GetName();
1642
1643   if(name == TextInputPopup::OPTION_SELECT_WORD)
1644   {
1645     std::size_t start = 0;
1646     std::size_t end = 0;
1647     Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1648
1649     SelectText( start, end );
1650   }
1651   else if(name == TextInputPopup::OPTION_SELECT_ALL)
1652   {
1653     SetCursorVisibility(false);
1654     StopCursorBlinkTimer();
1655
1656     std::size_t end = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
1657     std::size_t start = 0;
1658
1659     SelectText( start, end );
1660   }
1661   else if(name == TextInputPopup::OPTION_CUT)
1662   {
1663     bool ret = CopySelectedTextToClipboard();
1664
1665     if ( ret )
1666     {
1667       DeleteHighlightedText( true );
1668       CursorUpdate();
1669     }
1670
1671     SetCursorVisibility( true );
1672     StartCursorBlinkTimer();
1673
1674     HidePopup();
1675   }
1676   else if(name == TextInputPopup::OPTION_COPY)
1677   {
1678     CopySelectedTextToClipboard();
1679
1680     RemoveHighlight();
1681
1682     SetCursorVisibility( true );
1683     StartCursorBlinkTimer();
1684
1685     HidePopup();
1686   }
1687   else if(name == TextInputPopup::OPTION_PASTE)
1688   {
1689     const Text retrievedString( mClipboard.GetItem( 0 ) );  // currently can only get first item in clip board, index 0;
1690
1691     PasteText(retrievedString);
1692
1693     SetCursorVisibility( true );
1694     StartCursorBlinkTimer();
1695
1696     ShowGrabHandleAndSetVisibility( false );
1697
1698     HidePopup();
1699   }
1700   else if(name == TextInputPopup::OPTION_CLIPBOARD)
1701   {
1702     // In the case of clipboard being shown we do not want to show updated pop-up after hide animation completes
1703     // Hence pass the false parameter for signalFinished.
1704     HidePopup( true, false );
1705     mClipboard.ShowClipboard();
1706   }
1707
1708   return false;
1709 }
1710
1711 bool TextInput::OnCursorBlinkTimerTick()
1712 {
1713   // Cursor blinking
1714   mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1715   if ( mCursorRTLEnabled )
1716   {
1717     mCursorRTL.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1718   }
1719   mCursorBlinkStatus = !mCursorBlinkStatus;
1720
1721   return true;
1722 }
1723
1724 void TextInput::OnPopupHideFinished(TextInputPopup& popup)
1725 {
1726   popup.HideFinishedSignal().Disconnect( this, &TextInput::OnPopupHideFinished );
1727
1728   // Change Popup menu to Cut/Copy/Paste if text has been selected.
1729   if(mHighlightMeshActor && mState == StateEdit)
1730   {
1731     ShowPopupCutCopyPaste();
1732   }
1733 }
1734
1735 //FIXME this routine needs to be re-written as it contains too many branches.
1736 bool TextInput::OnKeyDownEvent(const KeyEvent& event)
1737 {
1738   std::string keyName = event.keyPressedName;
1739   std::string keyString = event.keyPressed;
1740
1741   DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyDownEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1742
1743   // Do not consume "Tab" and "Escape" keys.
1744   if(keyName == "Tab" || keyName == "Escape")
1745   {
1746     // Escape key to end the edit mode
1747     EndEditMode();
1748
1749     return false;
1750   }
1751
1752   HidePopup(); // If Pop-up shown then hides it as editing text.
1753
1754   // Update Flag, indicates whether to update the text-input contents or not.
1755   // Any key stroke that results in a visual change of the text-input should
1756   // set this flag to true.
1757   bool update(false);
1758
1759   // Whether to scroll text to cursor position.
1760   // Scroll is needed always the cursor is updated and after the pre-edit is received.
1761   bool scroll = false;
1762
1763   if (keyName == "Return")
1764   {
1765     if ( mNumberOflinesLimit > 1) // Prevents New line character / Return adding an extra line if limit set to 1
1766     {
1767       bool preEditFlagPreviouslySet( mPreEditFlag );
1768
1769       // replaces highlighted text with new line
1770       DeleteHighlightedText( false );
1771
1772       mCursorPosition = mCursorPosition + InsertAt( Text( NEWLINE ), mCursorPosition, 0 );
1773
1774       // If we are in pre-edit mode then pressing enter will cause a commit.  But the commit string does not include the
1775       // '\n' character so we need to ensure that the immediately following commit knows how it occurred.
1776       if ( mPreEditFlag )
1777       {
1778         mCommitByKeyInput = true;
1779       }
1780
1781       // If attempting to insert a new-line brings us out of PreEdit mode, then we should not ignore the next commit.
1782       if ( preEditFlagPreviouslySet && !mPreEditFlag )
1783       {
1784         mPreEditFlag = true;
1785         mIgnoreCommitFlag = false;
1786       }
1787       EmitTextModified();
1788       update = true;
1789     }
1790     else
1791     {
1792       RemoveHighlight();
1793     }
1794   } // Return
1795   else if ( keyName == "space" )
1796   {
1797     if ( mHighlightMeshActor )
1798     {
1799       // Some text is selected so erase it before adding space.
1800       DeleteHighlightedText( true );
1801       update = true;
1802     }
1803
1804     mCursorPosition = mCursorPosition + InsertAt(Text(keyString), mCursorPosition, 0);
1805
1806     // If we are in pre-edit mode then pressing the space-bar will cause a commit.  But the commit string does not include the
1807     // ' ' character so we need to ensure that the immediately following commit knows how it occurred.
1808     if ( mPreEditFlag )
1809     {
1810       mCommitByKeyInput = true;
1811     }
1812     EmitTextModified();
1813     update = true;
1814   } // space
1815   else if (keyName == "BackSpace")
1816   {
1817     if ( mHighlightMeshActor )
1818     {
1819       // Some text is selected so erase it
1820       DeleteHighlightedText( true );
1821       update = true;
1822     }
1823     else
1824     {
1825       if ( mCursorPosition > 0 )
1826       {
1827         DeleteCharacter( mCursorPosition );
1828         update = true;
1829       }
1830     }
1831     EmitTextModified();
1832   } // BackSpace
1833   else if (keyName == "Right")
1834   {
1835     AdvanceCursor();
1836     RemoveHighlight();
1837   }
1838   else if (keyName == "Left")
1839   {
1840     AdvanceCursor(true);
1841     RemoveHighlight();
1842   }
1843   else // event is a character
1844   {
1845     // Some text may be selected, hiding keyboard causes an empty keystring to be sent, we don't want to delete highlight in this case
1846     if ( !keyString.empty() )
1847     {
1848       // replaces highlighted text with new character
1849       DeleteHighlightedText( false );
1850
1851       // Received key String
1852       mCursorPosition += InsertAt( Text( keyString ), mCursorPosition, 0 );
1853       update = true;
1854       EmitTextModified();
1855     }
1856   }
1857
1858   // If key event has resulted in a change in the text/cursor, then trigger a relayout of text
1859   // as this is a costly operation.
1860   if(update)
1861   {
1862     CursorUpdate();
1863   }
1864
1865   if(update || scroll)
1866   {
1867     if( IsScrollEnabled() )
1868     {
1869       // Calculates the new cursor position (in actor coordinates)
1870       const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
1871
1872       ScrollTextViewToMakeCursorVisible( cursorPosition );
1873     }
1874   }
1875
1876   return true;
1877 }
1878
1879 bool TextInput::OnKeyUpEvent(const KeyEvent& event)
1880 {
1881   std::string keyName = event.keyPressedName;
1882   std::string keyString = event.keyPressed;
1883
1884   DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyUpEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1885
1886   // The selected text become deselected when the key code is DALI_KEY_BACK.
1887   if( IsTextSelected() && ( keyName == "XF86Stop" || keyName == "XF86Send") )
1888   {
1889     DeSelectText();
1890     return true;
1891   }
1892
1893   return false;
1894 }
1895
1896 void TextInput::OnTextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition )
1897 {
1898   // Updates the stored scroll position.
1899   mTextLayoutInfo.mScrollOffset = textView.GetScrollPosition();
1900
1901   const Vector3& controlSize = GetControlSize();
1902   Size cursorSize( CURSOR_THICKNESS, 0.f );
1903
1904   // Updates the cursor and grab handle position and visibility.
1905   if( mGrabHandle || mCursor )
1906   {
1907     cursorSize.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height;
1908     const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1909
1910     mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( cursorPosition, cursorSize, controlSize );
1911
1912     mActualGrabHandlePosition = cursorPosition.GetVectorXY();
1913
1914     if( mGrabHandle )
1915     {
1916       ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1917       mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1918     }
1919
1920     if( mCursor )
1921     {
1922       mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
1923       mCursor.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1924     }
1925   }
1926
1927   // Updates the selection handles and highlighted text position and visibility.
1928   if( mSelectionHandleOne && mSelectionHandleTwo )
1929   {
1930     const Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition(mSelectionHandleOnePosition);
1931     const Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition(mSelectionHandleTwoPosition);
1932     cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleOnePosition ) ).mSize.height;
1933     const bool isSelectionHandleOneVisible = IsPositionInsideBoundaries( cursorPositionOne, cursorSize, controlSize );
1934     cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleTwoPosition ) ).mSize.height;
1935     const bool isSelectionHandleTwoVisible = IsPositionInsideBoundaries( cursorPositionTwo, cursorSize, controlSize );
1936
1937     mSelectionHandleOneActualPosition = cursorPositionOne.GetVectorXY();
1938     mSelectionHandleTwoActualPosition = cursorPositionTwo.GetVectorXY();
1939
1940     mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
1941     mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
1942     mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
1943     mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
1944
1945     if( mHighlightMeshActor )
1946     {
1947       mHighlightMeshActor.SetVisible( true );
1948       UpdateHighlight();
1949     }
1950   }
1951 }
1952
1953 void TextInput::ScrollTextViewToMakeCursorVisible( const Vector3& cursorPosition )
1954 {
1955   // Scroll the text to make the cursor visible.
1956   const Size cursorSize( CURSOR_THICKNESS,
1957                          GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height );
1958
1959   // Need to scroll the text to make the cursor visible and to cover the whole text-input area.
1960
1961   const Vector3& controlSize = GetControlSize();
1962
1963   // Calculates the new scroll position.
1964   Vector2 scrollOffset = mTextLayoutInfo.mScrollOffset;
1965   if( ( cursorPosition.x < 0.f ) || ( cursorPosition.x > controlSize.width ) )
1966   {
1967     scrollOffset.x += cursorPosition.x;
1968   }
1969
1970   if( cursorPosition.y - cursorSize.height < 0.f || cursorPosition.y > controlSize.height )
1971   {
1972     scrollOffset.y += cursorPosition.y;
1973   }
1974
1975   // Sets the new scroll position.
1976   SetScrollPosition( Vector2::ZERO ); // TODO: need to reset to the zero position in order to make the scroll trim to work.
1977   SetScrollPosition( scrollOffset );
1978 }
1979
1980 void TextInput::StartScrollTimer()
1981 {
1982   if( !mScrollTimer )
1983   {
1984     mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1985     mScrollTimer.TickSignal().Connect( this, &TextInput::OnScrollTimerTick );
1986   }
1987
1988   if( !mScrollTimer.IsRunning() )
1989   {
1990     mScrollTimer.Start();
1991   }
1992 }
1993
1994 void TextInput::StopScrollTimer()
1995 {
1996   if( mScrollTimer )
1997   {
1998     mScrollTimer.Stop();
1999   }
2000 }
2001
2002 bool TextInput::OnScrollTimerTick()
2003 {
2004   // TODO: need to set the new style accordingly the new handle position.
2005
2006   if( !( mGrabHandleVisibility && mGrabHandle ) && !( mSelectionHandleOne && mSelectionHandleTwo ) )
2007   {
2008     // nothing to do if all handles are invisible or doesn't exist.
2009     return true;
2010   }
2011
2012   // Text scrolling
2013
2014   // Choose between the grab handle or the selection handles.
2015   Vector3& actualHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mActualGrabHandlePosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
2016   std::size_t& handlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCursorPosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
2017   Vector3& currentHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCurrentHandlePosition : mCurrentSelectionHandlePosition;
2018
2019   std::size_t newCursorPosition = 0;
2020   ReturnClosestIndex( actualHandlePosition.GetVectorXY(), newCursorPosition );
2021
2022   // Whether the handle's position is different of the previous one and in the case of the selection handle,
2023   // the new selection handle's position needs to be different of the other one.
2024   const bool differentSelectionHandles = ( mGrabHandleVisibility && mGrabHandle ) ? newCursorPosition != handlePosition :
2025                                          ( mCurrentSelectionId == HandleOne ) ? ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleTwoPosition ) :
2026                                                                                 ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleOnePosition );
2027
2028   if( differentSelectionHandles )
2029   {
2030     handlePosition = newCursorPosition;
2031
2032     const Vector3 actualPosition = GetActualPositionFromCharacterPosition( newCursorPosition );
2033
2034     Vector2 scrollDelta = ( actualPosition - currentHandlePosition ).GetVectorXY();
2035
2036     Vector2 scrollPosition = mDisplayedTextView.GetScrollPosition();
2037     scrollPosition += scrollDelta;
2038     SetScrollPosition( scrollPosition );
2039
2040     if( mDisplayedTextView.IsScrollPositionTrimmed() )
2041     {
2042       StopScrollTimer();
2043     }
2044
2045     currentHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition ).GetVectorXY();
2046   }
2047
2048   actualHandlePosition.x += mScrollDisplacement.x;
2049   actualHandlePosition.y += mScrollDisplacement.y;
2050
2051   return true;
2052 }
2053
2054 // Public Internal Methods (public for testing purpose)
2055
2056 void TextInput::SetUpTouchEvents()
2057 {
2058   if ( !mTapDetector )
2059   {
2060     mTapDetector = TapGestureDetector::New();
2061     // Attach the actors and connect the signal
2062     mTapDetector.Attach(Self());
2063
2064     // As contains children which may register for tap the default control detector is not used.
2065     mTapDetector.DetectedSignal().Connect(this, &TextInput::OnTextTap);
2066   }
2067
2068   if ( !mDoubleTapDetector )
2069   {
2070     mDoubleTapDetector = TapGestureDetector::New();
2071     mDoubleTapDetector.SetTapsRequired( 2 );
2072     mDoubleTapDetector.DetectedSignal().Connect(this, &TextInput::OnDoubleTap);
2073
2074     // Only attach and detach the actor to the double tap detector when we enter/leave edit mode
2075     // so that we do not, unnecessarily, have a double tap request all the time
2076   }
2077
2078   if ( !mPanGestureDetector )
2079   {
2080     mPanGestureDetector = PanGestureDetector::New();
2081     mPanGestureDetector.DetectedSignal().Connect(this, &TextInput::OnHandlePan);
2082   }
2083
2084   if ( !mLongPressDetector )
2085   {
2086     mLongPressDetector = LongPressGestureDetector::New();
2087     mLongPressDetector.DetectedSignal().Connect(this, &TextInput::OnLongPress);
2088     mLongPressDetector.Attach(Self());
2089   }
2090 }
2091
2092 void TextInput::CreateTextViewActor()
2093 {
2094   mDisplayedTextView = Toolkit::TextView::New();
2095   mDisplayedTextView.SetName( "DisplayedTextView ");
2096   mDisplayedTextView.SetMarkupProcessingEnabled( mMarkUpEnabled );
2097   mDisplayedTextView.SetParentOrigin(ParentOrigin::TOP_LEFT);
2098   mDisplayedTextView.SetAnchorPoint(AnchorPoint::TOP_LEFT);
2099   mDisplayedTextView.SetMultilinePolicy(Toolkit::TextView::SplitByWord);
2100   mDisplayedTextView.SetWidthExceedPolicy( Toolkit::TextView::Original );
2101   mDisplayedTextView.SetHeightExceedPolicy( Toolkit::TextView::Original );
2102   mDisplayedTextView.SetLineJustification( Toolkit::TextView::Left );
2103   mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>( Toolkit::Alignment::HorizontalLeft | Toolkit::Alignment::VerticalTop ) );
2104   mDisplayedTextView.SetPosition( Vector3( 0.0f, 0.0f, DISPLAYED_TEXT_VIEW_Z_OFFSET ) );
2105   mDisplayedTextView.SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
2106
2107   mDisplayedTextView.ScrolledSignal().Connect( this, &TextInput::OnTextViewScrolled );
2108
2109   Self().Add( mDisplayedTextView );
2110 }
2111
2112 // Start a timer to initiate, used by the cursor to blink.
2113 void TextInput::StartCursorBlinkTimer()
2114 {
2115   if ( !mCursorBlinkTimer )
2116   {
2117     mCursorBlinkTimer = Timer::New( CURSOR_BLINK_INTERVAL );
2118     mCursorBlinkTimer.TickSignal().Connect( this, &TextInput::OnCursorBlinkTimerTick );
2119   }
2120
2121   if ( !mCursorBlinkTimer.IsRunning() )
2122   {
2123     mCursorBlinkTimer.Start();
2124   }
2125 }
2126
2127 // Start a timer to initiate, used by the cursor to blink.
2128 void TextInput::StopCursorBlinkTimer()
2129 {
2130   if ( mCursorBlinkTimer )
2131   {
2132     mCursorBlinkTimer.Stop();
2133   }
2134 }
2135
2136 void TextInput::StartEditMode()
2137 {
2138   DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput StartEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2139
2140   if(!mEditModeActive)
2141   {
2142     SetKeyInputFocus();
2143   }
2144
2145   if ( mDoubleTapDetector )
2146   {
2147     mDoubleTapDetector.Attach( Self() );
2148   }
2149 }
2150
2151 void TextInput::EndEditMode()
2152 {
2153   DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput EndEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2154
2155   ClearKeyInputFocus();
2156
2157   if ( mDoubleTapDetector )
2158   {
2159     mDoubleTapDetector.Detach( Self() );
2160   }
2161 }
2162
2163 void TextInput::ApplyPreEditStyle( std::size_t preEditStartPosition, std::size_t preEditStringLength )
2164 {
2165   if ( mPreEditFlag && ( preEditStringLength > 0 ) )
2166   {
2167     mUnderlinedPriorToPreEdit = mInputStyle.IsUnderlineEnabled();
2168     TextStyle style;
2169     style.SetUnderline( true );
2170     ApplyStyleToRange( style, TextStyle::UNDERLINE , preEditStartPosition, preEditStartPosition + preEditStringLength -1 );
2171   }
2172 }
2173
2174 void TextInput::RemovePreEditStyle()
2175 {
2176   if ( !mUnderlinedPriorToPreEdit )
2177   {
2178     TextStyle style;
2179     style.SetUnderline( false );
2180     SetActiveStyle( style, TextStyle::UNDERLINE );
2181   }
2182 }
2183
2184 // IMF related methods
2185
2186
2187 ImfManager::ImfCallbackData TextInput::ImfEventReceived( Dali::ImfManager& imfManager, const ImfManager::ImfEventData&  imfEvent )
2188 {
2189   bool update( false );
2190   bool preeditResetRequired ( false );
2191
2192   if (imfEvent.eventName != ImfManager::GETSURROUNDING )
2193   {
2194     HidePopup(); // If Pop-up shown then hides it as editing text.
2195   }
2196
2197   switch ( imfEvent.eventName )
2198   {
2199     case ImfManager::PREEDIT:
2200     {
2201       mIgnoreFirstCommitFlag = false;
2202
2203       // 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
2204       if ( mHighlightMeshActor &&  (!imfEvent.predictiveString.empty()) )
2205       {
2206         // replaces highlighted text with new character
2207         DeleteHighlightedText( false );
2208       }
2209
2210       preeditResetRequired = PreEditReceived( imfEvent.predictiveString, imfEvent.cursorOffset );
2211
2212       if( IsScrollEnabled() )
2213       {
2214         // Calculates the new cursor position (in actor coordinates)
2215         const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2216         ScrollTextViewToMakeCursorVisible( cursorPosition );
2217       }
2218
2219       update = true;
2220
2221       break;
2222     }
2223     case ImfManager::COMMIT:
2224     {
2225       if( mIgnoreFirstCommitFlag )
2226       {
2227         // Do not commit in this case when keyboard sends a commit when shows for the first time (work-around for imf keyboard).
2228         mIgnoreFirstCommitFlag = false;
2229       }
2230       else
2231       {
2232         // A Commit message is a word that has been accepted, it may have been a pre-edit word previously but now commited.
2233
2234         // 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
2235         if ( mHighlightMeshActor &&  (!imfEvent.predictiveString.empty()) )
2236         {
2237           // replaces highlighted text with new character
2238           DeleteHighlightedText( false );
2239         }
2240
2241        // A PreEditReset can cause a commit message to be sent, the Ignore Commit flag is used in scenarios where the word is
2242        // not needed, one such scenario is when the pre-edit word is too long to fit.
2243        if ( !mIgnoreCommitFlag )
2244        {
2245          update = CommitReceived( imfEvent.predictiveString );
2246        }
2247        else
2248        {
2249          mIgnoreCommitFlag = false; // reset ignore flag so next commit is acted upon.
2250        }
2251       }
2252
2253       if( update )
2254       {
2255         if( IsScrollEnabled() )
2256         {
2257           // Calculates the new cursor position (in actor coordinates)
2258           const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2259
2260           ScrollTextViewToMakeCursorVisible( cursorPosition );
2261         }
2262       }
2263       break;
2264     }
2265     case ImfManager::DELETESURROUNDING:
2266     {
2267       DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - delete surrounding mPreEditFlag[%s] cursor offset[%d] characters to delete[%d] position to delete[%u] \n",
2268                      (mPreEditFlag)?"true":"false", imfEvent.cursorOffset, imfEvent.numberOfChars, static_cast<std::size_t>( mCursorPosition+imfEvent.cursorOffset) );
2269
2270       mPreEditFlag = false;
2271
2272       std::size_t toDelete = 0;
2273       std::size_t numberOfCharacters = 0;
2274
2275       if( mHighlightMeshActor )
2276       {
2277         // delete highlighted text.
2278         toDelete = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2279         numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - toDelete;
2280       }
2281       else
2282       {
2283         if( static_cast<std::size_t>(std::abs( imfEvent.cursorOffset )) < mCursorPosition )
2284         {
2285           toDelete = mCursorPosition + imfEvent.cursorOffset;
2286         }
2287         if( toDelete + imfEvent.numberOfChars > mStyledText.size() )
2288         {
2289           numberOfCharacters = mStyledText.size() - toDelete;
2290         }
2291         else
2292         {
2293           numberOfCharacters = imfEvent.numberOfChars;
2294         }
2295       }
2296       DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding pre-delete range mCursorPosition[%u] \n", mCursorPosition);
2297       DeleteRange( toDelete, numberOfCharacters );
2298
2299       mCursorPosition = toDelete;
2300       mNumberOfSurroundingCharactersDeleted = numberOfCharacters;
2301
2302       EmitTextModified();
2303
2304       DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding post-delete range mCursorPosition[%u] \n", mCursorPosition);
2305       break;
2306     }
2307     case ImfManager::GETSURROUNDING:
2308     {
2309       // 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
2310       // the next key pressed.  Instead the Select function sets the cursor position and surrounding text.
2311       if (! ( mHighlightMeshActor || mSelectingText ) )
2312       {
2313         std::string text( GetText() );
2314         DALI_LOG_INFO( gLogFilter, Debug::General, "OnKey - surrounding text - set text [%s] and cursor[%u] \n", text.c_str(), mCursorPosition );
2315
2316         imfManager.SetCursorPosition( mCursorPosition );
2317         imfManager.SetSurroundingText( text );
2318       }
2319
2320       if( 0 != mNumberOfSurroundingCharactersDeleted )
2321       {
2322         mDisplayedTextView.RemoveTextFrom( mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2323         mNumberOfSurroundingCharactersDeleted = 0;
2324
2325         if( mStyledText.empty() )
2326         {
2327           // Styled text is empty, so set the placeholder text.
2328           mDisplayedTextView.SetText( mStyledPlaceHolderText );
2329           mPlaceHolderSet = true;
2330         }
2331       }
2332       break;
2333     }
2334     case ImfManager::VOID:
2335     {
2336       DALI_ASSERT_DEBUG( false );
2337     }
2338   } // end switch
2339
2340   ImfManager::ImfCallbackData callbackData( update, mCursorPosition, GetText(), preeditResetRequired );
2341
2342   return callbackData;
2343 }
2344
2345 bool TextInput::PreEditReceived(const std::string& keyString, std::size_t cursorOffset )
2346 {
2347   mPreserveCursorPosition = false;  // As in pre-edit state we should have the cursor at the end of the word displayed not last touch position.
2348
2349   DALI_LOG_INFO(gLogFilter, Debug::General, ">>PreEditReceived preserveCursorPos[%d] mCursorPos[%d] mPreEditFlag[%d]\n",
2350                 mPreserveCursorPosition, mCursorPosition, mPreEditFlag );
2351
2352   bool preeditResetRequest ( false );
2353
2354   if( mPreEditFlag ) // Already in pre-edit state.
2355   {
2356     if( mStyledText.size() >= mMaxStringLength )
2357     {
2358       DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived styledTextSize >= mMaxStringLength \n");
2359       // Cannot fit these characters into field, clear pre-edit.
2360       if ( !mUnderlinedPriorToPreEdit )
2361       {
2362         TextStyle style;
2363         style.SetUnderline( false );
2364         ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
2365       }
2366       mIgnoreCommitFlag = true;
2367       preeditResetRequest = false; // this will reset the keyboard's predictive suggestions.
2368       mPreEditFlag = false;
2369       EmitMaxInputCharactersReachedSignal();
2370     }
2371     else
2372     {
2373       // delete existing pre-edit string
2374       const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2375
2376       // Store new pre-edit string
2377       mPreEditString.SetText( keyString );
2378
2379       if ( keyString.empty() )
2380       {
2381         mPreEditFlag = false;
2382         mCursorPosition = mPreEditStartPosition;
2383
2384         if( mStyledText.empty() )
2385         {
2386           // Styled text is empty, so set the placeholder text.
2387           mDisplayedTextView.SetText( mStyledPlaceHolderText );
2388           mPlaceHolderSet = true;
2389         }
2390         else
2391         {
2392           mDisplayedTextView.RemoveTextFrom( mPreEditStartPosition, numberOfCharactersToReplace );
2393         }
2394         GetTextLayoutInfo();
2395         EmitTextModified();
2396       }
2397       else
2398       {
2399         // Insert new pre-edit string. InsertAt updates the size and position table.
2400         mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersToReplace );
2401         // 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.
2402         mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2403         ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2404         DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] \n", mCursorPosition);
2405         EmitTextModified();
2406       }
2407       // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2408       DrawCursor();
2409     }
2410   }
2411   else  // mPreEditFlag not set
2412   {
2413     if ( !keyString.empty() ) // Imf can send an empty pre-edit followed by Backspace instead of a commit.
2414     {
2415       DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived Initial Pre-Edit string \n");
2416       // new pre-edit so move into pre-edit state by setting flag
2417       mPreEditFlag = true;
2418       mPreEditString.SetText( keyString ); // store new pre-edit string
2419       mPreEditStartPosition = mCursorPosition; // store starting cursor position of pre-edit so know where to re-start from
2420       mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, 0 );
2421       // 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.
2422       mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2423       ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2424       DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] mPreEditStartPosition[%u]\n", mCursorPosition, mPreEditStartPosition);
2425       // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2426       DrawCursor();
2427       EmitTextModified();
2428     }
2429     else
2430     {
2431       DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived with empty keyString\n");
2432     }
2433   }
2434
2435   return preeditResetRequest;
2436 }
2437
2438 bool TextInput::CommitReceived(const std::string& keyString )
2439 {
2440   DALI_LOG_INFO(gLogFilter, Debug::General, ">>CommitReceived preserveCursorPos[%d] mPreEditStartPosition [%d] mCursorPos[%d] mPreEditFlag[%d] mIgnoreCommitFlag[%s]\n",
2441       mPreserveCursorPosition, mPreEditStartPosition, mCursorPosition, mPreEditFlag, (mIgnoreCommitFlag)?"true":"false" );
2442
2443   bool update( false );
2444
2445   RemovePreEditStyle();
2446
2447   const std::size_t styledTextSize( mStyledText.size() );
2448   if( styledTextSize >= mMaxStringLength )
2449   {
2450     // Cannot fit these characters into field, clear pre-edit.
2451     if ( mPreEditFlag )
2452     {
2453       mIgnoreCommitFlag = true;
2454       mPreEditFlag = false;
2455     }
2456     EmitMaxInputCharactersReachedSignal();
2457   }
2458   else
2459   {
2460     if( mPreEditFlag )
2461     {
2462       // delete existing pre-edit string
2463       const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2464       mPreEditFlag = false;
2465
2466       DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived mPreserveCursorPosition[%s] mPreEditStartPosition[%u]\n",
2467                     (mPreserveCursorPosition)?"true":"false", mPreEditStartPosition );
2468
2469       if ( mPreserveCursorPosition ) // PreEditReset has been called triggering this commit.
2470       {
2471         // No need to update cursor position as Cursor location given by touch.
2472         InsertAt( Text( keyString ), mPreEditStartPosition, numberOfCharactersToReplace );
2473         mPreserveCursorPosition = false;
2474       }
2475       else
2476       {
2477         // Cursor not set by touch so needs to be re-positioned to input more text
2478         mCursorPosition = mPreEditStartPosition + InsertAt( Text(keyString), mPreEditStartPosition, numberOfCharactersToReplace ); // update cursor position as InsertAt, re-draw cursor with this
2479
2480         // If a space or enter caused the commit then our string is one longer than the string given to us by the commit key.
2481         if ( mCommitByKeyInput )
2482         {
2483           mCursorPosition = std::min ( mCursorPosition + 1, mStyledText.size() );
2484           mCommitByKeyInput = false;
2485         }
2486       }
2487
2488       EmitTextModified();
2489
2490       if ( mSelectTextOnCommit )
2491       {
2492         SelectText(mRequestedSelection.mStartOfSelection, mRequestedSelection.mEndOfSelection );
2493       }
2494
2495       update = true;
2496     }
2497     else // mPreEditFlag not set
2498     {
2499       if ( !mIgnoreCommitFlag ) // Check if this commit should be ignored.
2500       {
2501         if( mStyledText.empty() && mPlaceHolderSet )
2502         {
2503           // If the styled text is empty and the placeholder text is set, it needs to be cleared.
2504           mDisplayedTextView.SetText( "" );
2505           mNumberOfSurroundingCharactersDeleted = 0;
2506           mPlaceHolderSet = false;
2507         }
2508         mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2509         update = true;
2510         mNumberOfSurroundingCharactersDeleted = 0;
2511         EmitTextModified();
2512       }
2513       else
2514       {
2515         mIgnoreCommitFlag = false; // Reset flag so future commits will not be ignored.
2516       }
2517     }
2518   }
2519
2520   mSelectTextOnCommit = false;
2521
2522   DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived << mCursorPos[%d] mPreEditFlag[%d] update[%s] \n",
2523                                              mCursorPosition, mPreEditFlag, (update)?"true":"false" );
2524
2525   return update;
2526 }
2527
2528 // End of IMF related methods
2529
2530 std::size_t TextInput::DeletePreEdit()
2531 {
2532   DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeletePreEdit mPreEditFlag[%s] \n", (mPreEditFlag)?"true":"false");
2533
2534   DALI_ASSERT_DEBUG( mPreEditFlag );
2535
2536   const std::size_t preEditStringLength = mPreEditString.GetLength();
2537   const std::size_t styledTextSize = mStyledText.size();
2538
2539   std::size_t endPosition = mPreEditStartPosition + preEditStringLength;
2540
2541   // Prevents erase items outside mStyledText bounds.
2542   if( mPreEditStartPosition > styledTextSize )
2543   {
2544     DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. mPreEditStartPosition > mStyledText.size()" );
2545     mPreEditStartPosition = styledTextSize;
2546   }
2547
2548   if( ( endPosition > styledTextSize ) || ( endPosition < mPreEditStartPosition ) )
2549   {
2550     DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. ( endPosition > mStyledText.size() ) || ( endPosition < mPreEditStartPosition )" );
2551     endPosition = styledTextSize;
2552   }
2553
2554   mStyledText.erase( mStyledText.begin() + mPreEditStartPosition, mStyledText.begin() + endPosition );
2555
2556   // DeletePreEdit() doesn't remove characters from the text-view because may be followed by an InsertAt() which inserts characters,
2557   // in that case, the Insert should use the returned number of deleted characters and replace the text which helps the text-view to
2558   // reuse glyphs.
2559   // In case DeletePreEdit() is not followed by an InsertAt() characters must be deleted after this call.
2560
2561   return preEditStringLength;
2562 }
2563
2564 void TextInput::PreEditReset( bool preserveCursorPosition )
2565 {
2566   DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReset preserveCursorPos[%d] mCursorPos[%d] \n",
2567                 preserveCursorPosition, mCursorPosition);
2568
2569   // 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.
2570   mPreserveCursorPosition = preserveCursorPosition;
2571
2572   // Reset incase we are in a pre-edit state.
2573   ImfManager imfManager = ImfManager::Get();
2574   if ( imfManager )
2575   {
2576     imfManager.Reset(); // Will trigger a commit message
2577   }
2578 }
2579
2580 void TextInput::CursorUpdate()
2581 {
2582   DrawCursor();
2583
2584   ImfManager imfManager = ImfManager::Get();
2585   if ( imfManager )
2586   {
2587     std::string text( GetText() );
2588     imfManager.SetSurroundingText( text );  // Notifying IMF of a cursor change triggers a surrounding text request so updating it now.
2589     imfManager.SetCursorPosition ( mCursorPosition );
2590     imfManager.NotifyCursorPosition();
2591   }
2592 }
2593
2594 /* Delete highlighted characters redisplay*/
2595 void TextInput::DeleteHighlightedText( bool inheritStyle )
2596 {
2597   DALI_LOG_INFO( gLogFilter, Debug::General, "DeleteHighlightedText handlePosOne[%u] handlePosTwo[%u]\n", mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
2598
2599   if( mHighlightMeshActor )
2600   {
2601     mCursorPosition = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2602
2603     MarkupProcessor::StyledTextArray::iterator start = mStyledText.begin() + mCursorPosition;
2604     MarkupProcessor::StyledTextArray::iterator end =  mStyledText.begin() + std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2605
2606     // Get the styled text of the characters to be deleted as it may be needed if
2607     // the "exceed the text-input's boundaries" option is disabled.
2608     MarkupProcessor::StyledTextArray styledCharactersToDelete;
2609
2610     styledCharactersToDelete.insert( styledCharactersToDelete.begin(), start, end );
2611
2612     mStyledText.erase( start, end ); // erase range of characters
2613
2614     // Remove text from TextView.
2615
2616     if( mStyledText.empty() )
2617     {
2618       // Styled text is empty, so set the placeholder text.
2619       mDisplayedTextView.SetText( mStyledPlaceHolderText );
2620       mPlaceHolderSet = true;
2621     }
2622     else
2623     {
2624       const std::size_t numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - mCursorPosition;
2625
2626       mDisplayedTextView.RemoveTextFrom( mCursorPosition, numberOfCharacters );
2627
2628       // It may happen than after removing a white space or a new line character,
2629       // two words merge, this new word could be big enough to not fit in its
2630       // current line, so moved to the next one, and make some part of the text to
2631       // exceed the text-input's boundary.
2632       if( !mExceedEnabled )
2633       {
2634         // Get the new text layout after removing some characters.
2635         mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2636
2637         // Get text-input's size.
2638         const Vector3& size = GetControlSize();
2639
2640         if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2641             ( mTextLayoutInfo.mTextSize.height > size.height ) )
2642         {
2643           mDisplayedTextView.InsertTextAt( mCursorPosition, styledCharactersToDelete );
2644
2645           mStyledText.insert( mStyledText.begin() + mCursorPosition,
2646                               styledCharactersToDelete.begin(),
2647                               styledCharactersToDelete.end() );
2648         }
2649       }
2650     }
2651     GetTextLayoutInfo();
2652
2653     RemoveHighlight();
2654
2655     EmitTextModified();
2656
2657     if( inheritStyle )
2658     {
2659       const TextStyle oldInputStyle( mInputStyle );
2660
2661       mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2662
2663       if( oldInputStyle != mInputStyle )
2664       {
2665         // Updates the line height accordingly with the input style.
2666         UpdateLineHeight();
2667
2668         EmitStyleChangedSignal();
2669       }
2670     }
2671   }
2672 }
2673
2674 void TextInput::DeleteRange( const std::size_t start, const std::size_t ncharacters )
2675 {
2676   DALI_ASSERT_DEBUG( start <= mStyledText.size() );
2677   DALI_ASSERT_DEBUG( !mStyledText.empty() );
2678
2679   DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeleteRange pre mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2680
2681
2682   if ( ( !mStyledText.empty()) && ( ( start + ncharacters ) <= mStyledText.size() ) )
2683   {
2684     MarkupProcessor::StyledTextArray::iterator itStart =  mStyledText.begin() + start;
2685     MarkupProcessor::StyledTextArray::iterator itEnd =  mStyledText.begin() + start + ncharacters;
2686
2687     mStyledText.erase(itStart, itEnd);
2688
2689     // update the selection handles if they are visible.
2690     if( mHighlightMeshActor )
2691     {
2692       std::size_t& minHandle = ( mSelectionHandleOnePosition <= mSelectionHandleTwoPosition ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition );
2693       std::size_t& maxHandle = ( mSelectionHandleTwoPosition > mSelectionHandleOnePosition ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition );
2694
2695       if( minHandle >= start + ncharacters )
2696       {
2697         minHandle -= ncharacters;
2698       }
2699       else if( ( minHandle > start ) && ( minHandle < start + ncharacters ) )
2700       {
2701         minHandle = start;
2702       }
2703
2704       if( maxHandle >= start + ncharacters )
2705       {
2706         maxHandle -= ncharacters;
2707       }
2708       else if( ( maxHandle > start ) && ( maxHandle < start + ncharacters ) )
2709       {
2710         maxHandle = start;
2711       }
2712     }
2713
2714     // 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.
2715   }
2716
2717   DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteRange<< post mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2718
2719   // Although mStyledText has been set to a new text string we no longer re-draw the text or notify the cursor change.
2720   // This is a performance decision as the use of this function often means the text is being replaced or just deleted.
2721   // Mean we do not re-draw the text more than we have too.
2722 }
2723
2724 /* Delete character at current cursor position and redisplay*/
2725 void TextInput::DeleteCharacter( std::size_t positionToDelete )
2726 {
2727   // Ensure positionToDelete is not out of bounds.
2728   DALI_ASSERT_DEBUG( positionToDelete <= mStyledText.size() );
2729   DALI_ASSERT_DEBUG( !mStyledText.empty() );
2730   DALI_ASSERT_DEBUG( positionToDelete > 0 );
2731
2732   DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteCharacter positionToDelete[%u]", positionToDelete );
2733
2734
2735   if ( ( !mStyledText.empty()) && ( positionToDelete > 0 ) && positionToDelete <= mStyledText.size() )  // don't try to delete if no characters left of cursor
2736   {
2737     MarkupProcessor::StyledTextArray::iterator it =  mStyledText.begin() + positionToDelete - 1;
2738
2739     // Get the styled text of the character to be deleted as it may be needed if
2740     // the "exceed the text-input's boundaries" option is disabled.
2741     const MarkupProcessor::StyledText styledCharacterToDelete( *it );
2742
2743     mStyledText.erase(it);  // erase the character left of positionToDelete
2744
2745     if( mStyledText.empty() )
2746     {
2747       // Styled text is empty, so set the placeholder text.
2748       mDisplayedTextView.SetText( mStyledPlaceHolderText );
2749       mPlaceHolderSet = true;
2750     }
2751     else
2752     {
2753       mDisplayedTextView.RemoveTextFrom( positionToDelete - 1, 1 );
2754
2755       const Character characterToDelete = styledCharacterToDelete.mText[0];
2756
2757       // It may happen than after removing a white space or a new line character,
2758       // two words merge, this new word could be big enough to not fit in its
2759       // current line, so moved to the next one, and make some part of the text to
2760       // exceed the text-input's boundary.
2761       if( !mExceedEnabled )
2762       {
2763         if( characterToDelete.IsWhiteSpace() || characterToDelete.IsNewLine() )
2764         {
2765           // Get the new text layout after removing one character.
2766           mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2767
2768           // Get text-input's size.
2769           const Vector3& size = GetControlSize();
2770
2771           if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2772               ( mTextLayoutInfo.mTextSize.height > size.height ) )
2773           {
2774             MarkupProcessor::StyledTextArray array;
2775             array.push_back( styledCharacterToDelete );
2776             mDisplayedTextView.InsertTextAt( positionToDelete - 1, array );
2777
2778             mStyledText.insert( mStyledText.begin() + ( positionToDelete - 1 ), styledCharacterToDelete );
2779           }
2780         }
2781       }
2782     }
2783     GetTextLayoutInfo();
2784
2785     ShowGrabHandleAndSetVisibility( false );
2786
2787     mCursorPosition = positionToDelete -1;
2788
2789     const TextStyle oldInputStyle( mInputStyle );
2790
2791     mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2792
2793     if( oldInputStyle != mInputStyle )
2794     {
2795       // Updates the line height accordingly with the input style.
2796       UpdateLineHeight();
2797
2798       EmitStyleChangedSignal();
2799     }
2800   }
2801 }
2802
2803 /*Insert new character into the string and (optionally) redisplay text-input*/
2804 std::size_t TextInput::InsertAt( const Text& newText, const std::size_t insertionPosition, const std::size_t numberOfCharactersToReplace )
2805 {
2806   DALI_LOG_INFO(gLogFilter, Debug::General, "InsertAt insertionPosition[%u]\n", insertionPosition );
2807
2808   // Ensure insertionPosition is not out of bounds.
2809   DALI_ASSERT_ALWAYS( insertionPosition <= mStyledText.size() );
2810
2811   bool textExceedsMaximunNumberOfCharacters = false;
2812   bool textExceedsBoundary = false;
2813   std::size_t insertedStringLength = DoInsertAt( newText, insertionPosition, numberOfCharactersToReplace, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
2814
2815   ShowGrabHandleAndSetVisibility( false );
2816
2817   if( textExceedsMaximunNumberOfCharacters || textExceedsBoundary )
2818   {
2819     if( mPreEditFlag )
2820     {
2821       mIgnoreCommitFlag = true;
2822       mPreEditFlag = false;
2823       // A PreEditReset( false ) should be triggered from here if the keyboards predictive suggestions must be cleared.
2824       // Although can not directly call PreEditReset() as it will cause a recursive emit loop.
2825     }
2826
2827     if( textExceedsMaximunNumberOfCharacters )
2828     {
2829       EmitMaxInputCharactersReachedSignal();
2830     }
2831
2832     if( textExceedsBoundary )
2833     {
2834       EmitInputTextExceedsBoundariesSignal();
2835       PreEditReset( false );
2836     }
2837   }
2838
2839   return insertedStringLength;
2840 }
2841
2842 ImageActor TextInput::CreateCursor( const Vector4& color)
2843 {
2844   ImageActor cursor;
2845   cursor = CreateSolidColorActor(color);
2846   cursor.SetName( "Cursor" );
2847
2848   cursor.SetParentOrigin(ParentOrigin::TOP_LEFT);
2849   cursor.SetAnchorPoint(AnchorPoint::BOTTOM_LEFT);
2850   cursor.SetVisible(false);
2851
2852   return cursor;
2853 }
2854
2855 void TextInput::AdvanceCursor(bool reverse, std::size_t places)
2856 {
2857   // As cursor is not moving due to grab handle, handle should be hidden.
2858   ShowGrabHandleAndSetVisibility( false );
2859
2860   bool cursorPositionChanged = false;
2861   if (reverse)
2862   {
2863     if ( mCursorPosition >= places )
2864     {
2865       mCursorPosition = mCursorPosition - places;
2866       cursorPositionChanged = true;
2867     }
2868   }
2869   else
2870   {
2871     if ((mCursorPosition + places) <= mStyledText.size())
2872     {
2873       mCursorPosition = mCursorPosition + places;
2874       cursorPositionChanged = true;
2875     }
2876   }
2877
2878   if( cursorPositionChanged )
2879   {
2880     const std::size_t cursorPositionForStyle = ( 0 == mCursorPosition ? 0 : mCursorPosition - 1 );
2881
2882     const TextStyle oldInputStyle( mInputStyle );
2883     mInputStyle = GetStyleAt( cursorPositionForStyle ); // Inherit style from selected position.
2884
2885     DrawCursor();
2886
2887     if( oldInputStyle != mInputStyle )
2888     {
2889       // Updates the line height accordingly with the input style.
2890       UpdateLineHeight();
2891
2892       EmitStyleChangedSignal();
2893     }
2894
2895     ImfManager imfManager = ImfManager::Get();
2896     if ( imfManager )
2897     {
2898       imfManager.SetCursorPosition ( mCursorPosition );
2899       imfManager.NotifyCursorPosition();
2900     }
2901   }
2902 }
2903
2904 void TextInput::DrawCursor(const std::size_t nthChar)
2905 {
2906   // Get height of cursor and set its size
2907   Size size( CURSOR_THICKNESS, 0.0f );
2908   if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
2909   {
2910     size.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height;
2911   }
2912   else
2913   {
2914     // Measure Font so know how big text will be if no initial text to measure.
2915     size.height = mLineHeight;
2916   }
2917
2918   mCursor.SetSize(size);
2919
2920   // If the character is italic then the cursor also tilts.
2921   mCursor.SetRotation( mInputStyle.IsItalicsEnabled() ? Degree( mInputStyle.GetItalicsAngle() - CURSOR_ANGLE_OFFSET ) : Degree( 0.f ), Vector3::ZAXIS );
2922
2923   DALI_ASSERT_DEBUG( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
2924
2925   if ( ( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) )
2926   {
2927     Vector3 altPosition;    // Alternate (i.e. opposite direction) cursor position.
2928     bool altPositionValid;  // Alternate cursor validity flag.
2929     bool directionRTL;      // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
2930     Vector3 position = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
2931
2932     SetAltCursorEnabled( altPositionValid );
2933
2934     if(!altPositionValid)
2935     {
2936       mCursor.SetPosition( position + UI_OFFSET );
2937     }
2938     else
2939     {
2940       size.height *= 0.5f;
2941       mCursor.SetSize(size);
2942       mCursor.SetPosition( position + UI_OFFSET - Vector3(0.0f, directionRTL ? 0.0f : size.height, 0.0f) );
2943
2944       // TODO: change this cursor pos, to be the one where the cursor is sourced from.
2945       Size rowSize = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) );
2946       size.height = rowSize.height * 0.5f;
2947       mCursorRTL.SetSize(size);
2948       mCursorRTL.SetPosition( altPosition + UI_OFFSET - Vector3(0.0f, directionRTL ? size.height : 0.0f, 0.0f) );
2949     }
2950
2951     if( IsScrollEnabled() )
2952     {
2953       // Whether cursor and grab handle are inside the boundaries of the text-input when text scroll is enabled.
2954       mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( position, size, GetControlSize() );
2955     }
2956   } // EditMode
2957 }
2958
2959 void TextInput::SetAltCursorEnabled( bool enabled )
2960 {
2961   mCursorRTLEnabled = enabled;
2962   mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2963 }
2964
2965 void TextInput::SetCursorVisibility( bool visible )
2966 {
2967   mCursorVisibility = visible;
2968   mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
2969   mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2970 }
2971
2972 void TextInput::CreateGrabHandle( Dali::Image image )
2973 {
2974   if ( !mGrabHandle )
2975   {
2976     if ( !image )
2977     {
2978       mGrabHandleImage = Image::New(DEFAULT_GRAB_HANDLE);
2979     }
2980     else
2981     {
2982       mGrabHandleImage = image;
2983     }
2984
2985     mGrabHandle = ImageActor::New(mGrabHandleImage);
2986     mGrabHandle.SetParentOrigin(ParentOrigin::TOP_LEFT);
2987     mGrabHandle.SetAnchorPoint(AnchorPoint::TOP_CENTER);
2988
2989     mGrabHandle.SetDrawMode(DrawMode::OVERLAY);
2990
2991     ShowGrabHandleAndSetVisibility( false );
2992
2993     CreateGrabArea( mGrabHandle );
2994
2995     mActiveLayer.Add(mGrabHandle);
2996   }
2997 }
2998
2999 void TextInput::CreateGrabArea( Actor& parent )
3000 {
3001   mGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3002   mGrabArea.SetName( "GrabArea" );
3003   mGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3004   mGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE ) ) );  // grab area to be larger than text actor
3005   mGrabArea.TouchedSignal().Connect(this,&TextInput::OnPressDown);
3006   mTapDetector.Attach( mGrabArea );
3007   mPanGestureDetector.Attach( mGrabArea );
3008   mLongPressDetector.Attach( mGrabArea );
3009
3010   parent.Add(mGrabArea);
3011 }
3012
3013 Vector3 TextInput::MoveGrabHandle( const Vector2& displacement )
3014 {
3015   Vector3 actualHandlePosition;
3016
3017   if (mGrabHandle)
3018   {
3019     mActualGrabHandlePosition.x += displacement.x;
3020     mActualGrabHandlePosition.y += displacement.y;
3021
3022     // Grab handle should jump to the nearest character and take cursor with it
3023     std::size_t newCursorPosition = 0;
3024     ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY(), newCursorPosition );
3025
3026     actualHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition );
3027
3028     bool handleVisible = true;
3029
3030     if( IsScrollEnabled() )
3031     {
3032       const Vector3 controlSize = GetControlSize();
3033       const Size cursorSize = GetRowRectFromCharacterPosition( GetVisualPosition( newCursorPosition ) );
3034       // Scrolls the text if the handle is not in a visible position
3035       handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3036                                                   cursorSize,
3037                                                   controlSize );
3038
3039       if( handleVisible )
3040       {
3041         StopScrollTimer();
3042         mCurrentHandlePosition = actualHandlePosition;
3043         mScrollDisplacement = Vector2::ZERO;
3044       }
3045       else
3046       {
3047         if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3048         {
3049           mScrollDisplacement.x = -SCROLL_SPEED;
3050         }
3051         else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3052         {
3053           mScrollDisplacement.x = SCROLL_SPEED;
3054         }
3055         if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3056         {
3057           mScrollDisplacement.y = -SCROLL_SPEED;
3058         }
3059         else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3060         {
3061           mScrollDisplacement.y = SCROLL_SPEED;
3062         }
3063         StartScrollTimer();
3064       }
3065     }
3066
3067     if( handleVisible &&                           // Only redraw cursor and do updates if position changed
3068         ( newCursorPosition != mCursorPosition ) ) // and the new position is visible (if scroll is not enabled, it's always true).
3069     {
3070       mCursorPosition = newCursorPosition;
3071
3072       mGrabHandle.SetPosition( actualHandlePosition + UI_OFFSET );
3073
3074       const TextStyle oldInputStyle( mInputStyle );
3075
3076       mInputStyle = GetStyleAtCursor(); //Inherit style from cursor position
3077
3078       CursorUpdate();  // Let keyboard know the new cursor position so can 're-capture' for prediction.
3079
3080       if( oldInputStyle != mInputStyle )
3081       {
3082         // Updates the line height accordingly with the input style.
3083         UpdateLineHeight();
3084
3085         EmitStyleChangedSignal();
3086       }
3087     }
3088   }
3089
3090   return actualHandlePosition;
3091 }
3092
3093 void TextInput::ShowGrabHandle( bool visible )
3094 {
3095   if ( IsGrabHandleEnabled() )
3096   {
3097     if( mGrabHandle )
3098     {
3099       mGrabHandle.SetVisible( mGrabHandleVisibility );
3100     }
3101     StartMonitoringStageForTouch();
3102   }
3103 }
3104
3105 void TextInput::ShowGrabHandleAndSetVisibility( bool visible )
3106 {
3107   mGrabHandleVisibility = visible;
3108   ShowGrabHandle( visible );
3109 }
3110
3111 // Callbacks connected to be Property notifications for Boundary checking.
3112
3113 void TextInput::OnLeftBoundaryExceeded(PropertyNotification& source)
3114 {
3115   mIsSelectionHandleOneFlipped = true;
3116   mSelectionHandleOne.SetScale( -1.0f, 1.0f, 1.0f );
3117   mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3118 }
3119
3120 void TextInput::OnReturnToLeftBoundary(PropertyNotification& source)
3121 {
3122   mIsSelectionHandleOneFlipped = false;
3123   mSelectionHandleOne.SetScale( 1.0f, 1.0f, 1.0f );
3124   mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3125 }
3126
3127 void TextInput::OnRightBoundaryExceeded(PropertyNotification& source)
3128 {
3129   mIsSelectionHandleTwoFlipped = true;
3130   mSelectionHandleTwo.SetScale( -1.0f, 1.0f, 1.0f );
3131   mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3132 }
3133
3134 void TextInput::OnReturnToRightBoundary(PropertyNotification& source)
3135 {
3136   mIsSelectionHandleTwoFlipped = false;
3137   mSelectionHandleTwo.SetScale( 1.0f, 1.0f, 1.0f );
3138   mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3139 }
3140
3141 // todo change PropertyNotification signal definition to include Actor. Hence won't need duplicate functions.
3142 void TextInput::OnHandleOneLeavesBoundary( PropertyNotification& source)
3143 {
3144   mSelectionHandleOne.SetOpacity(0.0f);
3145 }
3146
3147 void TextInput::OnHandleOneWithinBoundary(PropertyNotification& source)
3148 {
3149   mSelectionHandleOne.SetOpacity(1.0f);
3150 }
3151
3152 void TextInput::OnHandleTwoLeavesBoundary( PropertyNotification& source)
3153 {
3154   mSelectionHandleTwo.SetOpacity(0.0f);
3155 }
3156
3157 void TextInput::OnHandleTwoWithinBoundary(PropertyNotification& source)
3158 {
3159   mSelectionHandleTwo.SetOpacity(1.0f);
3160 }
3161
3162 // End of Callbacks connected to be Property notifications for Boundary checking.
3163
3164 void TextInput::SetUpHandlePropertyNotifications()
3165 {
3166   /* Property notifications for handles exceeding the boundary and returning back within boundary */
3167
3168   Vector3 handlesize = GetSelectionHandleSize();
3169
3170   // Exceeding horizontal boundary
3171   PropertyNotification leftNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.x + handlesize.x) );
3172   leftNotification.NotifySignal().Connect( this, &TextInput::OnLeftBoundaryExceeded );
3173
3174   PropertyNotification rightNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.z - handlesize.x ) );
3175   rightNotification.NotifySignal().Connect( this, &TextInput::OnRightBoundaryExceeded );
3176
3177   // Within horizontal boundary
3178   PropertyNotification leftLeaveNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.x + 2*handlesize.x ) );
3179   leftLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToLeftBoundary );
3180
3181   PropertyNotification rightLeaveNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.z - 2*handlesize.x ) );
3182   rightLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToRightBoundary );
3183
3184   // Exceeding vertical boundary
3185   PropertyNotification verticalExceedNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3186                                                        OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3187                                                                          mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3188   verticalExceedNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneLeavesBoundary );
3189
3190   PropertyNotification verticalExceedNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3191                                                        OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3192                                                                          mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3193   verticalExceedNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoLeavesBoundary );
3194
3195   // Within vertical boundary
3196   PropertyNotification verticalWithinNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3197                                                        InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3198                                                                         mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3199   verticalWithinNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneWithinBoundary );
3200
3201   PropertyNotification verticalWithinNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3202                                                        InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3203                                                                         mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3204   verticalWithinNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoWithinBoundary );
3205 }
3206
3207 void TextInput::CreateSelectionHandles( std::size_t start, std::size_t end, Dali::Image handleOneImage,  Dali::Image handleTwoImage )
3208 {
3209   mSelectionHandleOnePosition = start;
3210   mSelectionHandleTwoPosition = end;
3211
3212   if ( !mSelectionHandleOne )
3213   {
3214     // create normal and pressed images
3215     mSelectionHandleOneImage = Image::New( DEFAULT_SELECTION_HANDLE_ONE );
3216     mSelectionHandleOneImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_ONE_PRESSED );
3217
3218     mSelectionHandleOne = ImageActor::New( mSelectionHandleOneImage );
3219     mSelectionHandleOne.SetName("SelectionHandleOne");
3220     mSelectionHandleOne.SetParentOrigin( ParentOrigin::TOP_LEFT );
3221     mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
3222     mIsSelectionHandleOneFlipped = false;
3223     mSelectionHandleOne.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text
3224
3225     mHandleOneGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3226     mHandleOneGrabArea.SetName("SelectionHandleOneGrabArea");
3227
3228     mHandleOneGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) );  // grab area to be larger than text actor
3229     mHandleOneGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3230
3231     mTapDetector.Attach( mHandleOneGrabArea );
3232     mPanGestureDetector.Attach( mHandleOneGrabArea );
3233
3234     mHandleOneGrabArea.TouchedSignal().Connect(this,&TextInput::OnHandleOneTouched);
3235
3236     mSelectionHandleOne.Add( mHandleOneGrabArea );
3237     mActiveLayer.Add( mSelectionHandleOne );
3238   }
3239
3240   if ( !mSelectionHandleTwo )
3241   {
3242     // create normal and pressed images
3243     mSelectionHandleTwoImage = Image::New( DEFAULT_SELECTION_HANDLE_TWO );
3244     mSelectionHandleTwoImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_TWO_PRESSED );
3245
3246     mSelectionHandleTwo = ImageActor::New( mSelectionHandleTwoImage );
3247     mSelectionHandleTwo.SetName("SelectionHandleTwo");
3248     mSelectionHandleTwo.SetParentOrigin( ParentOrigin::TOP_LEFT );
3249     mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT );
3250     mIsSelectionHandleTwoFlipped = false;
3251     mSelectionHandleTwo.SetDrawMode(DrawMode::OVERLAY); // ensure grab handle above text
3252
3253     mHandleTwoGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3254     mHandleTwoGrabArea.SetName("SelectionHandleTwoGrabArea");
3255     mHandleTwoGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) );  // grab area to be larger than text actor
3256     mHandleTwoGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3257
3258     mTapDetector.Attach( mHandleTwoGrabArea );
3259     mPanGestureDetector.Attach( mHandleTwoGrabArea );
3260
3261     mHandleTwoGrabArea.TouchedSignal().Connect(this, &TextInput::OnHandleTwoTouched);
3262
3263     mSelectionHandleTwo.Add( mHandleTwoGrabArea );
3264
3265     mActiveLayer.Add( mSelectionHandleTwo );
3266   }
3267
3268   SetUpHandlePropertyNotifications();
3269
3270   // update table as text may have changed.
3271   GetTextLayoutInfo();
3272
3273   mSelectionHandleOneActualPosition = GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition );
3274   mSelectionHandleTwoActualPosition = GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition );
3275
3276   mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
3277   mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
3278
3279   // Calculates and set the visibility if the scroll mode is enabled.
3280   bool isSelectionHandleOneVisible = true;
3281   bool isSelectionHandleTwoVisible = true;
3282   if( IsScrollEnabled() )
3283   {
3284     const Vector3& controlSize( GetControlSize() );
3285     isSelectionHandleOneVisible = IsPositionInsideBoundaries( mSelectionHandleOneActualPosition, Size::ZERO, controlSize );
3286     isSelectionHandleTwoVisible = IsPositionInsideBoundaries( mSelectionHandleTwoActualPosition, Size::ZERO, controlSize );
3287     mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
3288     mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
3289   }
3290
3291   CreateHighlight();  // function will only create highlight if not already created.
3292 }
3293
3294 Vector3 TextInput::MoveSelectionHandle( SelectionHandleId handleId, const Vector2& displacement )
3295 {
3296   Vector3 actualHandlePosition;
3297
3298   if ( mSelectionHandleOne && mSelectionHandleTwo )
3299   {
3300     const Vector3& controlSize = GetControlSize();
3301
3302     Size cursorSize( CURSOR_THICKNESS, 0.f );
3303
3304     // Get a reference of the wanted selection handle (handle one or two).
3305     Vector3& actualSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
3306
3307     // Get a reference for the current position of the handle and a copy of its pair
3308     std::size_t& currentSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3309     const std::size_t pairSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition;
3310
3311     // Get a handle of the selection handle actor
3312     ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3313
3314     // Selection handles should jump to the nearest character
3315     std::size_t newHandlePosition = 0;
3316     ReturnClosestIndex( actualSelectionHandlePosition.GetVectorXY(), newHandlePosition );
3317
3318     actualHandlePosition = GetActualPositionFromCharacterPosition( newHandlePosition );
3319
3320     bool handleVisible = true;
3321
3322     if( IsScrollEnabled() )
3323     {
3324       mCurrentSelectionId = handleId;
3325
3326       cursorSize.height = GetRowRectFromCharacterPosition( GetVisualPosition( newHandlePosition ) ).height;
3327       // Restricts the movement of the grab handle inside the boundaries of the text-input.
3328       handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3329                                                   cursorSize,
3330                                                   controlSize );
3331
3332       if( handleVisible )
3333       {
3334         StopScrollTimer();
3335         mCurrentSelectionHandlePosition = actualHandlePosition;
3336         mScrollDisplacement = Vector2::ZERO;
3337       }
3338       else
3339       {
3340         if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3341         {
3342           mScrollDisplacement.x = -SCROLL_SPEED;
3343         }
3344         else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3345         {
3346           mScrollDisplacement.x = SCROLL_SPEED;
3347         }
3348         if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3349         {
3350           mScrollDisplacement.y = -SCROLL_SPEED;
3351         }
3352         else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3353         {
3354           mScrollDisplacement.y = SCROLL_SPEED;
3355         }
3356         StartScrollTimer();
3357       }
3358     }
3359
3360     if ( handleVisible &&                                          // Ensure the handle is visible.
3361          ( newHandlePosition != pairSelectionHandlePosition ) &&   // Ensure handle one is not the same position as handle two.
3362          ( newHandlePosition != currentSelectionHandlePosition ) ) // Ensure the handle has moved.
3363     {
3364       currentSelectionHandlePosition = newHandlePosition;
3365
3366       Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3367       selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3368
3369       UpdateHighlight();
3370
3371       if ( handleId == HandleOne )
3372       {
3373         const TextStyle oldInputStyle( mInputStyle );
3374
3375         // Set Active Style to that of first character in selection
3376         if( mSelectionHandleOnePosition < mStyledText.size() )
3377         {
3378           mInputStyle = ( mStyledText.at( mSelectionHandleOnePosition ) ).mStyle;
3379         }
3380
3381         if( oldInputStyle != mInputStyle )
3382         {
3383           // Updates the line height accordingly with the input style.
3384           UpdateLineHeight();
3385
3386           EmitStyleChangedSignal();
3387         }
3388       }
3389     }
3390   }
3391
3392   return actualHandlePosition; // Returns Handle position passed in if new value not assigned.
3393 }
3394
3395 void TextInput::SetSelectionHandlePosition(SelectionHandleId handleId)
3396 {
3397
3398   const std::size_t selectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3399   ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3400
3401   if ( selectionHandleActor )
3402   {
3403     const Vector3 actualHandlePosition = GetActualPositionFromCharacterPosition( selectionHandlePosition );
3404     Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3405     selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3406
3407     if( IsScrollEnabled() )
3408     {
3409       const Size cursorSize( CURSOR_THICKNESS,
3410                              GetRowRectFromCharacterPosition( GetVisualPosition( selectionHandlePosition ) ).height );
3411       selectionHandleActor.SetVisible( IsPositionInsideBoundaries( actualHandlePosition,
3412                                                                    cursorSize,
3413                                                                    GetControlSize() ) );
3414     }
3415   }
3416 }
3417
3418 std::size_t TextInput::GetVisualPosition(std::size_t logicalPosition) const
3419 {
3420   // Note: we're allowing caller to request a logical position of size (i.e. end of string)
3421   // For now the visual position of end of logical string will be end of visual string.
3422   DALI_ASSERT_DEBUG( logicalPosition <= mTextLayoutInfo.mCharacterLogicalToVisualMap.size() );
3423
3424   return logicalPosition != mTextLayoutInfo.mCharacterLogicalToVisualMap.size() ? mTextLayoutInfo.mCharacterLogicalToVisualMap[logicalPosition] : mTextLayoutInfo.mCharacterLogicalToVisualMap.size();
3425 }
3426
3427 void TextInput::GetVisualTextSelection(std::vector<bool>& selectedVisualText, std::size_t startSelection, std::size_t endSelection)
3428 {
3429   std::vector<int>::iterator it = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin();
3430   std::vector<int>::iterator startSelectionIt = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::min(startSelection, endSelection);
3431   std::vector<int>::iterator endSelectionIt = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::max(startSelection, endSelection);
3432   std::vector<int>::iterator end = mTextLayoutInfo.mCharacterLogicalToVisualMap.end();
3433
3434   selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size() );
3435
3436   // Deselect text prior to startSelectionIt
3437   for(;it!=startSelectionIt;++it)
3438   {
3439     selectedVisualText[*it] = false;
3440   }
3441
3442   // Select text from startSelectionIt -> endSelectionIt
3443   for(;it!=endSelectionIt;++it)
3444   {
3445     selectedVisualText[*it] = true;
3446   }
3447
3448   // Deselect text after endSelection
3449   for(;it!=end;++it)
3450   {
3451     selectedVisualText[*it] = false;
3452   }
3453
3454   selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size(), false );
3455 }
3456
3457 // Calculate the dimensions of the quads they will make the highlight mesh
3458 TextInput::HighlightInfo TextInput::CalculateHighlightInfo()
3459 {
3460   // At the moment there is no public API to modify the block alignment option.
3461   const bool blockAlignEnabled = true;
3462
3463   mNewHighlightInfo.mQuadList.clear(); // clear last quad information.
3464
3465   if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
3466   {
3467     Toolkit::TextView::CharacterLayoutInfoContainer::iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3468     Toolkit::TextView::CharacterLayoutInfoContainer::iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
3469
3470     // Get vector of flags representing characters that are selected (true) vs unselected (false).
3471     std::vector<bool> selectedVisualText;
3472     GetVisualTextSelection(selectedVisualText, mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
3473     std::vector<bool>::iterator selectedIt(selectedVisualText.begin());
3474     std::vector<bool>::iterator selectedEndIt(selectedVisualText.end());
3475
3476     SelectionState selectionState = SelectionNone;          ///< Current selection status of cursor over entire text.
3477     float rowLeft = 0.0f;
3478     float rowRight = 0.0f;
3479     // Keep track of the TextView's min/max extents. Should be able to query this from TextView.
3480     float maxRowLeft = std::numeric_limits<float>::max();
3481     float maxRowRight = 0.0f;
3482
3483     Toolkit::TextView::CharacterLayoutInfoContainer::iterator lastIt = it;
3484
3485     // Scan through entire text.
3486     while(it != end)
3487     {
3488       // selectionState: None when not in selection, Started when in selection, and Ended when reached end of selection.
3489
3490       Toolkit::TextView::CharacterLayoutInfo& charInfo(*it);
3491       bool charSelected( false );
3492       if( selectedIt != selectedEndIt )
3493       {
3494         charSelected = *selectedIt++;
3495       }
3496
3497       if(selectionState == SelectionNone)
3498       {
3499         if(charSelected)
3500         {
3501           selectionState = SelectionStarted;
3502           rowLeft = charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3503           rowRight = rowLeft + charInfo.mSize.width;
3504         }
3505       }
3506       else if(selectionState == SelectionStarted)
3507       {
3508         // break selection on:
3509         // 1. new line causing selection break. (\n or wordwrap)
3510         // 2. character not selected.
3511         if(charInfo.mPosition.y - lastIt->mPosition.y > CHARACTER_THRESHOLD ||
3512            !charSelected)
3513         {
3514           // finished selection.
3515           // TODO: TextView should have a table of visual rows, and each character a reference to the row
3516           // that it resides on. That way this enumeration is not necessary.
3517           Vector2 min, max;
3518           if(lastIt->mIsNewParagraphChar)
3519           {
3520             // 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.
3521             lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3522           }
3523           const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3524           maxRowLeft = std::min(maxRowLeft, min.x);
3525           maxRowRight = std::max(maxRowRight, max.x);
3526           float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3527           float rowTop = rowBottom - rowSize.height;
3528
3529           // Still selected, and block-align mode then set rowRight to max, so it can be clamped afterwards
3530           if(charSelected && blockAlignEnabled)
3531           {
3532             rowRight = std::numeric_limits<float>::max();
3533           }
3534           mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3535
3536           selectionState = SelectionNone;
3537
3538           // Still selected? start a new selection
3539           if( charSelected )
3540           {
3541             // if block-align mode then set rowLeft to min, so it can be clamped afterwards
3542             rowLeft = blockAlignEnabled ? 0.0f : charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3543             rowRight = ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width;
3544             selectionState = SelectionStarted;
3545           }
3546         }
3547         else
3548         {
3549           // build up highlight(s) with this selection data.
3550           rowLeft = std::min( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x, rowLeft );
3551           rowRight = std::max( ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width, rowRight );
3552         }
3553       }
3554
3555       lastIt = it++;
3556     }
3557
3558     // If reached end, and still on selection, then close selection.
3559     if(it == end)
3560     {
3561       if(selectionState == SelectionStarted)
3562       {
3563         // finished selection.
3564         Vector2 min, max;
3565         if(lastIt->mIsNewParagraphChar)
3566         {
3567           lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3568         }
3569         const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3570         maxRowLeft = std::min(maxRowLeft, min.x);
3571         maxRowRight = std::max(maxRowRight, max.x);
3572         float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3573         float rowTop = rowBottom - rowSize.height;
3574         mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3575       }
3576     }
3577
3578     // Get the top left and bottom right corners.
3579     const Toolkit::TextView::CharacterLayoutInfo& firstCharacter( *mTextLayoutInfo.mCharacterLayoutInfoTable.begin() );
3580     const Vector2 topLeft( maxRowLeft, firstCharacter.mPosition.y - firstCharacter.mSize.height );
3581     const Vector2 bottomRight( topLeft.x + mTextLayoutInfo.mTextSize.width, topLeft.y + mTextLayoutInfo.mTextSize.height );
3582
3583     // Clamp quads so they appear to clip to borders of the whole text.
3584     mNewHighlightInfo.Clamp2D( topLeft, bottomRight );
3585
3586     // For block-align align Further Clamp quads to max left and right extents
3587     if(blockAlignEnabled)
3588     {
3589       // BlockAlign: Will adjust highlight to block:
3590       // i.e.
3591       //   H[ello] (top row right = max of all rows right)
3592       // [--this-] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3593       // [is some] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3594       // [text] (bottom row left = min of all rows left)
3595       // (common in SMS messaging selection)
3596       //
3597       // As opposed to the default which is tight text highlighting.
3598       //   H[ello]
3599       //   [this]
3600       // [is some]
3601       // [text]
3602       // (common in regular text editors/web browser selection)
3603
3604       mNewHighlightInfo.Clamp2D( Vector2(maxRowLeft, topLeft.y), Vector2(maxRowRight, bottomRight.y ) );
3605     }
3606
3607     // Finally clamp quads again so they don't exceed the boundry of the control.
3608     const Vector3& controlSize = GetControlSize();
3609     mNewHighlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
3610   } // end if
3611
3612   return mNewHighlightInfo;
3613 }
3614
3615 void TextInput::UpdateHighlight()
3616 {
3617 //  Construct a Mesh with a texture to be used as the highlight 'box' for selected text
3618 //
3619 //  Example scenarios where mesh is made from 3, 1, 2, 2 ,3 or 3 quads.
3620 //
3621 //   [ TOP   ]  [ TOP ]      [TOP ]  [ TOP    ]      [ TOP  ]      [ TOP  ]
3622 //  [ MIDDLE ]             [BOTTOM]  [BOTTOM]      [ MIDDLE ]   [ MIDDLE  ]
3623 //  [ BOTTOM]                                      [ MIDDLE ]   [ MIDDLE  ]
3624 //                                                 [BOTTOM]     [ MIDDLE  ]
3625 //                                                              [BOTTOM]
3626 //
3627 //  Each quad is created as 2 triangles.
3628 //  Middle is just 1 quad regardless of its size.
3629 //
3630 //  (0,0)         (0,0)
3631 //     0*    *2     0*       *2
3632 //     TOP          TOP
3633 //     3*    *1     3*       *1
3634 //  4*       *1     4*     *6
3635 //     MIDDLE         BOTTOM
3636 //  6*       *5     7*     *5
3637 //  6*    *8
3638 //   BOTTOM
3639 //  9*    *7
3640 //
3641
3642   if ( mHighlightMeshActor )
3643   {
3644     // vertex and triangle buffers should always be present if MeshActor is alive.
3645     HighlightInfo newHighlightInfo = CalculateHighlightInfo();
3646     MeshData::VertexContainer vertices;
3647     Dali::MeshData::FaceIndices faceIndices;
3648
3649     if( !newHighlightInfo.mQuadList.empty() )
3650     {
3651       std::vector<QuadCoordinates>::iterator iter = newHighlightInfo.mQuadList.begin();
3652       std::vector<QuadCoordinates>::iterator endIter = newHighlightInfo.mQuadList.end();
3653
3654       // vertex position defaults to (0 0 0)
3655       MeshData::Vertex vertex;
3656       // set normal for all vertices as (0 0 1) pointing outward from TextInput Actor.
3657       vertex.nZ = 1.0f;
3658
3659       for(std::size_t v = 0; iter != endIter; ++iter,v+=4 )
3660       {
3661         // Add each quad geometry (a sub-selection) to the mesh data.
3662
3663         // 0-----1
3664         // |\    |
3665         // | \ A |
3666         // |  \  |
3667         // | B \ |
3668         // |    \|
3669         // 2-----3
3670
3671         QuadCoordinates& quad = *iter;
3672         // top-left (v+0)
3673         vertex.x = quad.min.x;
3674         vertex.y = quad.min.y;
3675         vertices.push_back( vertex );
3676
3677         // top-right (v+1)
3678         vertex.x = quad.max.x;
3679         vertex.y = quad.min.y;
3680         vertices.push_back( vertex );
3681
3682         // bottom-left (v+2)
3683         vertex.x = quad.min.x;
3684         vertex.y = quad.max.y;
3685         vertices.push_back( vertex );
3686
3687         // bottom-right (v+3)
3688         vertex.x = quad.max.x;
3689         vertex.y = quad.max.y;
3690         vertices.push_back( vertex );
3691
3692         // triangle A (3, 1, 0)
3693         faceIndices.push_back( v + 3 );
3694         faceIndices.push_back( v + 1 );
3695         faceIndices.push_back( v );
3696
3697         // triangle B (0, 2, 3)
3698         faceIndices.push_back( v );
3699         faceIndices.push_back( v + 2 );
3700         faceIndices.push_back( v + 3 );
3701
3702         mMeshData.SetFaceIndices( faceIndices );
3703       }
3704
3705       BoneContainer bones(0); // passed empty as bones not required
3706       mMeshData.SetData( vertices, faceIndices, bones, mCustomMaterial );
3707       mHighlightMesh.UpdateMeshData(mMeshData);
3708     }
3709   }
3710 }
3711
3712 void TextInput::ClearPopup()
3713 {
3714   mPopupPanel.Clear();
3715 }
3716
3717 void TextInput::AddPopupOptions()
3718 {
3719   mPopupPanel.AddPopupOptions();
3720 }
3721
3722 void TextInput::SetPopupPosition( const Vector3& position, const Vector2& alternativePosition )
3723 {
3724   const Vector3& visiblePopUpSize = mPopupPanel.GetVisibileSize();
3725
3726   Vector3 clampedPosition ( position );
3727   Vector3 tailOffsetPosition ( position );
3728
3729   float xOffSet( 0.0f );
3730
3731   Actor self = Self();
3732   const Vector3 textViewTopLeftWorldPosition = self.GetCurrentWorldPosition() - self.GetCurrentSize()*0.5f;
3733
3734   const float popUpLeft = textViewTopLeftWorldPosition.x + position.x - visiblePopUpSize.width*0.5f;
3735   const float popUpTop = textViewTopLeftWorldPosition.y + position.y - visiblePopUpSize.height;
3736
3737   // Clamp to left or right or of boundary
3738   if( popUpLeft < mBoundingRectangleWorldCoordinates.x )
3739   {
3740     xOffSet = mBoundingRectangleWorldCoordinates.x - popUpLeft ;
3741   }
3742   else if ( popUpLeft + visiblePopUpSize.width > mBoundingRectangleWorldCoordinates.z )
3743   {
3744     xOffSet = mBoundingRectangleWorldCoordinates.z - ( popUpLeft + visiblePopUpSize.width );
3745   }
3746
3747   clampedPosition.x = position.x + xOffSet;
3748   tailOffsetPosition.x = -xOffSet;
3749
3750   // Check if top left of PopUp outside of top bounding rectangle, if so then flip to lower position.
3751   bool flipTail( false );
3752
3753   if ( popUpTop < mBoundingRectangleWorldCoordinates.y )
3754   {
3755     clampedPosition.y = alternativePosition.y + visiblePopUpSize.height;
3756     flipTail = true;
3757   }
3758
3759   mPopupPanel.GetRootActor().SetPosition( clampedPosition );
3760   mPopupPanel.SetTailPosition( tailOffsetPosition, flipTail );
3761 }
3762
3763 void TextInput::HidePopup(bool animate, bool signalFinished )
3764 {
3765   if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown )  )
3766   {
3767     mPopupPanel.Hide( animate );
3768
3769     if( animate && signalFinished )
3770     {
3771       mPopupPanel.HideFinishedSignal().Connect( this, &TextInput::OnPopupHideFinished );
3772     }
3773   }
3774 }
3775
3776 void TextInput::ShowPopup( bool animate )
3777 {
3778   Vector3 position;
3779   Vector2 alternativePopupPosition;
3780
3781   if(mHighlightMeshActor && mState == StateEdit)
3782   {
3783     Vector3 topHandle;
3784     Vector3 bottomHandle; // referring to the bottom most point of the handle or the bottom line of selection.
3785     Size rowSize;
3786     // When text is selected, show popup above top handle (and text), or below bottom handle.
3787     // topHandle: referring to the top most point of the handle or the top line of selection.
3788     if ( mSelectionHandleTwoActualPosition.y > mSelectionHandleOneActualPosition.y )
3789     {
3790       topHandle = mSelectionHandleOneActualPosition;
3791       bottomHandle = mSelectionHandleTwoActualPosition;
3792       rowSize= GetRowRectFromCharacterPosition( mSelectionHandleOnePosition );
3793     }
3794     else
3795     {
3796       topHandle = mSelectionHandleTwoActualPosition;
3797       bottomHandle = mSelectionHandleOneActualPosition;
3798       rowSize = GetRowRectFromCharacterPosition( mSelectionHandleTwoPosition );
3799     }
3800     topHandle.y += -mPopupOffsetFromText.y - rowSize.height;
3801     position = Vector3(topHandle.x, topHandle.y, 0.0f);
3802
3803     float xPosition = ( fabsf( topHandle.x - bottomHandle.x ) )*0.5f + std::min( mSelectionHandleOneActualPosition.x , mSelectionHandleTwoActualPosition.x );
3804
3805     position.x = xPosition;
3806
3807     // Alternative position if no upper space
3808     bottomHandle.y += GetSelectionHandleSize().y + mPopupOffsetFromText.w;
3809     alternativePopupPosition = Vector2 ( position.x, bottomHandle.y );
3810   }
3811   else
3812   {
3813     // When no text is selected, show popup at world position of grab handle or cursor
3814     position = GetActualPositionFromCharacterPosition( mCursorPosition );
3815     const Size rowSize = GetRowRectFromCharacterPosition( mCursorPosition );
3816     position.y -= ( mPopupOffsetFromText.y + rowSize.height );
3817     // if can't be positioned above, then position below row.
3818     alternativePopupPosition = Vector2( position.x, position.y ); // default if no grab handle
3819     if ( mGrabHandle )
3820     {
3821       // If grab handle enabled then position pop-up below the grab handle.
3822       alternativePopupPosition.y = rowSize.height + mGrabHandle.GetCurrentSize().height + mPopupOffsetFromText.w +50.0f;
3823     }
3824   }
3825
3826   SetPopupPosition( position, alternativePopupPosition );
3827
3828   // Show popup
3829   mPopupPanel.Show( Self(), animate );
3830   StartMonitoringStageForTouch();
3831
3832   mPopupPanel.PressedSignal().Connect( this, &TextInput::OnPopupButtonPressed );
3833 }
3834
3835 void TextInput::ShowPopupCutCopyPaste()
3836 {
3837   ClearPopup();
3838
3839   mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3840   // Check the selected text is whole text or not.
3841   if( IsTextSelected() && ( mStyledText.size() != GetSelectedText().size() ) )
3842   {
3843     mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3844   }
3845
3846   if ( !mStyledText.empty() )
3847   {
3848     mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCopy, true );
3849     mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, true );
3850   }
3851
3852   if( mClipboard && mClipboard.NumberOfItems() )
3853   {
3854     mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
3855     mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
3856   }
3857
3858   AddPopupOptions();
3859
3860   mPopupPanel.Hide(false);
3861   ShowPopup();
3862 }
3863
3864 void TextInput::SetUpPopupSelection( bool treatWhiteSpaceAsAnyOtherCharacter )
3865 {
3866   ClearPopup();
3867   mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3868   // If no text exists then don't offer to select
3869   if ( !mStyledText.empty() )
3870   {
3871     mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3872     mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelect, true );
3873     mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, treatWhiteSpaceAsAnyOtherCharacter );
3874   }
3875   // if clipboard has valid contents then offer paste option
3876   if( mClipboard && mClipboard.NumberOfItems() )
3877   {
3878     mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
3879     mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
3880   }
3881
3882   AddPopupOptions();
3883
3884   mPopupPanel.Hide(false);
3885 }
3886
3887 bool TextInput::ReturnClosestIndex(const Vector2& source, std::size_t& closestIndex )
3888 {
3889   bool found = false;
3890   closestIndex = 0;
3891
3892   std::vector<Toolkit::TextView::CharacterLayoutInfo> matchedCharacters;
3893   bool lastRightToLeftChar(false);          /// RTL state of previous character encountered (character on the left of touch point)
3894   bool rightToLeftChar(false);              /// RTL state of current character encountered (character on the right of touch point)
3895   float glyphIntersection(0.0f);            /// Glyph intersection, the point between the two nearest characters touched.
3896
3897   const Vector2 sourceScrollOffset( source + mTextLayoutInfo.mScrollOffset );
3898
3899   if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
3900   {
3901     float closestYdifference = std::numeric_limits<float>::max();
3902     std::size_t lineOffset = 0;                  /// Keep track of position of the first character on the matched line of interest.
3903     std::size_t numberOfMatchedCharacters = 0;
3904
3905     // 1. Find closest character line to y part of source, create vector of all entries in that Y position
3906     // TODO: There should be an easy call to enumerate through each visual line, instead of each character on all visual lines.
3907
3908     for( std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), endIt = mTextLayoutInfo.mCharacterLayoutInfoTable.end(); it != endIt; ++it )
3909     {
3910       const Toolkit::TextView::CharacterLayoutInfo& info( *it );
3911       float baselinePosition = info.mPosition.y - info.mDescender;
3912
3913       if( info.mIsVisible )
3914       {
3915         // store difference between source y point and the y position of the current character
3916         float currentYdifference = fabsf( sourceScrollOffset.y - ( baselinePosition ) );
3917
3918         if(  currentYdifference < closestYdifference  )
3919         {
3920           // closest so far; store this difference and clear previous matchedCharacters as no longer closest
3921           lineOffset = it - mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3922           closestYdifference = currentYdifference;
3923           matchedCharacters.clear();
3924           numberOfMatchedCharacters = 0; // reset count
3925         }
3926
3927         // add all characters that are on the same Y axis (within the CHARACTER_THRESHOLD) to the matched array.
3928         if( fabsf( closestYdifference - currentYdifference )  < CHARACTER_THRESHOLD )
3929         {
3930           // ignore new line character.
3931           if( !info.mIsNewParagraphChar )
3932           {
3933             matchedCharacters.push_back( info );
3934             numberOfMatchedCharacters++;
3935           }
3936         }
3937       }
3938     } // End of loop checking each character's y position in the character layout table
3939
3940     // Check if last character is a newline, if it is
3941     // then need pretend there is an imaginary line afterwards,
3942     // and check if user is touching below previous line.
3943     const Toolkit::TextView::CharacterLayoutInfo& lastInfo( mTextLayoutInfo.mCharacterLayoutInfoTable[mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1] );
3944
3945     if( ( lastInfo.mIsVisible ) && ( lastInfo.mIsNewParagraphChar ) && ( sourceScrollOffset.y > lastInfo.mPosition.y ) )
3946     {
3947       closestIndex = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
3948     }
3949     else
3950     {
3951       std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator it = matchedCharacters.begin();
3952       std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator endIt = matchedCharacters.end();
3953
3954       bool matched( false );
3955
3956       // 2 Iterate through matching list of y positions and find closest matching X position.
3957       for( ; it != endIt; ++it )
3958       {
3959         const Toolkit::TextView::CharacterLayoutInfo& info( *it );
3960
3961         if( info.mIsVisible )
3962         {
3963           // stop when on left side of character's center.
3964           const float characterMidPointPosition = info.mPosition.x  + ( info.mSize.width * 0.5f ) ;
3965           if( sourceScrollOffset.x < characterMidPointPosition )
3966           {
3967             if(info.mIsRightToLeftCharacter)
3968             {
3969               rightToLeftChar = true;
3970             }
3971             glyphIntersection = info.mPosition.x;
3972             matched = true;
3973             break;
3974           }
3975
3976           lastRightToLeftChar = info.mIsRightToLeftCharacter;
3977         }
3978       }
3979
3980       if( it == endIt )
3981       {
3982         rightToLeftChar = lastRightToLeftChar;
3983       }
3984
3985       std::size_t matchCharacterIndex = it - matchedCharacters.begin();
3986       closestIndex = lineOffset + matchCharacterIndex;
3987
3988       mClosestCursorPositionEOL = false; // reset
3989       if ( it == endIt && !matched )
3990       {
3991         mClosestCursorPositionEOL = true; // Reached end of matched characters in closest line but no match so cursor should be after last character.
3992       }
3993
3994       // For RTL characters, need to adjust closestIndex by 1 (as the inequality above would be reverse)
3995       if( rightToLeftChar && lastRightToLeftChar )
3996       {
3997         --closestIndex; // (-1 = numeric_limits<std::size_t>::max())
3998       }
3999     }
4000   }
4001
4002   // closestIndex is the visual index, need to convert it to the logical index
4003   if( !mTextLayoutInfo.mCharacterVisualToLogicalMap.empty() )
4004   {
4005     if( closestIndex < mTextLayoutInfo.mCharacterVisualToLogicalMap.size() )
4006     {
4007       // Checks for situations where user is touching between LTR and RTL
4008       // characters. To identify if the user means the end of a LTR string
4009       // or the beginning of an RTL string, and vice versa.
4010       if( closestIndex > 0 )
4011       {
4012         if( rightToLeftChar && !lastRightToLeftChar )
4013         {
4014           // [LTR] [RTL]
4015           //   |..|..|
4016           //   AAA BBB
4017           // A: In this touch range, the user is indicating that they wish to place
4018           // the cursor at the end of the LTR text.
4019           // B: In this touch range, the user is indicating that they wish to place
4020           // the cursor at the end of the RTL text.
4021
4022           // Result of touching A area:
4023           // [.....LTR]|[RTL......]+
4024           //
4025           // |: primary cursor (for typing LTR chars)
4026           // +: secondary cursor (for typing RTL chars)
4027
4028           // Result of touching B area:
4029           // [.....LTR]+[RTL......]|
4030           //
4031           // |: primary cursor (for typing RTL chars)
4032           // +: secondary cursor (for typing LTR chars)
4033
4034           if( sourceScrollOffset.x < glyphIntersection )
4035           {
4036             --closestIndex;
4037           }
4038         }
4039         else if( !rightToLeftChar && lastRightToLeftChar )
4040         {
4041           if( sourceScrollOffset.x < glyphIntersection )
4042           {
4043             --closestIndex;
4044           }
4045         }
4046       }
4047
4048       closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap[closestIndex];
4049       // If user touched a left-side of RTL char, and the character on the left was an LTR then position logical cursor
4050       // one further ahead
4051       if( rightToLeftChar && !lastRightToLeftChar )
4052       {
4053         ++closestIndex;
4054       }
4055     }
4056     else if( closestIndex == numeric_limits<std::size_t>::max() ) // -1 RTL (after last arabic character on line)
4057     {
4058       closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap.size();
4059     }
4060     else if( mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterVisualToLogicalMap[ closestIndex - 1 ] ].mIsRightToLeftCharacter ) // size() LTR (after last european character on line)
4061     {
4062       closestIndex = 0;
4063     }
4064   }
4065
4066   return found;
4067 }
4068
4069 float TextInput::GetLineJustificationPosition() const
4070 {
4071   const Vector3& size = mDisplayedTextView.GetCurrentSize();
4072   Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4073   float alignmentOffset = 0.f;
4074
4075   // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4076   if( alignment & Toolkit::Alignment::HorizontalLeft )
4077   {
4078     alignmentOffset = 0.f;
4079   }
4080   else if( alignment & Toolkit::Alignment::HorizontalCenter )
4081   {
4082     alignmentOffset = 0.5f * ( size.width - mTextLayoutInfo.mTextSize.width );
4083   }
4084   else if( alignment & Toolkit::Alignment::HorizontalRight )
4085   {
4086     alignmentOffset = size.width - mTextLayoutInfo.mTextSize.width;
4087   }
4088
4089   Toolkit::TextView::LineJustification justification = mDisplayedTextView.GetLineJustification();
4090   float justificationOffset = 0.f;
4091
4092   switch( justification )
4093   {
4094     case Toolkit::TextView::Left:
4095     {
4096       justificationOffset = 0.f;
4097       break;
4098     }
4099     case Toolkit::TextView::Center:
4100     {
4101       justificationOffset = 0.5f * mTextLayoutInfo.mTextSize.width;
4102       break;
4103     }
4104     case Toolkit::TextView::Right:
4105     {
4106       justificationOffset = mTextLayoutInfo.mTextSize.width;
4107       break;
4108     }
4109     case Toolkit::TextView::Justified:
4110     {
4111       justificationOffset = 0.f;
4112       break;
4113     }
4114     default:
4115     {
4116       DALI_ASSERT_ALWAYS( false );
4117     }
4118   } // end switch
4119
4120   return alignmentOffset + justificationOffset;
4121 }
4122
4123 Vector3 TextInput::PositionCursorAfterWordWrap( std::size_t characterPosition ) const
4124 {
4125   /* Word wrap occurs automatically in TextView when the exceed policy moves a word to the next line when not enough space on current.
4126      A newline character is not inserted in this case */
4127
4128   DALI_ASSERT_DEBUG( !(characterPosition <= 0 ));
4129
4130   Vector3 cursorPosition;
4131
4132   Toolkit::TextView::CharacterLayoutInfo currentCharInfo;
4133
4134   if ( characterPosition == mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4135   {
4136     // end character so use
4137     currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1 ];
4138     cursorPosition = Vector3(currentCharInfo.mPosition.x + currentCharInfo.mSize.width, currentCharInfo.mPosition.y, currentCharInfo.mPosition.z) ;
4139   }
4140   else
4141   {
4142     currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4143   }
4144
4145   Toolkit::TextView::CharacterLayoutInfo previousCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1];
4146
4147   // If previous character on a different line then use current characters position
4148   if ( fabsf( (currentCharInfo.mPosition.y - currentCharInfo.mDescender )  - ( previousCharInfo.mPosition.y - previousCharInfo.mDescender) ) > Math::MACHINE_EPSILON_1000 )
4149   {
4150     if ( mClosestCursorPositionEOL )
4151     {
4152       cursorPosition = Vector3(previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z) ;
4153     }
4154     else
4155     {
4156       cursorPosition = Vector3(currentCharInfo.mPosition);
4157     }
4158   }
4159   else
4160   {
4161     // Previous character is on same line so use position of previous character plus it's width.
4162     cursorPosition = Vector3(previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z) ;
4163   }
4164
4165   return cursorPosition;
4166 }
4167
4168 Vector3 TextInput::GetActualPositionFromCharacterPosition(std::size_t characterPosition) const
4169 {
4170   bool direction(false);
4171   Vector3 alternatePosition;
4172   bool alternatePositionValid(false);
4173
4174   return GetActualPositionFromCharacterPosition( characterPosition, direction, alternatePosition, alternatePositionValid );
4175 }
4176
4177 Vector3 TextInput::GetActualPositionFromCharacterPosition(std::size_t characterPosition, bool& directionRTL, Vector3& alternatePosition, bool& alternatePositionValid ) const
4178 {
4179   Vector3 cursorPosition( 0.f, 0.f, 0.f );
4180
4181   alternatePositionValid = false;
4182   directionRTL = false;
4183
4184   if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
4185   {
4186     std::size_t visualCharacterPosition;
4187
4188     // When cursor is not at beginning, consider possibility of
4189     // showing 2 cursors. (whereas at beginning we only ever show one cursor)
4190     if(characterPosition > 0)
4191     {
4192       // Cursor position should be the end of the last character.
4193       // If the last character is LTR, then the end is on the right side of the glyph.
4194       // If the last character is RTL, then the end is on the left side of the glyph.
4195       visualCharacterPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition - 1 ];
4196
4197       if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + visualCharacterPosition ) ).mIsVisible )
4198       {
4199         visualCharacterPosition = FindVisibleCharacter( Left, visualCharacterPosition );
4200       }
4201
4202       Toolkit::TextView::CharacterLayoutInfo info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4203       if( ( visualCharacterPosition > 0 ) && info.mIsNewParagraphChar && !IsScrollEnabled() )
4204       {
4205         // Prevents the cursor to exceed the boundary if the last visible character is a 'new line character' and the scroll is not enabled.
4206         const Vector3& size = GetControlSize();
4207
4208         if( info.mPosition.y + info.mSize.height - mDisplayedTextView.GetLineHeightOffset() > size.height )
4209         {
4210           --visualCharacterPosition;
4211         }
4212         info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4213       }
4214
4215       if(!info.mIsNewParagraphChar)
4216       {
4217         cursorPosition = PositionCursorAfterWordWrap( characterPosition ); // Get position of cursor/handles taking in account auto word wrap.
4218       }
4219       else
4220       {
4221         // When cursor points to first character on new line, position cursor at the start of this glyph.
4222         if(characterPosition < mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4223         {
4224           std::size_t visualCharacterNextPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition ];
4225           const Toolkit::TextView::CharacterLayoutInfo& infoNext = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterNextPosition ];
4226           const float start( infoNext.mIsRightToLeftCharacter ? infoNext.mSize.width : 0.0f );
4227
4228           cursorPosition.x = infoNext.mPosition.x + start;
4229           cursorPosition.y = infoNext.mPosition.y;
4230         }
4231         else
4232         {
4233           // If cursor points to the end of text, then can only position
4234           // cursor where the new line starts based on the line-justification position.
4235           cursorPosition.x = GetLineJustificationPosition();
4236
4237           if(characterPosition == mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4238           {
4239             // If this is after the last character, then we can assume that the new cursor
4240             // should be exactly one row below the current row.
4241
4242             const Size rowRect(GetRowRectFromCharacterPosition(characterPosition - 1));
4243             cursorPosition.y = info.mPosition.y + rowRect.height;
4244           }
4245           else
4246           {
4247             // If this is not after last character, then we can use this row's height.
4248             // should be exactly one row below the current row.
4249
4250             const Size rowRect(GetRowRectFromCharacterPosition(characterPosition));
4251             cursorPosition.y = info.mPosition.y + rowRect.height;
4252           }
4253         }
4254       }
4255
4256       directionRTL = info.mIsRightToLeftCharacter;
4257
4258       // 1. When the cursor is neither at the beginning or the end,
4259       // we can show multiple cursors under situations when the cursor is
4260       // between RTL and LTR text...
4261       if(characterPosition != mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4262       {
4263         std::size_t visualCharacterAltPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[characterPosition]; // VCC TODO: find why in the previous patch it was a -1 here.
4264
4265         DALI_ASSERT_ALWAYS(visualCharacterAltPosition < mTextLayoutInfo.mCharacterLayoutInfoTable.size());
4266         const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterAltPosition ];
4267
4268         if(!info.mIsRightToLeftCharacter && infoAlt.mIsRightToLeftCharacter)
4269         {
4270           // Stuation occurs when cursor is at the end of English text (LTR) and beginning of Arabic (RTL)
4271           // Text:     [...LTR...]|[...RTL...]
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 + infoAlt.mSize.width;
4277           alternatePosition.y = infoAlt.mPosition.y;
4278           alternatePositionValid = true;
4279         }
4280         else if(info.mIsRightToLeftCharacter && !infoAlt.mIsRightToLeftCharacter)
4281         {
4282           // Situation occurs when cursor is at end of the Arabic text (LTR) and beginning of English (RTL)
4283           // Text:           |[...RTL...] [...LTR....]
4284           // Cursor pos:     ^
4285           // Alternate cursor pos:       ^
4286           // In which case we need to display an alternate cursor for the RTL text.
4287
4288           alternatePosition.x = infoAlt.mPosition.x;
4289           alternatePosition.y = infoAlt.mPosition.y;
4290           alternatePositionValid = true;
4291         }
4292       }
4293       else
4294       {
4295         // 2. When the cursor is at the end of the text,
4296         // and we have multi-directional text,
4297         // we can also consider showing mulitple cursors.
4298         // The rule here is:
4299         // If first and last characters on row are different
4300         // Directions, then two cursors need to be displayed.
4301
4302         // Get first logical glyph on row
4303         std::size_t startCharacterPosition = GetRowStartFromCharacterPosition( characterPosition - 1 );
4304
4305         std::size_t visualCharacterStartPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ startCharacterPosition ];
4306         const Toolkit::TextView::CharacterLayoutInfo& infoStart= mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterStartPosition ];
4307
4308         if(info.mIsRightToLeftCharacter && !infoStart.mIsRightToLeftCharacter)
4309         {
4310           // For text Starting as LTR and ending as RTL. End cursor position is as follows:
4311           // Text:     [...LTR...]|[...RTL...]
4312           // Cursor pos:          ^
4313           // Alternate cursor pos:            ^
4314           // In which case we need to display an alternate cursor for the RTL text, this cursor
4315           // should be at the end of the given line.
4316
4317           const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1 ];
4318           alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width;
4319           alternatePosition.y = infoAlt.mPosition.y;
4320           alternatePositionValid = true;
4321         }
4322         else if(!info.mIsRightToLeftCharacter && infoStart.mIsRightToLeftCharacter) // starting RTL
4323         {
4324           // For text Starting as RTL and ending as LTR. End cursor position is as follows:
4325           // Text:           |[...RTL...] [...LTR....]
4326           // Cursor pos:     ^
4327           // Alternate cursor pos:       ^
4328           // In which case we need to display an alternate cursor for the RTL text.
4329
4330           const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ startCharacterPosition ];
4331           alternatePosition.x = infoAlt.mPosition.x;
4332           alternatePosition.y = infoAlt.mPosition.y;
4333           alternatePositionValid = true;
4334         }
4335       }
4336     } // characterPosition > 0
4337     else if(characterPosition == 0)
4338     {
4339       // When the cursor position is at the beginning, it should be at the start of the current character.
4340       // If the current character is LTR, then the start is on the right side of the glyph.
4341       // If the current character is RTL, then the start is on the left side of the glyph.
4342       visualCharacterPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition ];
4343
4344       if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + visualCharacterPosition ) ).mIsVisible )
4345       {
4346          visualCharacterPosition = FindVisibleCharacter( Right, visualCharacterPosition );
4347       }
4348
4349       const Toolkit::TextView::CharacterLayoutInfo& info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4350       const float start(info.mIsRightToLeftCharacter ? info.mSize.width : 0.0f);
4351
4352       cursorPosition.x = info.mPosition.x + start;
4353       cursorPosition.y = info.mPosition.y;
4354       directionRTL = info.mIsRightToLeftCharacter;
4355     }
4356   }
4357   else
4358   {
4359     // If the character table is void, place the cursor accordingly the text alignment.
4360     const Vector3& size = GetControlSize();
4361
4362     Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4363     float alignmentOffset = 0.f;
4364
4365     // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4366     if( alignment & Toolkit::Alignment::HorizontalLeft )
4367     {
4368       alignmentOffset = 0.f;
4369     }
4370     else if( alignment & Toolkit::Alignment::HorizontalCenter )
4371     {
4372       alignmentOffset = 0.5f * ( size.width );
4373     }
4374     else if( alignment & Toolkit::Alignment::HorizontalRight )
4375     {
4376       alignmentOffset = size.width;
4377     }
4378
4379     // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4380     cursorPosition.x = alignmentOffset;
4381
4382     // Work out cursor 'y' position when there are any character accordingly with the text view alignment settings.
4383     if( alignment & Toolkit::Alignment::VerticalTop )
4384     {
4385       cursorPosition.y = mLineHeight;
4386     }
4387     else if( alignment & Toolkit::Alignment::VerticalCenter )
4388     {
4389       cursorPosition.y = 0.5f * ( size.height + mLineHeight );
4390     }
4391     else if( alignment & Toolkit::Alignment::VerticalBottom )
4392     {
4393       cursorPosition.y = size.height;
4394     }
4395   }
4396
4397   cursorPosition.x -= mTextLayoutInfo.mScrollOffset.x;
4398   cursorPosition.y -= mTextLayoutInfo.mScrollOffset.y;
4399   if( alternatePositionValid )
4400   {
4401     alternatePosition.x -= mTextLayoutInfo.mScrollOffset.x;
4402     alternatePosition.y -= mTextLayoutInfo.mScrollOffset.y;
4403   }
4404
4405   return cursorPosition;
4406 }
4407
4408 std::size_t TextInput::GetRowStartFromCharacterPosition(std::size_t logicalPosition) const
4409 {
4410   // scan string from current position to beginning of current line to note direction of line
4411   while(logicalPosition)
4412   {
4413     logicalPosition--;
4414     std::size_t visualPosition = GetVisualPosition(logicalPosition);
4415     if(mTextLayoutInfo.mCharacterLayoutInfoTable[visualPosition].mIsNewParagraphChar)
4416     {
4417       logicalPosition++;
4418       break;
4419     }
4420   }
4421
4422   return logicalPosition;
4423 }
4424
4425 Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition) const
4426 {
4427   Vector2 min, max;
4428
4429   return GetRowRectFromCharacterPosition( characterPosition, min, max );
4430 }
4431
4432 Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition, Vector2& min, Vector2& max) const
4433 {
4434   // if we have no text content, then return position 0,0 with width 0, and height the same as cursor height.
4435   if( mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4436   {
4437     min = Vector2::ZERO;
4438     max = Vector2(0.0f, mLineHeight);
4439     return max;
4440   }
4441
4442   // TODO: This info should be readily available from text-view, we should not have to search hard for it.
4443   Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator begin = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
4444   Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
4445
4446   // If cursor is pointing to end of line, then start from last character.
4447   characterPosition = std::min( characterPosition, static_cast<std::size_t>(mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1) );
4448
4449   Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4450
4451   // 0. Find first a visible character. Draw a cursor beyound text-input bounds is not wanted.
4452   if( !it->mIsVisible )
4453   {
4454     characterPosition = FindVisibleCharacter( Left, characterPosition );
4455     it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4456   }
4457
4458   // Scan characters left and right of cursor, stopping when end of line/string reached or
4459   // y position greater than threshold of reference line.
4460
4461   // 1. scan left until we reach the beginning or a different line.
4462   Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator validCharIt = it;
4463   float referenceLine = it->mPosition.y - CHARACTER_THRESHOLD;
4464   // min-x position is the left-most char's left (x)
4465   // max-x position is the right-most char's right (x)
4466   // min-y position is the minimum of all character's top (y)
4467   // max-y position is the maximum of all character's bottom (y+height)
4468   min.y = validCharIt->mPosition.y;
4469   max.y = validCharIt->mPosition.y + validCharIt->mSize.y;
4470
4471   while(true)
4472   {
4473     validCharIt = it;
4474     min.y = std::min(min.y, validCharIt->mPosition.y);
4475     max.y = std::max(max.y, validCharIt->mPosition.y + validCharIt->mSize.y);
4476
4477     if(it == begin)
4478     {
4479       break;
4480     }
4481
4482     --it;
4483
4484     if( (it->mPosition.y < referenceLine) ||
4485         (it->mIsNewParagraphChar) ||
4486         (!it->mIsVisible) )
4487     {
4488       break;
4489     }
4490   }
4491
4492   // info refers to the first character on this line.
4493   min.x = validCharIt->mPosition.x;
4494
4495   // 2. scan right until we reach end or a different line
4496   it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4497   referenceLine = it->mPosition.y + CHARACTER_THRESHOLD;
4498
4499   while(it != end)
4500   {
4501     if( (it->mPosition.y > referenceLine) ||
4502         (it->mIsNewParagraphChar) ||
4503         (!it->mIsVisible) )
4504     {
4505       break;
4506     }
4507
4508     validCharIt = it;
4509     min.y = std::min(min.y, validCharIt->mPosition.y);
4510     max.y = std::max(max.y, validCharIt->mPosition.y + validCharIt->mSize.y);
4511
4512     ++it;
4513   }
4514
4515   DALI_ASSERT_DEBUG ( validCharIt != end  && "validCharIt invalid")
4516
4517   if ( validCharIt != end )
4518   {
4519     // info refers to the last character on this line.
4520     max.x = validCharIt->mPosition.x + validCharIt->mSize.x;
4521   }
4522
4523   return Size( max.x - min.x, max.y - min.y );
4524 }
4525
4526 bool TextInput::WasTouchedCheck( const Actor& touchedActor ) const
4527 {
4528   Actor popUpPanel = mPopupPanel.GetRootActor();
4529
4530   if ( ( touchedActor == Self() ) || ( touchedActor == popUpPanel ) )
4531   {
4532     return true;
4533   }
4534   else
4535   {
4536     Dali::Actor parent( touchedActor.GetParent() );
4537
4538     if ( parent )
4539     {
4540       return WasTouchedCheck( parent );
4541     }
4542   }
4543
4544   return false;
4545 }
4546
4547 void TextInput::StartMonitoringStageForTouch()
4548 {
4549   Stage stage = Stage::GetCurrent();
4550   stage.TouchedSignal().Connect( this, &TextInput::OnStageTouched );
4551 }
4552
4553 void TextInput::EndMonitoringStageForTouch()
4554 {
4555   Stage stage = Stage::GetCurrent();
4556   stage.TouchedSignal().Disconnect( this, &TextInput::OnStageTouched );
4557 }
4558
4559 void TextInput::OnStageTouched(const TouchEvent& event)
4560 {
4561   if( event.GetPointCount() > 0 )
4562   {
4563     if ( TouchPoint::Down == event.GetPoint(0).state )
4564     {
4565       const Actor touchedActor(event.GetPoint(0).hitActor);
4566
4567       bool popUpShown( false );
4568
4569       if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown ) )
4570       {
4571         popUpShown = true;
4572       }
4573
4574       bool textInputTouched = (touchedActor && WasTouchedCheck( touchedActor ));
4575
4576       if ( ( mHighlightMeshActor || popUpShown ) && !textInputTouched )
4577       {
4578         EndMonitoringStageForTouch();
4579         HidePopup( true, false );
4580       }
4581
4582       if ( ( IsGrabHandleEnabled() && mGrabHandle  ) && !textInputTouched )
4583       {
4584         EndMonitoringStageForTouch();
4585         ShowGrabHandleAndSetVisibility( false );
4586       }
4587     }
4588   }
4589 }
4590
4591 void TextInput::SelectText(std::size_t start, std::size_t end)
4592 {
4593   DALI_LOG_INFO(gLogFilter, Debug::General, "SelectText mEditModeActive[%s] grabHandle[%s] start[%u] end[%u] size[%u]\n", mEditModeActive?"true":"false",
4594                                                                                                                           IsGrabHandleEnabled()?"true":"false",
4595                                                                                                                           start, end, mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
4596   DALI_ASSERT_ALWAYS( start <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText start out of max range" );
4597   DALI_ASSERT_ALWAYS( end <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText end out of max range" );
4598
4599   StartMonitoringStageForTouch();
4600
4601   if ( mEditModeActive ) // Only allow text selection when in edit mode
4602   {
4603     // When replacing highlighted text keyboard should ignore current word at cursor hence notify keyboard that the cursor is at the start of the highlight.
4604     mSelectingText = true;
4605
4606     std::size_t selectionStartPosition = std::min( start, end );
4607
4608     // Hide grab handle when selecting.
4609     ShowGrabHandleAndSetVisibility( false );
4610
4611     if( start != end )  // something to select
4612     {
4613       SetCursorVisibility( false );
4614       StopCursorBlinkTimer();
4615
4616       CreateSelectionHandles(start, end);
4617       UpdateHighlight();
4618
4619       const TextStyle oldInputStyle( mInputStyle );
4620       mInputStyle = GetStyleAt( selectionStartPosition ); // Inherit style from selected position.
4621
4622       if( oldInputStyle != mInputStyle )
4623       {
4624         // Updates the line height accordingly with the input style.
4625         UpdateLineHeight();
4626
4627         EmitStyleChangedSignal();
4628       }
4629
4630       HidePopup();
4631     }
4632
4633     mSelectingText = false;
4634   }
4635 }
4636
4637 MarkupProcessor::StyledTextArray TextInput::GetSelectedText()
4638 {
4639   MarkupProcessor::StyledTextArray currentSelectedText;
4640
4641   if ( IsTextSelected() )
4642   {
4643     MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4644     MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4645
4646     for(; it != end; ++it)
4647     {
4648       MarkupProcessor::StyledText& styledText( *it );
4649       currentSelectedText.push_back( styledText );
4650     }
4651   }
4652   return currentSelectedText;
4653 }
4654
4655 void TextInput::ApplyStyleToRange(const TextStyle& style, const TextStyle::Mask mask, const std::size_t begin, const std::size_t end)
4656 {
4657   const std::size_t beginIndex = std::min( begin, end );
4658   const std::size_t endIndex = std::max( begin, end );
4659
4660   // Apply the style
4661   MarkupProcessor::SetTextStyleToRange( mStyledText, style, mask, beginIndex, endIndex );
4662
4663   // Create a styled text array used to replace the text into the text-view.
4664   MarkupProcessor::StyledTextArray text;
4665   text.insert( text.begin(), mStyledText.begin() + beginIndex, mStyledText.begin() + endIndex + 1 );
4666
4667   mDisplayedTextView.ReplaceTextFromTo( beginIndex, ( endIndex - beginIndex ) + 1, text );
4668   GetTextLayoutInfo();
4669
4670   if( IsScrollEnabled() )
4671   {
4672     // Need to set the scroll position as the text's size may have changed.
4673     ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
4674   }
4675
4676   ShowGrabHandleAndSetVisibility( false );
4677
4678   DrawCursor();
4679
4680   UpdateHighlight();
4681
4682   // Set Handle positioning as the new style may have repositioned the characters.
4683   SetSelectionHandlePosition(HandleOne);
4684   SetSelectionHandlePosition(HandleTwo);
4685 }
4686
4687 void TextInput::KeyboardStatusChanged(bool keyboardShown)
4688 {
4689   // Just hide the grab handle when keyboard is hidden.
4690   if (!keyboardShown )
4691   {
4692     ShowGrabHandleAndSetVisibility( false );
4693
4694     // If the keyboard is not now being shown, then hide the popup panel
4695     mPopupPanel.Hide( true );
4696   }
4697 }
4698
4699 // Removes highlight and resumes edit mode state
4700 void TextInput::RemoveHighlight( bool hidePopup )
4701 {
4702   DALI_LOG_INFO(gLogFilter, Debug::General, "RemoveHighlight\n");
4703
4704   if ( mHighlightMeshActor )
4705   {
4706     if ( mSelectionHandleOne )
4707     {
4708       mActiveLayer.Remove( mSelectionHandleOne );
4709       mSelectionHandleOne.Reset();
4710       mSelectionHandleOneOffset.x = 0.0f;
4711     }
4712     if ( mSelectionHandleTwo )
4713     {
4714       mActiveLayer.Remove( mSelectionHandleTwo );
4715       mSelectionHandleTwo.Reset();
4716       mSelectionHandleTwoOffset.x = 0.0f;
4717     }
4718
4719     mNewHighlightInfo.mQuadList.clear();
4720
4721     Self().Remove( mHighlightMeshActor );
4722
4723     SetCursorVisibility( true );
4724     StartCursorBlinkTimer();
4725
4726     mHighlightMeshActor.Reset();
4727     // NOTE: We cannot dereference mHighlightMesh, due
4728     // to a bug in how the scene-graph MeshRenderer uses the Mesh data incorrectly.
4729
4730     if ( hidePopup )
4731     {
4732       HidePopup();
4733     }
4734   }
4735
4736   mSelectionHandleOnePosition = 0;
4737   mSelectionHandleTwoPosition = 0;
4738 }
4739
4740 void TextInput::CreateHighlight()
4741 {
4742   if ( !mHighlightMeshActor )
4743   {
4744     mMeshData = MeshData( );
4745     mMeshData.SetHasNormals( true );
4746
4747     mCustomMaterial = Material::New("CustomMaterial");
4748     mCustomMaterial.SetDiffuseColor( mMaterialColor );
4749
4750     mMeshData.SetMaterial( mCustomMaterial );
4751
4752     mHighlightMesh = Mesh::New( mMeshData );
4753
4754     mHighlightMeshActor = MeshActor::New( mHighlightMesh );
4755     mHighlightMeshActor.SetName( "HighlightMeshActor" );
4756     mHighlightMeshActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
4757     mHighlightMeshActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
4758     mHighlightMeshActor.SetPosition( 0.0f, 0.0f, DISPLAYED_HIGHLIGHT_Z_OFFSET );
4759     mHighlightMeshActor.SetAffectedByLighting(false);
4760
4761     Self().Add(mHighlightMeshActor);
4762   }
4763 }
4764
4765
4766 bool TextInput::CopySelectedTextToClipboard()
4767 {
4768   mCurrentCopySelecton.clear();
4769
4770   mCurrentCopySelecton = GetSelectedText();
4771
4772   std::string stringToStore;
4773
4774   /* Create a StyledTextArray from the selected region so can use the MarkUpProcessor to produce
4775    * a marked up string.
4776    */
4777   MarkupProcessor::StyledTextArray selectedText(mCurrentCopySelecton.begin(),mCurrentCopySelecton.end());
4778   MarkupProcessor::GetPlainString( selectedText, stringToStore );
4779   bool success = mClipboard.SetItem( stringToStore );
4780   return success;
4781 }
4782
4783 void TextInput::PasteText( const Text& text )
4784 {
4785   // Update Flag, indicates whether to update the text-input contents or not.
4786   // Any key stroke that results in a visual change of the text-input should
4787   // set this flag to true.
4788   bool update = false;
4789   if( mHighlightMeshActor )
4790   {
4791     /* if highlighted, delete entire text, and position cursor at start of deleted text. */
4792     mCursorPosition = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4793
4794     ImfManager imfManager = ImfManager::Get();
4795     if ( imfManager )
4796     {
4797       imfManager.SetCursorPosition( mCursorPosition );
4798       imfManager.NotifyCursorPosition();
4799     }
4800     DeleteHighlightedText( true );
4801     update = true;
4802   }
4803
4804   bool textExceedsMaximunNumberOfCharacters = false;
4805   bool textExceedsBoundary = false;
4806
4807   std::size_t insertedStringLength = DoInsertAt( text, mCursorPosition, 0, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
4808
4809   mCursorPosition += insertedStringLength;
4810   ImfManager imfManager = ImfManager::Get();
4811   if ( imfManager )
4812   {
4813     imfManager.SetCursorPosition ( mCursorPosition );
4814     imfManager.NotifyCursorPosition();
4815   }
4816
4817   update = update || ( insertedStringLength > 0 );
4818   if( update )
4819   {
4820     CursorUpdate();
4821     EmitTextModified();
4822   }
4823
4824   if( insertedStringLength < text.GetLength() )
4825   {
4826     EmitMaxInputCharactersReachedSignal();
4827   }
4828
4829   if( textExceedsBoundary )
4830   {
4831     EmitInputTextExceedsBoundariesSignal();
4832   }
4833 }
4834
4835 void TextInput::SetTextDirection()
4836 {
4837   // Put the cursor to the right if we are empty and an RTL language is being used.
4838   if ( mStyledText.empty() )
4839   {
4840     VirtualKeyboard::TextDirection direction( VirtualKeyboard::GetTextDirection() );
4841
4842     // Get the current text alignment preserving the vertical alignment. Also preserve the horizontal center
4843     // alignment as we do not want to set the text direction if we've been asked to be in the center.
4844     //
4845     // TODO: Should split SetTextAlignment into two APIs to better handle this (sometimes apps just want to
4846     //       set vertical alignment but are being forced to set the horizontal alignment as well with the
4847     //       current API.
4848     int alignment( mDisplayedTextView.GetTextAlignment() &
4849                   ( Toolkit::Alignment::VerticalTop |
4850                     Toolkit::Alignment::VerticalCenter |
4851                     Toolkit::Alignment::VerticalBottom |
4852                     Toolkit::Alignment::HorizontalCenter ) );
4853     Toolkit::TextView::LineJustification justification( mDisplayedTextView.GetLineJustification() );
4854
4855     // If our alignment is in the center, then do not change.
4856     if ( !( alignment & Toolkit::Alignment::HorizontalCenter ) )
4857     {
4858       alignment |= ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight;
4859     }
4860
4861     // If our justification is in the center, then do not change.
4862     if ( justification != Toolkit::TextView::Center )
4863     {
4864       justification = ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::TextView::Left : Toolkit::TextView::Right;
4865     }
4866
4867     mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(alignment) );
4868     mDisplayedTextView.SetLineJustification( justification );
4869   }
4870 }
4871
4872 void TextInput::UpdateLineHeight()
4873 {
4874   Dali::Font font = Dali::Font::New( FontParameters( mInputStyle.GetFontName(), mInputStyle.GetFontStyle(), mInputStyle.GetFontPointSize() ) );
4875   mLineHeight = font.GetLineHeight();
4876
4877   // 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.
4878
4879   const bool shrink = mDisplayedTextView && ( Toolkit::TextView::ShrinkToFit == mDisplayedTextView.GetHeightExceedPolicy() ) && mStyledText.empty();
4880
4881   if( !mExceedEnabled || shrink )
4882   {
4883     mLineHeight = std::min( mLineHeight, GetControlSize().height );
4884   }
4885 }
4886
4887 std::size_t TextInput::FindVisibleCharacter( const FindVisibleCharacterDirection direction , const std::size_t cursorPosition ) const
4888 {
4889   std::size_t position = 0;
4890
4891   const std::size_t tableSize = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
4892
4893   switch( direction )
4894   {
4895     case Left:
4896     {
4897       position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4898
4899       if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1 : position ) ) ).mIsVisible )
4900       {
4901         position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4902       }
4903       break;
4904     }
4905     case Right:
4906     {
4907       position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4908       if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1 : position ) ) ).mIsVisible )
4909       {
4910         position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4911       }
4912       break;
4913     }
4914     case ByEnd:
4915     {
4916       position = FindVisibleCharacterLeft( 0, mTextLayoutInfo.mCharacterLayoutInfoTable );
4917       break;
4918     }
4919     default:
4920     {
4921       DALI_ASSERT_ALWAYS( !"TextInput::FindVisibleCharacter() Unknown direction." );
4922     }
4923   }
4924
4925   return position;
4926 }
4927
4928 void TextInput::SetSortModifier( float depthOffset )
4929 {
4930   if(mDisplayedTextView)
4931   {
4932     mDisplayedTextView.SetSortModifier(depthOffset);
4933   }
4934 }
4935
4936 void TextInput::SetSnapshotModeEnabled( bool enable )
4937 {
4938   if(mDisplayedTextView)
4939   {
4940     mDisplayedTextView.SetSnapshotModeEnabled( enable );
4941   }
4942 }
4943
4944 bool TextInput::IsSnapshotModeEnabled() const
4945 {
4946   bool snapshotEnabled = false;
4947
4948   if(mDisplayedTextView)
4949   {
4950     snapshotEnabled = mDisplayedTextView.IsSnapshotModeEnabled();
4951   }
4952
4953   return snapshotEnabled;
4954 }
4955
4956 void TextInput::SetMarkupProcessingEnabled( bool enable )
4957 {
4958   mMarkUpEnabled = enable;
4959 }
4960
4961 bool TextInput::IsMarkupProcessingEnabled() const
4962 {
4963   return mMarkUpEnabled;
4964 }
4965
4966 void TextInput::SetScrollEnabled( bool enable )
4967 {
4968   if( mDisplayedTextView )
4969   {
4970     mDisplayedTextView.SetScrollEnabled( enable );
4971   }
4972
4973   if( !enable )
4974   {
4975     // Don't set cursor's and handle's visibility to false if they are outside the
4976     // boundaries of the text-input.
4977     mIsCursorInScrollArea = true;
4978     mIsGrabHandleInScrollArea = true;
4979     if( mSelectionHandleOne && mSelectionHandleTwo )
4980     {
4981       mSelectionHandleOne.SetVisible( true );
4982       mSelectionHandleTwo.SetVisible( true );
4983
4984       if( mHighlightMeshActor )
4985       {
4986         mHighlightMeshActor.SetVisible( true );
4987       }
4988     }
4989   }
4990 }
4991
4992 bool TextInput::IsScrollEnabled() const
4993 {
4994   bool scrollEnabled = false;
4995
4996   if( mDisplayedTextView )
4997   {
4998     scrollEnabled = mDisplayedTextView.IsScrollEnabled();
4999   }
5000
5001   return scrollEnabled;
5002 }
5003
5004 void TextInput::SetScrollPosition( const Vector2& position )
5005 {
5006   if( mDisplayedTextView )
5007   {
5008     mDisplayedTextView.SetScrollPosition( position );
5009   }
5010 }
5011
5012 Vector2 TextInput::GetScrollPosition() const
5013 {
5014   Vector2 scrollPosition;
5015
5016   if( mDisplayedTextView )
5017   {
5018     scrollPosition = mDisplayedTextView.GetScrollPosition();
5019   }
5020
5021   return scrollPosition;
5022 }
5023
5024 std::size_t TextInput::DoInsertAt( const Text& text, const std::size_t position, const std::size_t numberOfCharactersToReplace, bool& textExceedsMaximunNumberOfCharacters, bool& textExceedsBoundary )
5025 {
5026   // determine number of characters that we can write to style text buffer, this is the insertStringLength
5027   std::size_t insertedStringLength = std::min( text.GetLength(), mMaxStringLength - mStyledText.size() );
5028   textExceedsMaximunNumberOfCharacters = insertedStringLength < text.GetLength();
5029
5030   // Add style to the new input text.
5031   MarkupProcessor::StyledTextArray textToInsert;
5032   for( std::size_t i = 0; i < insertedStringLength; ++i )
5033   {
5034     const MarkupProcessor::StyledText newStyledCharacter( text[i], mInputStyle );
5035     textToInsert.push_back( newStyledCharacter );
5036   }
5037
5038   //Insert text to the TextView.
5039   const bool emptyTextView = mStyledText.empty();
5040   if( emptyTextView && mPlaceHolderSet )
5041   {
5042     // There is no text set so call to TextView::SetText() is needed in order to clear the placeholder text.
5043     mDisplayedTextView.SetText( textToInsert );
5044   }
5045   else
5046   {
5047     if( 0 == numberOfCharactersToReplace )
5048     {
5049       mDisplayedTextView.InsertTextAt( position, textToInsert );
5050     }
5051     else
5052     {
5053       mDisplayedTextView.ReplaceTextFromTo( position, numberOfCharactersToReplace, textToInsert );
5054     }
5055   }
5056   mPlaceHolderSet = false;
5057
5058   if( textToInsert.empty() )
5059   {
5060     // If no text has been inserted, GetTextLayoutInfo() need to be called to check whether mStyledText has some text.
5061     GetTextLayoutInfo();
5062   }
5063   else
5064   {
5065     // GetTextLayoutInfo() can't be used here as mStyledText is not updated yet.
5066     mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5067   }
5068
5069   textExceedsBoundary = false;
5070
5071   if( !mExceedEnabled )
5072   {
5073     const Vector3& size = GetControlSize();
5074
5075     if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5076     {
5077       // If new text does not fit within TextView
5078       mDisplayedTextView.RemoveTextFrom( position, insertedStringLength );
5079       // previously inserted text has been removed. Call GetTextLayoutInfo() to check whether mStyledText has some text.
5080       GetTextLayoutInfo();
5081       textExceedsBoundary = true;
5082       insertedStringLength = 0;
5083     }
5084
5085     if( textExceedsBoundary )
5086     {
5087       // Add the part of the text which fits on the text-input.
5088
5089       // Split the text which doesn't fit in two halves.
5090       MarkupProcessor::StyledTextArray firstHalf;
5091       MarkupProcessor::StyledTextArray secondHalf;
5092       SplitText( textToInsert, firstHalf, secondHalf );
5093
5094       // Clear text. This text will be filled with the text inserted.
5095       textToInsert.clear();
5096
5097       // Where to insert the text.
5098       std::size_t positionToInsert = position;
5099
5100       bool end = text.GetLength() <= 1;
5101       while( !end )
5102       {
5103         // Insert text and check ...
5104         const std::size_t textLength = firstHalf.size();
5105         mDisplayedTextView.InsertTextAt( positionToInsert, firstHalf );
5106         mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5107
5108         if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5109         {
5110           // Inserted text doesn't fit.
5111
5112           // Remove inserted text
5113           mDisplayedTextView.RemoveTextFrom( positionToInsert, textLength );
5114           mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5115
5116           // The iteration finishes when only one character doesn't fit.
5117           end = textLength <= 1;
5118
5119           if( !end )
5120           {
5121             // Prepare next two halves for next iteration.
5122             MarkupProcessor::StyledTextArray copyText = firstHalf;
5123             SplitText( copyText, firstHalf, secondHalf );
5124           }
5125         }
5126         else
5127         {
5128           // Text fits.
5129
5130           // store text to be inserted in mStyledText.
5131           textToInsert.insert( textToInsert.end(), firstHalf.begin(), firstHalf.end() );
5132
5133           // Increase the inserted characters counter.
5134           insertedStringLength += textLength;
5135
5136           // Prepare next two halves for next iteration.
5137           MarkupProcessor::StyledTextArray copyText = secondHalf;
5138           SplitText( copyText, firstHalf, secondHalf );
5139
5140           // Update where next text has to be inserted
5141           positionToInsert += textLength;
5142         }
5143       }
5144     }
5145   }
5146
5147   if( textToInsert.empty() && emptyTextView )
5148   {
5149     // No character has been added and the text-view was empty.
5150     // Set the placeholder text.
5151     mDisplayedTextView.SetText( mStyledPlaceHolderText );
5152     mPlaceHolderSet = true;
5153   }
5154   else
5155   {
5156     MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + position;
5157     mStyledText.insert( it, textToInsert.begin(), textToInsert.end() );
5158     mPlaceHolderSet = false;
5159   }
5160
5161   return insertedStringLength;
5162 }
5163
5164 void TextInput::GetTextLayoutInfo()
5165 {
5166   if( mStyledText.empty() )
5167   {
5168     // The text-input has no text, clear the text-view's layout info.
5169     mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5170   }
5171   else
5172   {
5173     if( mDisplayedTextView )
5174     {
5175       mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5176     }
5177     else
5178     {
5179       // There is no text-view.
5180       mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5181     }
5182   }
5183 }
5184
5185 void TextInput::SetOffsetFromText( const Vector4& offset )
5186 {
5187   mPopupOffsetFromText = offset;
5188 }
5189
5190 const Vector4& TextInput::GetOffsetFromText() const
5191 {
5192   return mPopupOffsetFromText;
5193 }
5194
5195 void TextInput::SetProperty( BaseObject* object, Property::Index propertyIndex, const Property::Value& value )
5196 {
5197   Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5198
5199   if ( textInput )
5200   {
5201     TextInput& textInputImpl( GetImpl( textInput ) );
5202
5203     switch ( propertyIndex )
5204     {
5205       case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5206       {
5207         textInputImpl.SetMaterialDiffuseColor( value.Get< Vector4 >() );
5208         break;
5209       }
5210       case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5211       {
5212         textInputImpl.mPopupPanel.SetCutPastePopupColor( value.Get< Vector4 >() );
5213         break;
5214       }
5215       case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5216       {
5217         textInputImpl.mPopupPanel.SetCutPastePopupPressedColor( value.Get< Vector4 >() );
5218         break;
5219       }
5220       case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY:
5221       {
5222         textInputImpl.mPopupPanel.SetCutPastePopupBorderColor( value.Get< Vector4 >() );
5223         break;
5224       }
5225       case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5226       {
5227         textInputImpl.mPopupPanel.SetCutPastePopupIconColor( value.Get< Vector4 >() );
5228         break;
5229       }
5230       case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5231       {
5232         textInputImpl.mPopupPanel.SetCutPastePopupIconPressedColor( value.Get< Vector4 >() );
5233         break;
5234       }
5235       case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5236       {
5237         textInputImpl.mPopupPanel.SetCutPastePopupTextColor( value.Get< Vector4 >() );
5238         break;
5239       }
5240       case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5241       {
5242         textInputImpl.mPopupPanel.SetCutPastePopupTextPressedColor( value.Get< Vector4 >() );
5243         break;
5244       }
5245       case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5246       {
5247         textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCut, value.Get<unsigned int>() );
5248         break;
5249       }
5250       case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5251       {
5252         textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCopy, value.Get<unsigned int>() );
5253         break;
5254       }
5255       case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5256       {
5257         textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsPaste, value.Get<unsigned int>() );
5258         break;
5259       }
5260       case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5261       {
5262         textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelect, value.Get<unsigned int>() );
5263         break;
5264       }
5265       case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5266       {
5267         textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll, value.Get<unsigned int>() );
5268         break;
5269       }
5270       case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5271       {
5272         textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsClipboard, value.Get<unsigned int>() );
5273         break;
5274       }
5275       case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5276       {
5277         textInputImpl.SetOffsetFromText( value.Get< Vector4 >() );
5278         break;
5279       }
5280       case Toolkit::TextInput::CURSOR_COLOR_PROPERTY:
5281       {
5282         textInputImpl.mCursor.SetColor( value.Get< Vector4 >() );
5283       }
5284     }
5285   }
5286 }
5287
5288 Property::Value TextInput::GetProperty( BaseObject* object, Property::Index propertyIndex )
5289 {
5290   Property::Value value;
5291
5292   Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5293
5294   if ( textInput )
5295   {
5296     TextInput& textInputImpl( GetImpl( textInput ) );
5297
5298     switch ( propertyIndex )
5299     {
5300       case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5301       {
5302         value = textInputImpl.GetMaterialDiffuseColor();
5303         break;
5304       }
5305       case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5306       {
5307         value = textInputImpl.mPopupPanel.GetCutPastePopupColor();
5308         break;
5309       }
5310       case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5311       {
5312         value = textInputImpl.mPopupPanel.GetCutPastePopupPressedColor();
5313         break;
5314       }
5315       case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY :
5316       {
5317         value = textInputImpl.mPopupPanel.GetCutPastePopupBorderColor();
5318         break;
5319       }
5320       case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5321       {
5322         value = textInputImpl.mPopupPanel.GetCutPastePopupIconColor();
5323         break;
5324       }
5325       case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5326       {
5327         value = textInputImpl.mPopupPanel.GetCutPastePopupIconPressedColor();
5328         break;
5329       }
5330       case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5331       {
5332         value = textInputImpl.mPopupPanel.GetCutPastePopupTextColor();
5333         break;
5334       }
5335       case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5336       {
5337         value = textInputImpl.mPopupPanel.GetCutPastePopupTextPressedColor();
5338         break;
5339       }
5340       case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5341       {
5342         value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCut );
5343         break;
5344       }
5345       case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5346       {
5347         value =  textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCopy );
5348         break;
5349       }
5350       case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5351       {
5352         value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsPaste );
5353         break;
5354       }
5355       case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5356       {
5357         value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelect );
5358         break;
5359       }
5360       case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5361       {
5362         value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll );
5363         break;
5364       }
5365       case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5366       {
5367         value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsClipboard );
5368         break;
5369       }
5370       case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5371       {
5372         value = textInputImpl.GetOffsetFromText();
5373         break;
5374       }
5375       case Toolkit::TextInput::CURSOR_COLOR_PROPERTY:
5376       {
5377         value = textInputImpl.mCursor.GetCurrentColor();
5378       }
5379     }
5380   }
5381   return value;
5382 }
5383
5384 void TextInput::EmitStyleChangedSignal()
5385 {
5386   // emit signal if input style changes.
5387   Toolkit::TextInput handle( GetOwner() );
5388   mStyleChangedSignalV2.Emit( handle, mInputStyle );
5389 }
5390
5391 void TextInput::EmitTextModified()
5392 {
5393   // emit signal when text changes.
5394   Toolkit::TextInput handle( GetOwner() );
5395   mTextModifiedSignal.Emit( handle );
5396 }
5397
5398
5399 void TextInput::EmitMaxInputCharactersReachedSignal()
5400 {
5401   // emit signal if max characters is reached during text input.
5402   DALI_LOG_INFO(gLogFilter, Debug::General, "EmitMaxInputCharactersReachedSignal \n");
5403
5404   Toolkit::TextInput handle( GetOwner() );
5405   mMaxInputCharactersReachedSignalV2.Emit( handle );
5406 }
5407
5408 void TextInput::EmitInputTextExceedsBoundariesSignal()
5409 {
5410   // Emit a signal when the input text exceeds the boundaries of the text input.
5411
5412   Toolkit::TextInput handle( GetOwner() );
5413   mInputTextExceedBoundariesSignalV2.Emit( handle );
5414 }
5415
5416 } // namespace Internal
5417
5418 } // namespace Toolkit
5419
5420 } // namespace Dali