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