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