(TextInput) Moving selection handles when word selected does not de-select current...
[platform/core/uifw/dali-toolkit.git] / base / dali-toolkit / internal / controls / text-input / text-input-impl.cpp
1 /*
2  * Copyright (c) 2014 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 #include <dali/dali.h>
19
20 #include <dali-toolkit/internal/controls/text-input/text-input-impl.h>
21 #include <dali-toolkit/internal/controls/text-view/text-processor.h>
22 #include <dali-toolkit/public-api/controls/buttons/push-button.h>
23 #include <dali-toolkit/public-api/controls/alignment/alignment.h>
24 #include <dali-toolkit/public-api/controls/default-controls/solid-color-actor.h>
25
26 #include <dali/integration-api/debug.h>
27
28 #include <math.h>
29 #include <sstream>
30 #include <algorithm>
31
32 using namespace std;
33 using namespace Dali;
34
35 // Local Data
36 namespace
37 {
38
39 #if defined(DEBUG_ENABLED)
40 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_TEXT_INPUT");
41 #endif
42
43 const std::size_t DEFAULT_MAX_SIZE( std::numeric_limits<std::size_t>::max() ); // Max possible number
44 const std::size_t DEFAULT_NUMBER_OF_LINES_LIMIT( std::numeric_limits<std::size_t>::max() ); // Max possible number
45 const Vector3 DEFAULT_SELECTION_HANDLE_SIZE( 51.0f, 79.0f, 0.0f );  // Selection cursor image size
46 const Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.5f, 2.0f, 1.0f );
47 const Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.5f, 1.5f, 1.0f );
48 const Vector4 LIGHTBLUE( 0.07f, 0.41f, 0.59f, 1.0f );    // Used for Selection highlight
49
50 const char* DEFAULT_GRAB_HANDLE( DALI_IMAGE_DIR "insertpoint-icon.png" );
51 const char* DEFAULT_SELECTION_HANDLE_ONE( DALI_IMAGE_DIR "text-input-selection-handle-left.png" );
52 const char* DEFAULT_SELECTION_HANDLE_TWO( DALI_IMAGE_DIR "text-input-selection-handle-right.png" );
53 const char* DEFAULT_SELECTION_HANDLE_ONE_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-left-press.png" );
54 const char* DEFAULT_SELECTION_HANDLE_TWO_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-right-press.png" );
55
56 const std::size_t CURSOR_BLINK_INTERVAL = 500;                              ///< Cursor blink interval
57 const float CHARACTER_THRESHOLD( 2.5f );                                    ///< the threshold of a line.
58 const float DISPLAYED_HIGHLIGHT_Z_OFFSET( 0.1f );                           ///< 1. Highlight rendered (z-offset).
59 const float DISPLAYED_TEXT_VIEW_Z_OFFSET( 0.2f );                           ///< 2. Text rendered (z-offset).
60 const float UI_Z_OFFSET( 0.2f );                                            ///< 3. Text Selection Handles/Cursor z-offset.
61
62 const Vector3 UI_OFFSET(0.0f, 0.0f, UI_Z_OFFSET);                           ///< Text Selection Handles/Cursor offset.
63 const Vector3 DEFAULT_HANDLE_ONE_OFFSET(0.0f, -5.0f, 0.0f);                 ///< Handle One's Offset
64 const Vector3 DEFAULT_HANDLE_TWO_OFFSET(0.0f, -5.0f, 0.0f);                 ///< Handle Two's Offset
65 const float TOP_HANDLE_TOP_OFFSET( 34.0f);                                   ///< Offset between top handle and cutCopyPaste pop-up
66 const float BOTTOM_HANDLE_BOTTOM_OFFSET(34.0f);                              ///< Offset between bottom handle and cutCopyPaste pop-up
67 const float CURSOR_THICKNESS(4.0f);
68 const Degree CURSOR_ANGLE_OFFSET(2.0f);                                     ///< Offset from the angle of italic angle.
69 const Vector4 DEFAULT_CURSOR_COLOR(1.0f, 1.0f, 1.0f, 1.0f);
70
71 const std::string NEWLINE( "\n" );
72
73 const TextStyle DEFAULT_TEXT_STYLE;
74
75 const unsigned int SCROLL_TICK_INTERVAL = 50u;
76 const float SCROLL_THRESHOLD = 10.f;
77 const float SCROLL_SPEED = 15.f;
78
79 /**
80  * Selection state enumeration (FSM)
81  */
82 enum SelectionState
83 {
84   SelectionNone,                            ///< Currently not encountered selected section.
85   SelectionStarted,                         ///< Encountered selected section
86   SelectionFinished                         ///< Finished selected section
87 };
88
89 /**
90  * Whether the given style is the default style or not.
91  * @param[in] style The given style.
92  * @return \e true if the given style is the default. Otherwise it returns \e false.
93  */
94 bool IsDefaultStyle( const TextStyle& style )
95 {
96   return DEFAULT_TEXT_STYLE == style;
97 }
98
99 /**
100  * Whether the given styled text is using the default style or not.
101  * @param[in] textArray The given text.
102  * @return \e true if the given styled text is using the default style. Otherwise it returns \e false.
103  */
104 bool IsTextDefaultStyle( const Toolkit::MarkupProcessor::StyledTextArray& textArray )
105 {
106   for( Toolkit::MarkupProcessor::StyledTextArray::const_iterator it = textArray.begin(), endIt = textArray.end(); it != endIt; ++it )
107   {
108     const TextStyle& style( (*it).mStyle );
109
110     if( !IsDefaultStyle( style ) )
111     {
112       return false;
113     }
114   }
115
116   return true;
117 }
118
119 std::size_t FindVisibleCharacterLeft( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable )
120 {
121   for( Toolkit::TextView::CharacterLayoutInfoContainer::const_reverse_iterator it = characterLayoutInfoTable.rbegin() + characterLayoutInfoTable.size() - cursorPosition, endIt = characterLayoutInfoTable.rend();
122        it != endIt;
123        ++it )
124   {
125     if( ( *it ).mIsVisible )
126     {
127       return --cursorPosition;
128     }
129
130     --cursorPosition;
131   }
132
133   return 0;
134 }
135
136 std::size_t FindVisibleCharacterRight( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable  )
137 {
138   for( Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = characterLayoutInfoTable.begin() + cursorPosition, endIt = characterLayoutInfoTable.end(); it < endIt; ++it )
139   {
140     if( ( *it ).mIsVisible )
141     {
142       return cursorPosition;
143     }
144
145     ++cursorPosition;
146   }
147
148   return cursorPosition;
149 }
150
151 /**
152  * Whether the given position plus the cursor size offset is inside the given boundary.
153  *
154  * @param[in] position The given position.
155  * @param[in] cursorSize The cursor size.
156  * @param[in] controlSize The given boundary.
157  *
158  * @return whether the given position is inside the given boundary.
159  */
160 bool IsPositionInsideBoundaries( const Vector3& position, const Size& cursorSize, const Vector3& controlSize )
161 {
162   return ( position.x >= -Math::MACHINE_EPSILON_1000 ) &&
163          ( position.x <= controlSize.width + Math::MACHINE_EPSILON_1000 ) &&
164          ( position.y - cursorSize.height >= -Math::MACHINE_EPSILON_1000 ) &&
165          ( position.y <= controlSize.height + Math::MACHINE_EPSILON_1000 );
166 }
167
168 /**
169  * Splits a text in two halves.
170  *
171  * If the text's number of characters is odd, firstHalf has one more character.
172  *
173  * @param[in] text The text to be split.
174  * @param[out] firstHalf The first half of the text.
175  * @param[out] secondHalf The second half of the text.
176  */
177 void SplitText( const Toolkit::MarkupProcessor::StyledTextArray& text,
178                       Toolkit::MarkupProcessor::StyledTextArray& firstHalf,
179                       Toolkit::MarkupProcessor::StyledTextArray& secondHalf )
180 {
181   firstHalf.clear();
182   secondHalf.clear();
183
184   const std::size_t textLength = text.size();
185   const std::size_t half = ( textLength / 2 ) + ( textLength % 2 );
186
187   firstHalf.insert( firstHalf.end(), text.begin(), text.begin() + half );
188   secondHalf.insert( secondHalf.end(), text.begin() + half, text.end() );
189 }
190
191 } // end of namespace
192
193 namespace Dali
194 {
195
196 namespace Toolkit
197 {
198 // Properties
199 const Property::Index TextInput::HIGHLIGHT_COLOR_PROPERTY                     = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX;
200 const Property::Index TextInput::CUT_AND_PASTE_COLOR_PROPERTY                 = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+1;
201 const Property::Index TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY         = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+2;
202 const Property::Index TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY          = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+3;
203 const Property::Index TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY            = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+4;
204 const Property::Index TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY    = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+5;
205 const Property::Index TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY            = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+6;
206 const Property::Index TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY    = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+7;
207 const Property::Index TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY        = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+8;
208 const Property::Index TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY       = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+9;
209 const Property::Index TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY      = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+10;
210 const Property::Index TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY     = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+11;
211 const Property::Index TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+12;
212 const Property::Index TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY  = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+13;
213 const Property::Index TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY             = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+14;
214 const Property::Index TextInput::CURSOR_COLOR_PROPERTY                        = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+15;
215
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 PropertyRegistration property16( typeRegistration, "cursor-color", Toolkit::TextInput::CURSOR_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
255
256
257 // [TextInput::HighlightInfo] /////////////////////////////////////////////////
258
259 void TextInput::HighlightInfo::AddQuad( float x1, float y1, float x2, float y2 )
260 {
261   QuadCoordinates quad(x1, y1, x2, y2);
262   mQuadList.push_back( quad );
263 }
264
265 void TextInput::HighlightInfo::Clamp2D(const Vector2& min, const Vector2& max)
266 {
267   for(std::size_t i = 0;i < mQuadList.size(); i++)
268   {
269     QuadCoordinates& quad = mQuadList[i];
270
271     quad.min.Clamp(min, max);
272     quad.max.Clamp(min, max);
273   } // end for
274 }
275
276 // [TextInput] ////////////////////////////////////////////////////////////////
277
278 Dali::Toolkit::TextInput TextInput::New()
279 {
280   // Create the implementation
281   TextInputPtr textInput(new TextInput());
282   // Pass ownership to CustomActor via derived handle
283   Dali::Toolkit::TextInput handle(*textInput);
284   handle.SetName( "TextInput");
285
286   textInput->Initialize();
287   return handle;
288 }
289
290 TextInput::TextInput()
291 :Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS | REQUIRES_STYLE_CHANGE_SIGNALS ) ),
292  mState( StateEdit ),
293  mStyledText(),
294  mInputStyle(),
295  mLineHeight( 0.f ),
296  mDisplayedTextView(),
297  mStyledPlaceHolderText(),
298  mMaxStringLength( DEFAULT_MAX_SIZE ),
299  mNumberOflinesLimit( DEFAULT_NUMBER_OF_LINES_LIMIT ),
300  mCursorPosition( 0 ),
301  mActualGrabHandlePosition( 0.0f, 0.0f, 0.0f ),
302  mIsSelectionHandleOneFlipped( false ),
303  mIsSelectionHandleTwoFlipped( false ),
304  mSelectionHandleOneOffset( DEFAULT_HANDLE_ONE_OFFSET ),
305  mSelectionHandleTwoOffset( DEFAULT_HANDLE_TWO_OFFSET ),
306  mSelectionHandleOneActualPosition( 0.0f, 0.0f , 0.0f ),
307  mSelectionHandleTwoActualPosition( 0.0f, 0.0f , 0.0f ),
308  mSelectionHandleOnePosition( 0 ),
309  mSelectionHandleTwoPosition( 0 ),
310  mPreEditString(),
311  mPreEditStartPosition( 0 ),
312  mPreEditLength ( 0 ),
313  mNumberOfSurroundingCharactersDeleted( 0 ),
314  mTouchStartTime( 0 ),
315  mTextLayoutInfo(),
316  mCurrentCopySelecton(),
317  mPopupPanel(),
318  mScrollTimer(),
319  mScrollDisplacement(),
320  mCurrentHandlePosition(),
321  mCurrentSelectionId(),
322  mCurrentSelectionHandlePosition(),
323  mRequestedSelection( 0, 0 ),
324  mSelectionHandleFlipMargin( 0.0f, 0.0f, 0.0f, 0.0f ),
325  mBoundingRectangleWorldCoordinates( 0.0f, 0.0f, 0.0f, 0.0f ),
326  mClipboard(),
327  mMaterialColor( LIGHTBLUE ),
328  mPopupOffsetFromText ( Vector4( 0.0f, TOP_HANDLE_TOP_OFFSET, 0.0f, BOTTOM_HANDLE_BOTTOM_OFFSET ) ),
329  mOverrideAutomaticAlignment( false ),
330  mCursorRTLEnabled( false ),
331  mClosestCursorPositionEOL ( false ),
332  mCursorBlinkStatus( true ),
333  mCursorVisibility( false ),
334  mGrabHandleVisibility( false ),
335  mIsCursorInScrollArea( true ),
336  mIsGrabHandleInScrollArea( true ),
337  mEditModeActive( false ),
338  mEditOnTouch( true ),
339  mTextSelection( true ),
340  mExceedEnabled( true ),
341  mGrabHandleEnabled( true ),
342  mIsSelectionHandleFlipEnabled( true ),
343  mPreEditFlag( false ),
344  mIgnoreCommitFlag( false ),
345  mIgnoreFirstCommitFlag( false ),
346  mSelectingText( false ),
347  mPreserveCursorPosition( false ),
348  mSelectTextOnCommit( false ),
349  mUnderlinedPriorToPreEdit ( false ),
350  mCommitByKeyInput( false ),
351  mPlaceHolderSet( false ),
352  mMarkUpEnabled( false )
353 {
354   // Updates the line height accordingly with the input style.
355   UpdateLineHeight();
356 }
357
358 TextInput::~TextInput()
359 {
360   StopCursorBlinkTimer();
361 }
362
363 // Public
364
365 std::string TextInput::GetText() const
366 {
367   std::string text;
368
369   // Return text-view's text only if the text-input's text is not empty
370   // in order to not to return the placeholder text.
371   if( !mStyledText.empty() )
372   {
373     text = mDisplayedTextView.GetText();
374   }
375
376   return text;
377 }
378
379 std::string TextInput::GetMarkupText() const
380 {
381   std::string markupString;
382   MarkupProcessor::GetMarkupString( mStyledText, markupString );
383
384   return markupString;
385 }
386
387 void TextInput::SetPlaceholderText( const std::string& placeHolderText )
388 {
389   // Get the placeholder styled text array from the markup string.
390   MarkupProcessor::GetStyledTextArray( placeHolderText, mStyledPlaceHolderText, IsMarkupProcessingEnabled() );
391
392   if( mStyledText.empty() )
393   {
394     // Set the placeholder text only if the styled text is empty.
395     mDisplayedTextView.SetText( mStyledPlaceHolderText );
396     mPlaceHolderSet = true;
397   }
398 }
399
400 std::string TextInput::GetPlaceholderText()
401 {
402   // Traverses the styled placeholder array getting only the text.
403   //  Note that for some languages a 'character' could be represented by more than one 'char'
404
405   std::string placeholderText;
406   for( MarkupProcessor::StyledTextArray::const_iterator it = mStyledPlaceHolderText.begin(), endIt = mStyledPlaceHolderText.end(); it != endIt; ++it )
407   {
408     placeholderText.append( (*it).mText.GetText() );
409   }
410
411   return placeholderText ;
412 }
413
414 void TextInput::SetInitialText(const std::string& initialText)
415 {
416   DALI_LOG_INFO(gLogFilter, Debug::General, "SetInitialText string[%s]\n", initialText.c_str() );
417
418   if ( mPreEditFlag ) // If in the pre-edit state and text is being set then discard text being inserted.
419   {
420     mPreEditFlag = false;
421     mIgnoreCommitFlag = true;
422   }
423
424   SetText( initialText );
425   PreEditReset( false ); // Reset keyboard as text changed
426 }
427
428 void TextInput::SetText(const std::string& initialText)
429 {
430   DALI_LOG_INFO(gLogFilter, Debug::General, "SetText string[%s]\n", initialText.c_str() );
431
432   GetStyledTextArray( initialText, mStyledText, IsMarkupProcessingEnabled() );
433
434   if( mStyledText.empty() )
435   {
436     // If the initial text is empty, set the placeholder text.
437     mDisplayedTextView.SetText( mStyledPlaceHolderText );
438     mPlaceHolderSet = true;
439   }
440   else
441   {
442     mDisplayedTextView.SetText( mStyledText );
443     mPlaceHolderSet = false;
444   }
445
446   GetTextLayoutInfo();
447
448   mCursorPosition = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
449
450   ImfManager imfManager = ImfManager::Get();
451   if ( imfManager )
452   {
453     imfManager.SetCursorPosition( mCursorPosition );
454     imfManager.SetSurroundingText( initialText );
455     imfManager.NotifyCursorPosition();
456   }
457
458   if( IsScrollEnabled() )
459   {
460     ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
461   }
462
463   ShowGrabHandleAndSetVisibility( false );
464
465   RemoveHighlight();
466
467   DrawCursor();
468
469   EmitTextModified();
470 }
471
472 void TextInput::SetText( const MarkupProcessor::StyledTextArray& styleText )
473 {
474   DALI_LOG_INFO(gLogFilter, Debug::General, "SetText markup text\n" );
475
476   mDisplayedTextView.SetText( styleText );
477   mPlaceHolderSet = false;
478
479   // If text alignment hasn't been manually set by application developer, then we
480   // automatically determine the alignment based on the content of the text i.e. what
481   // language the text begins with.
482   // TODO: This should determine different alignments for each line (broken by '\n') of text.
483   if(!mOverrideAutomaticAlignment)
484   {
485     // Determine bidi direction of first character (skipping past whitespace, numbers, and symbols)
486     bool leftToRight(true);
487
488     if( !styleText.empty() )
489     {
490       bool breakOut(false);
491
492       for( MarkupProcessor::StyledTextArray::const_iterator textIter = styleText.begin(), textEndIter = styleText.end(); ( textIter != textEndIter ) && ( !breakOut ); ++textIter )
493       {
494         const Text& text = textIter->mText;
495
496         for( std::size_t i = 0; i < text.GetLength(); ++i )
497         {
498           Character character( text[i] );
499           if( character.GetCharacterDirection() != Character::Neutral )
500           {
501             leftToRight = ( character.GetCharacterDirection() == Character::LeftToRight );
502             breakOut = true;
503             break;
504           }
505         }
506       }
507     }
508
509     // Based on this direction, either left or right align text if not manually set by application developer.
510     mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(
511                                            ( leftToRight ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight) |
512                                              Toolkit::Alignment::VerticalTop ) );
513     mDisplayedTextView.SetLineJustification( leftToRight ? Toolkit::TextView::Left : Toolkit::TextView::Right);
514   }
515
516   EmitTextModified();
517 }
518
519 void TextInput::SetMaxCharacterLength(std::size_t maxChars)
520 {
521   mMaxStringLength = maxChars;
522 }
523
524 void TextInput::SetNumberOfLinesLimit(std::size_t maxLines)
525 {
526   DALI_ASSERT_DEBUG( maxLines > 0 )
527
528   if ( maxLines > 0)
529   {
530     mNumberOflinesLimit = maxLines;
531   }
532 }
533
534 std::size_t TextInput::GetNumberOfLinesLimit() const
535 {
536   return mNumberOflinesLimit;
537 }
538
539 std::size_t TextInput::GetNumberOfCharacters() const
540 {
541   return mStyledText.size();
542 }
543
544 // Styling
545 void TextInput::SetMaterialDiffuseColor( const Vector4& color )
546 {
547   mMaterialColor = color;
548   if ( mCustomMaterial )
549   {
550     mCustomMaterial.SetDiffuseColor( mMaterialColor );
551     mMeshData.SetMaterial( mCustomMaterial );
552   }
553 }
554
555 const Vector4& TextInput::GetMaterialDiffuseColor() const
556 {
557   return mMaterialColor;
558 }
559
560 // Signals
561
562 Toolkit::TextInput::InputSignalV2& TextInput::InputStartedSignal()
563 {
564   return mInputStartedSignalV2;
565 }
566
567 Toolkit::TextInput::InputSignalV2& TextInput::InputFinishedSignal()
568 {
569   return mInputFinishedSignalV2;
570 }
571
572 Toolkit::TextInput::InputSignalV2& TextInput::CutAndPasteToolBarDisplayedSignal()
573 {
574   return mCutAndPasteToolBarDisplayedV2;
575 }
576
577 Toolkit::TextInput::StyleChangedSignalV2& TextInput::StyleChangedSignal()
578 {
579   return mStyleChangedSignalV2;
580 }
581
582 Toolkit::TextInput::TextModifiedSignalType& TextInput::TextModifiedSignal()
583 {
584   return mTextModifiedSignal;
585 }
586
587 Toolkit::TextInput::MaxInputCharactersReachedSignalV2& TextInput::MaxInputCharactersReachedSignal()
588 {
589   return mMaxInputCharactersReachedSignalV2;
590 }
591
592 Toolkit::TextInput::InputTextExceedBoundariesSignalV2& TextInput::InputTextExceedBoundariesSignal()
593 {
594   return mInputTextExceedBoundariesSignalV2;
595 }
596
597 bool TextInput::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
598 {
599   Dali::BaseHandle handle( object );
600
601   bool connected( true );
602   Toolkit::TextInput textInput = Toolkit::TextInput::DownCast(handle);
603
604   if( Toolkit::TextInput::SIGNAL_START_INPUT == signalName )
605   {
606     textInput.InputStartedSignal().Connect( tracker, functor );
607   }
608   else if( Toolkit::TextInput::SIGNAL_END_INPUT == signalName )
609   {
610     textInput.InputFinishedSignal().Connect( tracker, functor );
611   }
612   else if( Toolkit::TextInput::SIGNAL_STYLE_CHANGED == signalName )
613   {
614     textInput.StyleChangedSignal().Connect( tracker, functor );
615   }
616   else if( Toolkit::TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED == signalName )
617   {
618     textInput.MaxInputCharactersReachedSignal().Connect( tracker, functor );
619   }
620   else if( Toolkit::TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES == signalName )
621   {
622     textInput.InputTextExceedBoundariesSignal().Connect( tracker, functor );
623   }
624   else
625   {
626     // signalName does not match any signal
627     connected = false;
628   }
629
630   return connected;
631 }
632
633 void TextInput::SetEditable(bool editMode, bool setCursorOnTouchPoint, const Vector2& touchPoint)
634 {
635   if(editMode)
636   {
637     // update line height before calculate the actual position.
638     UpdateLineHeight();
639
640     if(!mEditModeActive)
641     {
642       if( setCursorOnTouchPoint )
643       {
644         // Sets the cursor position for the given touch point.
645         ReturnClosestIndex( touchPoint, mCursorPosition );
646
647         // Creates the grab handle.
648         if( IsGrabHandleEnabled() )
649         {
650           const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
651
652           CreateGrabHandle();
653
654           mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
655           mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
656           mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
657           ShowGrabHandleAndSetVisibility( true );
658
659           // Scrolls the text-view if needed.
660           if( IsScrollEnabled() )
661           {
662             ScrollTextViewToMakeCursorVisible( cursorPosition );
663           }
664         }
665       }
666       else
667       {
668         mCursorPosition = mStyledText.size(); // Initially set cursor position to end of string.
669       }
670     }
671
672     StartEditMode();
673   }
674   else
675   {
676     EndEditMode();
677   }
678 }
679
680 bool TextInput::IsEditable() const
681 {
682   return mEditModeActive;
683 }
684
685 void TextInput::SetEditOnTouch( bool editOnTouch )
686 {
687   mEditOnTouch = editOnTouch;
688 }
689
690 bool TextInput::IsEditOnTouch() const
691 {
692   return mEditOnTouch;
693 }
694
695 void TextInput::SetTextSelectable( bool textSelectable )
696 {
697   mTextSelection = textSelectable;
698 }
699
700 bool TextInput::IsTextSelectable() const
701 {
702   return mTextSelection;
703 }
704
705 bool TextInput::IsTextSelected() const
706 {
707   return mHighlightMeshActor;
708 }
709
710 void TextInput::DeSelectText()
711 {
712   RemoveHighlight();
713   HidePopup();
714   CursorUpdate();
715 }
716
717 void TextInput::SetGrabHandleImage(Dali::Image image )
718 {
719   if (image)
720   {
721     CreateGrabHandle(image);
722   }
723 }
724
725 void TextInput::SetCursorImage(Dali::Image image, const Vector4& border )
726 {
727   DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
728
729   if ( image )
730   {
731     mCursor.SetImage( image );
732     mCursor.SetNinePatchBorder( border );
733   }
734 }
735
736 Vector3 TextInput::GetSelectionHandleSize()
737 {
738   return DEFAULT_SELECTION_HANDLE_SIZE;
739 }
740
741 void TextInput::SetRTLCursorImage(Dali::Image image, const Vector4& border )
742 {
743   DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
744
745   if ( image )
746   {
747     mCursorRTL.SetImage( image);
748     mCursorRTL.SetNinePatchBorder(  border );
749   }
750 }
751
752 void TextInput::EnableGrabHandle(bool toggle)
753 {
754   // enables grab handle with will in turn de-activate magnifier
755   mGrabHandleEnabled = toggle;
756 }
757
758 bool TextInput::IsGrabHandleEnabled()
759 {
760   // if false then magnifier will be shown instead.
761   return mGrabHandleEnabled;
762 }
763
764 void TextInput::EnableSelectionHandleFlip( bool toggle )
765 {
766   // Deprecated function.  To be removed.
767   mIsSelectionHandleFlipEnabled = toggle;
768 }
769
770 bool TextInput::IsSelectionHandleFlipEnabled()
771 {
772   // Deprecated function, To be removed. Returns true as handle flipping always enabled by default so handles do not exceed screen.
773   return true;
774 }
775
776 void TextInput::SetSelectionHandleFlipMargin( const Vector4& margin )
777 {
778   // Deprecated function, now just stores margin for retreival, remove completely once depricated Public API removed.
779   Vector3 textInputSize = mDisplayedTextView.GetCurrentSize();
780   const Vector4 flipBoundary( -margin.x, -margin.y, textInputSize.width + margin.z, textInputSize.height + margin.w );
781
782   mSelectionHandleFlipMargin = margin;
783 }
784
785 void TextInput::SetBoundingRectangle( const Rect<float>& boundingRectangle )
786 {
787   // Convert to world coordinates and store as a Vector4 to be compatiable with Property Notifications.
788   Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
789
790   const float originX = boundingRectangle.x - 0.5f * stageSize.width;
791   const float originY = boundingRectangle.y - 0.5f * stageSize.height;
792
793   const Vector4 boundary( originX,
794                           originY,
795                           originX + boundingRectangle.width,
796                           originY + boundingRectangle.height );
797
798   mBoundingRectangleWorldCoordinates = boundary;
799 }
800
801 const Rect<float> TextInput::GetBoundingRectangle() const
802 {
803   Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
804
805   const float originX = mBoundingRectangleWorldCoordinates.x + 0.5f * stageSize.width;
806   const float originY = mBoundingRectangleWorldCoordinates.y + 0.5f * stageSize.height;
807
808   Rect<float>boundingRect( originX, originY, mBoundingRectangleWorldCoordinates.z - mBoundingRectangleWorldCoordinates.x, mBoundingRectangleWorldCoordinates.w - mBoundingRectangleWorldCoordinates.y);
809
810   return boundingRect;
811 }
812
813 const Vector4& TextInput::GetSelectionHandleFlipMargin()
814 {
815   return mSelectionHandleFlipMargin;
816 }
817
818 void TextInput::SetTextColor( const Vector4& color )
819 {
820   mDisplayedTextView.SetColor( color );
821 }
822
823 void TextInput::SetActiveStyle( const TextStyle& style, const TextStyle::Mask mask )
824 {
825   if( style != mInputStyle )
826   {
827     // different style.
828     bool emitSignal = false;
829
830     // mask: modify style according to mask, if different emit signal.
831     const TextStyle oldInputStyle( mInputStyle );
832
833     // Copy the new style.
834     mInputStyle.Copy( style, mask );
835
836     // if style has changed, emit signal.
837     if( oldInputStyle != mInputStyle )
838     {
839       emitSignal = true;
840     }
841
842     // Updates the line height accordingly with the input style.
843     UpdateLineHeight();
844
845     // Changing font point size will require the cursor to be re-sized
846     DrawCursor();
847
848     if( emitSignal )
849     {
850       EmitStyleChangedSignal();
851     }
852   }
853 }
854
855 void TextInput::ApplyStyle( const TextStyle& style, const TextStyle::Mask mask )
856 {
857   if ( IsTextSelected() )
858   {
859     const std::size_t begin = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
860     const std::size_t end = std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition) - 1;
861
862     if( !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
863     {
864       ApplyStyleToRange(style, mask, mTextLayoutInfo.mCharacterLogicalToVisualMap[begin], mTextLayoutInfo.mCharacterLogicalToVisualMap[end]);
865     }
866
867     // Keeps the old style to be compared with the new one.
868     const TextStyle oldInputStyle( mInputStyle );
869
870     // Copy only those parameters from the style which are set in the mask.
871     mInputStyle.Copy( style, mask );
872
873     if( mInputStyle != oldInputStyle )
874     {
875       // Updates the line height accordingly with the input style.
876       UpdateLineHeight();
877
878       EmitStyleChangedSignal();
879     }
880   }
881 }
882
883 void TextInput::ApplyStyleToAll( const TextStyle& style, const TextStyle::Mask mask )
884 {
885   if( !mStyledText.empty() )
886   {
887     ApplyStyleToRange( style, mask, 0, mStyledText.size() - 1 );
888   }
889 }
890
891 TextStyle TextInput::GetStyleAtCursor() const
892 {
893   TextStyle style;
894
895   if ( !mStyledText.empty() && ( mCursorPosition > 0 ) )
896   {
897     DALI_ASSERT_DEBUG( ( 0 <= mCursorPosition-1 ) && ( mCursorPosition-1 < mStyledText.size() ) );
898     style = mStyledText.at( mCursorPosition-1 ).mStyle;
899   }
900   else // No text.
901   {
902     style = mInputStyle;
903
904     if ( mInputStyle.GetFontPointSize() <  Math::MACHINE_EPSILON_1000 )
905     {
906       Dali::Font defaultFont = Dali::Font::New();
907       style.SetFontPointSize( PointSize( defaultFont.GetPointSize()) );
908     }
909   }
910
911   return style;
912 }
913
914 TextStyle TextInput::GetStyleAt( std::size_t position ) const
915 {
916   DALI_ASSERT_DEBUG( ( 0 <= position ) && ( position <= mStyledText.size() ) );
917
918   if( position >= mStyledText.size() )
919   {
920     position = mStyledText.size() - 1;
921   }
922
923   return mStyledText.at( position ).mStyle;
924 }
925
926 void TextInput::SetTextAlignment( Toolkit::Alignment::Type align )
927 {
928   mDisplayedTextView.SetTextAlignment( align );
929   mOverrideAutomaticAlignment = true;
930 }
931
932 void TextInput::SetTextLineJustification( Toolkit::TextView::LineJustification justification )
933 {
934   mDisplayedTextView.SetLineJustification( justification );
935   mOverrideAutomaticAlignment = true;
936 }
937
938 void TextInput::SetFadeBoundary( const Toolkit::TextView::FadeBoundary& fadeBoundary )
939 {
940   mDisplayedTextView.SetFadeBoundary( fadeBoundary );
941 }
942
943 const Toolkit::TextView::FadeBoundary& TextInput::GetFadeBoundary() const
944 {
945   return mDisplayedTextView.GetFadeBoundary();
946 }
947
948 Toolkit::Alignment::Type TextInput::GetTextAlignment() const
949 {
950   return mDisplayedTextView.GetTextAlignment();
951 }
952
953 void TextInput::SetMultilinePolicy( Toolkit::TextView::MultilinePolicy policy )
954 {
955   mDisplayedTextView.SetMultilinePolicy( policy );
956 }
957
958 Toolkit::TextView::MultilinePolicy TextInput::GetMultilinePolicy() const
959 {
960   return mDisplayedTextView.GetMultilinePolicy();
961 }
962
963 void TextInput::SetWidthExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
964 {
965   mDisplayedTextView.SetWidthExceedPolicy( policy );
966 }
967
968 Toolkit::TextView::ExceedPolicy TextInput::GetWidthExceedPolicy() const
969 {
970   return mDisplayedTextView.GetWidthExceedPolicy();
971 }
972
973 void TextInput::SetHeightExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
974 {
975   mDisplayedTextView.SetHeightExceedPolicy( policy );
976 }
977
978 Toolkit::TextView::ExceedPolicy TextInput::GetHeightExceedPolicy() const
979 {
980   return mDisplayedTextView.GetHeightExceedPolicy();
981 }
982
983 void TextInput::SetExceedEnabled( bool enable )
984 {
985   mExceedEnabled = enable;
986 }
987
988 bool TextInput::GetExceedEnabled() const
989 {
990   return mExceedEnabled;
991 }
992
993 void TextInput::SetBackground(Dali::Image image )
994 {
995   // TODO Should add this function and add public api to match.
996 }
997
998 bool TextInput::OnTouchEvent(const TouchEvent& event)
999 {
1000   return false;
1001 }
1002
1003 bool TextInput::OnKeyEvent(const KeyEvent& event)
1004 {
1005   switch( event.state )
1006   {
1007     case KeyEvent::Down:
1008     {
1009       return OnKeyDownEvent(event);
1010     }
1011     break;
1012
1013     case KeyEvent::Up:
1014     {
1015       return OnKeyUpEvent(event);
1016     }
1017     break;
1018
1019     default:
1020     {
1021       return false;
1022     }
1023     break;
1024   }
1025 }
1026
1027 void TextInput::OnKeyInputFocusGained()
1028 {
1029   DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusGained\n" );
1030
1031   mEditModeActive = true;
1032
1033   mActiveLayer.RaiseToTop(); // Ensure layer holding handles is on top
1034
1035   mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1036
1037   // Updates the line height accordingly with the input style.
1038   UpdateLineHeight();
1039
1040   // Connect the signals to use in text input.
1041   VirtualKeyboard::StatusChangedSignal().Connect( this, &TextInput::KeyboardStatusChanged );
1042   VirtualKeyboard::LanguageChangedSignal().Connect( this, &TextInput::SetTextDirection );
1043
1044   // Set the text direction if empty and connect to the signal to ensure we change direction when the language changes.
1045   SetTextDirection();
1046
1047   GetTextLayoutInfo();
1048
1049   DrawCursor();
1050   SetCursorVisibility( true );
1051   StartCursorBlinkTimer();
1052
1053   Toolkit::TextInput handle( GetOwner() );
1054   mInputStartedSignalV2.Emit( handle );
1055
1056   ImfManager imfManager = ImfManager::Get();
1057
1058   if ( imfManager )
1059   {
1060     imfManager.EventReceivedSignal().Connect(this, &TextInput::ImfEventReceived);
1061
1062     // Notify that the text editing start.
1063     imfManager.Activate();
1064
1065     // When window gain lost focus, the imf manager is deactivated. Thus when window gain focus again, the imf manager must be activated.
1066     imfManager.SetRestoreAferFocusLost( true );
1067
1068     imfManager.SetCursorPosition( mCursorPosition );
1069     imfManager.NotifyCursorPosition();
1070   }
1071
1072   mClipboard = Clipboard::Get(); // Store handle to clipboard
1073
1074   // Now in edit mode we can accept string to paste from clipboard
1075   if( Adaptor::IsAvailable() )
1076   {
1077     ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1078     if ( notifier )
1079     {
1080       notifier.ContentSelectedSignal().Connect( this, &TextInput::OnClipboardTextSelected );
1081     }
1082   }
1083 }
1084
1085 void TextInput::OnKeyInputFocusLost()
1086 {
1087   DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusLost\n" );
1088
1089   if( mPreEditFlag )
1090   {
1091     // If key input focus is lost, it removes the
1092     // underline from the last pre-edit text.
1093     RemovePreEditStyle();
1094     const std::size_t numberOfCharactersDeleted = DeletePreEdit();
1095     InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersDeleted );
1096     EmitTextModified();
1097   }
1098
1099   ImfManager imfManager = ImfManager::Get();
1100   if ( imfManager )
1101   {
1102     // The text editing is finished. Therefore the imf manager don't have restore activation.
1103     imfManager.SetRestoreAferFocusLost( false );
1104
1105     // Notify that the text editing finish.
1106     imfManager.Deactivate();
1107
1108     imfManager.EventReceivedSignal().Disconnect(this, &TextInput::ImfEventReceived);
1109   }
1110   // Disconnect signal used the text input.
1111   VirtualKeyboard::LanguageChangedSignal().Disconnect( this, &TextInput::SetTextDirection );
1112
1113   Toolkit::TextInput handle( GetOwner() );
1114   mInputFinishedSignalV2.Emit( handle );
1115   mEditModeActive = false;
1116   mPreEditFlag = false;
1117   RemoveHighlight();
1118   SetCursorVisibility( false );
1119   StopCursorBlinkTimer();
1120
1121   ShowGrabHandleAndSetVisibility( false );
1122
1123   mClipboard.Reset();
1124   // No longer in edit mode so do not want to receive string from clipboard
1125   if( Adaptor::IsAvailable() )
1126   {
1127     ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1128     if ( notifier )
1129     {
1130       notifier.ContentSelectedSignal().Disconnect( this, &TextInput::OnClipboardTextSelected );
1131     }
1132     Clipboard clipboard = Clipboard::Get();
1133
1134     if ( clipboard )
1135     {
1136       clipboard.HideClipboard();
1137     }
1138   }
1139 }
1140
1141 void TextInput::OnControlStageConnection()
1142 {
1143   Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
1144
1145   if ( mBoundingRectangleWorldCoordinates == Vector4::ZERO )
1146   {
1147     SetBoundingRectangle( Rect<float>( 0.0f, 0.0f, stageSize.width, stageSize.height ));
1148   }
1149 }
1150
1151 void TextInput::CreateActiveLayer()
1152 {
1153   Actor self = Self();
1154   mActiveLayer = Layer::New();
1155   mActiveLayer.SetName ( "ActiveLayerActor" );
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   mCursor = CreateCursor(DEFAULT_CURSOR_COLOR);
1174   mCursorRTL = CreateCursor(DEFAULT_CURSOR_COLOR);
1175
1176   Actor self = Self();
1177   self.Add( mCursor );
1178   self.Add( mCursorRTL );
1179
1180   mCursorVisibility = false;
1181
1182   CreateActiveLayer(); // todo move this so layer only created when needed.
1183
1184   // Assign names to image actors
1185   mCursor.SetName("mainCursor");
1186   mCursorRTL.SetName("rtlCursor");
1187 }
1188
1189 void TextInput::OnControlSizeSet(const Vector3& targetSize)
1190 {
1191   mDisplayedTextView.SetSize( targetSize );
1192   GetTextLayoutInfo();
1193   mActiveLayer.SetSize(targetSize);
1194 }
1195
1196 void TextInput::OnRelaidOut( Vector2 size, ActorSizeContainer& container )
1197 {
1198   Relayout( mDisplayedTextView, size, container );
1199   Relayout( mPopupPanel.GetRootActor(), size, container );
1200
1201   GetTextLayoutInfo();
1202
1203   DrawCursor();
1204 }
1205
1206 Vector3 TextInput::GetNaturalSize()
1207 {
1208   Vector3 naturalSize = mDisplayedTextView.GetNaturalSize();
1209
1210   if( mEditModeActive && ( Vector3::ZERO == naturalSize ) )
1211   {
1212     // If the natural is zero, it means there is no text. Let's return the cursor height as the natural height.
1213     naturalSize.height = mLineHeight;
1214   }
1215
1216   return naturalSize;
1217 }
1218
1219 float TextInput::GetHeightForWidth( float width )
1220 {
1221   float height = mDisplayedTextView.GetHeightForWidth( width );
1222
1223   if( mEditModeActive && ( fabsf( height ) < Math::MACHINE_EPSILON_1000 ) )
1224   {
1225     // If the height is zero, it means there is no text. Let's return the cursor height.
1226     height = mLineHeight;
1227   }
1228
1229   return height;
1230 }
1231
1232 /*end of Virtual methods from parent*/
1233
1234 // Private Internal methods
1235
1236 void TextInput::OnHandlePan(Actor actor, PanGesture gesture)
1237 {
1238   switch (gesture.state)
1239   {
1240     case Gesture::Started:
1241     // fall through so code not duplicated
1242     case Gesture::Continuing:
1243     {
1244       if (actor == mGrabArea)
1245       {
1246         SetCursorVisibility( true );
1247         ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1248         MoveGrabHandle( gesture.displacement );
1249         HidePopup(); // Do not show popup whilst handle is moving
1250       }
1251       else if (actor == mHandleOneGrabArea)
1252       {
1253         // the displacement in PanGesture is affected by the actor's rotation.
1254         mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1255         mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1256
1257         MoveSelectionHandle( HandleOne, gesture.displacement );
1258
1259         mState = StateDraggingHandle;
1260         HidePopup();
1261       }
1262       else if (actor == mHandleTwoGrabArea)
1263       {
1264         // the displacement in PanGesture is affected by the actor's rotation.
1265         mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1266         mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1267
1268         MoveSelectionHandle( HandleTwo, gesture.displacement );
1269
1270         mState = StateDraggingHandle;
1271         HidePopup();
1272       }
1273     }
1274     break;
1275
1276     case Gesture::Finished:
1277     {
1278       // Revert back to non-pressed selection handle images
1279       if (actor == mGrabArea)
1280       {
1281         mActualGrabHandlePosition = MoveGrabHandle( gesture.displacement );
1282         SetCursorVisibility( true );
1283         SetUpPopupSelection();
1284         ShowPopup();
1285       }
1286       if (actor == mHandleOneGrabArea)
1287       {
1288         // the displacement in PanGesture is affected by the actor's rotation.
1289         mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1290         mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1291
1292         mSelectionHandleOneActualPosition = MoveSelectionHandle( HandleOne, gesture.displacement );
1293
1294         mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1295         mState = StateEdit;
1296         ShowPopupCutCopyPaste();
1297       }
1298       if (actor == mHandleTwoGrabArea)
1299       {
1300         // the displacement in PanGesture is affected by the actor's rotation.
1301         mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1302         mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1303
1304         mSelectionHandleTwoActualPosition = MoveSelectionHandle( HandleTwo, gesture.displacement );
1305
1306         mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1307         mState = StateEdit;
1308         ShowPopupCutCopyPaste();
1309       }
1310     }
1311     break;
1312     default:
1313       break;
1314   }
1315 }
1316
1317 // Stop the flashing animation so easy to see when moved.
1318 bool TextInput::OnPressDown(Dali::Actor actor, const TouchEvent& touch)
1319 {
1320   if (touch.GetPoint(0).state == TouchPoint::Down)
1321   {
1322     SetCursorVisibility( true );
1323     StopCursorBlinkTimer();
1324   }
1325   else if (touch.GetPoint(0).state == TouchPoint::Up)
1326   {
1327     SetCursorVisibility( true );
1328     StartCursorBlinkTimer();
1329   }
1330   return false;
1331 }
1332
1333 // selection handle one
1334 bool TextInput::OnHandleOneTouched(Dali::Actor actor, const TouchEvent& touch)
1335 {
1336   if (touch.GetPoint(0).state == TouchPoint::Down)
1337   {
1338     mSelectionHandleOne.SetImage( mSelectionHandleOneImagePressed );
1339   }
1340   else if (touch.GetPoint(0).state == TouchPoint::Up)
1341   {
1342     mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1343   }
1344   return false;
1345 }
1346
1347 // selection handle two
1348 bool TextInput::OnHandleTwoTouched(Dali::Actor actor, const TouchEvent& touch)
1349 {
1350   if (touch.GetPoint(0).state == TouchPoint::Down)
1351   {
1352     mSelectionHandleTwo.SetImage( mSelectionHandleTwoImagePressed );
1353   }
1354   else if (touch.GetPoint(0).state == TouchPoint::Up)
1355   {
1356     mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1357   }
1358   return false;
1359 }
1360
1361 void TextInput::OnDoubleTap(Dali::Actor actor, Dali::TapGesture tap)
1362 {
1363    // If text exists then select nearest word.
1364    if ( !mStyledText.empty())
1365    {
1366      HidePopup();
1367
1368      ShowGrabHandleAndSetVisibility( false );
1369
1370
1371      if ( mPreEditFlag )
1372      {
1373        // PreEdit will be committed here without needing a commit from IMF.  Remove pre-edit underline and reset flags which
1374        // converts the pre-edit word being displayed to a committed word.
1375        if ( !mUnderlinedPriorToPreEdit )
1376        {
1377          TextStyle style;
1378          style.SetUnderline( false );
1379          ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1380        }
1381        mPreEditFlag = false;
1382        mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1383        // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1384        PreEditReset( false );
1385      }
1386      mCursorPosition = 0;
1387
1388      mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1389      ReturnClosestIndex( tap.localPoint, mCursorPosition );
1390
1391      ImfManager imfManager = ImfManager::Get();
1392      if ( imfManager )
1393      {
1394        imfManager.SetCursorPosition ( mCursorPosition );
1395        imfManager.NotifyCursorPosition();
1396      }
1397
1398      std::size_t start = 0;
1399      std::size_t end = 0;
1400      Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1401
1402      SelectText( start, end );
1403    }
1404    // if no text but clipboard has content then show paste option
1405    if ( ( mClipboard && mClipboard.NumberOfItems() ) || !mStyledText.empty() )
1406    {
1407      ShowPopupCutCopyPaste();
1408    }
1409
1410    // If no text and clipboard empty then do nothing
1411 }
1412
1413 // TODO: Change the function name to be more general.
1414 void TextInput::OnTextTap(Dali::Actor actor, Dali::TapGesture tap)
1415 {
1416   DALI_LOG_INFO( gLogFilter, Debug::General, "OnTap mPreEditFlag[%s] mEditOnTouch[%s] mEditModeActive[%s] ", (mPreEditFlag)?"true":"false"
1417                                                                                                            , (mEditOnTouch)?"true":"false"
1418                                                                                                            , (mEditModeActive)?"true":"false");
1419
1420   if( mHandleOneGrabArea == actor || mHandleTwoGrabArea == actor )
1421   {
1422     return;
1423   }
1424
1425   if( mGrabArea == actor )
1426   {
1427     if( mPopupPanel.GetState() == TextInputPopup::StateHidden || mPopupPanel.GetState() == TextInputPopup::StateHiding )
1428     {
1429       SetUpPopupSelection();
1430       ShowPopup();
1431     }
1432
1433     return;
1434   }
1435
1436   HidePopup();
1437   RemoveHighlight();
1438
1439   mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1440
1441   // Initially don't create the grab handle.
1442   bool createGrabHandle = false;
1443
1444   if ( !mEditModeActive )
1445   {
1446     // update line height before calculate the actual position.
1447     UpdateLineHeight();
1448
1449     // Only start edit mode if TextInput configured to edit on touch
1450     if ( mEditOnTouch )
1451     {
1452       // Set the initial cursor position in the tap point.
1453       ReturnClosestIndex(tap.localPoint, mCursorPosition );
1454
1455       // Create the grab handle.
1456       // TODO Make this a re-usable function.
1457       if ( IsGrabHandleEnabled() )
1458       {
1459         const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1460
1461         CreateGrabHandle();
1462
1463         mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1464         mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1465         mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1466         ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1467
1468       }
1469
1470       // Edit mode started after grab handle created to ensure the signal InputStarted is sent last.
1471       // This is used to ensure if selecting text hides the grab handle then this code is run after grab handle is created,
1472       // otherwise the Grab handle will be shown when selecting.
1473
1474       StartEditMode();
1475     }
1476   }
1477   else
1478   {
1479     // Show the keyboard if it was hidden.
1480     if (!VirtualKeyboard::IsVisible())
1481     {
1482       VirtualKeyboard::Show();
1483     }
1484
1485     // Reset keyboard as tap event has occurred.
1486     // Set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1487     PreEditReset( true );
1488
1489     GetTextLayoutInfo();
1490
1491     if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() ) // If string empty we do not need a grab handle.
1492     {
1493       // 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.
1494
1495       ReturnClosestIndex(tap.localPoint, mCursorPosition );
1496
1497       DALI_LOG_INFO( gLogFilter, Debug::General, "mCursorPosition[%u]", mCursorPosition );
1498
1499       // Notify keyboard so it can 're-capture' word for predictive text.
1500       // As we have done a reset, is this required, expect IMF keyboard to request this information.
1501       ImfManager imfManager = ImfManager::Get();
1502       if ( imfManager )
1503       {
1504         imfManager.SetCursorPosition ( mCursorPosition );
1505         imfManager.NotifyCursorPosition();
1506       }
1507       const TextStyle oldInputStyle( mInputStyle );
1508
1509       mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1510
1511       DrawCursor();
1512
1513       // Create the grab handle.
1514       // Grab handle is created later.
1515       createGrabHandle = true;
1516
1517       if( oldInputStyle != mInputStyle )
1518       {
1519         // Updates the line height accordingly with the input style.
1520         UpdateLineHeight();
1521
1522         EmitStyleChangedSignal();
1523       }
1524     }
1525   }
1526
1527   if ( createGrabHandle && IsGrabHandleEnabled() )
1528   {
1529     const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1530
1531     CreateGrabHandle();
1532
1533     mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1534     mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1535     mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1536     ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1537
1538   }
1539 }
1540
1541 void TextInput::OnLongPress(Dali::Actor actor, Dali::LongPressGesture longPress)
1542 {
1543   DALI_LOG_INFO( gLogFilter, Debug::General, "OnLongPress\n" );
1544
1545   // Ignore longpress if in selection mode already
1546   if( mHighlightMeshActor )
1547   {
1548     return;
1549   }
1550
1551   if(longPress.state == Dali::Gesture::Started)
1552   {
1553     // Start edit mode on long press
1554     if ( !mEditModeActive )
1555     {
1556       StartEditMode();
1557     }
1558
1559     // If text exists then select nearest word.
1560     if ( !mStyledText.empty())
1561     {
1562       HidePopup();
1563
1564       ShowGrabHandleAndSetVisibility( false );
1565
1566
1567       if ( mPreEditFlag )
1568       {
1569         // PreEdit will be committed here without needing a commit from IMF.  Remove pre-edit underline and reset flags which
1570         // converts the pre-edit word being displayed to a committed word.
1571         if ( !mUnderlinedPriorToPreEdit )
1572         {
1573           TextStyle style;
1574           style.SetUnderline( false );
1575           ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1576         }
1577         mPreEditFlag = false;
1578         mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1579         // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1580         PreEditReset( false );
1581       }
1582       mCursorPosition = 0;
1583
1584       mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1585       ReturnClosestIndex( longPress.localPoint, mCursorPosition );
1586
1587       ImfManager imfManager = ImfManager::Get();
1588       if ( imfManager )
1589       {
1590         imfManager.SetCursorPosition ( mCursorPosition );
1591         imfManager.NotifyCursorPosition();
1592       }
1593       std::size_t start = 0;
1594       std::size_t end = 0;
1595       Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1596
1597       SelectText( start, end );
1598     }
1599
1600     // if no text but clipboard has content then show paste option, if no text and clipboard empty then do nothing
1601     if ( ( mClipboard && mClipboard.NumberOfItems() ) || !mStyledText.empty() )
1602     {
1603       ShowPopupCutCopyPaste();
1604     }
1605   }
1606 }
1607
1608 void TextInput::OnClipboardTextSelected( ClipboardEventNotifier& notifier )
1609 {
1610   const Text clipboardText( notifier.GetContent() );
1611   PasteText( clipboardText );
1612
1613   SetCursorVisibility( true );
1614   StartCursorBlinkTimer();
1615
1616   ShowGrabHandleAndSetVisibility( false );
1617
1618
1619   HidePopup();
1620 }
1621
1622 bool TextInput::OnPopupButtonPressed( Toolkit::Button button )
1623 {
1624   mPopupPanel.PressedSignal().Disconnect( this, &TextInput::OnPopupButtonPressed );
1625
1626   const std::string& name = button.GetName();
1627
1628   if(name == TextInputPopup::OPTION_SELECT_WORD)
1629   {
1630     std::size_t start = 0;
1631     std::size_t end = 0;
1632     Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1633
1634     SelectText( start, end );
1635   }
1636   else if(name == TextInputPopup::OPTION_SELECT_ALL)
1637   {
1638     SetCursorVisibility(false);
1639     StopCursorBlinkTimer();
1640
1641     std::size_t end = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
1642     std::size_t start = 0;
1643
1644     SelectText( start, end );
1645   }
1646   else if(name == TextInputPopup::OPTION_CUT)
1647   {
1648     bool ret = CopySelectedTextToClipboard();
1649
1650     if ( ret )
1651     {
1652       DeleteHighlightedText( true );
1653       CursorUpdate();
1654     }
1655
1656     SetCursorVisibility( true );
1657     StartCursorBlinkTimer();
1658
1659     HidePopup();
1660   }
1661   else if(name == TextInputPopup::OPTION_COPY)
1662   {
1663     CopySelectedTextToClipboard();
1664
1665     RemoveHighlight();
1666
1667     SetCursorVisibility( true );
1668     StartCursorBlinkTimer();
1669
1670     HidePopup();
1671   }
1672   else if(name == TextInputPopup::OPTION_PASTE)
1673   {
1674     const Text retrievedString( mClipboard.GetItem( 0 ) );  // currently can only get first item in clip board, index 0;
1675
1676     PasteText(retrievedString);
1677
1678     SetCursorVisibility( true );
1679     StartCursorBlinkTimer();
1680
1681     ShowGrabHandleAndSetVisibility( false );
1682
1683     HidePopup();
1684   }
1685   else if(name == TextInputPopup::OPTION_CLIPBOARD)
1686   {
1687     // In the case of clipboard being shown we do not want to show updated pop-up after hide animation completes
1688     // Hence pass the false parameter for signalFinished.
1689     HidePopup( true, false );
1690     mClipboard.ShowClipboard();
1691   }
1692
1693   return false;
1694 }
1695
1696 bool TextInput::OnCursorBlinkTimerTick()
1697 {
1698   // Cursor blinking
1699   mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1700   if ( mCursorRTLEnabled )
1701   {
1702     mCursorRTL.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1703   }
1704   mCursorBlinkStatus = !mCursorBlinkStatus;
1705
1706   return true;
1707 }
1708
1709 void TextInput::OnPopupHideFinished(TextInputPopup& popup)
1710 {
1711   popup.HideFinishedSignal().Disconnect( this, &TextInput::OnPopupHideFinished );
1712
1713   // Change Popup menu to Cut/Copy/Paste if text has been selected.
1714   if(mHighlightMeshActor && mState == StateEdit)
1715   {
1716     ShowPopupCutCopyPaste();
1717   }
1718 }
1719
1720 //FIXME this routine needs to be re-written as it contains too many branches.
1721 bool TextInput::OnKeyDownEvent(const KeyEvent& event)
1722 {
1723   std::string keyName = event.keyPressedName;
1724   std::string keyString = event.keyPressed;
1725
1726   DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyDownEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1727
1728   // Do not consume "Tab" and "Escape" keys.
1729   if(keyName == "Tab" || keyName == "Escape")
1730   {
1731     // Escape key to end the edit mode
1732     EndEditMode();
1733
1734     return false;
1735   }
1736
1737   HidePopup(); // If Pop-up shown then hides it as editing text.
1738
1739   // Update Flag, indicates whether to update the text-input contents or not.
1740   // Any key stroke that results in a visual change of the text-input should
1741   // set this flag to true.
1742   bool update(false);
1743
1744   // Whether to scroll text to cursor position.
1745   // Scroll is needed always the cursor is updated and after the pre-edit is received.
1746   bool scroll = false;
1747
1748   if (keyName == "Return")
1749   {
1750     if ( mNumberOflinesLimit > 1) // Prevents New line character / Return adding an extra line if limit set to 1
1751     {
1752       bool preEditFlagPreviouslySet( mPreEditFlag );
1753
1754       if (mHighlightMeshActor)
1755       {
1756         // replaces highlighted text with new line
1757         DeleteHighlightedText( false );
1758       }
1759       mCursorPosition = mCursorPosition + InsertAt( Text( NEWLINE ), mCursorPosition, 0 );
1760
1761       // If we are in pre-edit mode then pressing enter will cause a commit.  But the commit string does not include the
1762       // '\n' character so we need to ensure that the immediately following commit knows how it occurred.
1763       if ( mPreEditFlag )
1764       {
1765         mCommitByKeyInput = true;
1766       }
1767
1768       // If attempting to insert a new-line brings us out of PreEdit mode, then we should not ignore the next commit.
1769       if ( preEditFlagPreviouslySet && !mPreEditFlag )
1770       {
1771         mPreEditFlag = true;
1772         mIgnoreCommitFlag = false;
1773       }
1774       EmitTextModified();
1775       update = true;
1776     }
1777     else
1778     {
1779       RemoveHighlight();
1780     }
1781   } // Return
1782   else if ( keyName == "space" )
1783   {
1784     if ( mHighlightMeshActor )
1785     {
1786       // Some text is selected so erase it before adding space.
1787       DeleteHighlightedText( true );
1788       update = true;
1789     }
1790
1791     mCursorPosition = mCursorPosition + InsertAt(Text(keyString), mCursorPosition, 0);
1792
1793     // If we are in pre-edit mode then pressing the space-bar will cause a commit.  But the commit string does not include the
1794     // ' ' character so we need to ensure that the immediately following commit knows how it occurred.
1795     if ( mPreEditFlag )
1796     {
1797       mCommitByKeyInput = true;
1798     }
1799     EmitTextModified();
1800     update = true;
1801   } // space
1802   else if (keyName == "BackSpace")
1803   {
1804     if ( mHighlightMeshActor )
1805     {
1806       // Some text is selected so erase it
1807       DeleteHighlightedText( true );
1808       update = true;
1809     }
1810     else
1811     {
1812       if ( mCursorPosition > 0 )
1813       {
1814         DeleteCharacter( mCursorPosition );
1815         update = true;
1816       }
1817     }
1818     EmitTextModified();
1819   } // BackSpace
1820   else if (keyName == "Right")
1821   {
1822     AdvanceCursor();
1823     RemoveHighlight();
1824   }
1825   else if (keyName == "Left")
1826   {
1827     AdvanceCursor(true);
1828     RemoveHighlight();
1829   }
1830   else // event is a character
1831   {
1832     // Some text may be selected, hiding keyboard causes an empty keystring to be sent, we don't want to delete highlight in this case
1833     if ( !keyString.empty() )
1834     {
1835       if ( mHighlightMeshActor )
1836       {
1837         // replaces highlighted text with new character
1838         DeleteHighlightedText( false );
1839       }
1840
1841
1842       // Received key String
1843       mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, 0 );
1844       update = true;
1845       EmitTextModified();
1846     }
1847   }
1848
1849   // If key event has resulted in a change in the text/cursor, then trigger a relayout of text
1850   // as this is a costly operation.
1851   if(update)
1852   {
1853     CursorUpdate();
1854   }
1855
1856   if(update || scroll)
1857   {
1858     if( IsScrollEnabled() )
1859     {
1860       // Calculates the new cursor position (in actor coordinates)
1861       const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
1862
1863       ScrollTextViewToMakeCursorVisible( cursorPosition );
1864     }
1865   }
1866
1867   return true;
1868 }
1869
1870 bool TextInput::OnKeyUpEvent(const KeyEvent& event)
1871 {
1872   std::string keyName = event.keyPressedName;
1873   std::string keyString = event.keyPressed;
1874
1875   DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyUpEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1876
1877   // The selected text become deselected when the key code is DALI_KEY_BACK.
1878   if( IsTextSelected() && ( keyName == "XF86Stop" || keyName == "XF86Send") )
1879   {
1880     DeSelectText();
1881     return true;
1882   }
1883
1884   return false;
1885 }
1886
1887 void TextInput::OnTextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition )
1888 {
1889   // Updates the stored scroll position.
1890   mTextLayoutInfo.mScrollOffset = textView.GetScrollPosition();
1891
1892   const Vector3& controlSize = GetControlSize();
1893   Size cursorSize( CURSOR_THICKNESS, 0.f );
1894
1895   // Updates the cursor and grab handle position and visibility.
1896   if( mGrabHandle || mCursor )
1897   {
1898     cursorSize.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height;
1899     const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1900
1901     mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( cursorPosition, cursorSize, controlSize );
1902
1903     mActualGrabHandlePosition = cursorPosition.GetVectorXY();
1904
1905     if( mGrabHandle )
1906     {
1907       ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1908       mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1909     }
1910
1911     if( mCursor )
1912     {
1913       mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
1914       mCursor.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1915     }
1916   }
1917
1918   // Updates the selection handles and highlighted text position and visibility.
1919   if( mSelectionHandleOne && mSelectionHandleTwo )
1920   {
1921     const Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition(mSelectionHandleOnePosition);
1922     const Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition(mSelectionHandleTwoPosition);
1923     cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleOnePosition ) ).mSize.height;
1924     const bool isSelectionHandleOneVisible = IsPositionInsideBoundaries( cursorPositionOne, cursorSize, controlSize );
1925     cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleTwoPosition ) ).mSize.height;
1926     const bool isSelectionHandleTwoVisible = IsPositionInsideBoundaries( cursorPositionTwo, cursorSize, controlSize );
1927
1928     mSelectionHandleOneActualPosition = cursorPositionOne.GetVectorXY();
1929     mSelectionHandleTwoActualPosition = cursorPositionTwo.GetVectorXY();
1930
1931     mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
1932     mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
1933     mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
1934     mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
1935
1936     if( mHighlightMeshActor )
1937     {
1938       mHighlightMeshActor.SetVisible( true );
1939       UpdateHighlight();
1940     }
1941   }
1942 }
1943
1944 void TextInput::ScrollTextViewToMakeCursorVisible( const Vector3& cursorPosition )
1945 {
1946   // Scroll the text to make the cursor visible.
1947   const Size cursorSize( CURSOR_THICKNESS,
1948                          GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height );
1949
1950   // Need to scroll the text to make the cursor visible and to cover the whole text-input area.
1951
1952   const Vector3& controlSize = GetControlSize();
1953
1954   // Calculates the new scroll position.
1955   Vector2 scrollOffset = mTextLayoutInfo.mScrollOffset;
1956   if( ( cursorPosition.x < 0.f ) || ( cursorPosition.x > controlSize.width ) )
1957   {
1958     scrollOffset.x += cursorPosition.x;
1959   }
1960
1961   if( cursorPosition.y - cursorSize.height < 0.f || cursorPosition.y > controlSize.height )
1962   {
1963     scrollOffset.y += cursorPosition.y;
1964   }
1965
1966   // Sets the new scroll position.
1967   SetScrollPosition( Vector2::ZERO ); // TODO: need to reset to the zero position in order to make the scroll trim to work.
1968   SetScrollPosition( scrollOffset );
1969 }
1970
1971 void TextInput::StartScrollTimer()
1972 {
1973   if( !mScrollTimer )
1974   {
1975     mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1976     mScrollTimer.TickSignal().Connect( this, &TextInput::OnScrollTimerTick );
1977   }
1978
1979   if( !mScrollTimer.IsRunning() )
1980   {
1981     mScrollTimer.Start();
1982   }
1983 }
1984
1985 void TextInput::StopScrollTimer()
1986 {
1987   if( mScrollTimer )
1988   {
1989     mScrollTimer.Stop();
1990   }
1991 }
1992
1993 bool TextInput::OnScrollTimerTick()
1994 {
1995   // TODO: need to set the new style accordingly the new handle position.
1996
1997   if( !( mGrabHandleVisibility && mGrabHandle ) && !( mSelectionHandleOne && mSelectionHandleTwo ) )
1998   {
1999     // nothing to do if all handles are invisible or doesn't exist.
2000     return true;
2001   }
2002
2003   // Text scrolling
2004
2005   // Choose between the grab handle or the selection handles.
2006   Vector3& actualHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mActualGrabHandlePosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
2007   std::size_t& handlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCursorPosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
2008   Vector3& currentHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCurrentHandlePosition : mCurrentSelectionHandlePosition;
2009
2010   std::size_t newCursorPosition = 0;
2011   ReturnClosestIndex( actualHandlePosition.GetVectorXY(), newCursorPosition );
2012
2013   // Whether the handle's position is different of the previous one and in the case of the selection handle,
2014   // the new selection handle's position needs to be different of the other one.
2015   const bool differentSelectionHandles = ( mGrabHandleVisibility && mGrabHandle ) ? newCursorPosition != handlePosition :
2016                                          ( mCurrentSelectionId == HandleOne ) ? ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleTwoPosition ) :
2017                                                                                 ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleOnePosition );
2018
2019   if( differentSelectionHandles )
2020   {
2021     handlePosition = newCursorPosition;
2022
2023     const Vector3 actualPosition = GetActualPositionFromCharacterPosition( newCursorPosition );
2024
2025     Vector2 scrollDelta = ( actualPosition - currentHandlePosition ).GetVectorXY();
2026
2027     Vector2 scrollPosition = mDisplayedTextView.GetScrollPosition();
2028     scrollPosition += scrollDelta;
2029     SetScrollPosition( scrollPosition );
2030
2031     if( mDisplayedTextView.IsScrollPositionTrimmed() )
2032     {
2033       StopScrollTimer();
2034     }
2035
2036     currentHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition ).GetVectorXY();
2037   }
2038
2039   actualHandlePosition.x += mScrollDisplacement.x;
2040   actualHandlePosition.y += mScrollDisplacement.y;
2041
2042   return true;
2043 }
2044
2045 // Public Internal Methods (public for testing purpose)
2046
2047 void TextInput::SetUpTouchEvents()
2048 {
2049   if ( !mTapDetector )
2050   {
2051     mTapDetector = TapGestureDetector::New();
2052     // Attach the actors and connect the signal
2053     mTapDetector.Attach(Self());
2054
2055     // As contains children which may register for tap the default control detector is not used.
2056     mTapDetector.DetectedSignal().Connect(this, &TextInput::OnTextTap);
2057   }
2058
2059   if ( !mDoubleTapDetector )
2060   {
2061     mDoubleTapDetector = TapGestureDetector::New();
2062     mDoubleTapDetector.SetTapsRequired( 2 );
2063     mDoubleTapDetector.DetectedSignal().Connect(this, &TextInput::OnDoubleTap);
2064
2065     // Only attach and detach the actor to the double tap detector when we enter/leave edit mode
2066     // so that we do not, unnecessarily, have a double tap request all the time
2067   }
2068
2069   if ( !mPanGestureDetector )
2070   {
2071     mPanGestureDetector = PanGestureDetector::New();
2072     mPanGestureDetector.DetectedSignal().Connect(this, &TextInput::OnHandlePan);
2073   }
2074
2075   if ( !mLongPressDetector )
2076   {
2077     mLongPressDetector = LongPressGestureDetector::New();
2078     mLongPressDetector.DetectedSignal().Connect(this, &TextInput::OnLongPress);
2079     mLongPressDetector.Attach(Self());
2080   }
2081 }
2082
2083 void TextInput::CreateTextViewActor()
2084 {
2085   mDisplayedTextView = Toolkit::TextView::New();
2086   mDisplayedTextView.SetName( "TextView ");
2087   mDisplayedTextView.SetMarkupProcessingEnabled( mMarkUpEnabled );
2088   mDisplayedTextView.SetParentOrigin(ParentOrigin::TOP_LEFT);
2089   mDisplayedTextView.SetAnchorPoint(AnchorPoint::TOP_LEFT);
2090   mDisplayedTextView.SetMultilinePolicy(Toolkit::TextView::SplitByWord);
2091   mDisplayedTextView.SetWidthExceedPolicy( Toolkit::TextView::Original );
2092   mDisplayedTextView.SetHeightExceedPolicy( Toolkit::TextView::Original );
2093   mDisplayedTextView.SetLineJustification( Toolkit::TextView::Left );
2094   mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>( Toolkit::Alignment::HorizontalLeft | Toolkit::Alignment::VerticalTop ) );
2095   mDisplayedTextView.SetPosition( Vector3( 0.0f, 0.0f, DISPLAYED_TEXT_VIEW_Z_OFFSET ) );
2096   mDisplayedTextView.SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
2097
2098   mDisplayedTextView.ScrolledSignal().Connect( this, &TextInput::OnTextViewScrolled );
2099
2100   Self().Add( mDisplayedTextView );
2101 }
2102
2103 // Start a timer to initiate, used by the cursor to blink.
2104 void TextInput::StartCursorBlinkTimer()
2105 {
2106   if ( !mCursorBlinkTimer )
2107   {
2108     mCursorBlinkTimer = Timer::New( CURSOR_BLINK_INTERVAL );
2109     mCursorBlinkTimer.TickSignal().Connect( this, &TextInput::OnCursorBlinkTimerTick );
2110   }
2111
2112   if ( !mCursorBlinkTimer.IsRunning() )
2113   {
2114     mCursorBlinkTimer.Start();
2115   }
2116 }
2117
2118 // Start a timer to initiate, used by the cursor to blink.
2119 void TextInput::StopCursorBlinkTimer()
2120 {
2121   if ( mCursorBlinkTimer )
2122   {
2123     mCursorBlinkTimer.Stop();
2124   }
2125 }
2126
2127 void TextInput::StartEditMode()
2128 {
2129   DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput StartEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2130
2131   if(!mEditModeActive)
2132   {
2133     SetKeyInputFocus();
2134   }
2135
2136   if ( mDoubleTapDetector )
2137   {
2138     mDoubleTapDetector.Attach( Self() );
2139   }
2140 }
2141
2142 void TextInput::EndEditMode()
2143 {
2144   DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput EndEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2145
2146   ClearKeyInputFocus();
2147
2148   if ( mDoubleTapDetector )
2149   {
2150     mDoubleTapDetector.Detach( Self() );
2151   }
2152 }
2153
2154 void TextInput::ApplyPreEditStyle( std::size_t preEditStartPosition, std::size_t preEditStringLength )
2155 {
2156   if ( mPreEditFlag && ( preEditStringLength > 0 ) )
2157   {
2158     mUnderlinedPriorToPreEdit = mInputStyle.IsUnderlineEnabled();
2159     TextStyle style;
2160     style.SetUnderline( true );
2161     ApplyStyleToRange( style, TextStyle::UNDERLINE , preEditStartPosition, preEditStartPosition + preEditStringLength -1 );
2162   }
2163 }
2164
2165 void TextInput::RemovePreEditStyle()
2166 {
2167   if ( !mUnderlinedPriorToPreEdit )
2168   {
2169     TextStyle style;
2170     style.SetUnderline( false );
2171     SetActiveStyle( style, TextStyle::UNDERLINE );
2172   }
2173 }
2174
2175 // IMF related methods
2176
2177
2178 ImfManager::ImfCallbackData TextInput::ImfEventReceived( Dali::ImfManager& imfManager, const ImfManager::ImfEventData&  imfEvent )
2179 {
2180   bool update( false );
2181   bool preeditResetRequired ( false );
2182
2183   if (imfEvent.eventName != ImfManager::GETSURROUNDING )
2184   {
2185     HidePopup(); // If Pop-up shown then hides it as editing text.
2186   }
2187
2188   switch ( imfEvent.eventName )
2189   {
2190     case ImfManager::PREEDIT:
2191     {
2192       mIgnoreFirstCommitFlag = false;
2193
2194       // 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
2195       if ( mHighlightMeshActor &&  (!imfEvent.predictiveString.empty()) )
2196       {
2197         // replaces highlighted text with new character
2198         DeleteHighlightedText( false );
2199       }
2200
2201       preeditResetRequired = PreEditReceived( imfEvent.predictiveString, imfEvent.cursorOffset );
2202
2203       if( IsScrollEnabled() )
2204       {
2205         // Calculates the new cursor position (in actor coordinates)
2206         const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2207         ScrollTextViewToMakeCursorVisible( cursorPosition );
2208       }
2209
2210       update = true;
2211
2212       break;
2213     }
2214     case ImfManager::COMMIT:
2215     {
2216       if( mIgnoreFirstCommitFlag )
2217       {
2218         // Do not commit in this case when keyboard sends a commit when shows for the first time (work-around for imf keyboard).
2219         mIgnoreFirstCommitFlag = false;
2220       }
2221       else
2222       {
2223         // A Commit message is a word that has been accepted, it may have been a pre-edit word previously but now commited.
2224
2225         // 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
2226         if ( mHighlightMeshActor &&  (!imfEvent.predictiveString.empty()) )
2227         {
2228           // replaces highlighted text with new character
2229           DeleteHighlightedText( false );
2230         }
2231
2232        // A PreEditReset can cause a commit message to be sent, the Ignore Commit flag is used in scenarios where the word is
2233        // not needed, one such scenario is when the pre-edit word is too long to fit.
2234        if ( !mIgnoreCommitFlag )
2235        {
2236          update = CommitReceived( imfEvent.predictiveString );
2237        }
2238        else
2239        {
2240          mIgnoreCommitFlag = false; // reset ignore flag so next commit is acted upon.
2241        }
2242       }
2243
2244       if( update )
2245       {
2246         if( IsScrollEnabled() )
2247         {
2248           // Calculates the new cursor position (in actor coordinates)
2249           const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2250
2251           ScrollTextViewToMakeCursorVisible( cursorPosition );
2252         }
2253       }
2254       break;
2255     }
2256     case ImfManager::DELETESURROUNDING:
2257     {
2258       DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - delete surrounding mPreEditFlag[%s] cursor offset[%d] characters to delete[%d] position to delete[%u] \n",
2259                      (mPreEditFlag)?"true":"false", imfEvent.cursorOffset, imfEvent.numberOfChars, static_cast<std::size_t>( mCursorPosition+imfEvent.cursorOffset) );
2260
2261       mPreEditFlag = false;
2262
2263       std::size_t toDelete = 0;
2264       std::size_t numberOfCharacters = 0;
2265
2266       if( mHighlightMeshActor )
2267       {
2268         // delete highlighted text.
2269         toDelete = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2270         numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - toDelete;
2271       }
2272       else
2273       {
2274         if( static_cast<std::size_t>(std::abs( imfEvent.cursorOffset )) < mCursorPosition )
2275         {
2276           toDelete = mCursorPosition + imfEvent.cursorOffset;
2277         }
2278         if( toDelete + imfEvent.numberOfChars > mStyledText.size() )
2279         {
2280           numberOfCharacters = mStyledText.size() - toDelete;
2281         }
2282         else
2283         {
2284           numberOfCharacters = imfEvent.numberOfChars;
2285         }
2286       }
2287       DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding pre-delete range mCursorPosition[%u] \n", mCursorPosition);
2288       DeleteRange( toDelete, numberOfCharacters );
2289
2290       mCursorPosition = toDelete;
2291       mNumberOfSurroundingCharactersDeleted = numberOfCharacters;
2292
2293       EmitTextModified();
2294
2295       DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding post-delete range mCursorPosition[%u] \n", mCursorPosition);
2296       break;
2297     }
2298     case ImfManager::GETSURROUNDING:
2299     {
2300       // 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
2301       // the next key pressed.  Instead the Select function sets the cursor position and surrounding text.
2302       if (! ( mHighlightMeshActor || mSelectingText ) )
2303       {
2304         std::string text( GetText() );
2305         DALI_LOG_INFO( gLogFilter, Debug::General, "OnKey - surrounding text - set text [%s] and cursor[%u] \n", text.c_str(), mCursorPosition );
2306
2307         imfManager.SetCursorPosition( mCursorPosition );
2308         imfManager.SetSurroundingText( text );
2309       }
2310
2311       if( 0 != mNumberOfSurroundingCharactersDeleted )
2312       {
2313         mDisplayedTextView.RemoveTextFrom( mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2314         mNumberOfSurroundingCharactersDeleted = 0;
2315
2316         if( mStyledText.empty() )
2317         {
2318           // Styled text is empty, so set the placeholder text.
2319           mDisplayedTextView.SetText( mStyledPlaceHolderText );
2320           mPlaceHolderSet = true;
2321         }
2322       }
2323       break;
2324     }
2325     case ImfManager::VOID:
2326     {
2327       DALI_ASSERT_DEBUG( false );
2328     }
2329   } // end switch
2330
2331   ImfManager::ImfCallbackData callbackData( update, mCursorPosition, GetText(), preeditResetRequired );
2332
2333   return callbackData;
2334 }
2335
2336 bool TextInput::PreEditReceived(const std::string& keyString, std::size_t cursorOffset )
2337 {
2338   mPreserveCursorPosition = false;  // As in pre-edit state we should have the cursor at the end of the word displayed not last touch position.
2339
2340   DALI_LOG_INFO(gLogFilter, Debug::General, ">>PreEditReceived preserveCursorPos[%d] mCursorPos[%d] mPreEditFlag[%d]\n",
2341                 mPreserveCursorPosition, mCursorPosition, mPreEditFlag );
2342
2343   bool preeditResetRequest ( false );
2344
2345   if( mPreEditFlag ) // Already in pre-edit state.
2346   {
2347     if( mStyledText.size() >= mMaxStringLength )
2348     {
2349       DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived styledTextSize >= mMaxStringLength \n");
2350       // Cannot fit these characters into field, clear pre-edit.
2351       if ( !mUnderlinedPriorToPreEdit )
2352       {
2353         TextStyle style;
2354         style.SetUnderline( false );
2355         ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
2356       }
2357       mIgnoreCommitFlag = true;
2358       preeditResetRequest = false; // this will reset the keyboard's predictive suggestions.
2359       mPreEditFlag = false;
2360       EmitMaxInputCharactersReachedSignal();
2361     }
2362     else
2363     {
2364       // delete existing pre-edit string
2365       const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2366
2367       // Store new pre-edit string
2368       mPreEditString.SetText( keyString );
2369
2370       if ( keyString.empty() )
2371       {
2372         mPreEditFlag = false;
2373         mCursorPosition = mPreEditStartPosition;
2374
2375         if( mStyledText.empty() )
2376         {
2377           // Styled text is empty, so set the placeholder text.
2378           mDisplayedTextView.SetText( mStyledPlaceHolderText );
2379           mPlaceHolderSet = true;
2380         }
2381         else
2382         {
2383           mDisplayedTextView.RemoveTextFrom( mPreEditStartPosition, numberOfCharactersToReplace );
2384         }
2385         GetTextLayoutInfo();
2386         EmitTextModified();
2387       }
2388       else
2389       {
2390         // Insert new pre-edit string. InsertAt updates the size and position table.
2391         mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersToReplace );
2392         // 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.
2393         mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2394         ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2395         DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] \n", mCursorPosition);
2396         EmitTextModified();
2397       }
2398       // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2399       DrawCursor();
2400     }
2401   }
2402   else  // mPreEditFlag not set
2403   {
2404     if ( !keyString.empty() ) // Imf can send an empty pre-edit followed by Backspace instead of a commit.
2405     {
2406       DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived Initial Pre-Edit string \n");
2407       // new pre-edit so move into pre-edit state by setting flag
2408       mPreEditFlag = true;
2409       mPreEditString.SetText( keyString ); // store new pre-edit string
2410       mPreEditStartPosition = mCursorPosition; // store starting cursor position of pre-edit so know where to re-start from
2411       mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, 0 );
2412       // 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.
2413       mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2414       ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2415       DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] mPreEditStartPosition[%u]\n", mCursorPosition, mPreEditStartPosition);
2416       // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2417       DrawCursor();
2418       EmitTextModified();
2419     }
2420     else
2421     {
2422       DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived with empty keyString\n");
2423     }
2424   }
2425
2426   return preeditResetRequest;
2427 }
2428
2429 bool TextInput::CommitReceived(const std::string& keyString )
2430 {
2431   DALI_LOG_INFO(gLogFilter, Debug::General, ">>CommitReceived preserveCursorPos[%d] mPreEditStartPosition [%d] mCursorPos[%d] mPreEditFlag[%d] mIgnoreCommitFlag[%s]\n",
2432       mPreserveCursorPosition, mPreEditStartPosition, mCursorPosition, mPreEditFlag, (mIgnoreCommitFlag)?"true":"false" );
2433
2434   bool update( false );
2435
2436   RemovePreEditStyle();
2437
2438   const std::size_t styledTextSize( mStyledText.size() );
2439   if( styledTextSize >= mMaxStringLength )
2440   {
2441     // Cannot fit these characters into field, clear pre-edit.
2442     if ( mPreEditFlag )
2443     {
2444       mIgnoreCommitFlag = true;
2445       mPreEditFlag = false;
2446     }
2447     EmitMaxInputCharactersReachedSignal();
2448   }
2449   else
2450   {
2451     if( mPreEditFlag )
2452     {
2453       // delete existing pre-edit string
2454       const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2455       mPreEditFlag = false;
2456
2457       DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived mPreserveCursorPosition[%s] mPreEditStartPosition[%u]\n",
2458                     (mPreserveCursorPosition)?"true":"false", mPreEditStartPosition );
2459
2460       if ( mPreserveCursorPosition ) // PreEditReset has been called triggering this commit.
2461       {
2462         // No need to update cursor position as Cursor location given by touch.
2463         InsertAt( Text( keyString ), mPreEditStartPosition, numberOfCharactersToReplace );
2464         mPreserveCursorPosition = false;
2465       }
2466       else
2467       {
2468         // Cursor not set by touch so needs to be re-positioned to input more text
2469         mCursorPosition = mPreEditStartPosition + InsertAt( Text(keyString), mPreEditStartPosition, numberOfCharactersToReplace ); // update cursor position as InsertAt, re-draw cursor with this
2470
2471         // If a space or enter caused the commit then our string is one longer than the string given to us by the commit key.
2472         if ( mCommitByKeyInput )
2473         {
2474           mCursorPosition = std::min ( mCursorPosition + 1, mStyledText.size() );
2475           mCommitByKeyInput = false;
2476         }
2477       }
2478
2479       EmitTextModified();
2480
2481       if ( mSelectTextOnCommit )
2482       {
2483         SelectText(mRequestedSelection.mStartOfSelection, mRequestedSelection.mEndOfSelection );
2484       }
2485
2486       update = true;
2487     }
2488     else // mPreEditFlag not set
2489     {
2490       if ( !mIgnoreCommitFlag ) // Check if this commit should be ignored.
2491       {
2492         if( mStyledText.empty() && mPlaceHolderSet )
2493         {
2494           // If the styled text is empty and the placeholder text is set, it needs to be cleared.
2495           mDisplayedTextView.SetText( "" );
2496           mNumberOfSurroundingCharactersDeleted = 0;
2497           mPlaceHolderSet = false;
2498         }
2499         mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2500         update = true;
2501         mNumberOfSurroundingCharactersDeleted = 0;
2502         EmitTextModified();
2503       }
2504       else
2505       {
2506         mIgnoreCommitFlag = false; // Reset flag so future commits will not be ignored.
2507       }
2508     }
2509   }
2510
2511   mSelectTextOnCommit = false;
2512
2513   DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived << mCursorPos[%d] mPreEditFlag[%d] update[%s] \n",
2514                                              mCursorPosition, mPreEditFlag, (update)?"true":"false" );
2515
2516   return update;
2517 }
2518
2519 // End of IMF related methods
2520
2521 std::size_t TextInput::DeletePreEdit()
2522 {
2523   DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeletePreEdit mPreEditFlag[%s] \n", (mPreEditFlag)?"true":"false");
2524
2525   DALI_ASSERT_DEBUG( mPreEditFlag );
2526
2527   const std::size_t preEditStringLength = mPreEditString.GetLength();
2528   const std::size_t styledTextSize = mStyledText.size();
2529
2530   std::size_t endPosition = mPreEditStartPosition + preEditStringLength;
2531
2532   // Prevents erase items outside mStyledText bounds.
2533   if( mPreEditStartPosition > styledTextSize )
2534   {
2535     DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. mPreEditStartPosition > mStyledText.size()" );
2536     mPreEditStartPosition = styledTextSize;
2537   }
2538
2539   if( ( endPosition > styledTextSize ) || ( endPosition < mPreEditStartPosition ) )
2540   {
2541     DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. ( endPosition > mStyledText.size() ) || ( endPosition < mPreEditStartPosition )" );
2542     endPosition = styledTextSize;
2543   }
2544
2545   mStyledText.erase( mStyledText.begin() + mPreEditStartPosition, mStyledText.begin() + endPosition );
2546
2547   // DeletePreEdit() doesn't remove characters from the text-view because may be followed by an InsertAt() which inserts characters,
2548   // in that case, the Insert should use the returned number of deleted characters and replace the text which helps the text-view to
2549   // reuse glyphs.
2550   // In case DeletePreEdit() is not followed by an InsertAt() characters must be deleted after this call.
2551
2552   return preEditStringLength;
2553 }
2554
2555 void TextInput::PreEditReset( bool preserveCursorPosition )
2556 {
2557   DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReset preserveCursorPos[%d] mCursorPos[%d] \n",
2558                 preserveCursorPosition, mCursorPosition);
2559
2560   // 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.
2561   mPreserveCursorPosition = preserveCursorPosition;
2562
2563   // Reset incase we are in a pre-edit state.
2564   ImfManager imfManager = ImfManager::Get();
2565   if ( imfManager )
2566   {
2567     imfManager.Reset(); // Will trigger a commit message
2568   }
2569 }
2570
2571 void TextInput::CursorUpdate()
2572 {
2573   DrawCursor();
2574
2575   ImfManager imfManager = ImfManager::Get();
2576   if ( imfManager )
2577   {
2578     std::string text( GetText() );
2579     imfManager.SetSurroundingText( text );  // Notifying IMF of a cursor change triggers a surrounding text request so updating it now.
2580     imfManager.SetCursorPosition ( mCursorPosition );
2581     imfManager.NotifyCursorPosition();
2582   }
2583 }
2584
2585 /* Delete highlighted characters redisplay*/
2586 void TextInput::DeleteHighlightedText( bool inheritStyle )
2587 {
2588   DALI_LOG_INFO( gLogFilter, Debug::General, "DeleteHighlightedText handlePosOne[%u] handlePosTwo[%u]\n", mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
2589
2590   if(mHighlightMeshActor)
2591   {
2592     mCursorPosition = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2593
2594     MarkupProcessor::StyledTextArray::iterator start = mStyledText.begin() + mCursorPosition;
2595     MarkupProcessor::StyledTextArray::iterator end =  mStyledText.begin() + std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2596
2597     // Get the styled text of the characters to be deleted as it may be needed if
2598     // the "exceed the text-input's boundaries" option is disabled.
2599     MarkupProcessor::StyledTextArray styledCharactersToDelete;
2600
2601     styledCharactersToDelete.insert( styledCharactersToDelete.begin(), start, end );
2602
2603     mStyledText.erase( start, end ); // erase range of characters
2604
2605     // Remove text from TextView.
2606
2607     if( mStyledText.empty() )
2608     {
2609       // Styled text is empty, so set the placeholder text.
2610       mDisplayedTextView.SetText( mStyledPlaceHolderText );
2611       mPlaceHolderSet = true;
2612     }
2613     else
2614     {
2615       const std::size_t numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - mCursorPosition;
2616
2617       mDisplayedTextView.RemoveTextFrom( mCursorPosition, numberOfCharacters );
2618
2619       // It may happen than after removing a white space or a new line character,
2620       // two words merge, this new word could be big enough to not fit in its
2621       // current line, so moved to the next one, and make some part of the text to
2622       // exceed the text-input's boundary.
2623       if( !mExceedEnabled )
2624       {
2625         // Get the new text layout after removing some characters.
2626         mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2627
2628         // Get text-input's size.
2629         const Vector3& size = GetControlSize();
2630
2631         if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2632             ( mTextLayoutInfo.mTextSize.height > size.height ) )
2633         {
2634           mDisplayedTextView.InsertTextAt( mCursorPosition, styledCharactersToDelete );
2635
2636           mStyledText.insert( mStyledText.begin() + mCursorPosition,
2637                               styledCharactersToDelete.begin(),
2638                               styledCharactersToDelete.end() );
2639         }
2640       }
2641     }
2642     GetTextLayoutInfo();
2643
2644     RemoveHighlight();
2645
2646     EmitTextModified();
2647
2648     if( inheritStyle )
2649     {
2650       const TextStyle oldInputStyle( mInputStyle );
2651
2652       mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2653
2654       if( oldInputStyle != mInputStyle )
2655       {
2656         // Updates the line height accordingly with the input style.
2657         UpdateLineHeight();
2658
2659         EmitStyleChangedSignal();
2660       }
2661     }
2662   }
2663 }
2664
2665 void TextInput::DeleteRange( const std::size_t start, const std::size_t ncharacters )
2666 {
2667   DALI_ASSERT_DEBUG( start <= mStyledText.size() );
2668   DALI_ASSERT_DEBUG( !mStyledText.empty() );
2669
2670   DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeleteRange pre mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2671
2672
2673   if ( ( !mStyledText.empty()) && ( ( start + ncharacters ) <= mStyledText.size() ) )
2674   {
2675     MarkupProcessor::StyledTextArray::iterator itStart =  mStyledText.begin() + start;
2676     MarkupProcessor::StyledTextArray::iterator itEnd =  mStyledText.begin() + start + ncharacters;
2677
2678     mStyledText.erase(itStart, itEnd);
2679
2680     // update the selection handles if they are visible.
2681     if( mHighlightMeshActor )
2682     {
2683       std::size_t& minHandle = ( mSelectionHandleOnePosition <= mSelectionHandleTwoPosition ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition );
2684       std::size_t& maxHandle = ( mSelectionHandleTwoPosition > mSelectionHandleOnePosition ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition );
2685
2686       if( minHandle >= start + ncharacters )
2687       {
2688         minHandle -= ncharacters;
2689       }
2690       else if( ( minHandle > start ) && ( minHandle < start + ncharacters ) )
2691       {
2692         minHandle = start;
2693       }
2694
2695       if( maxHandle >= start + ncharacters )
2696       {
2697         maxHandle -= ncharacters;
2698       }
2699       else if( ( maxHandle > start ) && ( maxHandle < start + ncharacters ) )
2700       {
2701         maxHandle = start;
2702       }
2703     }
2704
2705     // 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.
2706   }
2707
2708   DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteRange<< post mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2709
2710   // Although mStyledText has been set to a new text string we no longer re-draw the text or notify the cursor change.
2711   // This is a performance decision as the use of this function often means the text is being replaced or just deleted.
2712   // Mean we do not re-draw the text more than we have too.
2713 }
2714
2715 /* Delete character at current cursor position and redisplay*/
2716 void TextInput::DeleteCharacter( std::size_t positionToDelete )
2717 {
2718   // Ensure positionToDelete is not out of bounds.
2719   DALI_ASSERT_DEBUG( positionToDelete <= mStyledText.size() );
2720   DALI_ASSERT_DEBUG( !mStyledText.empty() );
2721   DALI_ASSERT_DEBUG( positionToDelete > 0 );
2722
2723   DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteCharacter positionToDelete[%u]", positionToDelete );
2724
2725
2726   if ( ( !mStyledText.empty()) && ( positionToDelete > 0 ) && positionToDelete <= mStyledText.size() )  // don't try to delete if no characters left of cursor
2727   {
2728     MarkupProcessor::StyledTextArray::iterator it =  mStyledText.begin() + positionToDelete - 1;
2729
2730     // Get the styled text of the character to be deleted as it may be needed if
2731     // the "exceed the text-input's boundaries" option is disabled.
2732     const MarkupProcessor::StyledText styledCharacterToDelete( *it );
2733
2734     mStyledText.erase(it);  // erase the character left of positionToDelete
2735
2736     if( mStyledText.empty() )
2737     {
2738       // Styled text is empty, so set the placeholder text.
2739       mDisplayedTextView.SetText( mStyledPlaceHolderText );
2740       mPlaceHolderSet = true;
2741     }
2742     else
2743     {
2744       mDisplayedTextView.RemoveTextFrom( positionToDelete - 1, 1 );
2745
2746       const Character characterToDelete = styledCharacterToDelete.mText[0];
2747
2748       // It may happen than after removing a white space or a new line character,
2749       // two words merge, this new word could be big enough to not fit in its
2750       // current line, so moved to the next one, and make some part of the text to
2751       // exceed the text-input's boundary.
2752       if( !mExceedEnabled )
2753       {
2754         if( characterToDelete.IsWhiteSpace() || characterToDelete.IsNewLine() )
2755         {
2756           // Get the new text layout after removing one character.
2757           mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2758
2759           // Get text-input's size.
2760           const Vector3& size = GetControlSize();
2761
2762           if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2763               ( mTextLayoutInfo.mTextSize.height > size.height ) )
2764           {
2765             MarkupProcessor::StyledTextArray array;
2766             array.push_back( styledCharacterToDelete );
2767             mDisplayedTextView.InsertTextAt( positionToDelete - 1, array );
2768
2769             mStyledText.insert( mStyledText.begin() + ( positionToDelete - 1 ), styledCharacterToDelete );
2770           }
2771         }
2772       }
2773     }
2774     GetTextLayoutInfo();
2775
2776     ShowGrabHandleAndSetVisibility( false );
2777
2778     mCursorPosition = positionToDelete -1;
2779
2780     const TextStyle oldInputStyle( mInputStyle );
2781
2782     mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2783
2784     if( oldInputStyle != mInputStyle )
2785     {
2786       // Updates the line height accordingly with the input style.
2787       UpdateLineHeight();
2788
2789       EmitStyleChangedSignal();
2790     }
2791   }
2792 }
2793
2794 /*Insert new character into the string and (optionally) redisplay text-input*/
2795 std::size_t TextInput::InsertAt( const Text& newText, const std::size_t insertionPosition, const std::size_t numberOfCharactersToReplace )
2796 {
2797   DALI_LOG_INFO(gLogFilter, Debug::General, "InsertAt insertionPosition[%u]\n", insertionPosition );
2798
2799   // Ensure insertionPosition is not out of bounds.
2800   DALI_ASSERT_ALWAYS( insertionPosition <= mStyledText.size() );
2801
2802   bool textExceedsMaximunNumberOfCharacters = false;
2803   bool textExceedsBoundary = false;
2804   std::size_t insertedStringLength = DoInsertAt( newText, insertionPosition, numberOfCharactersToReplace, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
2805
2806   ShowGrabHandleAndSetVisibility( false );
2807
2808   if( textExceedsMaximunNumberOfCharacters || textExceedsBoundary )
2809   {
2810     if( mPreEditFlag )
2811     {
2812       mIgnoreCommitFlag = true;
2813       mPreEditFlag = false;
2814       // A PreEditReset( false ) should be triggered from here if the keyboards predictive suggestions must be cleared.
2815       // Although can not directly call PreEditReset() as it will cause a recursive emit loop.
2816     }
2817
2818     if( textExceedsMaximunNumberOfCharacters )
2819     {
2820       EmitMaxInputCharactersReachedSignal();
2821     }
2822
2823     if( textExceedsBoundary )
2824     {
2825       EmitInputTextExceedsBoundariesSignal();
2826       PreEditReset( false );
2827     }
2828   }
2829
2830   return insertedStringLength;
2831 }
2832
2833 ImageActor TextInput::CreateCursor( const Vector4& color)
2834 {
2835   ImageActor cursor;
2836   cursor = CreateSolidColorActor(color);
2837
2838   cursor.SetParentOrigin(ParentOrigin::TOP_LEFT);
2839   cursor.SetAnchorPoint(AnchorPoint::BOTTOM_CENTER);
2840   cursor.SetVisible(false);
2841
2842   return cursor;
2843 }
2844
2845 void TextInput::AdvanceCursor(bool reverse, std::size_t places)
2846 {
2847   // As cursor is not moving due to grab handle, handle should be hidden.
2848   ShowGrabHandleAndSetVisibility( false );
2849
2850   bool cursorPositionChanged = false;
2851   if (reverse)
2852   {
2853     if ( mCursorPosition >= places )
2854     {
2855       mCursorPosition = mCursorPosition - places;
2856       cursorPositionChanged = true;
2857     }
2858   }
2859   else
2860   {
2861     if ((mCursorPosition + places) <= mStyledText.size())
2862     {
2863       mCursorPosition = mCursorPosition + places;
2864       cursorPositionChanged = true;
2865     }
2866   }
2867
2868   if( cursorPositionChanged )
2869   {
2870     const std::size_t cursorPositionForStyle = ( 0 == mCursorPosition ? 0 : mCursorPosition - 1 );
2871
2872     const TextStyle oldInputStyle( mInputStyle );
2873     mInputStyle = GetStyleAt( cursorPositionForStyle ); // Inherit style from selected position.
2874
2875     DrawCursor();
2876
2877     if( oldInputStyle != mInputStyle )
2878     {
2879       // Updates the line height accordingly with the input style.
2880       UpdateLineHeight();
2881
2882       EmitStyleChangedSignal();
2883     }
2884
2885     ImfManager imfManager = ImfManager::Get();
2886     if ( imfManager )
2887     {
2888       imfManager.SetCursorPosition ( mCursorPosition );
2889       imfManager.NotifyCursorPosition();
2890     }
2891   }
2892 }
2893
2894 void TextInput::DrawCursor(const std::size_t nthChar)
2895 {
2896   // Get height of cursor and set its size
2897   Size size( CURSOR_THICKNESS, 0.0f );
2898   if (!mTextLayoutInfo.mCharacterLayoutInfoTable.empty())
2899   {
2900     size.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height;
2901   }
2902   else
2903   {
2904     // Measure Font so know how big text will be if no initial text to measure.
2905     size.height = mLineHeight;
2906   }
2907
2908   mCursor.SetSize(size);
2909
2910   // If the character is italic then the cursor also tilts.
2911   mCursor.SetRotation( mInputStyle.IsItalicsEnabled() ? Degree( mInputStyle.GetItalicsAngle() - CURSOR_ANGLE_OFFSET ) : Degree( 0.f ), Vector3::ZAXIS );
2912
2913   DALI_ASSERT_DEBUG( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
2914
2915   if ( ( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) )
2916   {
2917     Vector3 altPosition;    // Alternate (i.e. opposite direction) cursor position.
2918     bool altPositionValid;  // Alternate cursor validity flag.
2919     bool directionRTL;      // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
2920     Vector3 position = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
2921
2922     SetAltCursorEnabled( altPositionValid );
2923
2924     if(!altPositionValid)
2925     {
2926       mCursor.SetPosition( position + UI_OFFSET );
2927     }
2928     else
2929     {
2930       size.height *= 0.5f;
2931       mCursor.SetSize(size);
2932       mCursor.SetPosition( position + UI_OFFSET - Vector3(0.0f, directionRTL ? 0.0f : size.height, 0.0f) );
2933
2934       // TODO: change this cursor pos, to be the one where the cursor is sourced from.
2935       Size rowSize = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) );
2936       size.height = rowSize.height * 0.5f;
2937       mCursorRTL.SetSize(size);
2938       mCursorRTL.SetPosition( altPosition + UI_OFFSET - Vector3(0.0f, directionRTL ? size.height : 0.0f, 0.0f) );
2939     }
2940
2941     if( IsScrollEnabled() )
2942     {
2943       // Whether cursor and grab handle are inside the boundaries of the text-input when text scroll is enabled.
2944       mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( position, size, GetControlSize() );
2945     }
2946   } // EditMode
2947 }
2948
2949 void TextInput::SetAltCursorEnabled( bool enabled )
2950 {
2951   mCursorRTLEnabled = enabled;
2952   mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2953 }
2954
2955 void TextInput::SetCursorVisibility( bool visible )
2956 {
2957   mCursorVisibility = visible;
2958   mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
2959   mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2960 }
2961
2962 void TextInput::CreateGrabHandle( Dali::Image image )
2963 {
2964   if ( !mGrabHandle )
2965   {
2966     if ( !image )
2967     {
2968       mGrabHandleImage = Image::New(DEFAULT_GRAB_HANDLE);
2969     }
2970     else
2971     {
2972       mGrabHandleImage = image;
2973     }
2974
2975     mGrabHandle = ImageActor::New(mGrabHandleImage);
2976     mGrabHandle.SetParentOrigin(ParentOrigin::TOP_LEFT);
2977     mGrabHandle.SetAnchorPoint(AnchorPoint::TOP_CENTER);
2978
2979     mGrabHandle.SetDrawMode(DrawMode::OVERLAY);
2980
2981     ShowGrabHandleAndSetVisibility( false );
2982
2983     CreateGrabArea( mGrabHandle );
2984
2985     mActiveLayer.Add(mGrabHandle);
2986   }
2987 }
2988
2989 void TextInput::CreateGrabArea( Actor& parent )
2990 {
2991   mGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
2992   mGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
2993   mGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE ) ) );  // grab area to be larger than text actor
2994   mGrabArea.TouchedSignal().Connect(this,&TextInput::OnPressDown);
2995   mTapDetector.Attach( mGrabArea );
2996   mPanGestureDetector.Attach( mGrabArea );
2997
2998   parent.Add(mGrabArea);
2999 }
3000
3001 Vector3 TextInput::MoveGrabHandle( const Vector2& displacement )
3002 {
3003   Vector3 actualHandlePosition;
3004
3005   if (mGrabHandle)
3006   {
3007     mActualGrabHandlePosition.x += displacement.x;
3008     mActualGrabHandlePosition.y += displacement.y;
3009
3010     // Grab handle should jump to the nearest character and take cursor with it
3011     std::size_t newCursorPosition = 0;
3012     ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY(), newCursorPosition );
3013
3014     actualHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition );
3015
3016     bool handleVisible = true;
3017
3018     if( IsScrollEnabled() )
3019     {
3020       const Vector3 controlSize = GetControlSize();
3021       const Size cursorSize = GetRowRectFromCharacterPosition( GetVisualPosition( newCursorPosition ) );
3022       // Scrolls the text if the handle is not in a visible position
3023       handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3024                                                   cursorSize,
3025                                                   controlSize );
3026
3027       if( handleVisible )
3028       {
3029         StopScrollTimer();
3030         mCurrentHandlePosition = actualHandlePosition;
3031         mScrollDisplacement = Vector2::ZERO;
3032       }
3033       else
3034       {
3035         if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3036         {
3037           mScrollDisplacement.x = -SCROLL_SPEED;
3038         }
3039         else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )