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