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