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