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