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