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