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