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