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