Merge "Completely remove use of Adaptor class" into tizen
[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 0;
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( GetVisualPosition( 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( GetVisualPosition( 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(const std::size_t nthChar)
2852 {
2853   // Get height of cursor and set its size
2854   Size size( CURSOR_THICKNESS, 0.0f );
2855   if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
2856   {
2857     size.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height;
2858   }
2859   else
2860   {
2861     // Measure Font so know how big text will be if no initial text to measure.
2862     size.height = mLineHeight;
2863   }
2864
2865   mCursor.SetSize(size);
2866
2867   // If the character is italic then the cursor also tilts.
2868   mCursor.SetRotation( mInputStyle.IsItalicsEnabled() ? Degree( mInputStyle.GetItalicsAngle() - CURSOR_ANGLE_OFFSET ) : Degree( 0.f ), Vector3::ZAXIS );
2869
2870   DALI_ASSERT_DEBUG( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
2871
2872   if ( ( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) )
2873   {
2874     Vector3 altPosition;    // Alternate (i.e. opposite direction) cursor position.
2875     bool altPositionValid;  // Alternate cursor validity flag.
2876     bool directionRTL;      // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
2877     Vector3 position = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
2878
2879     SetAltCursorEnabled( altPositionValid );
2880
2881     if(!altPositionValid)
2882     {
2883       mCursor.SetPosition( position + UI_OFFSET );
2884     }
2885     else
2886     {
2887       size.height *= 0.5f;
2888       mCursor.SetSize(size);
2889       mCursor.SetPosition( position + UI_OFFSET - Vector3(0.0f, directionRTL ? 0.0f : size.height, 0.0f) );
2890
2891       // TODO: change this cursor pos, to be the one where the cursor is sourced from.
2892       Size rowSize = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) );
2893       size.height = rowSize.height * 0.5f;
2894       mCursorRTL.SetSize(size);
2895       mCursorRTL.SetPosition( altPosition + UI_OFFSET - Vector3(0.0f, directionRTL ? size.height : 0.0f, 0.0f) );
2896     }
2897
2898     if( IsScrollEnabled() )
2899     {
2900       // Whether cursor and grab handle are inside the boundaries of the text-input when text scroll is enabled.
2901       mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( position, size, GetControlSize() );
2902     }
2903   } // EditMode
2904 }
2905
2906 void TextInput::SetAltCursorEnabled( bool enabled )
2907 {
2908   mCursorRTLEnabled = enabled;
2909   mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2910 }
2911
2912 void TextInput::SetCursorVisibility( bool visible )
2913 {
2914   mCursorVisibility = visible;
2915   mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
2916   mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2917 }
2918
2919 void TextInput::CreateGrabHandle( Dali::Image image )
2920 {
2921   if ( !mGrabHandle )
2922   {
2923     if ( !image )
2924     {
2925       mGrabHandleImage = Image::New(DEFAULT_GRAB_HANDLE);
2926     }
2927     else
2928     {
2929       mGrabHandleImage = image;
2930     }
2931
2932     mGrabHandle = ImageActor::New(mGrabHandleImage);
2933     mGrabHandle.SetParentOrigin(ParentOrigin::TOP_LEFT);
2934     mGrabHandle.SetAnchorPoint(AnchorPoint::TOP_CENTER);
2935
2936     mGrabHandle.SetDrawMode(DrawMode::OVERLAY);
2937
2938     ShowGrabHandleAndSetVisibility( false );
2939
2940     CreateGrabArea( mGrabHandle );
2941
2942     mActiveLayer.Add(mGrabHandle);
2943   }
2944 }
2945
2946 void TextInput::CreateGrabArea( Actor& parent )
2947 {
2948   mGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
2949   mGrabArea.SetName( "GrabArea" );
2950   mGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
2951   mGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE ) ) );  // grab area to be larger than text actor
2952   mGrabArea.TouchedSignal().Connect(this,&TextInput::OnPressDown);
2953   mTapDetector.Attach( mGrabArea );
2954   mPanGestureDetector.Attach( mGrabArea );
2955   mLongPressDetector.Attach( mGrabArea );
2956
2957   parent.Add(mGrabArea);
2958 }
2959
2960 Vector3 TextInput::MoveGrabHandle( const Vector2& displacement )
2961 {
2962   Vector3 actualHandlePosition;
2963
2964   if (mGrabHandle)
2965   {
2966     mActualGrabHandlePosition.x += displacement.x;
2967     mActualGrabHandlePosition.y += displacement.y;
2968
2969     // Grab handle should jump to the nearest character and take cursor with it
2970     std::size_t newCursorPosition = 0;
2971     ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY(), newCursorPosition );
2972
2973     actualHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition );
2974
2975     bool handleVisible = true;
2976
2977     if( IsScrollEnabled() )
2978     {
2979       const Vector3 controlSize = GetControlSize();
2980       const Size cursorSize = GetRowRectFromCharacterPosition( GetVisualPosition( newCursorPosition ) );
2981       // Scrolls the text if the handle is not in a visible position
2982       handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
2983                                                   cursorSize,
2984                                                   controlSize );
2985
2986       if( handleVisible )
2987       {
2988         StopScrollTimer();
2989         mCurrentHandlePosition = actualHandlePosition;
2990         mScrollDisplacement = Vector2::ZERO;
2991       }
2992       else
2993       {
2994         if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
2995         {
2996           mScrollDisplacement.x = -SCROLL_SPEED;
2997         }
2998         else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
2999         {
3000           mScrollDisplacement.x = SCROLL_SPEED;
3001         }
3002         if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3003         {
3004           mScrollDisplacement.y = -SCROLL_SPEED;
3005         }
3006         else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3007         {
3008           mScrollDisplacement.y = SCROLL_SPEED;
3009         }
3010         StartScrollTimer();
3011       }
3012     }
3013
3014     if( handleVisible &&                           // Only redraw cursor and do updates if position changed
3015         ( newCursorPosition != mCursorPosition ) ) // and the new position is visible (if scroll is not enabled, it's always true).
3016     {
3017       mCursorPosition = newCursorPosition;
3018
3019       mGrabHandle.SetPosition( actualHandlePosition + UI_OFFSET );
3020
3021       const TextStyle oldInputStyle( mInputStyle );
3022
3023       mInputStyle = GetStyleAtCursor(); //Inherit style from cursor position
3024
3025       CursorUpdate();  // Let keyboard know the new cursor position so can 're-capture' for prediction.
3026
3027       if( oldInputStyle != mInputStyle )
3028       {
3029         // Updates the line height accordingly with the input style.
3030         UpdateLineHeight();
3031
3032         EmitStyleChangedSignal();
3033       }
3034     }
3035   }
3036
3037   return actualHandlePosition;
3038 }
3039
3040 void TextInput::ShowGrabHandle( bool visible )
3041 {
3042   if ( IsGrabHandleEnabled() )
3043   {
3044     if( mGrabHandle )
3045     {
3046       mGrabHandle.SetVisible( mGrabHandleVisibility );
3047     }
3048     StartMonitoringStageForTouch();
3049   }
3050 }
3051
3052 void TextInput::ShowGrabHandleAndSetVisibility( bool visible )
3053 {
3054   mGrabHandleVisibility = visible;
3055   ShowGrabHandle( visible );
3056 }
3057
3058 // Callbacks connected to be Property notifications for Boundary checking.
3059
3060 void TextInput::OnLeftBoundaryExceeded(PropertyNotification& source)
3061 {
3062   mIsSelectionHandleOneFlipped = true;
3063   mSelectionHandleOne.SetScale( -1.0f, 1.0f, 1.0f );
3064   mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3065 }
3066
3067 void TextInput::OnReturnToLeftBoundary(PropertyNotification& source)
3068 {
3069   mIsSelectionHandleOneFlipped = false;
3070   mSelectionHandleOne.SetScale( 1.0f, 1.0f, 1.0f );
3071   mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3072 }
3073
3074 void TextInput::OnRightBoundaryExceeded(PropertyNotification& source)
3075 {
3076   mIsSelectionHandleTwoFlipped = true;
3077   mSelectionHandleTwo.SetScale( -1.0f, 1.0f, 1.0f );
3078   mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3079 }
3080
3081 void TextInput::OnReturnToRightBoundary(PropertyNotification& source)
3082 {
3083   mIsSelectionHandleTwoFlipped = false;
3084   mSelectionHandleTwo.SetScale( 1.0f, 1.0f, 1.0f );
3085   mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3086 }
3087
3088 // todo change PropertyNotification signal definition to include Actor. Hence won't need duplicate functions.
3089 void TextInput::OnHandleOneLeavesBoundary( PropertyNotification& source)
3090 {
3091   mSelectionHandleOne.SetOpacity(0.0f);
3092 }
3093
3094 void TextInput::OnHandleOneWithinBoundary(PropertyNotification& source)
3095 {
3096   mSelectionHandleOne.SetOpacity(1.0f);
3097 }
3098
3099 void TextInput::OnHandleTwoLeavesBoundary( PropertyNotification& source)
3100 {
3101   mSelectionHandleTwo.SetOpacity(0.0f);
3102 }
3103
3104 void TextInput::OnHandleTwoWithinBoundary(PropertyNotification& source)
3105 {
3106   mSelectionHandleTwo.SetOpacity(1.0f);
3107 }
3108
3109 // End of Callbacks connected to be Property notifications for Boundary checking.
3110
3111 void TextInput::SetUpHandlePropertyNotifications()
3112 {
3113   /* Property notifications for handles exceeding the boundary and returning back within boundary */
3114
3115   Vector3 handlesize = GetSelectionHandleSize();
3116
3117   // Exceeding horizontal boundary
3118   PropertyNotification leftNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.x + handlesize.x) );
3119   leftNotification.NotifySignal().Connect( this, &TextInput::OnLeftBoundaryExceeded );
3120
3121   PropertyNotification rightNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.z - handlesize.x ) );
3122   rightNotification.NotifySignal().Connect( this, &TextInput::OnRightBoundaryExceeded );
3123
3124   // Within horizontal boundary
3125   PropertyNotification leftLeaveNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.x + 2*handlesize.x ) );
3126   leftLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToLeftBoundary );
3127
3128   PropertyNotification rightLeaveNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.z - 2*handlesize.x ) );
3129   rightLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToRightBoundary );
3130
3131   // Exceeding vertical boundary
3132   PropertyNotification verticalExceedNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3133                                                        OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3134                                                                          mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3135   verticalExceedNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneLeavesBoundary );
3136
3137   PropertyNotification verticalExceedNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3138                                                        OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3139                                                                          mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3140   verticalExceedNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoLeavesBoundary );
3141
3142   // Within vertical boundary
3143   PropertyNotification verticalWithinNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3144                                                        InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3145                                                                         mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3146   verticalWithinNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneWithinBoundary );
3147
3148   PropertyNotification verticalWithinNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3149                                                        InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3150                                                                         mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3151   verticalWithinNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoWithinBoundary );
3152 }
3153
3154 void TextInput::CreateSelectionHandles( std::size_t start, std::size_t end, Dali::Image handleOneImage,  Dali::Image handleTwoImage )
3155 {
3156   mSelectionHandleOnePosition = start;
3157   mSelectionHandleTwoPosition = end;
3158
3159   if ( !mSelectionHandleOne )
3160   {
3161     // create normal and pressed images
3162     mSelectionHandleOneImage = Image::New( DEFAULT_SELECTION_HANDLE_ONE );
3163     mSelectionHandleOneImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_ONE_PRESSED );
3164
3165     mSelectionHandleOne = ImageActor::New( mSelectionHandleOneImage );
3166     mSelectionHandleOne.SetName("SelectionHandleOne");
3167     mSelectionHandleOne.SetParentOrigin( ParentOrigin::TOP_LEFT );
3168     mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
3169     mIsSelectionHandleOneFlipped = false;
3170     mSelectionHandleOne.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text
3171
3172     mHandleOneGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3173     mHandleOneGrabArea.SetName("SelectionHandleOneGrabArea");
3174
3175     mHandleOneGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) );  // grab area to be larger than text actor
3176     mHandleOneGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3177
3178     mTapDetector.Attach( mHandleOneGrabArea );
3179     mPanGestureDetector.Attach( mHandleOneGrabArea );
3180
3181     mHandleOneGrabArea.TouchedSignal().Connect(this,&TextInput::OnHandleOneTouched);
3182
3183     mSelectionHandleOne.Add( mHandleOneGrabArea );
3184     mActiveLayer.Add( mSelectionHandleOne );
3185   }
3186
3187   if ( !mSelectionHandleTwo )
3188   {
3189     // create normal and pressed images
3190     mSelectionHandleTwoImage = Image::New( DEFAULT_SELECTION_HANDLE_TWO );
3191     mSelectionHandleTwoImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_TWO_PRESSED );
3192
3193     mSelectionHandleTwo = ImageActor::New( mSelectionHandleTwoImage );
3194     mSelectionHandleTwo.SetName("SelectionHandleTwo");
3195     mSelectionHandleTwo.SetParentOrigin( ParentOrigin::TOP_LEFT );
3196     mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT );
3197     mIsSelectionHandleTwoFlipped = false;
3198     mSelectionHandleTwo.SetDrawMode(DrawMode::OVERLAY); // ensure grab handle above text
3199
3200     mHandleTwoGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3201     mHandleTwoGrabArea.SetName("SelectionHandleTwoGrabArea");
3202     mHandleTwoGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) );  // grab area to be larger than text actor
3203     mHandleTwoGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3204
3205     mTapDetector.Attach( mHandleTwoGrabArea );
3206     mPanGestureDetector.Attach( mHandleTwoGrabArea );
3207
3208     mHandleTwoGrabArea.TouchedSignal().Connect(this, &TextInput::OnHandleTwoTouched);
3209
3210     mSelectionHandleTwo.Add( mHandleTwoGrabArea );
3211
3212     mActiveLayer.Add( mSelectionHandleTwo );
3213   }
3214
3215   SetUpHandlePropertyNotifications();
3216
3217   // update table as text may have changed.
3218   GetTextLayoutInfo();
3219
3220   mSelectionHandleOneActualPosition = GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition );
3221   mSelectionHandleTwoActualPosition = GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition );
3222
3223   mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
3224   mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
3225
3226   // Calculates and set the visibility if the scroll mode is enabled.
3227   bool isSelectionHandleOneVisible = true;
3228   bool isSelectionHandleTwoVisible = true;
3229   if( IsScrollEnabled() )
3230   {
3231     const Vector3& controlSize( GetControlSize() );
3232     isSelectionHandleOneVisible = IsPositionInsideBoundaries( mSelectionHandleOneActualPosition, Size::ZERO, controlSize );
3233     isSelectionHandleTwoVisible = IsPositionInsideBoundaries( mSelectionHandleTwoActualPosition, Size::ZERO, controlSize );
3234     mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
3235     mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
3236   }
3237
3238   CreateHighlight();  // function will only create highlight if not already created.
3239 }
3240
3241 Vector3 TextInput::MoveSelectionHandle( SelectionHandleId handleId, const Vector2& displacement )
3242 {
3243   Vector3 actualHandlePosition;
3244
3245   if ( mSelectionHandleOne && mSelectionHandleTwo )
3246   {
3247     const Vector3& controlSize = GetControlSize();
3248
3249     Size cursorSize( CURSOR_THICKNESS, 0.f );
3250
3251     // Get a reference of the wanted selection handle (handle one or two).
3252     Vector3& actualSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
3253
3254     // Get a reference for the current position of the handle and a copy of its pair
3255     std::size_t& currentSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3256     const std::size_t pairSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition;
3257
3258     // Get a handle of the selection handle actor
3259     ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3260
3261     // Selection handles should jump to the nearest character
3262     std::size_t newHandlePosition = 0;
3263     ReturnClosestIndex( actualSelectionHandlePosition.GetVectorXY(), newHandlePosition );
3264
3265     actualHandlePosition = GetActualPositionFromCharacterPosition( newHandlePosition );
3266
3267     bool handleVisible = true;
3268
3269     if( IsScrollEnabled() )
3270     {
3271       mCurrentSelectionId = handleId;
3272
3273       cursorSize.height = GetRowRectFromCharacterPosition( GetVisualPosition( newHandlePosition ) ).height;
3274       // Restricts the movement of the grab handle inside the boundaries of the text-input.
3275       handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3276                                                   cursorSize,
3277                                                   controlSize );
3278
3279       if( handleVisible )
3280       {
3281         StopScrollTimer();
3282         mCurrentSelectionHandlePosition = actualHandlePosition;
3283         mScrollDisplacement = Vector2::ZERO;
3284       }
3285       else
3286       {
3287         if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3288         {
3289           mScrollDisplacement.x = -SCROLL_SPEED;
3290         }
3291         else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3292         {
3293           mScrollDisplacement.x = SCROLL_SPEED;
3294         }
3295         if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3296         {
3297           mScrollDisplacement.y = -SCROLL_SPEED;
3298         }
3299         else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3300         {
3301           mScrollDisplacement.y = SCROLL_SPEED;
3302         }
3303         StartScrollTimer();
3304       }
3305     }
3306
3307     if ( handleVisible &&                                          // Ensure the handle is visible.
3308          ( newHandlePosition != pairSelectionHandlePosition ) &&   // Ensure handle one is not the same position as handle two.
3309          ( newHandlePosition != currentSelectionHandlePosition ) ) // Ensure the handle has moved.
3310     {
3311       currentSelectionHandlePosition = newHandlePosition;
3312
3313       Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3314       selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3315
3316       UpdateHighlight();
3317
3318       if ( handleId == HandleOne )
3319       {
3320         const TextStyle oldInputStyle( mInputStyle );
3321
3322         // Set Active Style to that of first character in selection
3323         if( mSelectionHandleOnePosition < mStyledText.size() )
3324         {
3325           mInputStyle = ( mStyledText.at( mSelectionHandleOnePosition ) ).mStyle;
3326         }
3327
3328         if( oldInputStyle != mInputStyle )
3329         {
3330           // Updates the line height accordingly with the input style.
3331           UpdateLineHeight();
3332
3333           EmitStyleChangedSignal();
3334         }
3335       }
3336     }
3337   }
3338
3339   return actualHandlePosition; // Returns Handle position passed in if new value not assigned.
3340 }
3341
3342 void TextInput::SetSelectionHandlePosition(SelectionHandleId handleId)
3343 {
3344
3345   const std::size_t selectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3346   ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3347
3348   if ( selectionHandleActor )
3349   {
3350     const Vector3 actualHandlePosition = GetActualPositionFromCharacterPosition( selectionHandlePosition );
3351     Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3352     selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3353
3354     if( IsScrollEnabled() )
3355     {
3356       const Size cursorSize( CURSOR_THICKNESS,
3357                              GetRowRectFromCharacterPosition( GetVisualPosition( selectionHandlePosition ) ).height );
3358       selectionHandleActor.SetVisible( IsPositionInsideBoundaries( actualHandlePosition,
3359                                                                    cursorSize,
3360                                                                    GetControlSize() ) );
3361     }
3362   }
3363 }
3364
3365 std::size_t TextInput::GetVisualPosition(std::size_t logicalPosition) const
3366 {
3367   // Note: we're allowing caller to request a logical position of size (i.e. end of string)
3368   // For now the visual position of end of logical string will be end of visual string.
3369   DALI_ASSERT_DEBUG( logicalPosition <= mTextLayoutInfo.mCharacterLogicalToVisualMap.size() );
3370
3371   return logicalPosition != mTextLayoutInfo.mCharacterLogicalToVisualMap.size() ? mTextLayoutInfo.mCharacterLogicalToVisualMap[logicalPosition] : mTextLayoutInfo.mCharacterLogicalToVisualMap.size();
3372 }
3373
3374 void TextInput::GetVisualTextSelection(std::vector<bool>& selectedVisualText, std::size_t startSelection, std::size_t endSelection)
3375 {
3376   std::vector<int>::iterator it = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin();
3377   std::vector<int>::iterator startSelectionIt = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::min(startSelection, endSelection);
3378   std::vector<int>::iterator endSelectionIt = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::max(startSelection, endSelection);
3379   std::vector<int>::iterator end = mTextLayoutInfo.mCharacterLogicalToVisualMap.end();
3380
3381   selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size() );
3382
3383   // Deselect text prior to startSelectionIt
3384   for(;it!=startSelectionIt;++it)
3385   {
3386     selectedVisualText[*it] = false;
3387   }
3388
3389   // Select text from startSelectionIt -> endSelectionIt
3390   for(;it!=endSelectionIt;++it)
3391   {
3392     selectedVisualText[*it] = true;
3393   }
3394
3395   // Deselect text after endSelection
3396   for(;it!=end;++it)
3397   {
3398     selectedVisualText[*it] = false;
3399   }
3400
3401   selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size(), false );
3402 }
3403
3404 // Calculate the dimensions of the quads they will make the highlight mesh
3405 TextInput::HighlightInfo TextInput::CalculateHighlightInfo()
3406 {
3407   // At the moment there is no public API to modify the block alignment option.
3408   const bool blockAlignEnabled = true;
3409
3410   mNewHighlightInfo.mQuadList.clear(); // clear last quad information.
3411
3412   if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
3413   {
3414     Toolkit::TextView::CharacterLayoutInfoContainer::iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3415     Toolkit::TextView::CharacterLayoutInfoContainer::iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
3416
3417     // Get vector of flags representing characters that are selected (true) vs unselected (false).
3418     std::vector<bool> selectedVisualText;
3419     GetVisualTextSelection(selectedVisualText, mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
3420     std::vector<bool>::iterator selectedIt(selectedVisualText.begin());
3421     std::vector<bool>::iterator selectedEndIt(selectedVisualText.end());
3422
3423     SelectionState selectionState = SelectionNone;          ///< Current selection status of cursor over entire text.
3424     float rowLeft = 0.0f;
3425     float rowRight = 0.0f;
3426     // Keep track of the TextView's min/max extents. Should be able to query this from TextView.
3427     float maxRowLeft = std::numeric_limits<float>::max();
3428     float maxRowRight = 0.0f;
3429
3430     Toolkit::TextView::CharacterLayoutInfoContainer::iterator lastIt = it;
3431
3432     // Scan through entire text.
3433     while(it != end)
3434     {
3435       // selectionState: None when not in selection, Started when in selection, and Ended when reached end of selection.
3436
3437       Toolkit::TextView::CharacterLayoutInfo& charInfo(*it);
3438       bool charSelected( false );
3439       if( selectedIt != selectedEndIt )
3440       {
3441         charSelected = *selectedIt++;
3442       }
3443
3444       if(selectionState == SelectionNone)
3445       {
3446         if(charSelected)
3447         {
3448           selectionState = SelectionStarted;
3449           rowLeft = charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3450           rowRight = rowLeft + charInfo.mSize.width;
3451         }
3452       }
3453       else if(selectionState == SelectionStarted)
3454       {
3455         // break selection on:
3456         // 1. new line causing selection break. (\n or wordwrap)
3457         // 2. character not selected.
3458         if(charInfo.mPosition.y - lastIt->mPosition.y > CHARACTER_THRESHOLD ||
3459            !charSelected)
3460         {
3461           // finished selection.
3462           // TODO: TextView should have a table of visual rows, and each character a reference to the row
3463           // that it resides on. That way this enumeration is not necessary.
3464           Vector2 min, max;
3465           if(lastIt->mIsNewParagraphChar)
3466           {
3467             // 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.
3468             lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3469           }
3470           const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3471           maxRowLeft = std::min(maxRowLeft, min.x);
3472           maxRowRight = std::max(maxRowRight, max.x);
3473           float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3474           float rowTop = rowBottom - rowSize.height;
3475
3476           // Still selected, and block-align mode then set rowRight to max, so it can be clamped afterwards
3477           if(charSelected && blockAlignEnabled)
3478           {
3479             rowRight = std::numeric_limits<float>::max();
3480           }
3481           mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3482
3483           selectionState = SelectionNone;
3484
3485           // Still selected? start a new selection
3486           if( charSelected )
3487           {
3488             // if block-align mode then set rowLeft to min, so it can be clamped afterwards
3489             rowLeft = blockAlignEnabled ? 0.0f : charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3490             rowRight = ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width;
3491             selectionState = SelectionStarted;
3492           }
3493         }
3494         else
3495         {
3496           // build up highlight(s) with this selection data.
3497           rowLeft = std::min( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x, rowLeft );
3498           rowRight = std::max( ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width, rowRight );
3499         }
3500       }
3501
3502       lastIt = it++;
3503     }
3504
3505     // If reached end, and still on selection, then close selection.
3506     if(it == end)
3507     {
3508       if(selectionState == SelectionStarted)
3509       {
3510         // finished selection.
3511         Vector2 min, max;
3512         if(lastIt->mIsNewParagraphChar)
3513         {
3514           lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3515         }
3516         const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3517         maxRowLeft = std::min(maxRowLeft, min.x);
3518         maxRowRight = std::max(maxRowRight, max.x);
3519         float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3520         float rowTop = rowBottom - rowSize.height;
3521         mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3522       }
3523     }
3524
3525     // Get the top left and bottom right corners.
3526     const Toolkit::TextView::CharacterLayoutInfo& firstCharacter( *mTextLayoutInfo.mCharacterLayoutInfoTable.begin() );
3527     const Vector2 topLeft( maxRowLeft, firstCharacter.mPosition.y - firstCharacter.mSize.height );
3528     const Vector2 bottomRight( topLeft.x + mTextLayoutInfo.mTextSize.width, topLeft.y + mTextLayoutInfo.mTextSize.height );
3529
3530     // Clamp quads so they appear to clip to borders of the whole text.
3531     mNewHighlightInfo.Clamp2D( topLeft, bottomRight );
3532
3533     // For block-align align Further Clamp quads to max left and right extents
3534     if(blockAlignEnabled)
3535     {
3536       // BlockAlign: Will adjust highlight to block:
3537       // i.e.
3538       //   H[ello] (top row right = max of all rows right)
3539       // [--this-] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3540       // [is some] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3541       // [text] (bottom row left = min of all rows left)
3542       // (common in SMS messaging selection)
3543       //
3544       // As opposed to the default which is tight text highlighting.
3545       //   H[ello]
3546       //   [this]
3547       // [is some]
3548       // [text]
3549       // (common in regular text editors/web browser selection)
3550
3551       mNewHighlightInfo.Clamp2D( Vector2(maxRowLeft, topLeft.y), Vector2(maxRowRight, bottomRight.y ) );
3552     }
3553
3554     // Finally clamp quads again so they don't exceed the boundry of the control.
3555     const Vector3& controlSize = GetControlSize();
3556     mNewHighlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
3557   } // end if
3558
3559   return mNewHighlightInfo;
3560 }
3561
3562 void TextInput::UpdateHighlight()
3563 {
3564 //  Construct a Mesh with a texture to be used as the highlight 'box' for selected text
3565 //
3566 //  Example scenarios where mesh is made from 3, 1, 2, 2 ,3 or 3 quads.
3567 //
3568 //   [ TOP   ]  [ TOP ]      [TOP ]  [ TOP    ]      [ TOP  ]      [ TOP  ]
3569 //  [ MIDDLE ]             [BOTTOM]  [BOTTOM]      [ MIDDLE ]   [ MIDDLE  ]
3570 //  [ BOTTOM]                                      [ MIDDLE ]   [ MIDDLE  ]
3571 //                                                 [BOTTOM]     [ MIDDLE  ]
3572 //                                                              [BOTTOM]
3573 //
3574 //  Each quad is created as 2 triangles.
3575 //  Middle is just 1 quad regardless of its size.
3576 //
3577 //  (0,0)         (0,0)
3578 //     0*    *2     0*       *2
3579 //     TOP          TOP
3580 //     3*    *1     3*       *1
3581 //  4*       *1     4*     *6
3582 //     MIDDLE         BOTTOM
3583 //  6*       *5     7*     *5
3584 //  6*    *8
3585 //   BOTTOM
3586 //  9*    *7
3587 //
3588
3589   if ( mHighlightMeshActor )
3590   {
3591     // vertex and triangle buffers should always be present if MeshActor is alive.
3592     HighlightInfo newHighlightInfo = CalculateHighlightInfo();
3593     MeshData::VertexContainer vertices;
3594     Dali::MeshData::FaceIndices faceIndices;
3595
3596     if( !newHighlightInfo.mQuadList.empty() )
3597     {
3598       std::vector<QuadCoordinates>::iterator iter = newHighlightInfo.mQuadList.begin();
3599       std::vector<QuadCoordinates>::iterator endIter = newHighlightInfo.mQuadList.end();
3600
3601       // vertex position defaults to (0 0 0)
3602       MeshData::Vertex vertex;
3603       // set normal for all vertices as (0 0 1) pointing outward from TextInput Actor.
3604       vertex.nZ = 1.0f;
3605
3606       for(std::size_t v = 0; iter != endIter; ++iter,v+=4 )
3607       {
3608         // Add each quad geometry (a sub-selection) to the mesh data.
3609
3610         // 0-----1
3611         // |\    |
3612         // | \ A |
3613         // |  \  |
3614         // | B \ |
3615         // |    \|
3616         // 2-----3
3617
3618         QuadCoordinates& quad = *iter;
3619         // top-left (v+0)
3620         vertex.x = quad.min.x;
3621         vertex.y = quad.min.y;
3622         vertices.push_back( vertex );
3623
3624         // top-right (v+1)
3625         vertex.x = quad.max.x;
3626         vertex.y = quad.min.y;
3627         vertices.push_back( vertex );
3628
3629         // bottom-left (v+2)
3630         vertex.x = quad.min.x;
3631         vertex.y = quad.max.y;
3632         vertices.push_back( vertex );
3633
3634         // bottom-right (v+3)
3635         vertex.x = quad.max.x;
3636         vertex.y = quad.max.y;
3637         vertices.push_back( vertex );
3638
3639         // triangle A (3, 1, 0)
3640         faceIndices.push_back( v + 3 );
3641         faceIndices.push_back( v + 1 );
3642         faceIndices.push_back( v );
3643
3644         // triangle B (0, 2, 3)
3645         faceIndices.push_back( v );
3646         faceIndices.push_back( v + 2 );
3647         faceIndices.push_back( v + 3 );
3648
3649         mMeshData.SetFaceIndices( faceIndices );
3650       }
3651
3652       BoneContainer bones(0); // passed empty as bones not required
3653       mMeshData.SetData( vertices, faceIndices, bones, mCustomMaterial );
3654       mHighlightMesh.UpdateMeshData(mMeshData);
3655     }
3656   }
3657 }
3658
3659 void TextInput::ClearPopup()
3660 {
3661   mPopupPanel.Clear();
3662 }
3663
3664 void TextInput::AddPopupOptions()
3665 {
3666   mPopupPanel.AddPopupOptions();
3667 }
3668
3669 void TextInput::SetPopupPosition( const Vector3& position, const Vector2& alternativePosition )
3670 {
3671   const Vector3& visiblePopUpSize = mPopupPanel.GetVisibileSize();
3672
3673   Vector3 clampedPosition ( position );
3674   Vector3 tailOffsetPosition ( position );
3675
3676   float xOffSet( 0.0f );
3677
3678   Actor self = Self();
3679   const Vector3 textViewTopLeftWorldPosition = self.GetCurrentWorldPosition() - self.GetCurrentSize()*0.5f;
3680
3681   const float popUpLeft = textViewTopLeftWorldPosition.x + position.x - visiblePopUpSize.width*0.5f;
3682   const float popUpTop = textViewTopLeftWorldPosition.y + position.y - visiblePopUpSize.height;
3683
3684   // Clamp to left or right or of boundary
3685   if( popUpLeft < mBoundingRectangleWorldCoordinates.x )
3686   {
3687     xOffSet = mBoundingRectangleWorldCoordinates.x - popUpLeft ;
3688   }
3689   else if ( popUpLeft + visiblePopUpSize.width > mBoundingRectangleWorldCoordinates.z )
3690   {
3691     xOffSet = mBoundingRectangleWorldCoordinates.z - ( popUpLeft + visiblePopUpSize.width );
3692   }
3693
3694   clampedPosition.x = position.x + xOffSet;
3695   tailOffsetPosition.x = -xOffSet;
3696
3697   // Check if top left of PopUp outside of top bounding rectangle, if so then flip to lower position.
3698   bool flipTail( false );
3699
3700   if ( popUpTop < mBoundingRectangleWorldCoordinates.y )
3701   {
3702     clampedPosition.y = alternativePosition.y + visiblePopUpSize.height;
3703     flipTail = true;
3704   }
3705
3706   mPopupPanel.GetRootActor().SetPosition( clampedPosition );
3707   mPopupPanel.SetTailPosition( tailOffsetPosition, flipTail );
3708 }
3709
3710 void TextInput::HidePopup(bool animate, bool signalFinished )
3711 {
3712   if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown )  )
3713   {
3714     mPopupPanel.Hide( animate );
3715
3716     if( animate && signalFinished )
3717     {
3718       mPopupPanel.HideFinishedSignal().Connect( this, &TextInput::OnPopupHideFinished );
3719     }
3720   }
3721 }
3722
3723 void TextInput::ShowPopup( bool animate )
3724 {
3725   Vector3 position;
3726   Vector2 alternativePopupPosition;
3727
3728   if(mHighlightMeshActor && mState == StateEdit)
3729   {
3730     Vector3 topHandle;
3731     Vector3 bottomHandle; // referring to the bottom most point of the handle or the bottom line of selection.
3732     Size rowSize;
3733     // When text is selected, show popup above top handle (and text), or below bottom handle.
3734     // topHandle: referring to the top most point of the handle or the top line of selection.
3735     if ( mSelectionHandleTwoActualPosition.y > mSelectionHandleOneActualPosition.y )
3736     {
3737       topHandle = mSelectionHandleOneActualPosition;
3738       bottomHandle = mSelectionHandleTwoActualPosition;
3739       rowSize= GetRowRectFromCharacterPosition( mSelectionHandleOnePosition );
3740     }
3741     else
3742     {
3743       topHandle = mSelectionHandleTwoActualPosition;
3744       bottomHandle = mSelectionHandleOneActualPosition;
3745       rowSize = GetRowRectFromCharacterPosition( mSelectionHandleTwoPosition );
3746     }
3747     topHandle.y += -mPopupOffsetFromText.y - rowSize.height;
3748     position = Vector3(topHandle.x, topHandle.y, 0.0f);
3749
3750     float xPosition = ( fabsf( topHandle.x - bottomHandle.x ) )*0.5f + std::min( mSelectionHandleOneActualPosition.x , mSelectionHandleTwoActualPosition.x );
3751
3752     position.x = xPosition;
3753
3754     // Alternative position if no upper space
3755     bottomHandle.y += GetSelectionHandleSize().y + mPopupOffsetFromText.w;
3756     alternativePopupPosition = Vector2 ( position.x, bottomHandle.y );
3757   }
3758   else
3759   {
3760     // When no text is selected, show popup at world position of grab handle or cursor
3761     position = GetActualPositionFromCharacterPosition( mCursorPosition );
3762     const Size rowSize = GetRowRectFromCharacterPosition( mCursorPosition );
3763     position.y -= ( mPopupOffsetFromText.y + rowSize.height );
3764     // if can't be positioned above, then position below row.
3765     alternativePopupPosition = Vector2( position.x, position.y ); // default if no grab handle
3766     if ( mGrabHandle )
3767     {
3768       // If grab handle enabled then position pop-up below the grab handle.
3769       alternativePopupPosition.y = rowSize.height + mGrabHandle.GetCurrentSize().height + mPopupOffsetFromText.w +50.0f;
3770     }
3771   }
3772
3773   SetPopupPosition( position, alternativePopupPosition );
3774
3775   // Show popup
3776   mPopupPanel.Show( Self(), animate );
3777   StartMonitoringStageForTouch();
3778
3779   mPopupPanel.PressedSignal().Connect( this, &TextInput::OnPopupButtonPressed );
3780 }
3781
3782 void TextInput::ShowPopupCutCopyPaste()
3783 {
3784   ClearPopup();
3785
3786   mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3787   // Check the selected text is whole text or not.
3788   if( IsTextSelected() && ( mStyledText.size() != GetSelectedText().size() ) )
3789   {
3790     mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3791   }
3792
3793   if ( !mStyledText.empty() )
3794   {
3795     mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCopy, true );
3796     mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, true );
3797   }
3798
3799   if( mClipboard && mClipboard.NumberOfItems() )
3800   {
3801     mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
3802     mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
3803   }
3804
3805   AddPopupOptions();
3806
3807   mPopupPanel.Hide(false);
3808   ShowPopup();
3809 }
3810
3811 void TextInput::SetUpPopupSelection( bool showCutButton )
3812 {
3813   ClearPopup();
3814   mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3815   // If no text exists then don't offer to select
3816   if ( !mStyledText.empty() )
3817   {
3818     mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3819     mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelect, true );
3820     mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, showCutButton );
3821   }
3822   // if clipboard has valid contents then offer paste option
3823   if( mClipboard && mClipboard.NumberOfItems() )
3824   {
3825     mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
3826     mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
3827   }
3828
3829   AddPopupOptions();
3830
3831   mPopupPanel.Hide(false);
3832 }
3833
3834 bool TextInput::ReturnClosestIndex(const Vector2& source, std::size_t& closestIndex )
3835 {
3836   bool found = false;
3837   closestIndex = 0;
3838
3839   std::vector<Toolkit::TextView::CharacterLayoutInfo> matchedCharacters;
3840   bool lastRightToLeftChar(false);          /// RTL state of previous character encountered (character on the left of touch point)
3841   bool rightToLeftChar(false);              /// RTL state of current character encountered (character on the right of touch point)
3842   float glyphIntersection(0.0f);            /// Glyph intersection, the point between the two nearest characters touched.
3843
3844   const Vector2 sourceScrollOffset( source + mTextLayoutInfo.mScrollOffset );
3845
3846   if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
3847   {
3848     float closestYdifference = std::numeric_limits<float>::max();
3849     std::size_t lineOffset = 0;                  /// Keep track of position of the first character on the matched line of interest.
3850     std::size_t numberOfMatchedCharacters = 0;
3851
3852     // 1. Find closest character line to y part of source, create vector of all entries in that Y position
3853     // TODO: There should be an easy call to enumerate through each visual line, instead of each character on all visual lines.
3854
3855     for( std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), endIt = mTextLayoutInfo.mCharacterLayoutInfoTable.end(); it != endIt; ++it )
3856     {
3857       const Toolkit::TextView::CharacterLayoutInfo& info( *it );
3858       float baselinePosition = info.mPosition.y - info.mDescender;
3859
3860       if( info.mIsVisible )
3861       {
3862         // store difference between source y point and the y position of the current character
3863         float currentYdifference = fabsf( sourceScrollOffset.y - ( baselinePosition ) );
3864
3865         if(  currentYdifference < closestYdifference  )
3866         {
3867           // closest so far; store this difference and clear previous matchedCharacters as no longer closest
3868           lineOffset = it - mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3869           closestYdifference = currentYdifference;
3870           matchedCharacters.clear();
3871           numberOfMatchedCharacters = 0; // reset count
3872         }
3873
3874         // add all characters that are on the same Y axis (within the CHARACTER_THRESHOLD) to the matched array.
3875         if( fabsf( closestYdifference - currentYdifference )  < CHARACTER_THRESHOLD )
3876         {
3877           // ignore new line character.
3878           if( !info.mIsNewParagraphChar )
3879           {
3880             matchedCharacters.push_back( info );
3881             numberOfMatchedCharacters++;
3882           }
3883         }
3884       }
3885     } // End of loop checking each character's y position in the character layout table
3886
3887     // Check if last character is a newline, if it is
3888     // then need pretend there is an imaginary line afterwards,
3889     // and check if user is touching below previous line.
3890     const Toolkit::TextView::CharacterLayoutInfo& lastInfo( mTextLayoutInfo.mCharacterLayoutInfoTable[mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1] );
3891
3892     if( ( lastInfo.mIsVisible ) && ( lastInfo.mIsNewParagraphChar ) && ( sourceScrollOffset.y > lastInfo.mPosition.y ) )
3893     {
3894       closestIndex = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
3895     }
3896     else
3897     {
3898       std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator it = matchedCharacters.begin();
3899       std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator endIt = matchedCharacters.end();
3900
3901       bool matched( false );
3902
3903       // 2 Iterate through matching list of y positions and find closest matching X position.
3904       for( ; it != endIt; ++it )
3905       {
3906         const Toolkit::TextView::CharacterLayoutInfo& info( *it );
3907
3908         if( info.mIsVisible )
3909         {
3910           // stop when on left side of character's center.
3911           const float characterMidPointPosition = info.mPosition.x  + ( info.mSize.width * 0.5f ) ;
3912           if( sourceScrollOffset.x < characterMidPointPosition )
3913           {
3914             if(info.mIsRightToLeftCharacter)
3915             {
3916               rightToLeftChar = true;
3917             }
3918             glyphIntersection = info.mPosition.x;
3919             matched = true;
3920             break;
3921           }
3922
3923           lastRightToLeftChar = info.mIsRightToLeftCharacter;
3924         }
3925       }
3926
3927       if( it == endIt )
3928       {
3929         rightToLeftChar = lastRightToLeftChar;
3930       }
3931
3932       std::size_t matchCharacterIndex = it - matchedCharacters.begin();
3933       closestIndex = lineOffset + matchCharacterIndex;
3934
3935       mClosestCursorPositionEOL = false; // reset
3936       if ( it == endIt && !matched )
3937       {
3938         mClosestCursorPositionEOL = true; // Reached end of matched characters in closest line but no match so cursor should be after last character.
3939       }
3940
3941       // For RTL characters, need to adjust closestIndex by 1 (as the inequality above would be reverse)
3942       if( rightToLeftChar && lastRightToLeftChar )
3943       {
3944         --closestIndex; // (-1 = numeric_limits<std::size_t>::max())
3945       }
3946     }
3947   }
3948
3949   // closestIndex is the visual index, need to convert it to the logical index
3950   if( !mTextLayoutInfo.mCharacterVisualToLogicalMap.empty() )
3951   {
3952     if( closestIndex < mTextLayoutInfo.mCharacterVisualToLogicalMap.size() )
3953     {
3954       // Checks for situations where user is touching between LTR and RTL
3955       // characters. To identify if the user means the end of a LTR string
3956       // or the beginning of an RTL string, and vice versa.
3957       if( closestIndex > 0 )
3958       {
3959         if( rightToLeftChar && !lastRightToLeftChar )
3960         {
3961           // [LTR] [RTL]
3962           //   |..|..|
3963           //   AAA BBB
3964           // A: In this touch range, the user is indicating that they wish to place
3965           // the cursor at the end of the LTR text.
3966           // B: In this touch range, the user is indicating that they wish to place
3967           // the cursor at the end of the RTL text.
3968
3969           // Result of touching A area:
3970           // [.....LTR]|[RTL......]+
3971           //
3972           // |: primary cursor (for typing LTR chars)
3973           // +: secondary cursor (for typing RTL chars)
3974
3975           // Result of touching B area:
3976           // [.....LTR]+[RTL......]|
3977           //
3978           // |: primary cursor (for typing RTL chars)
3979           // +: secondary cursor (for typing LTR chars)
3980
3981           if( sourceScrollOffset.x < glyphIntersection )
3982           {
3983             --closestIndex;
3984           }
3985         }
3986         else if( !rightToLeftChar && lastRightToLeftChar )
3987         {
3988           if( sourceScrollOffset.x < glyphIntersection )
3989           {
3990             --closestIndex;
3991           }
3992         }
3993       }
3994
3995       closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap[closestIndex];
3996       // If user touched a left-side of RTL char, and the character on the left was an LTR then position logical cursor
3997       // one further ahead
3998       if( rightToLeftChar && !lastRightToLeftChar )
3999       {
4000         ++closestIndex;
4001       }
4002     }
4003     else if( closestIndex == numeric_limits<std::size_t>::max() ) // -1 RTL (after last arabic character on line)
4004     {
4005       closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap.size();
4006     }
4007     else if( mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterVisualToLogicalMap[ closestIndex - 1 ] ].mIsRightToLeftCharacter ) // size() LTR (after last european character on line)
4008     {
4009       closestIndex = 0;
4010     }
4011   }
4012
4013   return found;
4014 }
4015
4016 float TextInput::GetLineJustificationPosition() const
4017 {
4018   const Vector3& size = mDisplayedTextView.GetCurrentSize();
4019   Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4020   float alignmentOffset = 0.f;
4021
4022   // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4023   if( alignment & Toolkit::Alignment::HorizontalLeft )
4024   {
4025     alignmentOffset = 0.f;
4026   }
4027   else if( alignment & Toolkit::Alignment::HorizontalCenter )
4028   {
4029     alignmentOffset = 0.5f * ( size.width - mTextLayoutInfo.mTextSize.width );
4030   }
4031   else if( alignment & Toolkit::Alignment::HorizontalRight )
4032   {
4033     alignmentOffset = size.width - mTextLayoutInfo.mTextSize.width;
4034   }
4035
4036   Toolkit::TextView::LineJustification justification = mDisplayedTextView.GetLineJustification();
4037   float justificationOffset = 0.f;
4038
4039   switch( justification )
4040   {
4041     case Toolkit::TextView::Left:
4042     {
4043       justificationOffset = 0.f;
4044       break;
4045     }
4046     case Toolkit::TextView::Center:
4047     {
4048       justificationOffset = 0.5f * mTextLayoutInfo.mTextSize.width;
4049       break;
4050     }
4051     case Toolkit::TextView::Right:
4052     {
4053       justificationOffset = mTextLayoutInfo.mTextSize.width;
4054       break;
4055     }
4056     case Toolkit::TextView::Justified:
4057     {
4058       justificationOffset = 0.f;
4059       break;
4060     }
4061     default:
4062     {
4063       DALI_ASSERT_ALWAYS( false );
4064     }
4065   } // end switch
4066
4067   return alignmentOffset + justificationOffset;
4068 }
4069
4070 Vector3 TextInput::PositionCursorAfterWordWrap( std::size_t characterPosition ) const
4071 {
4072   /* Word wrap occurs automatically in TextView when the exceed policy moves a word to the next line when not enough space on current.
4073      A newline character is not inserted in this case */
4074
4075   DALI_ASSERT_DEBUG( !(characterPosition <= 0 ));
4076
4077   Vector3 cursorPosition;
4078
4079   Toolkit::TextView::CharacterLayoutInfo currentCharInfo;
4080
4081   if ( characterPosition == mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4082   {
4083     // end character so use
4084     currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1 ];
4085     cursorPosition = Vector3(currentCharInfo.mPosition.x + currentCharInfo.mSize.width, currentCharInfo.mPosition.y, currentCharInfo.mPosition.z) ;
4086   }
4087   else
4088   {
4089     currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4090   }
4091
4092   Toolkit::TextView::CharacterLayoutInfo previousCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1];
4093
4094   // If previous character on a different line then use current characters position
4095   if ( fabsf( (currentCharInfo.mPosition.y - currentCharInfo.mDescender )  - ( previousCharInfo.mPosition.y - previousCharInfo.mDescender) ) > Math::MACHINE_EPSILON_1000 )
4096   {
4097     if ( mClosestCursorPositionEOL )
4098     {
4099       cursorPosition = Vector3(previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z) ;
4100     }
4101     else
4102     {
4103       cursorPosition = Vector3(currentCharInfo.mPosition);
4104     }
4105   }
4106   else
4107   {
4108     // Previous character is on same line so use position of previous character plus it's width.
4109     cursorPosition = Vector3(previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z) ;
4110   }
4111
4112   return cursorPosition;
4113 }
4114
4115 Vector3 TextInput::GetActualPositionFromCharacterPosition(std::size_t characterPosition) const
4116 {
4117   bool direction(false);
4118   Vector3 alternatePosition;
4119   bool alternatePositionValid(false);
4120
4121   return GetActualPositionFromCharacterPosition( characterPosition, direction, alternatePosition, alternatePositionValid );
4122 }
4123
4124 Vector3 TextInput::GetActualPositionFromCharacterPosition(std::size_t characterPosition, bool& directionRTL, Vector3& alternatePosition, bool& alternatePositionValid ) const
4125 {
4126   Vector3 cursorPosition( 0.f, 0.f, 0.f );
4127
4128   alternatePositionValid = false;
4129   directionRTL = false;
4130
4131   if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
4132   {
4133     std::size_t visualCharacterPosition;
4134
4135     // When cursor is not at beginning, consider possibility of
4136     // showing 2 cursors. (whereas at beginning we only ever show one cursor)
4137     if(characterPosition > 0)
4138     {
4139       // Cursor position should be the end of the last character.
4140       // If the last character is LTR, then the end is on the right side of the glyph.
4141       // If the last character is RTL, then the end is on the left side of the glyph.
4142       visualCharacterPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition - 1 ];
4143
4144       if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + visualCharacterPosition ) ).mIsVisible )
4145       {
4146         visualCharacterPosition = FindVisibleCharacter( Left, visualCharacterPosition );
4147       }
4148
4149       Toolkit::TextView::CharacterLayoutInfo info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4150       if( ( visualCharacterPosition > 0 ) && info.mIsNewParagraphChar && !IsScrollEnabled() )
4151       {
4152         // Prevents the cursor to exceed the boundary if the last visible character is a 'new line character' and the scroll is not enabled.
4153         const Vector3& size = GetControlSize();
4154
4155         if( info.mPosition.y + info.mSize.height - mDisplayedTextView.GetLineHeightOffset() > size.height )
4156         {
4157           --visualCharacterPosition;
4158         }
4159         info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4160       }
4161
4162       if(!info.mIsNewParagraphChar)
4163       {
4164         cursorPosition = PositionCursorAfterWordWrap( characterPosition ); // Get position of cursor/handles taking in account auto word wrap.
4165       }
4166       else
4167       {
4168         // When cursor points to first character on new line, position cursor at the start of this glyph.
4169         if(characterPosition < mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4170         {
4171           std::size_t visualCharacterNextPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition ];
4172           const Toolkit::TextView::CharacterLayoutInfo& infoNext = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterNextPosition ];
4173           const float start( infoNext.mIsRightToLeftCharacter ? infoNext.mSize.width : 0.0f );
4174
4175           cursorPosition.x = infoNext.mPosition.x + start;
4176           cursorPosition.y = infoNext.mPosition.y;
4177         }
4178         else
4179         {
4180           // If cursor points to the end of text, then can only position
4181           // cursor where the new line starts based on the line-justification position.
4182           cursorPosition.x = GetLineJustificationPosition();
4183
4184           if(characterPosition == mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4185           {
4186             // If this is after the last character, then we can assume that the new cursor
4187             // should be exactly one row below the current row.
4188
4189             const Size rowRect(GetRowRectFromCharacterPosition(characterPosition - 1));
4190             cursorPosition.y = info.mPosition.y + rowRect.height;
4191           }
4192           else
4193           {
4194             // If this is not after last character, then we can use this row's height.
4195             // should be exactly one row below the current row.
4196
4197             const Size rowRect(GetRowRectFromCharacterPosition(characterPosition));
4198             cursorPosition.y = info.mPosition.y + rowRect.height;
4199           }
4200         }
4201       }
4202
4203       directionRTL = info.mIsRightToLeftCharacter;
4204
4205       // 1. When the cursor is neither at the beginning or the end,
4206       // we can show multiple cursors under situations when the cursor is
4207       // between RTL and LTR text...
4208       if(characterPosition != mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4209       {
4210         std::size_t visualCharacterAltPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[characterPosition]; // VCC TODO: find why in the previous patch it was a -1 here.
4211
4212         DALI_ASSERT_ALWAYS(visualCharacterAltPosition < mTextLayoutInfo.mCharacterLayoutInfoTable.size());
4213         const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterAltPosition ];
4214
4215         if(!info.mIsRightToLeftCharacter && infoAlt.mIsRightToLeftCharacter)
4216         {
4217           // Stuation occurs when cursor is at the end of English text (LTR) and beginning of Arabic (RTL)
4218           // Text:     [...LTR...]|[...RTL...]
4219           // Cursor pos:          ^
4220           // Alternate cursor pos:            ^
4221           // In which case we need to display an alternate cursor for the RTL text.
4222
4223           alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width;
4224           alternatePosition.y = infoAlt.mPosition.y;
4225           alternatePositionValid = true;
4226         }
4227         else if(info.mIsRightToLeftCharacter && !infoAlt.mIsRightToLeftCharacter)
4228         {
4229           // Situation occurs when cursor is at end of the Arabic text (LTR) and beginning of English (RTL)
4230           // Text:           |[...RTL...] [...LTR....]
4231           // Cursor pos:     ^
4232           // Alternate cursor pos:       ^
4233           // In which case we need to display an alternate cursor for the RTL text.
4234
4235           alternatePosition.x = infoAlt.mPosition.x;
4236           alternatePosition.y = infoAlt.mPosition.y;
4237           alternatePositionValid = true;
4238         }
4239       }
4240       else
4241       {
4242         // 2. When the cursor is at the end of the text,
4243         // and we have multi-directional text,
4244         // we can also consider showing mulitple cursors.
4245         // The rule here is:
4246         // If first and last characters on row are different
4247         // Directions, then two cursors need to be displayed.
4248
4249         // Get first logical glyph on row
4250         std::size_t startCharacterPosition = GetRowStartFromCharacterPosition( characterPosition - 1 );
4251
4252         std::size_t visualCharacterStartPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ startCharacterPosition ];
4253         const Toolkit::TextView::CharacterLayoutInfo& infoStart= mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterStartPosition ];
4254
4255         if(info.mIsRightToLeftCharacter && !infoStart.mIsRightToLeftCharacter)
4256         {
4257           // For text Starting as LTR and ending as RTL. End cursor position is as follows:
4258           // Text:     [...LTR...]|[...RTL...]
4259           // Cursor pos:          ^
4260           // Alternate cursor pos:            ^
4261           // In which case we need to display an alternate cursor for the RTL text, this cursor
4262           // should be at the end of the given line.
4263
4264           const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1 ];
4265           alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width;
4266           alternatePosition.y = infoAlt.mPosition.y;
4267           alternatePositionValid = true;
4268         }
4269         else if(!info.mIsRightToLeftCharacter && infoStart.mIsRightToLeftCharacter) // starting RTL
4270         {
4271           // For text Starting as RTL and ending as LTR. End cursor position is as follows:
4272           // Text:           |[...RTL...] [...LTR....]
4273           // Cursor pos:     ^
4274           // Alternate cursor pos:       ^
4275           // In which case we need to display an alternate cursor for the RTL text.
4276
4277           const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ startCharacterPosition ];
4278           alternatePosition.x = infoAlt.mPosition.x;
4279           alternatePosition.y = infoAlt.mPosition.y;
4280           alternatePositionValid = true;
4281         }
4282       }
4283     } // characterPosition > 0
4284     else if(characterPosition == 0)
4285     {
4286       // When the cursor position is at the beginning, it should be at the start of the current character.
4287       // If the current character is LTR, then the start is on the right side of the glyph.
4288       // If the current character is RTL, then the start is on the left side of the glyph.
4289       visualCharacterPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition ];
4290
4291       if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + visualCharacterPosition ) ).mIsVisible )
4292       {
4293          visualCharacterPosition = FindVisibleCharacter( Right, visualCharacterPosition );
4294       }
4295
4296       const Toolkit::TextView::CharacterLayoutInfo& info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4297       const float start(info.mIsRightToLeftCharacter ? info.mSize.width : 0.0f);
4298
4299       cursorPosition.x = info.mPosition.x + start;
4300       cursorPosition.y = info.mPosition.y;
4301       directionRTL = info.mIsRightToLeftCharacter;
4302     }
4303   }
4304   else
4305   {
4306     // If the character table is void, place the cursor accordingly the text alignment.
4307     const Vector3& size = GetControlSize();
4308
4309     Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4310     float alignmentOffset = 0.f;
4311
4312     // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4313     if( alignment & Toolkit::Alignment::HorizontalLeft )
4314     {
4315       alignmentOffset = 0.f;
4316     }
4317     else if( alignment & Toolkit::Alignment::HorizontalCenter )
4318     {
4319       alignmentOffset = 0.5f * ( size.width );
4320     }
4321     else if( alignment & Toolkit::Alignment::HorizontalRight )
4322     {
4323       alignmentOffset = size.width;
4324     }
4325
4326     // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4327     cursorPosition.x = alignmentOffset;
4328
4329     // Work out cursor 'y' position when there are any character accordingly with the text view alignment settings.
4330     if( alignment & Toolkit::Alignment::VerticalTop )
4331     {
4332       cursorPosition.y = mLineHeight;
4333     }
4334     else if( alignment & Toolkit::Alignment::VerticalCenter )
4335     {
4336       cursorPosition.y = 0.5f * ( size.height + mLineHeight );
4337     }
4338     else if( alignment & Toolkit::Alignment::VerticalBottom )
4339     {
4340       cursorPosition.y = size.height;
4341     }
4342   }
4343
4344   cursorPosition.x -= mTextLayoutInfo.mScrollOffset.x;
4345   cursorPosition.y -= mTextLayoutInfo.mScrollOffset.y;
4346   if( alternatePositionValid )
4347   {
4348     alternatePosition.x -= mTextLayoutInfo.mScrollOffset.x;
4349     alternatePosition.y -= mTextLayoutInfo.mScrollOffset.y;
4350   }
4351
4352   return cursorPosition;
4353 }
4354
4355 std::size_t TextInput::GetRowStartFromCharacterPosition(std::size_t logicalPosition) const
4356 {
4357   // scan string from current position to beginning of current line to note direction of line
4358   while(logicalPosition)
4359   {
4360     logicalPosition--;
4361     std::size_t visualPosition = GetVisualPosition(logicalPosition);
4362     if(mTextLayoutInfo.mCharacterLayoutInfoTable[visualPosition].mIsNewParagraphChar)
4363     {
4364       logicalPosition++;
4365       break;
4366     }
4367   }
4368
4369   return logicalPosition;
4370 }
4371
4372 Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition) const
4373 {
4374   Vector2 min, max;
4375
4376   return GetRowRectFromCharacterPosition( characterPosition, min, max );
4377 }
4378
4379 Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition, Vector2& min, Vector2& max) const
4380 {
4381   // if we have no text content, then return position 0,0 with width 0, and height the same as cursor height.
4382   if( mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4383   {
4384     min = Vector2::ZERO;
4385     max = Vector2(0.0f, mLineHeight);
4386     return max;
4387   }
4388
4389   // TODO: This info should be readily available from text-view, we should not have to search hard for it.
4390   Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator begin = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
4391   Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
4392
4393   // If cursor is pointing to end of line, then start from last character.
4394   characterPosition = std::min( characterPosition, static_cast<std::size_t>(mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1) );
4395
4396   Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4397
4398   // 0. Find first a visible character. Draw a cursor beyound text-input bounds is not wanted.
4399   if( !it->mIsVisible )
4400   {
4401     characterPosition = FindVisibleCharacter( Left, characterPosition );
4402     it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4403   }
4404
4405   // Scan characters left and right of cursor, stopping when end of line/string reached or
4406   // y position greater than threshold of reference line.
4407
4408   // 1. scan left until we reach the beginning or a different line.
4409   Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator validCharIt = it;
4410   float referenceLine = it->mPosition.y - CHARACTER_THRESHOLD;
4411   // min-x position is the left-most char's left (x)
4412   // max-x position is the right-most char's right (x)
4413   // min-y position is the minimum of all character's top (y)
4414   // max-y position is the maximum of all character's bottom (y+height)
4415   min.y = validCharIt->mPosition.y;
4416   max.y = validCharIt->mPosition.y + validCharIt->mSize.y;
4417
4418   while(true)
4419   {
4420     validCharIt = it;
4421     min.y = std::min(min.y, validCharIt->mPosition.y);
4422     max.y = std::max(max.y, validCharIt->mPosition.y + validCharIt->mSize.y);
4423
4424     if(it == begin)
4425     {
4426       break;
4427     }
4428
4429     --it;
4430
4431     if( (it->mPosition.y < referenceLine) ||
4432         (it->mIsNewParagraphChar) ||
4433         (!it->mIsVisible) )
4434     {
4435       break;
4436     }
4437   }
4438
4439   // info refers to the first character on this line.
4440   min.x = validCharIt->mPosition.x;
4441
4442   // 2. scan right until we reach end or a different line
4443   it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4444   referenceLine = it->mPosition.y + CHARACTER_THRESHOLD;
4445
4446   while(it != end)
4447   {
4448     if( (it->mPosition.y > referenceLine) ||
4449         (it->mIsNewParagraphChar) ||
4450         (!it->mIsVisible) )
4451     {
4452       break;
4453     }
4454
4455     validCharIt = it;
4456     min.y = std::min(min.y, validCharIt->mPosition.y);
4457     max.y = std::max(max.y, validCharIt->mPosition.y + validCharIt->mSize.y);
4458
4459     ++it;
4460   }
4461
4462   DALI_ASSERT_DEBUG ( validCharIt != end  && "validCharIt invalid")
4463
4464   if ( validCharIt != end )
4465   {
4466     // info refers to the last character on this line.
4467     max.x = validCharIt->mPosition.x + validCharIt->mSize.x;
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( const FindVisibleCharacterDirection direction , const std::size_t cursorPosition ) const
4835 {
4836   std::size_t position = 0;
4837
4838   const std::size_t tableSize = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
4839
4840   switch( direction )
4841   {
4842     case Left:
4843     {
4844       position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4845
4846       if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1 : position ) ) ).mIsVisible )
4847       {
4848         position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4849       }
4850       break;
4851     }
4852     case Right:
4853     {
4854       position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4855       if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1 : position ) ) ).mIsVisible )
4856       {
4857         position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4858       }
4859       break;
4860     }
4861     case ByEnd:
4862     {
4863       position = FindVisibleCharacterLeft( 0, mTextLayoutInfo.mCharacterLayoutInfoTable );
4864       break;
4865     }
4866     default:
4867     {
4868       DALI_ASSERT_ALWAYS( !"TextInput::FindVisibleCharacter() Unknown direction." );
4869     }
4870   }
4871
4872   return position;
4873 }
4874
4875 void TextInput::SetSortModifier( float depthOffset )
4876 {
4877   if(mDisplayedTextView)
4878   {
4879     mDisplayedTextView.SetSortModifier(depthOffset);
4880   }
4881 }
4882
4883 void TextInput::SetSnapshotModeEnabled( bool enable )
4884 {
4885   if(mDisplayedTextView)
4886   {
4887     mDisplayedTextView.SetSnapshotModeEnabled( enable );
4888   }
4889 }
4890
4891 bool TextInput::IsSnapshotModeEnabled() const
4892 {
4893   bool snapshotEnabled = false;
4894
4895   if(mDisplayedTextView)
4896   {
4897     snapshotEnabled = mDisplayedTextView.IsSnapshotModeEnabled();
4898   }
4899
4900   return snapshotEnabled;
4901 }
4902
4903 void TextInput::SetMarkupProcessingEnabled( bool enable )
4904 {
4905   mMarkUpEnabled = enable;
4906 }
4907
4908 bool TextInput::IsMarkupProcessingEnabled() const
4909 {
4910   return mMarkUpEnabled;
4911 }
4912
4913 void TextInput::SetScrollEnabled( bool enable )
4914 {
4915   if( mDisplayedTextView )
4916   {
4917     mDisplayedTextView.SetScrollEnabled( enable );
4918   }
4919
4920   if( !enable )
4921   {
4922     // Don't set cursor's and handle's visibility to false if they are outside the
4923     // boundaries of the text-input.
4924     mIsCursorInScrollArea = true;
4925     mIsGrabHandleInScrollArea = true;
4926     if( mSelectionHandleOne && mSelectionHandleTwo )
4927     {
4928       mSelectionHandleOne.SetVisible( true );
4929       mSelectionHandleTwo.SetVisible( true );
4930
4931       if( mHighlightMeshActor )
4932       {
4933         mHighlightMeshActor.SetVisible( true );
4934       }
4935     }
4936   }
4937 }
4938
4939 bool TextInput::IsScrollEnabled() const
4940 {
4941   bool scrollEnabled = false;
4942
4943   if( mDisplayedTextView )
4944   {
4945     scrollEnabled = mDisplayedTextView.IsScrollEnabled();
4946   }
4947
4948   return scrollEnabled;
4949 }
4950
4951 void TextInput::SetScrollPosition( const Vector2& position )
4952 {
4953   if( mDisplayedTextView )
4954   {
4955     mDisplayedTextView.SetScrollPosition( position );
4956   }
4957 }
4958
4959 Vector2 TextInput::GetScrollPosition() const
4960 {
4961   Vector2 scrollPosition;
4962
4963   if( mDisplayedTextView )
4964   {
4965     scrollPosition = mDisplayedTextView.GetScrollPosition();
4966   }
4967
4968   return scrollPosition;
4969 }
4970
4971 std::size_t TextInput::DoInsertAt( const Text& text, const std::size_t position, const std::size_t numberOfCharactersToReplace, bool& textExceedsMaximunNumberOfCharacters, bool& textExceedsBoundary )
4972 {
4973   // determine number of characters that we can write to style text buffer, this is the insertStringLength
4974   std::size_t insertedStringLength = std::min( text.GetLength(), mMaxStringLength - mStyledText.size() );
4975   textExceedsMaximunNumberOfCharacters = insertedStringLength < text.GetLength();
4976
4977   // Add style to the new input text.
4978   MarkupProcessor::StyledTextArray textToInsert;
4979   for( std::size_t i = 0; i < insertedStringLength; ++i )
4980   {
4981     const MarkupProcessor::StyledText newStyledCharacter( text[i], mInputStyle );
4982     textToInsert.push_back( newStyledCharacter );
4983   }
4984
4985   //Insert text to the TextView.
4986   const bool emptyTextView = mStyledText.empty();
4987   if( emptyTextView && mPlaceHolderSet )
4988   {
4989     // There is no text set so call to TextView::SetText() is needed in order to clear the placeholder text.
4990     mDisplayedTextView.SetText( textToInsert );
4991   }
4992   else
4993   {
4994     if( 0 == numberOfCharactersToReplace )
4995     {
4996       mDisplayedTextView.InsertTextAt( position, textToInsert );
4997     }
4998     else
4999     {
5000       mDisplayedTextView.ReplaceTextFromTo( position, numberOfCharactersToReplace, textToInsert );
5001     }
5002   }
5003   mPlaceHolderSet = false;
5004
5005   if( textToInsert.empty() )
5006   {
5007     // If no text has been inserted, GetTextLayoutInfo() need to be called to check whether mStyledText has some text.
5008     GetTextLayoutInfo();
5009   }
5010   else
5011   {
5012     // GetTextLayoutInfo() can't be used here as mStyledText is not updated yet.
5013     mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5014   }
5015
5016   textExceedsBoundary = false;
5017
5018   if( !mExceedEnabled )
5019   {
5020     const Vector3& size = GetControlSize();
5021
5022     if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5023     {
5024       // If new text does not fit within TextView
5025       mDisplayedTextView.RemoveTextFrom( position, insertedStringLength );
5026       // previously inserted text has been removed. Call GetTextLayoutInfo() to check whether mStyledText has some text.
5027       GetTextLayoutInfo();
5028       textExceedsBoundary = true;
5029       insertedStringLength = 0;
5030     }
5031
5032     if( textExceedsBoundary )
5033     {
5034       // Add the part of the text which fits on the text-input.
5035
5036       // Split the text which doesn't fit in two halves.
5037       MarkupProcessor::StyledTextArray firstHalf;
5038       MarkupProcessor::StyledTextArray secondHalf;
5039       SplitText( textToInsert, firstHalf, secondHalf );
5040
5041       // Clear text. This text will be filled with the text inserted.
5042       textToInsert.clear();
5043
5044       // Where to insert the text.
5045       std::size_t positionToInsert = position;
5046
5047       bool end = text.GetLength() <= 1;
5048       while( !end )
5049       {
5050         // Insert text and check ...
5051         const std::size_t textLength = firstHalf.size();
5052         mDisplayedTextView.InsertTextAt( positionToInsert, firstHalf );
5053         mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5054
5055         if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5056         {
5057           // Inserted text doesn't fit.
5058
5059           // Remove inserted text
5060           mDisplayedTextView.RemoveTextFrom( positionToInsert, textLength );
5061           mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5062
5063           // The iteration finishes when only one character doesn't fit.
5064           end = textLength <= 1;
5065
5066           if( !end )
5067           {
5068             // Prepare next two halves for next iteration.
5069             MarkupProcessor::StyledTextArray copyText = firstHalf;
5070             SplitText( copyText, firstHalf, secondHalf );
5071           }
5072         }
5073         else
5074         {
5075           // Text fits.
5076
5077           // store text to be inserted in mStyledText.
5078           textToInsert.insert( textToInsert.end(), firstHalf.begin(), firstHalf.end() );
5079
5080           // Increase the inserted characters counter.
5081           insertedStringLength += textLength;
5082
5083           // Prepare next two halves for next iteration.
5084           MarkupProcessor::StyledTextArray copyText = secondHalf;
5085           SplitText( copyText, firstHalf, secondHalf );
5086
5087           // Update where next text has to be inserted
5088           positionToInsert += textLength;
5089         }
5090       }
5091     }
5092   }
5093
5094   if( textToInsert.empty() && emptyTextView )
5095   {
5096     // No character has been added and the text-view was empty.
5097     // Set the placeholder text.
5098     mDisplayedTextView.SetText( mStyledPlaceHolderText );
5099     mPlaceHolderSet = true;
5100   }
5101   else
5102   {
5103     MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + position;
5104     mStyledText.insert( it, textToInsert.begin(), textToInsert.end() );
5105     mPlaceHolderSet = false;
5106   }
5107
5108   return insertedStringLength;
5109 }
5110
5111 void TextInput::GetTextLayoutInfo()
5112 {
5113   if( mStyledText.empty() )
5114   {
5115     // The text-input has no text, clear the text-view's layout info.
5116     mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5117   }
5118   else
5119   {
5120     if( mDisplayedTextView )
5121     {
5122       mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5123     }
5124     else
5125     {
5126       // There is no text-view.
5127       mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5128     }
5129   }
5130 }
5131
5132 void TextInput::SetOffsetFromText( const Vector4& offset )
5133 {
5134   mPopupOffsetFromText = offset;
5135 }
5136
5137 const Vector4& TextInput::GetOffsetFromText() const
5138 {
5139   return mPopupOffsetFromText;
5140 }
5141
5142 void TextInput::SetProperty( BaseObject* object, Property::Index propertyIndex, const Property::Value& value )
5143 {
5144   Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5145
5146   if ( textInput )
5147   {
5148     TextInput& textInputImpl( GetImpl( textInput ) );
5149
5150     switch ( propertyIndex )
5151     {
5152       case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5153       {
5154         textInputImpl.SetMaterialDiffuseColor( value.Get< Vector4 >() );
5155         break;
5156       }
5157       case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5158       {
5159         textInputImpl.mPopupPanel.SetCutPastePopupColor( value.Get< Vector4 >() );
5160         break;
5161       }
5162       case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5163       {
5164         textInputImpl.mPopupPanel.SetCutPastePopupPressedColor( value.Get< Vector4 >() );
5165         break;
5166       }
5167       case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY:
5168       {
5169         textInputImpl.mPopupPanel.SetCutPastePopupBorderColor( value.Get< Vector4 >() );
5170         break;
5171       }
5172       case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5173       {
5174         textInputImpl.mPopupPanel.SetCutPastePopupIconColor( value.Get< Vector4 >() );
5175         break;
5176       }
5177       case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5178       {
5179         textInputImpl.mPopupPanel.SetCutPastePopupIconPressedColor( value.Get< Vector4 >() );
5180         break;
5181       }
5182       case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5183       {
5184         textInputImpl.mPopupPanel.SetCutPastePopupTextColor( value.Get< Vector4 >() );
5185         break;
5186       }
5187       case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5188       {
5189         textInputImpl.mPopupPanel.SetCutPastePopupTextPressedColor( value.Get< Vector4 >() );
5190         break;
5191       }
5192       case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5193       {
5194         textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCut, value.Get<unsigned int>() );
5195         break;
5196       }
5197       case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5198       {
5199         textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCopy, value.Get<unsigned int>() );
5200         break;
5201       }
5202       case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5203       {
5204         textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsPaste, value.Get<unsigned int>() );
5205         break;
5206       }
5207       case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5208       {
5209         textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelect, value.Get<unsigned int>() );
5210         break;
5211       }
5212       case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5213       {
5214         textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll, value.Get<unsigned int>() );
5215         break;
5216       }
5217       case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5218       {
5219         textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsClipboard, value.Get<unsigned int>() );
5220         break;
5221       }
5222       case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5223       {
5224         textInputImpl.SetOffsetFromText( value.Get< Vector4 >() );
5225         break;
5226       }
5227       case Toolkit::TextInput::CURSOR_COLOR_PROPERTY:
5228       {
5229         textInputImpl.mCursor.SetColor( value.Get< Vector4 >() );
5230       }
5231     }
5232   }
5233 }
5234
5235 Property::Value TextInput::GetProperty( BaseObject* object, Property::Index propertyIndex )
5236 {
5237   Property::Value value;
5238
5239   Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5240
5241   if ( textInput )
5242   {
5243     TextInput& textInputImpl( GetImpl( textInput ) );
5244
5245     switch ( propertyIndex )
5246     {
5247       case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5248       {
5249         value = textInputImpl.GetMaterialDiffuseColor();
5250         break;
5251       }
5252       case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5253       {
5254         value = textInputImpl.mPopupPanel.GetCutPastePopupColor();
5255         break;
5256       }
5257       case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5258       {
5259         value = textInputImpl.mPopupPanel.GetCutPastePopupPressedColor();
5260         break;
5261       }
5262       case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY :
5263       {
5264         value = textInputImpl.mPopupPanel.GetCutPastePopupBorderColor();
5265         break;
5266       }
5267       case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5268       {
5269         value = textInputImpl.mPopupPanel.GetCutPastePopupIconColor();
5270         break;
5271       }
5272       case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5273       {
5274         value = textInputImpl.mPopupPanel.GetCutPastePopupIconPressedColor();
5275         break;
5276       }
5277       case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5278       {
5279         value = textInputImpl.mPopupPanel.GetCutPastePopupTextColor();
5280         break;
5281       }
5282       case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5283       {
5284         value = textInputImpl.mPopupPanel.GetCutPastePopupTextPressedColor();
5285         break;
5286       }
5287       case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5288       {
5289         value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCut );
5290         break;
5291       }
5292       case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5293       {
5294         value =  textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCopy );
5295         break;
5296       }
5297       case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5298       {
5299         value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsPaste );
5300         break;
5301       }
5302       case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5303       {
5304         value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelect );
5305         break;
5306       }
5307       case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5308       {
5309         value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll );
5310         break;
5311       }
5312       case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5313       {
5314         value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsClipboard );
5315         break;
5316       }
5317       case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5318       {
5319         value = textInputImpl.GetOffsetFromText();
5320         break;
5321       }
5322       case Toolkit::TextInput::CURSOR_COLOR_PROPERTY:
5323       {
5324         value = textInputImpl.mCursor.GetCurrentColor();
5325       }
5326     }
5327   }
5328   return value;
5329 }
5330
5331 void TextInput::EmitStyleChangedSignal()
5332 {
5333   // emit signal if input style changes.
5334   Toolkit::TextInput handle( GetOwner() );
5335   mStyleChangedSignalV2.Emit( handle, mInputStyle );
5336 }
5337
5338 void TextInput::EmitTextModified()
5339 {
5340   // emit signal when text changes.
5341   Toolkit::TextInput handle( GetOwner() );
5342   mTextModifiedSignal.Emit( handle );
5343 }
5344
5345
5346 void TextInput::EmitMaxInputCharactersReachedSignal()
5347 {
5348   // emit signal if max characters is reached during text input.
5349   DALI_LOG_INFO(gLogFilter, Debug::General, "EmitMaxInputCharactersReachedSignal \n");
5350
5351   Toolkit::TextInput handle( GetOwner() );
5352   mMaxInputCharactersReachedSignalV2.Emit( handle );
5353 }
5354
5355 void TextInput::EmitInputTextExceedsBoundariesSignal()
5356 {
5357   // Emit a signal when the input text exceeds the boundaries of the text input.
5358
5359   Toolkit::TextInput handle( GetOwner() );
5360   mInputTextExceedBoundariesSignalV2.Emit( handle );
5361 }
5362
5363 } // namespace Internal
5364
5365 } // namespace Toolkit
5366
5367 } // namespace Dali