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