8b962fc8e5e95a7c5757a5589e5ca6ec7188adfb
[platform/core/uifw/dali-toolkit.git] / base / dali-toolkit / internal / controls / text-input / text-input-impl.cpp
1 /*
2  * Copyright (c) 2014 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 #include <dali/dali.h>
19
20 #include <dali-toolkit/internal/controls/text-input/text-input-impl.h>
21 #include <dali-toolkit/internal/controls/text-view/text-processor.h>
22 #include <dali-toolkit/public-api/controls/buttons/push-button.h>
23 #include <dali-toolkit/public-api/controls/alignment/alignment.h>
24
25 #include <dali/integration-api/debug.h>
26
27 #include <math.h>
28 #include <sstream>
29 #include <algorithm>
30
31 using namespace std;
32 using namespace Dali;
33
34 // Local Data
35 namespace
36 {
37
38 #if defined(DEBUG_ENABLED)
39 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_TEXT_INPUT");
40 #endif
41
42 const std::size_t DEFAULT_MAX_SIZE( std::numeric_limits<std::size_t>::max() ); // Max possible number
43 const std::size_t DEFAULT_NUMBER_OF_LINES_LIMIT( std::numeric_limits<std::size_t>::max() ); // Max possible number
44 const Vector3 DEFAULT_SELECTION_HANDLE_SIZE( 51.0f, 79.0f, 0.0f );  // Selection cursor image size
45 const Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.5f, 2.0f, 1.0f );
46 const Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.5f, 1.5f, 1.0f );
47 const Vector4 LIGHTBLUE( 10.0f/255.0f, 140.0f/255.0f, 210.0f/255.0f, 1.0f );    // Used for Selection highlight
48
49 const char* DEFAULT_GRAB_HANDLE( DALI_IMAGE_DIR "insertpoint-icon.png" );
50 const char* DEFAULT_SELECTION_HANDLE_ONE( DALI_IMAGE_DIR "text-input-selection-handle-left.png" );
51 const char* DEFAULT_SELECTION_HANDLE_TWO( DALI_IMAGE_DIR "text-input-selection-handle-right.png" );
52 const char* DEFAULT_SELECTION_HANDLE_ONE_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-left-press.png" );
53 const char* DEFAULT_SELECTION_HANDLE_TWO_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-right-press.png" );
54 const char* DEFAULT_CURSOR( DALI_IMAGE_DIR "cursor.png" );
55
56 const Vector4 DEFAULT_CURSOR_IMAGE_9_BORDER( 2.0f, 2.0f, 2.0f, 2.0f );
57
58 const std::size_t CURSOR_BLINK_INTERVAL = 500;                              ///< Cursor blink interval
59 const float CHARACTER_THRESHOLD( 2.5f );                                    ///< the threshold of a line.
60 const float DISPLAYED_HIGHLIGHT_Z_OFFSET( 0.0f );                           ///< 1. Highlight rendered (z-offset).
61 const float DISPLAYED_TEXT_VIEW_Z_OFFSET( 0.1f );                           ///< 2. Text rendered (z-offset).
62 const float UI_Z_OFFSET( 0.2f );                                            ///< 3. Text Selection Handles/Cursor z-offset.
63
64 const Vector3 UI_OFFSET(0.0f, 0.0f, UI_Z_OFFSET);                           ///< Text Selection Handles/Cursor offset.
65 const Vector3 DEFAULT_HANDLE_ONE_OFFSET(0.0f, -5.0f, 0.0f);                 ///< Handle One's Offset
66 const Vector3 DEFAULT_HANDLE_TWO_OFFSET(0.0f, -5.0f, 0.0f);                 ///< Handle Two's Offset
67 const float TOP_HANDLE_TOP_OFFSET(-1.5f);                                   ///< Offset between top handle and cutCopyPaste pop-up
68 const float BOTTOM_HANDLE_BOTTOM_OFFSET(1.5f);                              ///< Offset between bottom handle and cutCopyPaste pop-up
69 const float CURSOR_THICKNESS(6.0f);
70 const Degree CURSOR_ANGLE_OFFSET(2.0f);                                     ///< Offset from the angle of italic angle.
71
72 const std::string NEWLINE( "\n" );
73
74 const TextStyle DEFAULT_TEXT_STYLE;
75
76 const unsigned int SCROLL_TICK_INTERVAL = 50u;
77 const float SCROLL_THRESHOLD = 10.f;
78 const float SCROLL_SPEED = 15.f;
79
80 /**
81  * Selection state enumeration (FSM)
82  */
83 enum SelectionState
84 {
85   SelectionNone,                            ///< Currently not encountered selected section.
86   SelectionStarted,                         ///< Encountered selected section
87   SelectionFinished                         ///< Finished selected section
88 };
89
90 /**
91  * Whether the given style is the default style or not.
92  * @param[in] style The given style.
93  * @return \e true if the given style is the default. Otherwise it returns \e false.
94  */
95 bool IsDefaultStyle( const TextStyle& style )
96 {
97   return DEFAULT_TEXT_STYLE == style;
98 }
99
100 /**
101  * Whether the given styled text is using the default style or not.
102  * @param[in] textArray The given text.
103  * @return \e true if the given styled text is using the default style. Otherwise it returns \e false.
104  */
105 bool IsTextDefaultStyle( const Toolkit::MarkupProcessor::StyledTextArray& textArray )
106 {
107   for( Toolkit::MarkupProcessor::StyledTextArray::const_iterator it = textArray.begin(), endIt = textArray.end(); it != endIt; ++it )
108   {
109     const TextStyle& style( (*it).mStyle );
110
111     if( !IsDefaultStyle( style ) )
112     {
113       return false;
114     }
115   }
116
117   return true;
118 }
119
120 std::size_t FindVisibleCharacterLeft( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable )
121 {
122   for( Toolkit::TextView::CharacterLayoutInfoContainer::const_reverse_iterator it = characterLayoutInfoTable.rbegin() + characterLayoutInfoTable.size() - cursorPosition, endIt = characterLayoutInfoTable.rend();
123        it != endIt;
124        ++it )
125   {
126     if( ( *it ).mIsVisible )
127     {
128       return --cursorPosition;
129     }
130
131     --cursorPosition;
132   }
133
134   return 0;
135 }
136
137 std::size_t FindVisibleCharacterRight( std::size_t cursorPosition, const Toolkit::TextView::CharacterLayoutInfoContainer& characterLayoutInfoTable  )
138 {
139   for( Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = characterLayoutInfoTable.begin() + cursorPosition, endIt = characterLayoutInfoTable.end(); it < endIt; ++it )
140   {
141     if( ( *it ).mIsVisible )
142     {
143       return cursorPosition;
144     }
145
146     ++cursorPosition;
147   }
148
149   return cursorPosition;
150 }
151
152 /**
153  * Whether the given position plus the cursor size offset is inside the given boundary.
154  *
155  * @param[in] position The given position.
156  * @param[in] cursorSize The cursor size.
157  * @param[in] controlSize The given boundary.
158  *
159  * @return whether the given position is inside the given boundary.
160  */
161 bool IsPositionInsideBoundaries( const Vector3& position, const Size& cursorSize, const Vector3& controlSize )
162 {
163   return ( position.x >= -Math::MACHINE_EPSILON_1000 ) &&
164          ( position.x <= controlSize.width + Math::MACHINE_EPSILON_1000 ) &&
165          ( position.y - cursorSize.height >= -Math::MACHINE_EPSILON_1000 ) &&
166          ( position.y <= controlSize.height + Math::MACHINE_EPSILON_1000 );
167 }
168
169 /**
170  * Splits a text in two halves.
171  *
172  * If the text's number of characters is odd, firstHalf has one more character.
173  *
174  * @param[in] text The text to be split.
175  * @param[out] firstHalf The first half of the text.
176  * @param[out] secondHalf The second half of the text.
177  */
178 void SplitText( const Toolkit::MarkupProcessor::StyledTextArray& text,
179                       Toolkit::MarkupProcessor::StyledTextArray& firstHalf,
180                       Toolkit::MarkupProcessor::StyledTextArray& secondHalf )
181 {
182   firstHalf.clear();
183   secondHalf.clear();
184
185   const std::size_t textLength = text.size();
186   const std::size_t half = ( textLength / 2 ) + ( textLength % 2 );
187
188   firstHalf.insert( firstHalf.end(), text.begin(), text.begin() + half );
189   secondHalf.insert( secondHalf.end(), text.begin() + half, text.end() );
190 }
191
192 } // end of namespace
193
194 namespace Dali
195 {
196
197 namespace Toolkit
198 {
199 // Properties
200 const Property::Index TextInput::HIGHLIGHT_COLOR_PROPERTY                     = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX;
201 const Property::Index TextInput::CUT_AND_PASTE_COLOR_PROPERTY                 = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+1;
202 const Property::Index TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY         = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+2;
203
204 const Property::Index TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY        = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+3;
205 const Property::Index TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY       = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+4;
206 const Property::Index TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY      = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+5;
207 const Property::Index TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY     = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+6;
208 const Property::Index TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+7;
209 const Property::Index TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY  = Internal::TextInput::TEXTINPUT_PROPERTY_START_INDEX+8;
210
211 namespace Internal
212 {
213
214 namespace
215 {
216
217 BaseHandle Create()
218 {
219   return Toolkit::TextInput::New();
220 }
221
222 TypeRegistration typeRegistration( typeid(Toolkit::TextInput), typeid(Toolkit::Control), Create );
223
224 SignalConnectorType signalConnector1( typeRegistration, Toolkit::TextInput::SIGNAL_START_INPUT,                  &TextInput::DoConnectSignal );
225 SignalConnectorType signalConnector2( typeRegistration, Toolkit::TextInput::SIGNAL_END_INPUT,                    &TextInput::DoConnectSignal );
226 SignalConnectorType signalConnector3( typeRegistration, Toolkit::TextInput::SIGNAL_STYLE_CHANGED,                &TextInput::DoConnectSignal );
227 SignalConnectorType signalConnector4( typeRegistration, Toolkit::TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED, &TextInput::DoConnectSignal );
228 SignalConnectorType signalConnector5( typeRegistration, Toolkit::TextInput::SIGNAL_TOOLBAR_DISPLAYED,            &TextInput::DoConnectSignal );
229 SignalConnectorType signalConnector6( typeRegistration, Toolkit::TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES,       &TextInput::DoConnectSignal );
230
231 }
232
233 PropertyRegistration property1( typeRegistration, "highlight-color",  Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
234 PropertyRegistration property2( typeRegistration, "cut-and-paste-bg-color",  Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
235 PropertyRegistration property3( typeRegistration, "cut-and-paste-pressed-color",  Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY, Property::VECTOR4, &TextInput::SetProperty, &TextInput::GetProperty );
236 PropertyRegistration property4( typeRegistration, "cut-button-position-priority",  Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
237 PropertyRegistration property5( typeRegistration, "copy-button-position-priority",  Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
238 PropertyRegistration property6( typeRegistration, "paste-button-position-priority",  Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
239 PropertyRegistration property7( typeRegistration, "select-button-position-priority",  Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
240 PropertyRegistration property8( typeRegistration, "select-all-button-position-priority",  Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
241 PropertyRegistration property9( typeRegistration, "clipboard-button-position-priority",  Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY, Property::UNSIGNED_INTEGER, &TextInput::SetProperty, &TextInput::GetProperty );
242
243
244 // [TextInput::HighlightInfo] /////////////////////////////////////////////////
245
246 void TextInput::HighlightInfo::AddQuad( float x1, float y1, float x2, float y2 )
247 {
248   QuadCoordinates quad(x1, y1, x2, y2);
249   mQuadList.push_back( quad );
250 }
251
252 void TextInput::HighlightInfo::Clamp2D(const Vector2& min, const Vector2& max)
253 {
254   for(std::size_t i = 0;i < mQuadList.size(); i++)
255   {
256     QuadCoordinates& quad = mQuadList[i];
257
258     quad.min.Clamp(min, max);
259     quad.max.Clamp(min, max);
260   } // end for
261 }
262
263 // [TextInput] ////////////////////////////////////////////////////////////////
264
265 Dali::Toolkit::TextInput TextInput::New()
266 {
267   // Create the implementation
268   TextInputPtr textInput(new TextInput());
269   // Pass ownership to CustomActor via derived handle
270   Dali::Toolkit::TextInput handle(*textInput);
271
272   textInput->Initialize();
273
274   return handle;
275 }
276
277 TextInput::TextInput()
278 :Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS | REQUIRES_STYLE_CHANGE_SIGNALS ) ),
279  mState( StateEdit ),
280  mStyledText(),
281  mInputStyle(),
282  mLineHeight( 0.f ),
283  mDisplayedTextView(),
284  mStyledPlaceHolderText(),
285  mMaxStringLength( DEFAULT_MAX_SIZE ),
286  mNumberOflinesLimit( DEFAULT_NUMBER_OF_LINES_LIMIT ),
287  mCursorPosition( 0 ),
288  mActualGrabHandlePosition( 0.0f, 0.0f, 0.0f ),
289  mIsSelectionHandleOneFlipped( false ),
290  mIsSelectionHandleTwoFlipped( false ),
291  mSelectionHandleOneOffset( DEFAULT_HANDLE_ONE_OFFSET ),
292  mSelectionHandleTwoOffset( DEFAULT_HANDLE_TWO_OFFSET ),
293  mSelectionHandleOneActualPosition( 0.0f, 0.0f , 0.0f ),
294  mSelectionHandleTwoActualPosition( 0.0f, 0.0f , 0.0f ),
295  mSelectionHandleOnePosition( 0 ),
296  mSelectionHandleTwoPosition( 0 ),
297  mPreEditString(),
298  mPreEditStartPosition( 0 ),
299  mPreEditLength ( 0 ),
300  mNumberOfSurroundingCharactersDeleted( 0 ),
301  mTouchStartTime( 0 ),
302  mTextLayoutInfo(),
303  mCurrentCopySelecton(),
304  mPopUpPanel(),
305  mScrollTimer(),
306  mScrollDisplacement(),
307  mCurrentHandlePosition(),
308  mCurrentSelectionId(),
309  mCurrentSelectionHandlePosition(),
310  mRequestedSelection( 0, 0 ),
311  mSelectionHandleFlipMargin( 0.0f, 0.0f, 0.0f, 0.0f ),
312  mBoundingRectangleWorldCoordinates( 0.0f, 0.0f, 0.0f, 0.0f ),
313  mClipboard(),
314  mMaterialColor( LIGHTBLUE ),
315  mOverrideAutomaticAlignment( false ),
316  mCursorRTLEnabled( false ),
317  mClosestCursorPositionEOL ( false ),
318  mCursorBlinkStatus( true ),
319  mCursorVisibility( false ),
320  mGrabHandleVisibility( false ),
321  mIsCursorInScrollArea( true ),
322  mIsGrabHandleInScrollArea( true ),
323  mEditModeActive( false ),
324  mEditOnTouch( true ),
325  mTextSelection( true ),
326  mExceedEnabled( true ),
327  mGrabHandleEnabled( true ),
328  mIsSelectionHandleFlipEnabled( true ),
329  mPreEditFlag( false ),
330  mIgnoreCommitFlag( false ),
331  mIgnoreFirstCommitFlag( false ),
332  mSelectingText( false ),
333  mPreserveCursorPosition( false ),
334  mSelectTextOnCommit( false ),
335  mUnderlinedPriorToPreEdit ( false ),
336  mCommitByKeyInput( false ),
337  mPlaceHolderSet( false ),
338  mMarkUpEnabled( false )
339 {
340   // Updates the line height accordingly with the input style.
341   UpdateLineHeight();
342 }
343
344 TextInput::~TextInput()
345 {
346   StopCursorBlinkTimer();
347 }
348
349 // Public
350
351 std::string TextInput::GetText() const
352 {
353   std::string text;
354
355   // Return text-view's text only if the text-input's text is not empty
356   // in order to not to return the placeholder text.
357   if( !mStyledText.empty() )
358   {
359     text = mDisplayedTextView.GetText();
360   }
361
362   return text;
363 }
364
365 std::string TextInput::GetMarkupText() const
366 {
367   std::string markupString;
368   MarkupProcessor::GetMarkupString( mStyledText, markupString );
369
370   return markupString;
371 }
372
373 void TextInput::SetPlaceholderText( const std::string& placeHolderText )
374 {
375   // Get the placeholder styled text array from the markup string.
376   MarkupProcessor::GetStyledTextArray( placeHolderText, mStyledPlaceHolderText, IsMarkupProcessingEnabled() );
377
378   if( mStyledText.empty() )
379   {
380     // Set the placeholder text only if the styled text is empty.
381     mDisplayedTextView.SetText( mStyledPlaceHolderText );
382     mPlaceHolderSet = true;
383   }
384 }
385
386 std::string TextInput::GetPlaceholderText()
387 {
388   // Traverses the styled placeholder array getting only the text.
389   //  Note that for some languages a 'character' could be represented by more than one 'char'
390
391   std::string placeholderText;
392   for( MarkupProcessor::StyledTextArray::const_iterator it = mStyledPlaceHolderText.begin(), endIt = mStyledPlaceHolderText.end(); it != endIt; ++it )
393   {
394     placeholderText.append( (*it).mText.GetText() );
395   }
396
397   return placeholderText ;
398 }
399
400 void TextInput::SetInitialText(const std::string& initialText)
401 {
402   DALI_LOG_INFO(gLogFilter, Debug::General, "SetInitialText string[%s]\n", initialText.c_str() );
403
404   if ( mPreEditFlag ) // If in the pre-edit state and text is being set then discard text being inserted.
405   {
406     mPreEditFlag = false;
407     mIgnoreCommitFlag = true;
408   }
409
410   SetText( initialText );
411   PreEditReset( false ); // Reset keyboard as text changed
412 }
413
414 void TextInput::SetText(const std::string& initialText)
415 {
416   DALI_LOG_INFO(gLogFilter, Debug::General, "SetText string[%s]\n", initialText.c_str() );
417
418   GetStyledTextArray( initialText, mStyledText, IsMarkupProcessingEnabled() );
419
420   if( mStyledText.empty() )
421   {
422     // If the initial text is empty, set the placeholder text.
423     mDisplayedTextView.SetText( mStyledPlaceHolderText );
424     mPlaceHolderSet = true;
425   }
426   else
427   {
428     mDisplayedTextView.SetText( mStyledText );
429     mPlaceHolderSet = false;
430   }
431
432   GetTextLayoutInfo();
433
434   mCursorPosition = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
435
436   ImfManager imfManager = ImfManager::Get();
437   if ( imfManager )
438   {
439     imfManager.SetCursorPosition( mCursorPosition );
440     imfManager.SetSurroundingText( initialText );
441     imfManager.NotifyCursorPosition();
442   }
443
444   if( IsScrollEnabled() )
445   {
446     ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
447   }
448
449   ShowGrabHandleAndSetVisibility( false );
450
451   RemoveHighlight();
452
453   DrawCursor();
454
455   EmitTextModified();
456 }
457
458 void TextInput::SetText( const MarkupProcessor::StyledTextArray& styleText )
459 {
460   DALI_LOG_INFO(gLogFilter, Debug::General, "SetText markup text\n" );
461
462   mDisplayedTextView.SetText( styleText );
463   mPlaceHolderSet = false;
464
465   // If text alignment hasn't been manually set by application developer, then we
466   // automatically determine the alignment based on the content of the text i.e. what
467   // language the text begins with.
468   // TODO: This should determine different alignments for each line (broken by '\n') of text.
469   if(!mOverrideAutomaticAlignment)
470   {
471     // Determine bidi direction of first character (skipping past whitespace, numbers, and symbols)
472     bool leftToRight(true);
473
474     if( !styleText.empty() )
475     {
476       bool breakOut(false);
477
478       for( MarkupProcessor::StyledTextArray::const_iterator textIter = styleText.begin(), textEndIter = styleText.end(); ( textIter != textEndIter ) && ( !breakOut ); ++textIter )
479       {
480         const Text& text = textIter->mText;
481
482         for( std::size_t i = 0; i < text.GetLength(); ++i )
483         {
484           Character character( text[i] );
485           if( character.GetCharacterDirection() != Character::Neutral )
486           {
487             leftToRight = ( character.GetCharacterDirection() == Character::LeftToRight );
488             breakOut = true;
489             break;
490           }
491         }
492       }
493     }
494
495     // Based on this direction, either left or right align text if not manually set by application developer.
496     mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(
497                                            ( leftToRight ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight) |
498                                              Toolkit::Alignment::VerticalTop ) );
499     mDisplayedTextView.SetLineJustification( leftToRight ? Toolkit::TextView::Left : Toolkit::TextView::Right);
500   }
501
502   EmitTextModified();
503 }
504
505 void TextInput::SetMaxCharacterLength(std::size_t maxChars)
506 {
507   mMaxStringLength = maxChars;
508 }
509
510 void TextInput::SetNumberOfLinesLimit(std::size_t maxLines)
511 {
512   DALI_ASSERT_DEBUG( maxLines > 0 )
513
514   if ( maxLines > 0)
515   {
516     mNumberOflinesLimit = maxLines;
517   }
518 }
519
520 std::size_t TextInput::GetNumberOfLinesLimit() const
521 {
522   return mNumberOflinesLimit;
523 }
524
525 std::size_t TextInput::GetNumberOfCharacters() const
526 {
527   return mStyledText.size();
528 }
529
530 // Styling
531 void TextInput::SetMaterialDiffuseColor( const Vector4& color )
532 {
533   mMaterialColor = color;
534   if ( mCustomMaterial )
535   {
536     mCustomMaterial.SetDiffuseColor( mMaterialColor );
537     mMeshData.SetMaterial( mCustomMaterial );
538   }
539 }
540
541 const Vector4& TextInput::GetMaterialDiffuseColor() const
542 {
543   return mMaterialColor;
544 }
545
546 // Signals
547
548 Toolkit::TextInput::InputSignalV2& TextInput::InputStartedSignal()
549 {
550   return mInputStartedSignalV2;
551 }
552
553 Toolkit::TextInput::InputSignalV2& TextInput::InputFinishedSignal()
554 {
555   return mInputFinishedSignalV2;
556 }
557
558 Toolkit::TextInput::InputSignalV2& TextInput::CutAndPasteToolBarDisplayedSignal()
559 {
560   return mCutAndPasteToolBarDisplayedV2;
561 }
562
563 Toolkit::TextInput::StyleChangedSignalV2& TextInput::StyleChangedSignal()
564 {
565   return mStyleChangedSignalV2;
566 }
567
568 Toolkit::TextInput::TextModifiedSignalType& TextInput::TextModifiedSignal()
569 {
570   return mTextModifiedSignal;
571 }
572
573 Toolkit::TextInput::MaxInputCharactersReachedSignalV2& TextInput::MaxInputCharactersReachedSignal()
574 {
575   return mMaxInputCharactersReachedSignalV2;
576 }
577
578 Toolkit::TextInput::InputTextExceedBoundariesSignalV2& TextInput::InputTextExceedBoundariesSignal()
579 {
580   return mInputTextExceedBoundariesSignalV2;
581 }
582
583 bool TextInput::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
584 {
585   Dali::BaseHandle handle( object );
586
587   bool connected( true );
588   Toolkit::TextInput textInput = Toolkit::TextInput::DownCast(handle);
589
590   if( Toolkit::TextInput::SIGNAL_START_INPUT == signalName )
591   {
592     textInput.InputStartedSignal().Connect( tracker, functor );
593   }
594   else if( Toolkit::TextInput::SIGNAL_END_INPUT == signalName )
595   {
596     textInput.InputFinishedSignal().Connect( tracker, functor );
597   }
598   else if( Toolkit::TextInput::SIGNAL_STYLE_CHANGED == signalName )
599   {
600     textInput.StyleChangedSignal().Connect( tracker, functor );
601   }
602   else if( Toolkit::TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED == signalName )
603   {
604     textInput.MaxInputCharactersReachedSignal().Connect( tracker, functor );
605   }
606   else if( Toolkit::TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES == signalName )
607   {
608     textInput.InputTextExceedBoundariesSignal().Connect( tracker, functor );
609   }
610   else
611   {
612     // signalName does not match any signal
613     connected = false;
614   }
615
616   return connected;
617 }
618
619 void TextInput::SetEditable(bool editMode, bool setCursorOnTouchPoint, const Vector2& touchPoint)
620 {
621   if(editMode)
622   {
623     // update line height before calculate the actual position.
624     UpdateLineHeight();
625
626     if(!mEditModeActive)
627     {
628       if( setCursorOnTouchPoint )
629       {
630         // Sets the cursor position for the given touch point.
631         ReturnClosestIndex( touchPoint, mCursorPosition );
632
633         // Creates the grab handle.
634         if( IsGrabHandleEnabled() )
635         {
636           const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
637
638           CreateGrabHandle();
639
640           mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
641           mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
642           mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
643           ShowGrabHandleAndSetVisibility( true );
644
645           // Scrolls the text-view if needed.
646           if( IsScrollEnabled() )
647           {
648             ScrollTextViewToMakeCursorVisible( cursorPosition );
649           }
650         }
651       }
652       else
653       {
654         mCursorPosition = mStyledText.size(); // Initially set cursor position to end of string.
655       }
656     }
657
658     StartEditMode();
659   }
660   else
661   {
662     EndEditMode();
663   }
664 }
665
666 bool TextInput::IsEditable() const
667 {
668   return mEditModeActive;
669 }
670
671 void TextInput::SetEditOnTouch( bool editOnTouch )
672 {
673   mEditOnTouch = editOnTouch;
674 }
675
676 bool TextInput::IsEditOnTouch() const
677 {
678   return mEditOnTouch;
679 }
680
681 void TextInput::SetTextSelectable( bool textSelectable )
682 {
683   mTextSelection = textSelectable;
684 }
685
686 bool TextInput::IsTextSelectable() const
687 {
688   return mTextSelection;
689 }
690
691 bool TextInput::IsTextSelected() const
692 {
693   return mHighlightMeshActor;
694 }
695
696 void TextInput::DeSelectText()
697 {
698   RemoveHighlight();
699   HidePopup();
700   CursorUpdate();
701 }
702
703 void TextInput::SetGrabHandleImage(Dali::Image image )
704 {
705   if (image)
706   {
707     CreateGrabHandle(image);
708   }
709 }
710
711 void TextInput::SetCursorImage(Dali::Image image, const Vector4& border )
712 {
713   DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
714
715   if ( image )
716   {
717     mCursor.SetImage( image );
718     mCursor.SetNinePatchBorder( border );
719   }
720 }
721
722 Vector3 TextInput::GetSelectionHandleSize()
723 {
724   return DEFAULT_SELECTION_HANDLE_SIZE;
725 }
726
727 void TextInput::SetRTLCursorImage(Dali::Image image, const Vector4& border )
728 {
729   DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
730
731   if ( image )
732   {
733     mCursorRTL.SetImage( image);
734     mCursorRTL.SetNinePatchBorder(  border );
735   }
736 }
737
738 void TextInput::EnableGrabHandle(bool toggle)
739 {
740   // enables grab handle with will in turn de-activate magnifier
741   mGrabHandleEnabled = toggle;
742 }
743
744 bool TextInput::IsGrabHandleEnabled()
745 {
746   // if false then magnifier will be shown instead.
747   return mGrabHandleEnabled;
748 }
749
750 void TextInput::EnableSelectionHandleFlip( bool toggle )
751 {
752   // Deprecated function.  To be removed.
753   mIsSelectionHandleFlipEnabled = toggle;
754 }
755
756 bool TextInput::IsSelectionHandleFlipEnabled()
757 {
758   // Deprecated function, To be removed. Returns true as handle flipping always enabled by default so handles do not exceed screen.
759   return true;
760 }
761
762 void TextInput::SetSelectionHandleFlipMargin( const Vector4& margin )
763 {
764   // Deprecated function, now just stores margin for retreival, remove completely once depricated Public API removed.
765   Vector3 textInputSize = mDisplayedTextView.GetCurrentSize();
766   const Vector4 flipBoundary( -margin.x, -margin.y, textInputSize.width + margin.z, textInputSize.height + margin.w );
767
768   mSelectionHandleFlipMargin = margin;
769 }
770
771 void TextInput::SetBoundingRectangle( const Rect<float>& boundingRectangle )
772 {
773   // Convert to world coordinates and store as a Vector4 to be compatiable with Property Notifications.
774   Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
775
776   const float originX = boundingRectangle.x - 0.5f * stageSize.width;
777   const float originY = boundingRectangle.y - 0.5f * stageSize.height;
778
779   const Vector4 boundary( originX,
780                           originY,
781                           originX + boundingRectangle.width,
782                           originY + boundingRectangle.height );
783
784   mBoundingRectangleWorldCoordinates = boundary;
785 }
786
787 const Rect<float> TextInput::GetBoundingRectangle() const
788 {
789   Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
790
791   const float originX = mBoundingRectangleWorldCoordinates.x + 0.5f * stageSize.width;
792   const float originY = mBoundingRectangleWorldCoordinates.y + 0.5f * stageSize.height;
793
794   Rect<float>boundingRect( originX, originY, mBoundingRectangleWorldCoordinates.z - mBoundingRectangleWorldCoordinates.x, mBoundingRectangleWorldCoordinates.w - mBoundingRectangleWorldCoordinates.y);
795
796   return boundingRect;
797 }
798
799 const Vector4& TextInput::GetSelectionHandleFlipMargin()
800 {
801   return mSelectionHandleFlipMargin;
802 }
803
804 void TextInput::SetTextColor( const Vector4& color )
805 {
806   mDisplayedTextView.SetColor( color );
807 }
808
809 void TextInput::SetActiveStyle( const TextStyle& style, const TextStyle::Mask mask )
810 {
811   if( style != mInputStyle )
812   {
813     // different style.
814     bool emitSignal = false;
815
816     // mask: modify style according to mask, if different emit signal.
817     const TextStyle oldInputStyle( mInputStyle );
818
819     // Copy the new style.
820     mInputStyle.Copy( style, mask );
821
822     // if style has changed, emit signal.
823     if( oldInputStyle != mInputStyle )
824     {
825       emitSignal = true;
826     }
827
828     // Updates the line height accordingly with the input style.
829     UpdateLineHeight();
830
831     // Changing font point size will require the cursor to be re-sized
832     DrawCursor();
833
834     if( emitSignal )
835     {
836       EmitStyleChangedSignal();
837     }
838   }
839 }
840
841 void TextInput::ApplyStyle( const TextStyle& style, const TextStyle::Mask mask )
842 {
843   if ( IsTextSelected() )
844   {
845     const std::size_t begin = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
846     const std::size_t end = std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition) - 1;
847
848     if( !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
849     {
850       ApplyStyleToRange(style, mask, mTextLayoutInfo.mCharacterLogicalToVisualMap[begin], mTextLayoutInfo.mCharacterLogicalToVisualMap[end]);
851     }
852
853     // Keeps the old style to be compared with the new one.
854     const TextStyle oldInputStyle( mInputStyle );
855
856     // Copy only those parameters from the style which are set in the mask.
857     mInputStyle.Copy( style, mask );
858
859     if( mInputStyle != oldInputStyle )
860     {
861       // Updates the line height accordingly with the input style.
862       UpdateLineHeight();
863
864       EmitStyleChangedSignal();
865     }
866   }
867 }
868
869 void TextInput::ApplyStyleToAll( const TextStyle& style, const TextStyle::Mask mask )
870 {
871   if( !mStyledText.empty() )
872   {
873     ApplyStyleToRange( style, mask, 0, mStyledText.size() - 1 );
874   }
875 }
876
877 TextStyle TextInput::GetStyleAtCursor() const
878 {
879   TextStyle style;
880
881   if ( !mStyledText.empty() && ( mCursorPosition > 0 ) )
882   {
883     DALI_ASSERT_DEBUG( ( 0 <= mCursorPosition-1 ) && ( mCursorPosition-1 < mStyledText.size() ) );
884     style = mStyledText.at( mCursorPosition-1 ).mStyle;
885   }
886   else // No text.
887   {
888     style = mInputStyle;
889
890     if ( mInputStyle.GetFontPointSize() <  Math::MACHINE_EPSILON_1000 )
891     {
892       Dali::Font defaultFont = Dali::Font::New();
893       style.SetFontPointSize( PointSize( defaultFont.GetPointSize()) );
894     }
895   }
896
897   return style;
898 }
899
900 TextStyle TextInput::GetStyleAt( std::size_t position ) const
901 {
902   DALI_ASSERT_DEBUG( ( 0 <= position ) && ( position <= mStyledText.size() ) );
903
904   if( position >= mStyledText.size() )
905   {
906     position = mStyledText.size() - 1;
907   }
908
909   return mStyledText.at( position ).mStyle;
910 }
911
912 void TextInput::SetTextAlignment( Toolkit::Alignment::Type align )
913 {
914   mDisplayedTextView.SetTextAlignment( align );
915   mOverrideAutomaticAlignment = true;
916 }
917
918 void TextInput::SetTextLineJustification( Toolkit::TextView::LineJustification justification )
919 {
920   mDisplayedTextView.SetLineJustification( justification );
921   mOverrideAutomaticAlignment = true;
922 }
923
924 void TextInput::SetFadeBoundary( const Toolkit::TextView::FadeBoundary& fadeBoundary )
925 {
926   mDisplayedTextView.SetFadeBoundary( fadeBoundary );
927 }
928
929 const Toolkit::TextView::FadeBoundary& TextInput::GetFadeBoundary() const
930 {
931   return mDisplayedTextView.GetFadeBoundary();
932 }
933
934 Toolkit::Alignment::Type TextInput::GetTextAlignment() const
935 {
936   return mDisplayedTextView.GetTextAlignment();
937 }
938
939 void TextInput::SetMultilinePolicy( Toolkit::TextView::MultilinePolicy policy )
940 {
941   mDisplayedTextView.SetMultilinePolicy( policy );
942 }
943
944 Toolkit::TextView::MultilinePolicy TextInput::GetMultilinePolicy() const
945 {
946   return mDisplayedTextView.GetMultilinePolicy();
947 }
948
949 void TextInput::SetWidthExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
950 {
951   mDisplayedTextView.SetWidthExceedPolicy( policy );
952 }
953
954 Toolkit::TextView::ExceedPolicy TextInput::GetWidthExceedPolicy() const
955 {
956   return mDisplayedTextView.GetWidthExceedPolicy();
957 }
958
959 void TextInput::SetHeightExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
960 {
961   mDisplayedTextView.SetHeightExceedPolicy( policy );
962 }
963
964 Toolkit::TextView::ExceedPolicy TextInput::GetHeightExceedPolicy() const
965 {
966   return mDisplayedTextView.GetHeightExceedPolicy();
967 }
968
969 void TextInput::SetExceedEnabled( bool enable )
970 {
971   mExceedEnabled = enable;
972 }
973
974 bool TextInput::GetExceedEnabled() const
975 {
976   return mExceedEnabled;
977 }
978
979 void TextInput::SetBackground(Dali::Image image )
980 {
981   // TODO Should add this function and add public api to match.
982 }
983
984 bool TextInput::OnTouchEvent(const TouchEvent& event)
985 {
986   return false;
987 }
988
989 bool TextInput::OnKeyEvent(const KeyEvent& event)
990 {
991   switch( event.state )
992   {
993     case KeyEvent::Down:
994     {
995       return OnKeyDownEvent(event);
996     }
997     break;
998
999     case KeyEvent::Up:
1000     {
1001       return OnKeyUpEvent(event);
1002     }
1003     break;
1004
1005     default:
1006     {
1007       return false;
1008     }
1009     break;
1010   }
1011 }
1012
1013 void TextInput::OnKeyInputFocusGained()
1014 {
1015   DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusGained\n" );
1016
1017   mEditModeActive = true;
1018
1019   mActiveLayer.RaiseToTop(); // Ensure layer holding handles is on top
1020
1021   mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1022
1023   // Updates the line height accordingly with the input style.
1024   UpdateLineHeight();
1025
1026   // Connect the signals to use in text input.
1027   VirtualKeyboard::StatusChangedSignal().Connect( this, &TextInput::KeyboardStatusChanged );
1028   VirtualKeyboard::LanguageChangedSignal().Connect( this, &TextInput::SetTextDirection );
1029
1030   // Set the text direction if empty and connect to the signal to ensure we change direction when the language changes.
1031   SetTextDirection();
1032
1033   GetTextLayoutInfo();
1034
1035   DrawCursor();
1036   SetCursorVisibility( true );
1037   StartCursorBlinkTimer();
1038
1039   Toolkit::TextInput handle( GetOwner() );
1040   mInputStartedSignalV2.Emit( handle );
1041
1042   ImfManager imfManager = ImfManager::Get();
1043
1044   if ( imfManager )
1045   {
1046     imfManager.EventReceivedSignal().Connect(this, &TextInput::ImfEventReceived);
1047
1048     // Notify that the text editing start.
1049     imfManager.Activate();
1050
1051     // When window gain lost focus, the imf manager is deactivated. Thus when window gain focus again, the imf manager must be activated.
1052     imfManager.SetRestoreAferFocusLost( true );
1053
1054     imfManager.SetCursorPosition( mCursorPosition );
1055     imfManager.NotifyCursorPosition();
1056   }
1057
1058   mClipboard = Clipboard::Get(); // Store handle to clipboard
1059
1060   // Now in edit mode we can accept string to paste from clipboard
1061   if( Adaptor::IsAvailable() )
1062   {
1063     ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1064     if ( notifier )
1065     {
1066       notifier.ContentSelectedSignal().Connect( this, &TextInput::OnClipboardTextSelected );
1067     }
1068   }
1069 }
1070
1071 void TextInput::OnKeyInputFocusLost()
1072 {
1073   DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusLost\n" );
1074
1075   if( mPreEditFlag )
1076   {
1077     // If key input focus is lost, it removes the
1078     // underline from the last pre-edit text.
1079     RemovePreEditStyle();
1080     const std::size_t numberOfCharactersDeleted = DeletePreEdit();
1081     InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersDeleted );
1082     EmitTextModified();
1083   }
1084
1085   ImfManager imfManager = ImfManager::Get();
1086   if ( imfManager )
1087   {
1088     // The text editing is finished. Therefore the imf manager don't have restore activation.
1089     imfManager.SetRestoreAferFocusLost( false );
1090
1091     // Notify that the text editing finish.
1092     imfManager.Deactivate();
1093
1094     imfManager.EventReceivedSignal().Disconnect(this, &TextInput::ImfEventReceived);
1095   }
1096   // Disconnect signal used the text input.
1097   VirtualKeyboard::LanguageChangedSignal().Disconnect( this, &TextInput::SetTextDirection );
1098
1099   Toolkit::TextInput handle( GetOwner() );
1100   mInputFinishedSignalV2.Emit( handle );
1101   mEditModeActive = false;
1102   mPreEditFlag = false;
1103   RemoveHighlight();
1104   SetCursorVisibility( false );
1105   StopCursorBlinkTimer();
1106
1107   ShowGrabHandleAndSetVisibility( false );
1108
1109   mClipboard.Reset();
1110   // No longer in edit mode so do not want to receive string from clipboard
1111   if( Adaptor::IsAvailable() )
1112   {
1113     ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1114     if ( notifier )
1115     {
1116       notifier.ContentSelectedSignal().Disconnect( this, &TextInput::OnClipboardTextSelected );
1117     }
1118     Clipboard clipboard = Clipboard::Get();
1119
1120     if ( clipboard )
1121     {
1122       clipboard.HideClipboard();
1123     }
1124   }
1125 }
1126
1127 void TextInput::OnControlStageConnection()
1128 {
1129   Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
1130
1131   if ( mBoundingRectangleWorldCoordinates == Vector4::ZERO )
1132   {
1133     SetBoundingRectangle( Rect<float>( 0.0f, 0.0f, stageSize.width, stageSize.height ));
1134   }
1135 }
1136
1137 void TextInput::CreateActiveLayer()
1138 {
1139   Actor self = Self();
1140   mActiveLayer = Layer::New();
1141
1142   mActiveLayer.SetAnchorPoint( AnchorPoint::CENTER);
1143   mActiveLayer.SetParentOrigin( ParentOrigin::CENTER);
1144   mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
1145
1146   self.Add( mActiveLayer );
1147   mActiveLayer.RaiseToTop();
1148 }
1149
1150 void TextInput::OnInitialize()
1151 {
1152   CreateTextViewActor();
1153
1154   SetUpTouchEvents();
1155
1156   // Create 2 cursors (standard LTR and RTL cursor for when text can be added at
1157   // different positions depending on language)
1158   Image mCursorImage = Image::New( DEFAULT_CURSOR );
1159   mCursor = CreateCursor( mCursorImage, DEFAULT_CURSOR_IMAGE_9_BORDER );
1160   mCursorRTL = CreateCursor( mCursorImage, DEFAULT_CURSOR_IMAGE_9_BORDER );
1161
1162   Actor self = Self();
1163   self.Add( mCursor );
1164   self.Add( mCursorRTL );
1165
1166   mCursorVisibility = false;
1167
1168   CreateActiveLayer(); // todo move this so layer only created when needed.
1169
1170   // Assign names to image actors
1171   mCursor.SetName("mainCursor");
1172   mCursorRTL.SetName("rtlCursor");
1173 }
1174
1175 void TextInput::OnControlSizeSet(const Vector3& targetSize)
1176 {
1177   mDisplayedTextView.SetSize( targetSize );
1178   GetTextLayoutInfo();
1179   mActiveLayer.SetSize(targetSize);
1180 }
1181
1182 void TextInput::OnRelaidOut( Vector2 size, ActorSizeContainer& container )
1183 {
1184   Relayout( mDisplayedTextView, size, container );
1185   GetTextLayoutInfo();
1186
1187   DrawCursor();
1188 }
1189
1190 Vector3 TextInput::GetNaturalSize()
1191 {
1192   Vector3 naturalSize = mDisplayedTextView.GetNaturalSize();
1193
1194   if( mEditModeActive && ( Vector3::ZERO == naturalSize ) )
1195   {
1196     // If the natural is zero, it means there is no text. Let's return the cursor height as the natural height.
1197     naturalSize.height = mLineHeight;
1198   }
1199
1200   return naturalSize;
1201 }
1202
1203 float TextInput::GetHeightForWidth( float width )
1204 {
1205   float height = mDisplayedTextView.GetHeightForWidth( width );
1206
1207   if( mEditModeActive && ( fabsf( height ) < Math::MACHINE_EPSILON_1000 ) )
1208   {
1209     // If the height is zero, it means there is no text. Let's return the cursor height.
1210     height = mLineHeight;
1211   }
1212
1213   return height;
1214 }
1215
1216 /*end of Virtual methods from parent*/
1217
1218 // Private Internal methods
1219
1220 void TextInput::OnHandlePan(Actor actor, PanGesture gesture)
1221 {
1222   switch (gesture.state)
1223   {
1224     case Gesture::Started:
1225     // fall through so code not duplicated
1226     case Gesture::Continuing:
1227     {
1228       if (actor == mGrabArea)
1229       {
1230         SetCursorVisibility( true );
1231         ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1232         MoveGrabHandle( gesture.displacement );
1233         HidePopup(); // Do not show popup whilst handle is moving
1234       }
1235       else if (actor == mHandleOneGrabArea)
1236       {
1237         // the displacement in PanGesture is affected by the actor's rotation.
1238         mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1239         mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1240
1241         MoveSelectionHandle( HandleOne, gesture.displacement );
1242
1243         mState = StateDraggingHandle;
1244         HidePopup();
1245       }
1246       else if (actor == mHandleTwoGrabArea)
1247       {
1248         // the displacement in PanGesture is affected by the actor's rotation.
1249         mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1250         mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1251
1252         MoveSelectionHandle( HandleTwo, gesture.displacement );
1253
1254         mState = StateDraggingHandle;
1255         HidePopup();
1256       }
1257     }
1258     break;
1259
1260     case Gesture::Finished:
1261     {
1262       // Revert back to non-pressed selection handle images
1263       if (actor == mGrabArea)
1264       {
1265         mActualGrabHandlePosition = MoveGrabHandle( gesture.displacement );
1266         SetCursorVisibility( true );
1267         SetUpPopUpSelection();
1268         ShowPopup();
1269       }
1270       if (actor == mHandleOneGrabArea)
1271       {
1272         // the displacement in PanGesture is affected by the actor's rotation.
1273         mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1274         mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1275
1276         mSelectionHandleOneActualPosition = MoveSelectionHandle( HandleOne, gesture.displacement );
1277
1278         mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1279         mState = StateEdit;
1280         ShowPopupCutCopyPaste();
1281       }
1282       if (actor == mHandleTwoGrabArea)
1283       {
1284         // the displacement in PanGesture is affected by the actor's rotation.
1285         mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1286         mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1287
1288         mSelectionHandleTwoActualPosition = MoveSelectionHandle( HandleTwo, gesture.displacement );
1289
1290         mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1291         mState = StateEdit;
1292         ShowPopupCutCopyPaste();
1293       }
1294     }
1295     break;
1296     default:
1297       break;
1298   }
1299 }
1300
1301 // Stop the flashing animation so easy to see when moved.
1302 bool TextInput::OnPressDown(Dali::Actor actor, const TouchEvent& touch)
1303 {
1304   if (touch.GetPoint(0).state == TouchPoint::Down)
1305   {
1306     SetCursorVisibility( true );
1307     StopCursorBlinkTimer();
1308   }
1309   else if (touch.GetPoint(0).state == TouchPoint::Up)
1310   {
1311     SetCursorVisibility( true );
1312     StartCursorBlinkTimer();
1313   }
1314   return false;
1315 }
1316
1317 // selection handle one
1318 bool TextInput::OnHandleOneTouched(Dali::Actor actor, const TouchEvent& touch)
1319 {
1320   if (touch.GetPoint(0).state == TouchPoint::Down)
1321   {
1322     mSelectionHandleOne.SetImage( mSelectionHandleOneImagePressed );
1323   }
1324   else if (touch.GetPoint(0).state == TouchPoint::Up)
1325   {
1326     mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1327   }
1328   return false;
1329 }
1330
1331 // selection handle two
1332 bool TextInput::OnHandleTwoTouched(Dali::Actor actor, const TouchEvent& touch)
1333 {
1334   if (touch.GetPoint(0).state == TouchPoint::Down)
1335   {
1336     mSelectionHandleTwo.SetImage( mSelectionHandleTwoImagePressed );
1337   }
1338   else if (touch.GetPoint(0).state == TouchPoint::Up)
1339   {
1340     mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1341   }
1342   return false;
1343 }
1344
1345 void TextInput::OnDoubleTap(Dali::Actor actor, Dali::TapGesture tap)
1346 {
1347    // If text exists then select nearest word.
1348    if ( !mStyledText.empty())
1349    {
1350      HidePopup();
1351
1352      ShowGrabHandleAndSetVisibility( false );
1353
1354
1355      if ( mPreEditFlag )
1356      {
1357        // PreEdit will be committed here without needing a commit from IMF.  Remove pre-edit underline and reset flags which
1358        // converts the pre-edit word being displayed to a committed word.
1359        if ( !mUnderlinedPriorToPreEdit )
1360        {
1361          TextStyle style;
1362          style.SetUnderline( false );
1363          ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1364        }
1365        mPreEditFlag = false;
1366        mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1367        // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1368        PreEditReset( false );
1369      }
1370      mCursorPosition = 0;
1371
1372      mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1373      ReturnClosestIndex( tap.localPoint, mCursorPosition );
1374
1375      ImfManager imfManager = ImfManager::Get();
1376      if ( imfManager )
1377      {
1378        imfManager.SetCursorPosition ( mCursorPosition );
1379        imfManager.NotifyCursorPosition();
1380      }
1381
1382      std::size_t start = 0;
1383      std::size_t end = 0;
1384      Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1385
1386      SelectText( start, end );
1387    }
1388    // if no text but clipboard has content then show paste option
1389    if ( mClipboard.NumberOfItems() || !mStyledText.empty() )
1390    {
1391      ShowPopupCutCopyPaste();
1392    }
1393
1394    // If no text and clipboard empty then do nothing
1395 }
1396
1397 // TODO: Change the function name to be more general.
1398 void TextInput::OnTextTap(Dali::Actor actor, Dali::TapGesture tap)
1399 {
1400   DALI_LOG_INFO( gLogFilter, Debug::General, "OnTap mPreEditFlag[%s] mEditOnTouch[%s] mEditModeActive[%s] ", (mPreEditFlag)?"true":"false"
1401                                                                                                            , (mEditOnTouch)?"true":"false"
1402                                                                                                            , (mEditModeActive)?"true":"false");
1403
1404   if( mHandleOneGrabArea == actor || mHandleTwoGrabArea == actor )
1405   {
1406     return;
1407   }
1408
1409   if( mGrabArea == actor )
1410   {
1411     if( mPopUpPanel.GetState() == TextInputPopup::StateHidden || mPopUpPanel.GetState() == TextInputPopup::StateHiding )
1412     {
1413       SetUpPopUpSelection();
1414       ShowPopup();
1415     }
1416
1417     return;
1418   }
1419
1420   HidePopup();
1421   RemoveHighlight();
1422
1423   mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1424
1425   // Initially don't create the grab handle.
1426   bool createGrabHandle = false;
1427
1428   if ( !mEditModeActive )
1429   {
1430     // update line height before calculate the actual position.
1431     UpdateLineHeight();
1432
1433     // Only start edit mode if TextInput configured to edit on touch
1434     if ( mEditOnTouch )
1435     {
1436       // Set the initial cursor position in the tap point.
1437       ReturnClosestIndex(tap.localPoint, mCursorPosition );
1438
1439       // Create the grab handle.
1440       // TODO Make this a re-usable function.
1441       if ( IsGrabHandleEnabled() )
1442       {
1443         const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1444
1445         CreateGrabHandle();
1446
1447         mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1448         mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1449         mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1450         ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1451
1452       }
1453
1454       // Edit mode started after grab handle created to ensure the signal InputStarted is sent last.
1455       // This is used to ensure if selecting text hides the grab handle then this code is run after grab handle is created,
1456       // otherwise the Grab handle will be shown when selecting.
1457
1458       StartEditMode();
1459     }
1460   }
1461   else
1462   {
1463     // Show the keyboard if it was hidden.
1464     if (!VirtualKeyboard::IsVisible())
1465     {
1466       VirtualKeyboard::Show();
1467     }
1468
1469     // Reset keyboard as tap event has occurred.
1470     // Set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1471     PreEditReset( true );
1472
1473     GetTextLayoutInfo();
1474
1475     if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() ) // If string empty we do not need a grab handle.
1476     {
1477       // As already in edit mode, reposition cursor near tap and show grab handle for cursor, if grab handle not enabled then magnifier will be used instead.
1478
1479       ReturnClosestIndex(tap.localPoint, mCursorPosition );
1480
1481       DALI_LOG_INFO( gLogFilter, Debug::General, "mCursorPosition[%u]", mCursorPosition );
1482
1483       // Notify keyboard so it can 're-capture' word for predictive text.
1484       // As we have done a reset, is this required, expect IMF keyboard to request this information.
1485       ImfManager imfManager = ImfManager::Get();
1486       if ( imfManager )
1487       {
1488         imfManager.SetCursorPosition ( mCursorPosition );
1489         imfManager.NotifyCursorPosition();
1490       }
1491       const TextStyle oldInputStyle( mInputStyle );
1492
1493       mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1494
1495       DrawCursor();
1496
1497       // Create the grab handle.
1498       // Grab handle is created later.
1499       createGrabHandle = true;
1500
1501       if( oldInputStyle != mInputStyle )
1502       {
1503         // Updates the line height accordingly with the input style.
1504         UpdateLineHeight();
1505
1506         EmitStyleChangedSignal();
1507       }
1508     }
1509   }
1510
1511   if ( createGrabHandle && IsGrabHandleEnabled() )
1512   {
1513     const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1514
1515     CreateGrabHandle();
1516
1517     mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1518     mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1519     mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1520     ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1521
1522   }
1523 }
1524
1525 void TextInput::OnLongPress(Dali::Actor actor, Dali::LongPressGesture longPress)
1526 {
1527   DALI_LOG_INFO( gLogFilter, Debug::General, "OnLongPress\n" );
1528
1529   if(longPress.state == Dali::Gesture::Started)
1530   {
1531     // Start edit mode on long press
1532     if ( !mEditModeActive )
1533     {
1534       StartEditMode();
1535     }
1536
1537     // If text exists then select nearest word.
1538     if ( !mStyledText.empty())
1539     {
1540       HidePopup();
1541
1542       ShowGrabHandleAndSetVisibility( false );
1543
1544
1545       if ( mPreEditFlag )
1546       {
1547         // PreEdit will be committed here without needing a commit from IMF.  Remove pre-edit underline and reset flags which
1548         // converts the pre-edit word being displayed to a committed word.
1549         if ( !mUnderlinedPriorToPreEdit )
1550         {
1551           TextStyle style;
1552           style.SetUnderline( false );
1553           ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1554         }
1555         mPreEditFlag = false;
1556         mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1557         // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1558         PreEditReset( false );
1559       }
1560       mCursorPosition = 0;
1561
1562       mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1563       ReturnClosestIndex( longPress.localPoint, mCursorPosition );
1564
1565       ImfManager imfManager = ImfManager::Get();
1566       if ( imfManager )
1567       {
1568         imfManager.SetCursorPosition ( mCursorPosition );
1569         imfManager.NotifyCursorPosition();
1570       }
1571       std::size_t start = 0;
1572       std::size_t end = 0;
1573       Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1574
1575       SelectText( start, end );
1576     }
1577
1578     // if no text but clipboard has content then show paste option, if no text and clipboard empty then do nothing
1579     if ( mClipboard.NumberOfItems() || !mStyledText.empty() )
1580     {
1581       ShowPopupCutCopyPaste();
1582     }
1583   }
1584 }
1585
1586 void TextInput::OnClipboardTextSelected( ClipboardEventNotifier& notifier )
1587 {
1588   const Text clipboardText( notifier.GetContent() );
1589   PasteText( clipboardText );
1590
1591   SetCursorVisibility( true );
1592   StartCursorBlinkTimer();
1593
1594   ShowGrabHandleAndSetVisibility( false );
1595
1596
1597   HidePopup();
1598 }
1599
1600 bool TextInput::OnPopupButtonPressed( Toolkit::Button button )
1601 {
1602   mPopUpPanel.PressedSignal().Disconnect( this, &TextInput::OnPopupButtonPressed );
1603
1604   const std::string& name = button.GetName();
1605
1606   if(name == TextInputPopup::OPTION_SELECT_WORD)
1607   {
1608     std::size_t start = 0;
1609     std::size_t end = 0;
1610     Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1611
1612     SelectText( start, end );
1613   }
1614   else if(name == TextInputPopup::OPTION_SELECT_ALL)
1615   {
1616     SetCursorVisibility(false);
1617     StopCursorBlinkTimer();
1618
1619     std::size_t end = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
1620     std::size_t start = 0;
1621
1622     SelectText( start, end );
1623   }
1624   else if(name == TextInputPopup::OPTION_CUT)
1625   {
1626     bool ret = CopySelectedTextToClipboard();
1627
1628     if ( ret )
1629     {
1630       DeleteHighlightedText( true );
1631       CursorUpdate();
1632     }
1633
1634     SetCursorVisibility( true );
1635     StartCursorBlinkTimer();
1636
1637     HidePopup();
1638   }
1639   else if(name == TextInputPopup::OPTION_COPY)
1640   {
1641     CopySelectedTextToClipboard();
1642
1643     RemoveHighlight();
1644
1645     SetCursorVisibility( true );
1646     StartCursorBlinkTimer();
1647
1648     HidePopup();
1649   }
1650   else if(name == TextInputPopup::OPTION_PASTE)
1651   {
1652     const Text retrievedString( mClipboard.GetItem( 0 ) );  // currently can only get first item in clip board, index 0;
1653
1654     PasteText(retrievedString);
1655
1656     SetCursorVisibility( true );
1657     StartCursorBlinkTimer();
1658
1659     ShowGrabHandleAndSetVisibility( false );
1660
1661     HidePopup();
1662   }
1663   else if(name == TextInputPopup::OPTION_CLIPBOARD)
1664   {
1665     // In the case of clipboard being shown we do not want to show updated pop-up after hide animation completes
1666     // Hence pass the false parameter for signalFinished.
1667     HidePopup( true, false );
1668     mClipboard.ShowClipboard();
1669   }
1670
1671   return false;
1672 }
1673
1674 bool TextInput::OnCursorBlinkTimerTick()
1675 {
1676   // Cursor blinking
1677   mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1678   if ( mCursorRTLEnabled )
1679   {
1680     mCursorRTL.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1681   }
1682   mCursorBlinkStatus = !mCursorBlinkStatus;
1683
1684   return true;
1685 }
1686
1687 void TextInput::OnPopupHideFinished(TextInputPopup& popup)
1688 {
1689   popup.HideFinishedSignal().Disconnect( this, &TextInput::OnPopupHideFinished );
1690
1691   // Change Popup menu to Cut/Copy/Paste if text has been selected.
1692   if(mHighlightMeshActor && mState == StateEdit)
1693   {
1694     ShowPopupCutCopyPaste();
1695   }
1696 }
1697
1698 //FIXME this routine needs to be re-written as it contains too many branches.
1699 bool TextInput::OnKeyDownEvent(const KeyEvent& event)
1700 {
1701   std::string keyName = event.keyPressedName;
1702   std::string keyString = event.keyPressed;
1703
1704   DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyDownEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1705
1706   // Do not consume "Tab" and "Escape" keys.
1707   if(keyName == "Tab" || keyName == "Escape")
1708   {
1709     // Escape key to end the edit mode
1710     EndEditMode();
1711
1712     return false;
1713   }
1714
1715   HidePopup(); // If Pop-up shown then hides it as editing text.
1716
1717   // Update Flag, indicates whether to update the text-input contents or not.
1718   // Any key stroke that results in a visual change of the text-input should
1719   // set this flag to true.
1720   bool update(false);
1721
1722   // Whether to scroll text to cursor position.
1723   // Scroll is needed always the cursor is updated and after the pre-edit is received.
1724   bool scroll = false;
1725
1726   if (keyName == "Return")
1727   {
1728     if ( mNumberOflinesLimit > 1) // Prevents New line character / Return adding an extra line if limit set to 1
1729     {
1730       bool preEditFlagPreviouslySet( mPreEditFlag );
1731
1732       if (mHighlightMeshActor)
1733       {
1734         // replaces highlighted text with new line
1735         DeleteHighlightedText( false );
1736       }
1737       mCursorPosition = mCursorPosition + InsertAt( Text( NEWLINE ), mCursorPosition, 0 );
1738
1739       // If we are in pre-edit mode then pressing enter will cause a commit.  But the commit string does not include the
1740       // '\n' character so we need to ensure that the immediately following commit knows how it occurred.
1741       if ( mPreEditFlag )
1742       {
1743         mCommitByKeyInput = true;
1744       }
1745
1746       // If attempting to insert a new-line brings us out of PreEdit mode, then we should not ignore the next commit.
1747       if ( preEditFlagPreviouslySet && !mPreEditFlag )
1748       {
1749         mPreEditFlag = true;
1750         mIgnoreCommitFlag = false;
1751       }
1752       EmitTextModified();
1753       update = true;
1754     }
1755     else
1756     {
1757       RemoveHighlight();
1758     }
1759   } // Return
1760   else if ( keyName == "space" )
1761   {
1762     if ( mHighlightMeshActor )
1763     {
1764       // Some text is selected so erase it before adding space.
1765       DeleteHighlightedText( true );
1766       update = true;
1767     }
1768
1769     mCursorPosition = mCursorPosition + InsertAt(Text(keyString), mCursorPosition, 0);
1770
1771     // If we are in pre-edit mode then pressing the space-bar will cause a commit.  But the commit string does not include the
1772     // ' ' character so we need to ensure that the immediately following commit knows how it occurred.
1773     if ( mPreEditFlag )
1774     {
1775       mCommitByKeyInput = true;
1776     }
1777     EmitTextModified();
1778     update = true;
1779   } // space
1780   else if (keyName == "BackSpace")
1781   {
1782     if ( mHighlightMeshActor )
1783     {
1784       // Some text is selected so erase it
1785       DeleteHighlightedText( true );
1786       update = true;
1787     }
1788     else
1789     {
1790       if ( mCursorPosition > 0 )
1791       {
1792         DeleteCharacter( mCursorPosition );
1793         update = true;
1794       }
1795     }
1796     EmitTextModified();
1797   } // BackSpace
1798   else if (keyName == "Right")
1799   {
1800     AdvanceCursor();
1801     RemoveHighlight();
1802   }
1803   else if (keyName == "Left")
1804   {
1805     AdvanceCursor(true);
1806     RemoveHighlight();
1807   }
1808   else // event is a character
1809   {
1810     // Some text may be selected, hiding keyboard causes an empty keystring to be sent, we don't want to delete highlight in this case
1811     if ( !keyString.empty() )
1812     {
1813       if ( mHighlightMeshActor )
1814       {
1815         // replaces highlighted text with new character
1816         DeleteHighlightedText( false );
1817       }
1818
1819
1820       // Received key String
1821       mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, 0 );
1822       update = true;
1823       EmitTextModified();
1824     }
1825   }
1826
1827   // If key event has resulted in a change in the text/cursor, then trigger a relayout of text
1828   // as this is a costly operation.
1829   if(update)
1830   {
1831     CursorUpdate();
1832   }
1833
1834   if(update || scroll)
1835   {
1836     if( IsScrollEnabled() )
1837     {
1838       // Calculates the new cursor position (in actor coordinates)
1839       const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
1840
1841       ScrollTextViewToMakeCursorVisible( cursorPosition );
1842     }
1843   }
1844
1845   return true;
1846 }
1847
1848 bool TextInput::OnKeyUpEvent(const KeyEvent& event)
1849 {
1850   std::string keyName = event.keyPressedName;
1851   std::string keyString = event.keyPressed;
1852
1853   DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyUpEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1854
1855   // The selected text become deselected when the key code is DALI_KEY_BACK.
1856   if( IsTextSelected() && ( keyName == "XF86Stop" || keyName == "XF86Send") )
1857   {
1858     DeSelectText();
1859     return true;
1860   }
1861
1862   return false;
1863 }
1864
1865 void TextInput::OnTextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition )
1866 {
1867   // Updates the stored scroll position.
1868   mTextLayoutInfo.mScrollOffset = textView.GetScrollPosition();
1869
1870   const Vector3& controlSize = GetControlSize();
1871   Size cursorSize( CURSOR_THICKNESS, 0.f );
1872
1873   // Updates the cursor and grab handle position and visibility.
1874   if( mGrabHandle || mCursor )
1875   {
1876     cursorSize.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height;
1877     const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1878
1879     mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( cursorPosition, cursorSize, controlSize );
1880
1881     mActualGrabHandlePosition = cursorPosition.GetVectorXY();
1882
1883     if( mGrabHandle )
1884     {
1885       ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1886       mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1887     }
1888
1889     if( mCursor )
1890     {
1891       mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
1892       mCursor.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1893     }
1894   }
1895
1896   // Updates the selection handles and highlighted text position and visibility.
1897   if( mSelectionHandleOne && mSelectionHandleTwo )
1898   {
1899     const Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition(mSelectionHandleOnePosition);
1900     const Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition(mSelectionHandleTwoPosition);
1901     cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleOnePosition ) ).mSize.height;
1902     const bool isSelectionHandleOneVisible = IsPositionInsideBoundaries( cursorPositionOne, cursorSize, controlSize );
1903     cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleTwoPosition ) ).mSize.height;
1904     const bool isSelectionHandleTwoVisible = IsPositionInsideBoundaries( cursorPositionTwo, cursorSize, controlSize );
1905
1906     mSelectionHandleOneActualPosition = cursorPositionOne.GetVectorXY();
1907     mSelectionHandleTwoActualPosition = cursorPositionTwo.GetVectorXY();
1908
1909     mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
1910     mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
1911     mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
1912     mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
1913
1914     if( mHighlightMeshActor )
1915     {
1916       mHighlightMeshActor.SetVisible( true );
1917       UpdateHighlight();
1918     }
1919   }
1920 }
1921
1922 void TextInput::ScrollTextViewToMakeCursorVisible( const Vector3& cursorPosition )
1923 {
1924   // Scroll the text to make the cursor visible.
1925   const Size cursorSize( CURSOR_THICKNESS,
1926                          GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height );
1927
1928   // Need to scroll the text to make the cursor visible and to cover the whole text-input area.
1929
1930   const Vector3& controlSize = GetControlSize();
1931
1932   // Calculates the new scroll position.
1933   Vector2 scrollOffset = mTextLayoutInfo.mScrollOffset;
1934   if( ( cursorPosition.x < 0.f ) || ( cursorPosition.x > controlSize.width ) )
1935   {
1936     scrollOffset.x += cursorPosition.x;
1937   }
1938
1939   if( cursorPosition.y - cursorSize.height < 0.f )
1940   {
1941     scrollOffset.y += ( cursorPosition.y - cursorSize.height );
1942   }
1943   else if( cursorPosition.y > controlSize.height )
1944   {
1945     scrollOffset.y += cursorPosition.y;
1946   }
1947
1948   // Sets the new scroll position.
1949   SetScrollPosition( Vector2::ZERO ); // TODO: need to reset to the zero position in order to make the scroll trim to work.
1950   SetScrollPosition( scrollOffset );
1951 }
1952
1953 void TextInput::StartScrollTimer()
1954 {
1955   if( !mScrollTimer )
1956   {
1957     mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1958     mScrollTimer.TickSignal().Connect( this, &TextInput::OnScrollTimerTick );
1959   }
1960
1961   if( !mScrollTimer.IsRunning() )
1962   {
1963     mScrollTimer.Start();
1964   }
1965 }
1966
1967 void TextInput::StopScrollTimer()
1968 {
1969   if( mScrollTimer )
1970   {
1971     mScrollTimer.Stop();
1972   }
1973 }
1974
1975 bool TextInput::OnScrollTimerTick()
1976 {
1977   // TODO: need to set the new style accordingly the new handle position.
1978
1979   if( !( mGrabHandleVisibility && mGrabHandle ) && !( mSelectionHandleOne && mSelectionHandleTwo ) )
1980   {
1981     // nothing to do if all handles are invisible or doesn't exist.
1982     return true;
1983   }
1984
1985   // Text scrolling
1986
1987   // Choose between the grab handle or the selection handles.
1988   Vector3& actualHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mActualGrabHandlePosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
1989   std::size_t& handlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCursorPosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
1990   Vector3& currentHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCurrentHandlePosition : mCurrentSelectionHandlePosition;
1991
1992   std::size_t newCursorPosition = 0;
1993   ReturnClosestIndex( actualHandlePosition.GetVectorXY(), newCursorPosition );
1994
1995   // Whether the handle's position is different of the previous one and in the case of the selection handle,
1996   // the new selection handle's position needs to be different of the other one.
1997   const bool differentSelectionHandles = ( mGrabHandleVisibility && mGrabHandle ) ? newCursorPosition != handlePosition :
1998                                          ( mCurrentSelectionId == HandleOne ) ? ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleTwoPosition ) :
1999                                                                                 ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleOnePosition );
2000
2001   if( differentSelectionHandles )
2002   {
2003     handlePosition = newCursorPosition;
2004
2005     const Vector3 actualPosition = GetActualPositionFromCharacterPosition( newCursorPosition );
2006
2007     Vector2 scrollDelta = ( actualPosition - currentHandlePosition ).GetVectorXY();
2008
2009     Vector2 scrollPosition = mDisplayedTextView.GetScrollPosition();
2010     scrollPosition += scrollDelta;
2011     SetScrollPosition( scrollPosition );
2012
2013     if( mDisplayedTextView.IsScrollPositionTrimmed() )
2014     {
2015       StopScrollTimer();
2016     }
2017
2018     currentHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition ).GetVectorXY();
2019   }
2020
2021   actualHandlePosition.x += mScrollDisplacement.x;
2022   actualHandlePosition.y += mScrollDisplacement.y;
2023
2024   return true;
2025 }
2026
2027 // Public Internal Methods (public for testing purpose)
2028
2029 void TextInput::SetUpTouchEvents()
2030 {
2031   if ( !mTapDetector )
2032   {
2033     mTapDetector = TapGestureDetector::New();
2034     // Attach the actors and connect the signal
2035     mTapDetector.Attach(Self());
2036
2037     // As contains children which may register for tap the default control detector is not used.
2038     mTapDetector.DetectedSignal().Connect(this, &TextInput::OnTextTap);
2039   }
2040
2041   if ( !mDoubleTapDetector )
2042   {
2043     mDoubleTapDetector = TapGestureDetector::New();
2044     mDoubleTapDetector.SetTapsRequired( 2 );
2045     mDoubleTapDetector.DetectedSignal().Connect(this, &TextInput::OnDoubleTap);
2046
2047     // Only attach and detach the actor to the double tap detector when we enter/leave edit mode
2048     // so that we do not, unnecessarily, have a double tap request all the time
2049   }
2050
2051   if ( !mPanGestureDetector )
2052   {
2053     mPanGestureDetector = PanGestureDetector::New();
2054     mPanGestureDetector.DetectedSignal().Connect(this, &TextInput::OnHandlePan);
2055   }
2056
2057   if ( !mLongPressDetector )
2058   {
2059     mLongPressDetector = LongPressGestureDetector::New();
2060     mLongPressDetector.DetectedSignal().Connect(this, &TextInput::OnLongPress);
2061     mLongPressDetector.Attach(Self());
2062   }
2063 }
2064
2065 void TextInput::CreateTextViewActor()
2066 {
2067   mDisplayedTextView = Toolkit::TextView::New();
2068   mDisplayedTextView.SetMarkupProcessingEnabled( mMarkUpEnabled );
2069   mDisplayedTextView.SetParentOrigin(ParentOrigin::TOP_LEFT);
2070   mDisplayedTextView.SetAnchorPoint(AnchorPoint::TOP_LEFT);
2071   mDisplayedTextView.SetMultilinePolicy(Toolkit::TextView::SplitByWord);
2072   mDisplayedTextView.SetWidthExceedPolicy( Toolkit::TextView::Original );
2073   mDisplayedTextView.SetHeightExceedPolicy( Toolkit::TextView::Original );
2074   mDisplayedTextView.SetLineJustification( Toolkit::TextView::Left );
2075   mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>( Toolkit::Alignment::HorizontalLeft | Toolkit::Alignment::VerticalTop ) );
2076   mDisplayedTextView.SetPosition( Vector3( 0.0f, 0.0f, DISPLAYED_TEXT_VIEW_Z_OFFSET ) );
2077   mDisplayedTextView.SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
2078
2079   mDisplayedTextView.ScrolledSignal().Connect( this, &TextInput::OnTextViewScrolled );
2080
2081   Self().Add( mDisplayedTextView );
2082 }
2083
2084 // Start a timer to initiate, used by the cursor to blink.
2085 void TextInput::StartCursorBlinkTimer()
2086 {
2087   if ( !mCursorBlinkTimer )
2088   {
2089     mCursorBlinkTimer = Timer::New( CURSOR_BLINK_INTERVAL );
2090     mCursorBlinkTimer.TickSignal().Connect( this, &TextInput::OnCursorBlinkTimerTick );
2091   }
2092
2093   if ( !mCursorBlinkTimer.IsRunning() )
2094   {
2095     mCursorBlinkTimer.Start();
2096   }
2097 }
2098
2099 // Start a timer to initiate, used by the cursor to blink.
2100 void TextInput::StopCursorBlinkTimer()
2101 {
2102   if ( mCursorBlinkTimer )
2103   {
2104     mCursorBlinkTimer.Stop();
2105   }
2106 }
2107
2108 void TextInput::StartEditMode()
2109 {
2110   DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput StartEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2111
2112   if(!mEditModeActive)
2113   {
2114     SetKeyInputFocus();
2115   }
2116
2117   if ( mDoubleTapDetector )
2118   {
2119     mDoubleTapDetector.Attach( Self() );
2120   }
2121 }
2122
2123 void TextInput::EndEditMode()
2124 {
2125   DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput EndEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2126
2127   ClearKeyInputFocus();
2128
2129   if ( mDoubleTapDetector )
2130   {
2131     mDoubleTapDetector.Detach( Self() );
2132   }
2133 }
2134
2135 void TextInput::ApplyPreEditStyle( std::size_t preEditStartPosition, std::size_t preEditStringLength )
2136 {
2137   if ( mPreEditFlag && ( preEditStringLength > 0 ) )
2138   {
2139     mUnderlinedPriorToPreEdit = mInputStyle.IsUnderlineEnabled();
2140     TextStyle style;
2141     style.SetUnderline( true );
2142     ApplyStyleToRange( style, TextStyle::UNDERLINE , preEditStartPosition, preEditStartPosition + preEditStringLength -1 );
2143   }
2144 }
2145
2146 void TextInput::RemovePreEditStyle()
2147 {
2148   if ( !mUnderlinedPriorToPreEdit )
2149   {
2150     TextStyle style;
2151     style.SetUnderline( false );
2152     SetActiveStyle( style, TextStyle::UNDERLINE );
2153   }
2154 }
2155
2156 // IMF related methods
2157
2158
2159 ImfManager::ImfCallbackData TextInput::ImfEventReceived( Dali::ImfManager& imfManager, const ImfManager::ImfEventData&  imfEvent )
2160 {
2161   bool update( false );
2162   bool preeditResetRequired ( false );
2163
2164   if (imfEvent.eventName != ImfManager::GETSURROUNDING )
2165   {
2166     HidePopup(); // If Pop-up shown then hides it as editing text.
2167   }
2168
2169   switch ( imfEvent.eventName )
2170   {
2171     case ImfManager::PREEDIT:
2172     {
2173       mIgnoreFirstCommitFlag = false;
2174
2175       // Some text may be selected, hiding keyboard causes an empty predictive string to be sent, we don't want to delete highlight in this case
2176       if ( mHighlightMeshActor &&  (!imfEvent.predictiveString.empty()) )
2177       {
2178         // replaces highlighted text with new character
2179         DeleteHighlightedText( false );
2180       }
2181
2182       preeditResetRequired = PreEditReceived( imfEvent.predictiveString, imfEvent.cursorOffset );
2183
2184       if( IsScrollEnabled() )
2185       {
2186         // Calculates the new cursor position (in actor coordinates)
2187         const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2188         ScrollTextViewToMakeCursorVisible( cursorPosition );
2189       }
2190
2191       update = true;
2192
2193       break;
2194     }
2195     case ImfManager::COMMIT:
2196     {
2197       if( mIgnoreFirstCommitFlag )
2198       {
2199         // Do not commit in this case when keyboard sends a commit when shows for the first time (work-around for imf keyboard).
2200         mIgnoreFirstCommitFlag = false;
2201       }
2202       else
2203       {
2204         // A Commit message is a word that has been accepted, it may have been a pre-edit word previously but now commited.
2205
2206         // Some text may be selected, hiding keyboard causes an empty predictive string to be sent, we don't want to delete highlight in this case
2207         if ( mHighlightMeshActor &&  (!imfEvent.predictiveString.empty()) )
2208         {
2209           // replaces highlighted text with new character
2210           DeleteHighlightedText( false );
2211         }
2212
2213        // A PreEditReset can cause a commit message to be sent, the Ignore Commit flag is used in scenarios where the word is
2214        // not needed, one such scenario is when the pre-edit word is too long to fit.
2215        if ( !mIgnoreCommitFlag )
2216        {
2217          update = CommitReceived( imfEvent.predictiveString );
2218        }
2219        else
2220        {
2221          mIgnoreCommitFlag = false; // reset ignore flag so next commit is acted upon.
2222        }
2223       }
2224
2225       if( update )
2226       {
2227         if( IsScrollEnabled() )
2228         {
2229           // Calculates the new cursor position (in actor coordinates)
2230           const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2231
2232           ScrollTextViewToMakeCursorVisible( cursorPosition );
2233         }
2234       }
2235       break;
2236     }
2237     case ImfManager::DELETESURROUNDING:
2238     {
2239       DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - delete surrounding mPreEditFlag[%s] cursor offset[%d] characters to delete[%d] position to delete[%u] \n",
2240                      (mPreEditFlag)?"true":"false", imfEvent.cursorOffset, imfEvent.numberOfChars, static_cast<std::size_t>( mCursorPosition+imfEvent.cursorOffset) );
2241
2242       mPreEditFlag = false;
2243
2244       std::size_t toDelete = 0;
2245       std::size_t numberOfCharacters = 0;
2246
2247       if( mHighlightMeshActor )
2248       {
2249         // delete highlighted text.
2250         toDelete = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2251         numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - toDelete;
2252       }
2253       else
2254       {
2255         if( std::abs( imfEvent.cursorOffset ) < mCursorPosition )
2256         {
2257           toDelete = mCursorPosition + imfEvent.cursorOffset;
2258         }
2259         if( toDelete + imfEvent.numberOfChars > mStyledText.size() )
2260         {
2261           numberOfCharacters = mStyledText.size() - toDelete;
2262         }
2263         else
2264         {
2265           numberOfCharacters = imfEvent.numberOfChars;
2266         }
2267       }
2268       DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding pre-delete range mCursorPosition[%u] \n", mCursorPosition);
2269       DeleteRange( toDelete, numberOfCharacters );
2270
2271       mCursorPosition = toDelete;
2272       mNumberOfSurroundingCharactersDeleted = numberOfCharacters;
2273
2274       EmitTextModified();
2275
2276       DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding post-delete range mCursorPosition[%u] \n", mCursorPosition);
2277       break;
2278     }
2279     case ImfManager::GETSURROUNDING:
2280     {
2281       // If text is selected/highlighted and surrounding text received we do not want the keyboard to store the word at cursor and return it as a predictive word along with
2282       // the next key pressed.  Instead the Select function sets the cursor position and surrounding text.
2283       if (! ( mHighlightMeshActor || mSelectingText ) )
2284       {
2285         std::string text( GetText() );
2286         DALI_LOG_INFO( gLogFilter, Debug::General, "OnKey - surrounding text - set text [%s] and cursor[%u] \n", text.c_str(), mCursorPosition );
2287
2288         imfManager.SetCursorPosition( mCursorPosition );
2289         imfManager.SetSurroundingText( text );
2290       }
2291
2292       if( 0 != mNumberOfSurroundingCharactersDeleted )
2293       {
2294         mDisplayedTextView.RemoveTextFrom( mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2295         mNumberOfSurroundingCharactersDeleted = 0;
2296
2297         if( mStyledText.empty() )
2298         {
2299           // Styled text is empty, so set the placeholder text.
2300           mDisplayedTextView.SetText( mStyledPlaceHolderText );
2301           mPlaceHolderSet = true;
2302         }
2303       }
2304       break;
2305     }
2306     case ImfManager::VOID:
2307     {
2308       DALI_ASSERT_DEBUG( false );
2309     }
2310   } // end switch
2311
2312   ImfManager::ImfCallbackData callbackData( update, mCursorPosition, GetText(), preeditResetRequired );
2313
2314   return callbackData;
2315 }
2316
2317 bool TextInput::PreEditReceived(const std::string& keyString, std::size_t cursorOffset )
2318 {
2319   mPreserveCursorPosition = false;  // As in pre-edit state we should have the cursor at the end of the word displayed not last touch position.
2320
2321   DALI_LOG_INFO(gLogFilter, Debug::General, ">>PreEditReceived preserveCursorPos[%d] mCursorPos[%d] mPreEditFlag[%d]\n",
2322                 mPreserveCursorPosition, mCursorPosition, mPreEditFlag );
2323
2324   bool preeditResetRequest ( false );
2325
2326   if( mPreEditFlag ) // Already in pre-edit state.
2327   {
2328     if( mStyledText.size() >= mMaxStringLength )
2329     {
2330       DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived styledTextSize >= mMaxStringLength \n");
2331       // Cannot fit these characters into field, clear pre-edit.
2332       if ( !mUnderlinedPriorToPreEdit )
2333       {
2334         TextStyle style;
2335         style.SetUnderline( false );
2336         ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
2337       }
2338       mIgnoreCommitFlag = true;
2339       preeditResetRequest = false; // this will reset the keyboard's predictive suggestions.
2340       mPreEditFlag = false;
2341       EmitMaxInputCharactersReachedSignal();
2342     }
2343     else
2344     {
2345       // delete existing pre-edit string
2346       const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2347
2348       // Store new pre-edit string
2349       mPreEditString.SetText( keyString );
2350
2351       if ( keyString.empty() )
2352       {
2353         mPreEditFlag = false;
2354         mCursorPosition = mPreEditStartPosition;
2355
2356         if( mStyledText.empty() )
2357         {
2358           // Styled text is empty, so set the placeholder text.
2359           mDisplayedTextView.SetText( mStyledPlaceHolderText );
2360           mPlaceHolderSet = true;
2361         }
2362         else
2363         {
2364           mDisplayedTextView.RemoveTextFrom( mPreEditStartPosition, numberOfCharactersToReplace );
2365         }
2366         GetTextLayoutInfo();
2367         EmitTextModified();
2368       }
2369       else
2370       {
2371         // Insert new pre-edit string. InsertAt updates the size and position table.
2372         mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersToReplace );
2373         // If word was too long to be inserted then cursorOffset would be out of range as keyboard assumes there is not limit. Hence use of std::min.
2374         mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2375         ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2376         DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] \n", mCursorPosition);
2377         EmitTextModified();
2378       }
2379       // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2380       DrawCursor();
2381     }
2382   }
2383   else  // mPreEditFlag not set
2384   {
2385     if ( !keyString.empty() ) // Imf can send an empty pre-edit followed by Backspace instead of a commit.
2386     {
2387       DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived Initial Pre-Edit string \n");
2388       // new pre-edit so move into pre-edit state by setting flag
2389       mPreEditFlag = true;
2390       mPreEditString.SetText( keyString ); // store new pre-edit string
2391       mPreEditStartPosition = mCursorPosition; // store starting cursor position of pre-edit so know where to re-start from
2392       mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, 0 );
2393       // If word was too long to be inserted then cursorOffset would be out of range as keyboard assumes there is not limit. Hence use of std::min.
2394       mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2395       ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2396       DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] mPreEditStartPosition[%u]\n", mCursorPosition, mPreEditStartPosition);
2397       // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2398       DrawCursor();
2399       EmitTextModified();
2400     }
2401     else
2402     {
2403       DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived with empty keyString\n");
2404     }
2405   }
2406
2407   return preeditResetRequest;
2408 }
2409
2410 bool TextInput::CommitReceived(const std::string& keyString )
2411 {
2412   DALI_LOG_INFO(gLogFilter, Debug::General, ">>CommitReceived preserveCursorPos[%d] mPreEditStartPosition [%d] mCursorPos[%d] mPreEditFlag[%d] mIgnoreCommitFlag[%s]\n",
2413       mPreserveCursorPosition, mPreEditStartPosition, mCursorPosition, mPreEditFlag, (mIgnoreCommitFlag)?"true":"false" );
2414
2415   bool update( false );
2416
2417   RemovePreEditStyle();
2418
2419   const std::size_t styledTextSize( mStyledText.size() );
2420   if( styledTextSize >= mMaxStringLength )
2421   {
2422     // Cannot fit these characters into field, clear pre-edit.
2423     if ( mPreEditFlag )
2424     {
2425       mIgnoreCommitFlag = true;
2426       mPreEditFlag = false;
2427     }
2428     EmitMaxInputCharactersReachedSignal();
2429   }
2430   else
2431   {
2432     if( mPreEditFlag )
2433     {
2434       // delete existing pre-edit string
2435       const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2436       mPreEditFlag = false;
2437
2438       DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived mPreserveCursorPosition[%s] mPreEditStartPosition[%u]\n",
2439                     (mPreserveCursorPosition)?"true":"false", mPreEditStartPosition );
2440
2441       if ( mPreserveCursorPosition ) // PreEditReset has been called triggering this commit.
2442       {
2443         // No need to update cursor position as Cursor location given by touch.
2444         InsertAt( Text( keyString ), mPreEditStartPosition, numberOfCharactersToReplace );
2445         mPreserveCursorPosition = false;
2446       }
2447       else
2448       {
2449         // Cursor not set by touch so needs to be re-positioned to input more text
2450         mCursorPosition = mPreEditStartPosition + InsertAt( Text(keyString), mPreEditStartPosition, numberOfCharactersToReplace ); // update cursor position as InsertAt, re-draw cursor with this
2451
2452         // If a space or enter caused the commit then our string is one longer than the string given to us by the commit key.
2453         if ( mCommitByKeyInput )
2454         {
2455           mCursorPosition = std::min ( mCursorPosition + 1, mStyledText.size() );
2456           mCommitByKeyInput = false;
2457         }
2458       }
2459
2460       EmitTextModified();
2461
2462       if ( mSelectTextOnCommit )
2463       {
2464         SelectText(mRequestedSelection.mStartOfSelection, mRequestedSelection.mEndOfSelection );
2465       }
2466
2467       update = true;
2468     }
2469     else // mPreEditFlag not set
2470     {
2471       if ( !mIgnoreCommitFlag ) // Check if this commit should be ignored.
2472       {
2473         if( mStyledText.empty() && mPlaceHolderSet )
2474         {
2475           // If the styled text is empty and the placeholder text is set, it needs to be cleared.
2476           mDisplayedTextView.SetText( "" );
2477           mNumberOfSurroundingCharactersDeleted = 0;
2478           mPlaceHolderSet = false;
2479         }
2480         mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2481         update = true;
2482         mNumberOfSurroundingCharactersDeleted = 0;
2483         EmitTextModified();
2484       }
2485       else
2486       {
2487         mIgnoreCommitFlag = false; // Reset flag so future commits will not be ignored.
2488       }
2489     }
2490   }
2491
2492   mSelectTextOnCommit = false;
2493
2494   DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived << mCursorPos[%d] mPreEditFlag[%d] update[%s] \n",
2495                                              mCursorPosition, mPreEditFlag, (update)?"true":"false" );
2496
2497   return update;
2498 }
2499
2500 // End of IMF related methods
2501
2502 std::size_t TextInput::DeletePreEdit()
2503 {
2504   DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeletePreEdit mPreEditFlag[%s] \n", (mPreEditFlag)?"true":"false");
2505
2506   DALI_ASSERT_DEBUG( mPreEditFlag );
2507
2508   const std::size_t preEditStringLength = mPreEditString.GetLength();
2509   const std::size_t styledTextSize = mStyledText.size();
2510
2511   std::size_t endPosition = mPreEditStartPosition + preEditStringLength;
2512
2513   // Prevents erase items outside mStyledText bounds.
2514   if( mPreEditStartPosition > styledTextSize )
2515   {
2516     DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. mPreEditStartPosition > mStyledText.size()" );
2517     mPreEditStartPosition = styledTextSize;
2518   }
2519
2520   if( ( endPosition > styledTextSize ) || ( endPosition < mPreEditStartPosition ) )
2521   {
2522     DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. ( endPosition > mStyledText.size() ) || ( endPosition < mPreEditStartPosition )" );
2523     endPosition = styledTextSize;
2524   }
2525
2526   mStyledText.erase( mStyledText.begin() + mPreEditStartPosition, mStyledText.begin() + endPosition );
2527
2528   // DeletePreEdit() doesn't remove characters from the text-view because may be followed by an InsertAt() which inserts characters,
2529   // in that case, the Insert should use the returned number of deleted characters and replace the text which helps the text-view to
2530   // reuse glyphs.
2531   // In case DeletePreEdit() is not followed by an InsertAt() characters must be deleted after this call.
2532
2533   return preEditStringLength;
2534 }
2535
2536 void TextInput::PreEditReset( bool preserveCursorPosition )
2537 {
2538   DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReset preserveCursorPos[%d] mCursorPos[%d] \n",
2539                 preserveCursorPosition, mCursorPosition);
2540
2541   // Store flag to indicate that we do not want to lose the cursor position as the reset may have occurred due to touch event moving the cursor.
2542   mPreserveCursorPosition = preserveCursorPosition;
2543
2544   // Reset incase we are in a pre-edit state.
2545   ImfManager imfManager = ImfManager::Get();
2546   if ( imfManager )
2547   {
2548     imfManager.Reset(); // Will trigger a commit message
2549   }
2550 }
2551
2552 void TextInput::CursorUpdate()
2553 {
2554   DrawCursor();
2555
2556   ImfManager imfManager = ImfManager::Get();
2557   if ( imfManager )
2558   {
2559     std::string text( GetText() );
2560     imfManager.SetSurroundingText( text );  // Notifying IMF of a cursor change triggers a surrounding text request so updating it now.
2561     imfManager.SetCursorPosition ( mCursorPosition );
2562     imfManager.NotifyCursorPosition();
2563   }
2564 }
2565
2566 /* Delete highlighted characters redisplay*/
2567 void TextInput::DeleteHighlightedText( bool inheritStyle )
2568 {
2569   DALI_LOG_INFO( gLogFilter, Debug::General, "DeleteHighlightedText handlePosOne[%u] handlePosTwo[%u]\n", mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
2570
2571   if(mHighlightMeshActor)
2572   {
2573     mCursorPosition = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2574
2575     MarkupProcessor::StyledTextArray::iterator start = mStyledText.begin() + mCursorPosition;
2576     MarkupProcessor::StyledTextArray::iterator end =  mStyledText.begin() + std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2577
2578     // Get the styled text of the characters to be deleted as it may be needed if
2579     // the "exceed the text-input's boundaries" option is disabled.
2580     MarkupProcessor::StyledTextArray styledCharactersToDelete;
2581
2582     styledCharactersToDelete.insert( styledCharactersToDelete.begin(), start, end );
2583
2584     mStyledText.erase( start, end ); // erase range of characters
2585
2586     // Remove text from TextView.
2587
2588     if( mStyledText.empty() )
2589     {
2590       // Styled text is empty, so set the placeholder text.
2591       mDisplayedTextView.SetText( mStyledPlaceHolderText );
2592       mPlaceHolderSet = true;
2593     }
2594     else
2595     {
2596       const std::size_t numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - mCursorPosition;
2597
2598       mDisplayedTextView.RemoveTextFrom( mCursorPosition, numberOfCharacters );
2599
2600       // It may happen than after removing a white space or a new line character,
2601       // two words merge, this new word could be big enough to not fit in its
2602       // current line, so moved to the next one, and make some part of the text to
2603       // exceed the text-input's boundary.
2604       if( !mExceedEnabled )
2605       {
2606         // Get the new text layout after removing some characters.
2607         mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2608
2609         // Get text-input's size.
2610         const Vector3& size = GetControlSize();
2611
2612         if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2613             ( mTextLayoutInfo.mTextSize.height > size.height ) )
2614         {
2615           mDisplayedTextView.InsertTextAt( mCursorPosition, styledCharactersToDelete );
2616
2617           mStyledText.insert( mStyledText.begin() + mCursorPosition,
2618                               styledCharactersToDelete.begin(),
2619                               styledCharactersToDelete.end() );
2620         }
2621       }
2622     }
2623     GetTextLayoutInfo();
2624
2625     RemoveHighlight();
2626
2627     if( inheritStyle )
2628     {
2629       const TextStyle oldInputStyle( mInputStyle );
2630
2631       mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2632
2633       if( oldInputStyle != mInputStyle )
2634       {
2635         // Updates the line height accordingly with the input style.
2636         UpdateLineHeight();
2637
2638         EmitStyleChangedSignal();
2639       }
2640     }
2641   }
2642 }
2643
2644 void TextInput::DeleteRange( const std::size_t start, const std::size_t ncharacters )
2645 {
2646   DALI_ASSERT_DEBUG( start <= mStyledText.size() );
2647   DALI_ASSERT_DEBUG( !mStyledText.empty() );
2648
2649   DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeleteRange pre mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2650
2651
2652   if ( ( !mStyledText.empty()) && ( ( start + ncharacters ) <= mStyledText.size() ) )
2653   {
2654     MarkupProcessor::StyledTextArray::iterator itStart =  mStyledText.begin() + start;
2655     MarkupProcessor::StyledTextArray::iterator itEnd =  mStyledText.begin() + start + ncharacters;
2656
2657     mStyledText.erase(itStart, itEnd);
2658
2659     // update the selection handles if they are visible.
2660     if( mHighlightMeshActor )
2661     {
2662       std::size_t& minHandle = ( mSelectionHandleOnePosition <= mSelectionHandleTwoPosition ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition );
2663       std::size_t& maxHandle = ( mSelectionHandleTwoPosition > mSelectionHandleOnePosition ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition );
2664
2665       if( minHandle >= start + ncharacters )
2666       {
2667         minHandle -= ncharacters;
2668       }
2669       else if( ( minHandle > start ) && ( minHandle < start + ncharacters ) )
2670       {
2671         minHandle = start;
2672       }
2673
2674       if( maxHandle >= start + ncharacters )
2675       {
2676         maxHandle -= ncharacters;
2677       }
2678       else if( ( maxHandle > start ) && ( maxHandle < start + ncharacters ) )
2679       {
2680         maxHandle = start;
2681       }
2682     }
2683
2684     // Set text is not called here as currently it can not process the set text from deletion and then the set text from the in-coming pre-edit.
2685   }
2686
2687   DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteRange<< post mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2688
2689   // Although mStyledText has been set to a new text string we no longer re-draw the text or notify the cursor change.
2690   // This is a performance decision as the use of this function often means the text is being replaced or just deleted.
2691   // Mean we do not re-draw the text more than we have too.
2692 }
2693
2694 /* Delete character at current cursor position and redisplay*/
2695 void TextInput::DeleteCharacter( std::size_t positionToDelete )
2696 {
2697   // Ensure positionToDelete is not out of bounds.
2698   DALI_ASSERT_DEBUG( positionToDelete <= mStyledText.size() );
2699   DALI_ASSERT_DEBUG( !mStyledText.empty() );
2700   DALI_ASSERT_DEBUG( positionToDelete > 0 );
2701
2702   DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteCharacter positionToDelete[%u]", positionToDelete );
2703
2704
2705   if ( ( !mStyledText.empty()) && ( positionToDelete > 0 ) && positionToDelete <= mStyledText.size() )  // don't try to delete if no characters left of cursor
2706   {
2707     MarkupProcessor::StyledTextArray::iterator it =  mStyledText.begin() + positionToDelete - 1;
2708
2709     // Get the styled text of the character to be deleted as it may be needed if
2710     // the "exceed the text-input's boundaries" option is disabled.
2711     const MarkupProcessor::StyledText styledCharacterToDelete( *it );
2712
2713     mStyledText.erase(it);  // erase the character left of positionToDelete
2714
2715     if( mStyledText.empty() )
2716     {
2717       // Styled text is empty, so set the placeholder text.
2718       mDisplayedTextView.SetText( mStyledPlaceHolderText );
2719       mPlaceHolderSet = true;
2720     }
2721     else
2722     {
2723       mDisplayedTextView.RemoveTextFrom( positionToDelete - 1, 1 );
2724
2725       const Character characterToDelete = styledCharacterToDelete.mText[0];
2726
2727       // It may happen than after removing a white space or a new line character,
2728       // two words merge, this new word could be big enough to not fit in its
2729       // current line, so moved to the next one, and make some part of the text to
2730       // exceed the text-input's boundary.
2731       if( !mExceedEnabled )
2732       {
2733         if( characterToDelete.IsWhiteSpace() || characterToDelete.IsNewLine() )
2734         {
2735           // Get the new text layout after removing one character.
2736           mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2737
2738           // Get text-input's size.
2739           const Vector3& size = GetControlSize();
2740
2741           if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2742               ( mTextLayoutInfo.mTextSize.height > size.height ) )
2743           {
2744             MarkupProcessor::StyledTextArray array;
2745             array.push_back( styledCharacterToDelete );
2746             mDisplayedTextView.InsertTextAt( positionToDelete - 1, array );
2747
2748             mStyledText.insert( mStyledText.begin() + ( positionToDelete - 1 ), styledCharacterToDelete );
2749           }
2750         }
2751       }
2752     }
2753     GetTextLayoutInfo();
2754
2755     ShowGrabHandleAndSetVisibility( false );
2756
2757     mCursorPosition = positionToDelete -1;
2758
2759     const TextStyle oldInputStyle( mInputStyle );
2760
2761     mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2762
2763     if( oldInputStyle != mInputStyle )
2764     {
2765       // Updates the line height accordingly with the input style.
2766       UpdateLineHeight();
2767
2768       EmitStyleChangedSignal();
2769     }
2770   }
2771 }
2772
2773 /*Insert new character into the string and (optionally) redisplay text-input*/
2774 std::size_t TextInput::InsertAt( const Text& newText, const std::size_t insertionPosition, const std::size_t numberOfCharactersToReplace )
2775 {
2776   DALI_LOG_INFO(gLogFilter, Debug::General, "InsertAt insertionPosition[%u]\n", insertionPosition );
2777
2778   // Ensure insertionPosition is not out of bounds.
2779   DALI_ASSERT_ALWAYS( insertionPosition <= mStyledText.size() );
2780
2781   bool textExceedsMaximunNumberOfCharacters = false;
2782   bool textExceedsBoundary = false;
2783   std::size_t insertedStringLength = DoInsertAt( newText, insertionPosition, numberOfCharactersToReplace, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
2784
2785   ShowGrabHandleAndSetVisibility( false );
2786
2787   if( textExceedsMaximunNumberOfCharacters || textExceedsBoundary )
2788   {
2789     if( mPreEditFlag )
2790     {
2791       mIgnoreCommitFlag = true;
2792       mPreEditFlag = false;
2793       // A PreEditReset( false ) should be triggered from here if the keyboards predictive suggestions must be cleared.
2794       // Although can not directly call PreEditReset() as it will cause a recursive emit loop.
2795     }
2796
2797     if( textExceedsMaximunNumberOfCharacters )
2798     {
2799       EmitMaxInputCharactersReachedSignal();
2800     }
2801
2802     if( textExceedsBoundary )
2803     {
2804       EmitInputTextExceedsBoundariesSignal();
2805       PreEditReset( false );
2806     }
2807   }
2808
2809   return insertedStringLength;
2810 }
2811
2812 ImageActor TextInput::CreateCursor( Image cursorImage, const Vector4& border )
2813 {
2814   ImageActor cursor;
2815
2816   if ( cursorImage )
2817   {
2818     cursor = ImageActor::New( cursorImage );
2819   }
2820   else
2821   {
2822     cursor = ImageActor::New( Image::New( DEFAULT_CURSOR ) );
2823   }
2824
2825   cursor.SetStyle(ImageActor::STYLE_NINE_PATCH);
2826   cursor.SetNinePatchBorder( border );
2827
2828   cursor.SetParentOrigin(ParentOrigin::TOP_LEFT);
2829   cursor.SetAnchorPoint(AnchorPoint::BOTTOM_CENTER);
2830   cursor.SetVisible(false);
2831
2832   return cursor;
2833 }
2834
2835 void TextInput::AdvanceCursor(bool reverse, std::size_t places)
2836 {
2837   // As cursor is not moving due to grab handle, handle should be hidden.
2838   ShowGrabHandleAndSetVisibility( false );
2839
2840   bool cursorPositionChanged = false;
2841   if (reverse)
2842   {
2843     if ( mCursorPosition >= places )
2844     {
2845       mCursorPosition = mCursorPosition - places;
2846       cursorPositionChanged = true;
2847     }
2848   }
2849   else
2850   {
2851     if ((mCursorPosition + places) <= mStyledText.size())
2852     {
2853       mCursorPosition = mCursorPosition + places;
2854       cursorPositionChanged = true;
2855     }
2856   }
2857
2858   if( cursorPositionChanged )
2859   {
2860     const std::size_t cursorPositionForStyle = ( 0 == mCursorPosition ? 0 : mCursorPosition - 1 );
2861
2862     const TextStyle oldInputStyle( mInputStyle );
2863     mInputStyle = GetStyleAt( cursorPositionForStyle ); // Inherit style from selected position.
2864
2865     DrawCursor();
2866
2867     if( oldInputStyle != mInputStyle )
2868     {
2869       // Updates the line height accordingly with the input style.
2870       UpdateLineHeight();
2871
2872       EmitStyleChangedSignal();
2873     }
2874
2875     ImfManager imfManager = ImfManager::Get();
2876     if ( imfManager )
2877     {
2878       imfManager.SetCursorPosition ( mCursorPosition );
2879       imfManager.NotifyCursorPosition();
2880     }
2881   }
2882 }
2883
2884 void TextInput::DrawCursor(const std::size_t nthChar)
2885 {
2886   // Get height of cursor and set its size
2887   Size size( CURSOR_THICKNESS, 0.0f );
2888   if (!mTextLayoutInfo.mCharacterLayoutInfoTable.empty())
2889   {
2890     size.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height;
2891   }
2892   else
2893   {
2894     // Measure Font so know how big text will be if no initial text to measure.
2895     size.height = mLineHeight;
2896   }
2897
2898   mCursor.SetSize(size);
2899
2900   // If the character is italic then the cursor also tilts.
2901   mCursor.SetRotation( mInputStyle.IsItalicsEnabled() ? Degree( mInputStyle.GetItalicsAngle() - CURSOR_ANGLE_OFFSET ) : Degree( 0.f ), Vector3::ZAXIS );
2902
2903   DALI_ASSERT_DEBUG( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
2904
2905   if ( ( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) )
2906   {
2907     Vector3 altPosition;    // Alternate (i.e. opposite direction) cursor position.
2908     bool altPositionValid;  // Alternate cursor validity flag.
2909     bool directionRTL;      // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
2910     Vector3 position = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
2911
2912     SetAltCursorEnabled( altPositionValid );
2913
2914     if(!altPositionValid)
2915     {
2916       mCursor.SetPosition( position + UI_OFFSET );
2917     }
2918     else
2919     {
2920       size.height *= 0.5f;
2921       mCursor.SetSize(size);
2922       mCursor.SetPosition( position + UI_OFFSET - Vector3(0.0f, directionRTL ? 0.0f : size.height, 0.0f) );
2923
2924       // TODO: change this cursor pos, to be the one where the cursor is sourced from.
2925       Size rowSize = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) );
2926       size.height = rowSize.height * 0.5f;
2927       mCursorRTL.SetSize(size);
2928       mCursorRTL.SetPosition( altPosition + UI_OFFSET - Vector3(0.0f, directionRTL ? size.height : 0.0f, 0.0f) );
2929     }
2930
2931     if( IsScrollEnabled() )
2932     {
2933       // Whether cursor and grab handle are inside the boundaries of the text-input when text scroll is enabled.
2934       mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( position, size, GetControlSize() );
2935     }
2936   } // EditMode
2937 }
2938
2939 void TextInput::SetAltCursorEnabled( bool enabled )
2940 {
2941   mCursorRTLEnabled = enabled;
2942   mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2943 }
2944
2945 void TextInput::SetCursorVisibility( bool visible )
2946 {
2947   mCursorVisibility = visible;
2948   mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
2949   mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2950 }
2951
2952 void TextInput::CreateGrabHandle( Dali::Image image )
2953 {
2954   if ( !mGrabHandle )
2955   {
2956     if ( !image )
2957     {
2958       mGrabHandleImage = Image::New(DEFAULT_GRAB_HANDLE);
2959     }
2960     else
2961     {
2962       mGrabHandleImage = image;
2963     }
2964
2965     mGrabHandle = ImageActor::New(mGrabHandleImage);
2966     mGrabHandle.SetParentOrigin(ParentOrigin::TOP_LEFT);
2967     mGrabHandle.SetAnchorPoint(AnchorPoint::TOP_CENTER);
2968
2969     mGrabHandle.SetDrawMode(DrawMode::OVERLAY);
2970
2971     ShowGrabHandleAndSetVisibility( false );
2972
2973     CreateGrabArea( mGrabHandle );
2974
2975     mActiveLayer.Add(mGrabHandle);
2976   }
2977 }
2978
2979 void TextInput::CreateGrabArea( Actor& parent )
2980 {
2981   mGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
2982   mGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
2983   mGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE ) ) );  // grab area to be larger than text actor
2984   mGrabArea.TouchedSignal().Connect(this,&TextInput::OnPressDown);
2985   mTapDetector.Attach( mGrabArea );
2986   mPanGestureDetector.Attach( mGrabArea );
2987
2988   parent.Add(mGrabArea);
2989 }
2990
2991 Vector3 TextInput::MoveGrabHandle( const Vector2& displacement )
2992 {
2993   Vector3 actualHandlePosition;
2994
2995   if (mGrabHandle)
2996   {
2997     mActualGrabHandlePosition.x += displacement.x;
2998     mActualGrabHandlePosition.y += displacement.y;
2999
3000     // Grab handle should jump to the nearest character and take cursor with it
3001     std::size_t newCursorPosition = 0;
3002     ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY(), newCursorPosition );
3003
3004     actualHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition );
3005
3006     bool handleVisible = true;
3007
3008     if( IsScrollEnabled() )
3009     {
3010       const Vector3 controlSize = GetControlSize();
3011       const Size cursorSize = GetRowRectFromCharacterPosition( GetVisualPosition( newCursorPosition ) );
3012       // Scrolls the text if the handle is not in a visible position
3013       handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3014                                                   cursorSize,
3015                                                   controlSize );
3016
3017       if( handleVisible )
3018       {
3019         StopScrollTimer();
3020         mCurrentHandlePosition = actualHandlePosition;
3021         mScrollDisplacement = Vector2::ZERO;
3022       }
3023       else
3024       {
3025         if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3026         {
3027           mScrollDisplacement.x = -SCROLL_SPEED;
3028         }
3029         else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3030         {
3031           mScrollDisplacement.x = SCROLL_SPEED;
3032         }
3033         if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3034         {
3035           mScrollDisplacement.y = -SCROLL_SPEED;
3036         }
3037         else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3038         {
3039           mScrollDisplacement.y = SCROLL_SPEED;
3040         }
3041         StartScrollTimer();
3042       }
3043     }
3044
3045     if( handleVisible &&                           // Only redraw cursor and do updates if position changed
3046         ( newCursorPosition != mCursorPosition ) ) // and the new position is visible (if scroll is not enabled, it's always true).
3047     {
3048       mCursorPosition = newCursorPosition;
3049
3050       mGrabHandle.SetPosition( actualHandlePosition + UI_OFFSET );
3051
3052       const TextStyle oldInputStyle( mInputStyle );
3053
3054       mInputStyle = GetStyleAtCursor(); //Inherit style from cursor position
3055
3056       CursorUpdate();  // Let keyboard know the new cursor position so can 're-capture' for prediction.
3057
3058       if( oldInputStyle != mInputStyle )
3059       {
3060         // Updates the line height accordingly with the input style.
3061         UpdateLineHeight();
3062
3063         EmitStyleChangedSignal();
3064       }
3065     }
3066   }
3067
3068   return actualHandlePosition;
3069 }
3070
3071 void TextInput::ShowGrabHandle( bool visible )
3072 {
3073   if ( IsGrabHandleEnabled() )
3074   {
3075     if( mGrabHandle )
3076     {
3077       mGrabHandle.SetVisible( mGrabHandleVisibility );
3078     }
3079     StartMonitoringStageForTouch();
3080   }
3081 }
3082
3083 void TextInput::ShowGrabHandleAndSetVisibility( bool visible )
3084 {
3085   mGrabHandleVisibility = visible;
3086   ShowGrabHandle( visible );
3087 }
3088
3089 // Callbacks connected to be Property notifications for Boundary checking.
3090
3091 void TextInput::OnLeftBoundaryExceeded(PropertyNotification& source)
3092 {
3093   mIsSelectionHandleOneFlipped = true;
3094   mSelectionHandleOne.SetScale( -1.0f, 1.0f, 1.0f );
3095   mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3096 }
3097
3098 void TextInput::OnReturnToLeftBoundary(PropertyNotification& source)
3099 {
3100   mIsSelectionHandleOneFlipped = false;
3101   mSelectionHandleOne.SetScale( 1.0f, 1.0f, 1.0f );
3102   mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3103 }
3104
3105 void TextInput::OnRightBoundaryExceeded(PropertyNotification& source)
3106 {
3107   mIsSelectionHandleTwoFlipped = true;
3108   mSelectionHandleTwo.SetScale( -1.0f, 1.0f, 1.0f );
3109   mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3110 }
3111
3112 void TextInput::OnReturnToRightBoundary(PropertyNotification& source)
3113 {
3114   mIsSelectionHandleTwoFlipped = false;
3115   mSelectionHandleTwo.SetScale( 1.0f, 1.0f, 1.0f );
3116   mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3117 }
3118
3119 // todo change PropertyNotification signal definition to include Actor. Hence won't need duplicate functions.
3120 void TextInput::OnHandleOneLeavesBoundary( PropertyNotification& source)
3121 {
3122   mSelectionHandleOne.SetOpacity(0.0f);
3123 }
3124
3125 void TextInput::OnHandleOneWithinBoundary(PropertyNotification& source)
3126 {
3127   mSelectionHandleOne.SetOpacity(1.0f);
3128 }
3129
3130 void TextInput::OnHandleTwoLeavesBoundary( PropertyNotification& source)
3131 {
3132   mSelectionHandleTwo.SetOpacity(0.0f);
3133 }
3134
3135 void TextInput::OnHandleTwoWithinBoundary(PropertyNotification& source)
3136 {
3137   mSelectionHandleTwo.SetOpacity(1.0f);
3138 }
3139
3140 // End of Callbacks connected to be Property notifications for Boundary checking.
3141
3142 void TextInput::SetUpHandlePropertyNotifications()
3143 {
3144   /* Property notifications for handles exceeding the boundary and returning back within boundary */
3145
3146   Vector3 handlesize = GetSelectionHandleSize();
3147
3148   // Exceeding horizontal boundary
3149   PropertyNotification leftNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.x + handlesize.x) );
3150   leftNotification.NotifySignal().Connect( this, &TextInput::OnLeftBoundaryExceeded );
3151
3152   PropertyNotification rightNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.z - handlesize.x ) );
3153   rightNotification.NotifySignal().Connect( this, &TextInput::OnRightBoundaryExceeded );
3154
3155   // Within horizontal boundary
3156   PropertyNotification leftLeaveNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.x + 2*handlesize.x ) );
3157   leftLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToLeftBoundary );
3158
3159   PropertyNotification rightLeaveNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.z - 2*handlesize.x ) );
3160   rightLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToRightBoundary );
3161
3162   // Exceeding vertical boundary
3163   PropertyNotification verticalExceedNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3164                                                        OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3165                                                                          mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3166   verticalExceedNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneLeavesBoundary );
3167
3168   PropertyNotification verticalExceedNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3169                                                        OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3170                                                                          mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3171   verticalExceedNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoLeavesBoundary );
3172
3173   // Within vertical boundary
3174   PropertyNotification verticalWithinNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3175                                                        InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3176                                                                         mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3177   verticalWithinNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneWithinBoundary );
3178
3179   PropertyNotification verticalWithinNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3180                                                        InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3181                                                                         mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3182   verticalWithinNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoWithinBoundary );
3183 }
3184
3185 void TextInput::CreateSelectionHandles( std::size_t start, std::size_t end, Dali::Image handleOneImage,  Dali::Image handleTwoImage )
3186 {
3187   mSelectionHandleOnePosition = start;
3188   mSelectionHandleTwoPosition = end;
3189
3190   if ( !mSelectionHandleOne )
3191   {
3192     // create normal and pressed images
3193     mSelectionHandleOneImage = Image::New( DEFAULT_SELECTION_HANDLE_ONE );
3194     mSelectionHandleOneImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_ONE_PRESSED );
3195
3196     mSelectionHandleOne = ImageActor::New( mSelectionHandleOneImage );
3197     mSelectionHandleOne.SetName("SelectionHandleOne");
3198     mSelectionHandleOne.SetParentOrigin( ParentOrigin::TOP_LEFT );
3199     mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
3200     mIsSelectionHandleOneFlipped = false;
3201     mSelectionHandleOne.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text
3202
3203     mHandleOneGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3204     mHandleOneGrabArea.SetName("SelectionHandleOneGrabArea");
3205
3206     mHandleOneGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) );  // grab area to be larger than text actor
3207     mHandleOneGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3208
3209     mTapDetector.Attach( mHandleOneGrabArea );
3210     mPanGestureDetector.Attach( mHandleOneGrabArea );
3211
3212     mHandleOneGrabArea.TouchedSignal().Connect(this,&TextInput::OnHandleOneTouched);
3213
3214     mSelectionHandleOne.Add( mHandleOneGrabArea );
3215     mActiveLayer.Add( mSelectionHandleOne );
3216   }
3217
3218   if ( !mSelectionHandleTwo )
3219   {
3220     // create normal and pressed images
3221     mSelectionHandleTwoImage = Image::New( DEFAULT_SELECTION_HANDLE_TWO );
3222     mSelectionHandleTwoImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_TWO_PRESSED );
3223
3224     mSelectionHandleTwo = ImageActor::New( mSelectionHandleTwoImage );
3225     mSelectionHandleTwo.SetName("SelectionHandleTwo");
3226     mSelectionHandleTwo.SetParentOrigin( ParentOrigin::TOP_LEFT );
3227     mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT );
3228     mIsSelectionHandleTwoFlipped = false;
3229     mSelectionHandleTwo.SetDrawMode(DrawMode::OVERLAY); // ensure grab handle above text
3230
3231     mHandleTwoGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3232     mHandleTwoGrabArea.SetName("SelectionHandleTwoGrabArea");
3233     mHandleTwoGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) );  // grab area to be larger than text actor
3234     mHandleTwoGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3235
3236     mTapDetector.Attach( mHandleTwoGrabArea );
3237     mPanGestureDetector.Attach( mHandleTwoGrabArea );
3238
3239     mHandleTwoGrabArea.TouchedSignal().Connect(this, &TextInput::OnHandleTwoTouched);
3240
3241     mSelectionHandleTwo.Add( mHandleTwoGrabArea );
3242
3243     mActiveLayer.Add( mSelectionHandleTwo );
3244   }
3245
3246   SetUpHandlePropertyNotifications();
3247
3248   // update table as text may have changed.
3249   GetTextLayoutInfo();
3250
3251   mSelectionHandleOneActualPosition = GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition );
3252   mSelectionHandleTwoActualPosition = GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition );
3253
3254   mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
3255   mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
3256
3257   // Calculates and set the visibility if the scroll mode is enabled.
3258   bool isSelectionHandleOneVisible = true;
3259   bool isSelectionHandleTwoVisible = true;
3260   if( IsScrollEnabled() )
3261   {
3262     const Vector3& controlSize( GetControlSize() );
3263     isSelectionHandleOneVisible = IsPositionInsideBoundaries( mSelectionHandleOneActualPosition, Size::ZERO, controlSize );
3264     isSelectionHandleTwoVisible = IsPositionInsideBoundaries( mSelectionHandleTwoActualPosition, Size::ZERO, controlSize );
3265     mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
3266     mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
3267   }
3268
3269   CreateHighlight();  // function will only create highlight if not already created.
3270 }
3271
3272 Vector3 TextInput::MoveSelectionHandle( SelectionHandleId handleId, const Vector2& displacement )
3273 {
3274   Vector3 actualHandlePosition;
3275
3276   if ( mSelectionHandleOne && mSelectionHandleTwo )
3277   {
3278     const Vector3& controlSize = GetControlSize();
3279
3280     Size cursorSize( CURSOR_THICKNESS, 0.f );
3281
3282     // Get a reference of the wanted selection handle (handle one or two).
3283     Vector3& actualSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
3284
3285     // Get a reference for the current position of the handle and a copy of its pair
3286     std::size_t& currentSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3287     const std::size_t pairSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition;
3288
3289     // Get a handle of the selection handle actor
3290     ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3291
3292     // Selection handles should jump to the nearest character
3293     std::size_t newHandlePosition = 0;
3294     ReturnClosestIndex( actualSelectionHandlePosition.GetVectorXY(), newHandlePosition );
3295
3296     actualHandlePosition = GetActualPositionFromCharacterPosition( newHandlePosition );
3297
3298     bool handleVisible = true;
3299
3300     if( IsScrollEnabled() )
3301     {
3302       mCurrentSelectionId = handleId;
3303
3304       cursorSize.height = GetRowRectFromCharacterPosition( GetVisualPosition( newHandlePosition ) ).height;
3305       // Restricts the movement of the grab handle inside the boundaries of the text-input.
3306       handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3307                                                   cursorSize,
3308                                                   controlSize );
3309
3310       if( handleVisible )
3311       {
3312         StopScrollTimer();
3313         mCurrentSelectionHandlePosition = actualHandlePosition;
3314         mScrollDisplacement = Vector2::ZERO;
3315       }
3316       else
3317       {
3318         if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3319         {
3320           mScrollDisplacement.x = -SCROLL_SPEED;
3321         }
3322         else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3323         {
3324           mScrollDisplacement.x = SCROLL_SPEED;
3325         }
3326         if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3327         {
3328           mScrollDisplacement.y = -SCROLL_SPEED;
3329         }
3330         else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3331         {
3332           mScrollDisplacement.y = SCROLL_SPEED;
3333         }
3334         StartScrollTimer();
3335       }
3336     }
3337
3338     if ( handleVisible &&                                          // Ensure the handle is visible.
3339          ( newHandlePosition != pairSelectionHandlePosition ) &&   // Ensure handle one is not the same position as handle two.
3340          ( newHandlePosition != currentSelectionHandlePosition ) ) // Ensure the handle has moved.
3341     {
3342       currentSelectionHandlePosition = newHandlePosition;
3343
3344       Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3345       selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3346
3347       UpdateHighlight();
3348
3349       if ( handleId == HandleOne )
3350       {
3351         const TextStyle oldInputStyle( mInputStyle );
3352
3353         // Set Active Style to that of first character in selection
3354         if( mSelectionHandleOnePosition < mStyledText.size() )
3355         {
3356           mInputStyle = ( mStyledText.at( mSelectionHandleOnePosition ) ).mStyle;
3357         }
3358
3359         if( oldInputStyle != mInputStyle )
3360         {
3361           // Updates the line height accordingly with the input style.
3362           UpdateLineHeight();
3363
3364           EmitStyleChangedSignal();
3365         }
3366       }
3367     }
3368   }
3369
3370   return actualHandlePosition; // Returns Handle position passed in if new value not assigned.
3371 }
3372
3373 void TextInput::SetSelectionHandlePosition(SelectionHandleId handleId)
3374 {
3375
3376   const std::size_t selectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3377   ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3378
3379   if ( selectionHandleActor )
3380   {
3381     const Vector3 actualHandlePosition = GetActualPositionFromCharacterPosition( selectionHandlePosition );
3382     Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3383     selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3384
3385     if( IsScrollEnabled() )
3386     {
3387       const Size cursorSize( CURSOR_THICKNESS,
3388                              GetRowRectFromCharacterPosition( GetVisualPosition( selectionHandlePosition ) ).height );
3389       selectionHandleActor.SetVisible( IsPositionInsideBoundaries( actualHandlePosition,
3390                                                                    cursorSize,
3391                                                                    GetControlSize() ) );
3392     }
3393   }
3394 }
3395
3396 std::size_t TextInput::GetVisualPosition(std::size_t logicalPosition) const
3397 {
3398   // Note: we're allowing caller to request a logical position of size (i.e. end of string)
3399   // For now the visual position of end of logical string will be end of visual string.
3400   DALI_ASSERT_DEBUG( logicalPosition <= mTextLayoutInfo.mCharacterLogicalToVisualMap.size() );
3401
3402   return logicalPosition != mTextLayoutInfo.mCharacterLogicalToVisualMap.size() ? mTextLayoutInfo.mCharacterLogicalToVisualMap[logicalPosition] : mTextLayoutInfo.mCharacterLogicalToVisualMap.size();
3403 }
3404
3405 void TextInput::GetVisualTextSelection(std::vector<bool>& selectedVisualText, std::size_t startSelection, std::size_t endSelection)
3406 {
3407   std::vector<int>::iterator it = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin();
3408   std::vector<int>::iterator startSelectionIt = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::min(startSelection, endSelection);
3409   std::vector<int>::iterator endSelectionIt = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::max(startSelection, endSelection);
3410   std::vector<int>::iterator end = mTextLayoutInfo.mCharacterLogicalToVisualMap.end();
3411
3412   selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size() );
3413
3414   // Deselect text prior to startSelectionIt
3415   for(;it!=startSelectionIt;++it)
3416   {
3417     selectedVisualText[*it] = false;
3418   }
3419
3420   // Select text from startSelectionIt -> endSelectionIt
3421   for(;it!=endSelectionIt;++it)
3422   {
3423     selectedVisualText[*it] = true;
3424   }
3425
3426   // Deselect text after endSelection
3427   for(;it!=end;++it)
3428   {
3429     selectedVisualText[*it] = false;
3430   }
3431
3432   selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size(), false );
3433 }
3434
3435 // Calculate the dimensions of the quads they will make the highlight mesh
3436 TextInput::HighlightInfo TextInput::CalculateHighlightInfo()
3437 {
3438   // At the moment there is no public API to modify the block alignment option.
3439   const bool blockAlignEnabled = true;
3440
3441   mNewHighlightInfo.mQuadList.clear(); // clear last quad information.
3442
3443   if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
3444   {
3445     Toolkit::TextView::CharacterLayoutInfoContainer::iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3446     Toolkit::TextView::CharacterLayoutInfoContainer::iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
3447
3448     // Get vector of flags representing characters that are selected (true) vs unselected (false).
3449     std::vector<bool> selectedVisualText;
3450     GetVisualTextSelection(selectedVisualText, mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
3451     std::vector<bool>::iterator selectedIt(selectedVisualText.begin());
3452     std::vector<bool>::iterator selectedEndIt(selectedVisualText.end());
3453
3454     SelectionState selectionState = SelectionNone;          ///< Current selection status of cursor over entire text.
3455     float rowLeft = 0.0f;
3456     float rowRight = 0.0f;
3457     // Keep track of the TextView's min/max extents. Should be able to query this from TextView.
3458     float maxRowLeft = std::numeric_limits<float>::max();
3459     float maxRowRight = 0.0f;
3460
3461     Toolkit::TextView::CharacterLayoutInfoContainer::iterator lastIt = it;
3462
3463     // Scan through entire text.
3464     while(it != end)
3465     {
3466       // selectionState: None when not in selection, Started when in selection, and Ended when reached end of selection.
3467
3468       Toolkit::TextView::CharacterLayoutInfo& charInfo(*it);
3469       bool charSelected( false );
3470       if( selectedIt != selectedEndIt )