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