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