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