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