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