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