24710e72a808227402a731d8dd213bbdbaca3379
[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
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
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   } // BackSpace
1771   else if (keyName == "Right")
1772   {
1773     AdvanceCursor();
1774     RemoveHighlight();
1775   }
1776   else if (keyName == "Left")
1777   {
1778     AdvanceCursor(true);
1779     RemoveHighlight();
1780   }
1781   else // event is a character
1782   {
1783     // Some text may be selected, hiding keyboard causes an empty keystring to be sent, we don't want to delete highlight in this case
1784     if ( !keyString.empty() )
1785     {
1786       if ( mHighlightMeshActor )
1787       {
1788         // replaces highlighted text with new character
1789         DeleteHighlightedText( false );
1790       }
1791
1792
1793       // Received key String
1794       mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, 0 );
1795       update = true;
1796       EmitTextModified();
1797     }
1798   }
1799
1800   // If key event has resulted in a change in the text/cursor, then trigger a relayout of text
1801   // as this is a costly operation.
1802   if(update)
1803   {
1804     CursorUpdate();
1805   }
1806
1807   if(update || scroll)
1808   {
1809     if( IsScrollEnabled() )
1810     {
1811       // Calculates the new cursor position (in actor coordinates)
1812       const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
1813
1814       ScrollTextViewToMakeCursorVisible( cursorPosition );
1815     }
1816   }
1817
1818   return true;
1819 }
1820
1821 bool TextInput::OnKeyUpEvent(const KeyEvent& event)
1822 {
1823   std::string keyName = event.keyPressedName;
1824   std::string keyString = event.keyPressed;
1825
1826   DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyUpEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1827
1828   // The selected text become deselected when the key code is DALI_KEY_BACK.
1829   if( IsTextSelected() && ( keyName == "XF86Stop" || keyName == "XF86Send") )
1830   {
1831     DeSelectText();
1832     return true;
1833   }
1834
1835   return false;
1836 }
1837
1838 void TextInput::OnTextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition )
1839 {
1840   // Updates the stored scroll position.
1841   mTextLayoutInfo.mScrollOffset = textView.GetScrollPosition();
1842
1843   const Vector3& controlSize = GetControlSize();
1844   Size cursorSize( CURSOR_THICKNESS, 0.f );
1845
1846   // Updates the cursor and grab handle position and visibility.
1847   if( mGrabHandle || mCursor )
1848   {
1849     cursorSize.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height;
1850     const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1851
1852     mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( cursorPosition, cursorSize, controlSize );
1853
1854     mActualGrabHandlePosition = cursorPosition.GetVectorXY();
1855
1856     if( mGrabHandle )
1857     {
1858       ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1859       mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1860     }
1861
1862     if( mCursor )
1863     {
1864       mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
1865       mCursor.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1866     }
1867   }
1868
1869   // Updates the selection handles and highlighted text position and visibility.
1870   if( mSelectionHandleOne && mSelectionHandleTwo )
1871   {
1872     const Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition(mSelectionHandleOnePosition);
1873     const Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition(mSelectionHandleTwoPosition);
1874     cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleOnePosition ) ).mSize.height;
1875     const bool isSelectionHandleOneVisible = IsPositionInsideBoundaries( cursorPositionOne, cursorSize, controlSize );
1876     cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleTwoPosition ) ).mSize.height;
1877     const bool isSelectionHandleTwoVisible = IsPositionInsideBoundaries( cursorPositionTwo, cursorSize, controlSize );
1878
1879     mSelectionHandleOneActualPosition = cursorPositionOne.GetVectorXY();
1880     mSelectionHandleTwoActualPosition = cursorPositionTwo.GetVectorXY();
1881
1882     mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
1883     mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
1884     mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
1885     mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
1886
1887     if( mHighlightMeshActor )
1888     {
1889       mHighlightMeshActor.SetVisible( true );
1890       UpdateHighlight();
1891     }
1892   }
1893 }
1894
1895 void TextInput::ScrollTextViewToMakeCursorVisible( const Vector3& cursorPosition )
1896 {
1897   // Scroll the text to make the cursor visible.
1898   const Size cursorSize( CURSOR_THICKNESS,
1899                          GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height );
1900
1901   // Need to scroll the text to make the cursor visible and to cover the whole text-input area.
1902
1903   const Vector3& controlSize = GetControlSize();
1904
1905   // Calculates the new scroll position.
1906   Vector2 scrollOffset = mTextLayoutInfo.mScrollOffset;
1907   if( ( cursorPosition.x < 0.f ) || ( cursorPosition.x > controlSize.width ) )
1908   {
1909     scrollOffset.x += cursorPosition.x;
1910   }
1911
1912   if( cursorPosition.y - cursorSize.height < 0.f )
1913   {
1914     scrollOffset.y += ( cursorPosition.y - cursorSize.height );
1915   }
1916   else if( cursorPosition.y > controlSize.height )
1917   {
1918     scrollOffset.y += cursorPosition.y;
1919   }
1920
1921   // Sets the new scroll position.
1922   SetScrollPosition( Vector2::ZERO ); // TODO: need to reset to the zero position in order to make the scroll trim to work.
1923   SetScrollPosition( scrollOffset );
1924 }
1925
1926 void TextInput::StartScrollTimer()
1927 {
1928   if( !mScrollTimer )
1929   {
1930     mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1931     mScrollTimer.TickSignal().Connect( this, &TextInput::OnScrollTimerTick );
1932   }
1933
1934   if( !mScrollTimer.IsRunning() )
1935   {
1936     mScrollTimer.Start();
1937   }
1938 }
1939
1940 void TextInput::StopScrollTimer()
1941 {
1942   if( mScrollTimer )
1943   {
1944     mScrollTimer.Stop();
1945   }
1946 }
1947
1948 bool TextInput::OnScrollTimerTick()
1949 {
1950   // TODO: need to set the new style accordingly the new handle position.
1951
1952   if( !( mGrabHandleVisibility && mGrabHandle ) && !( mSelectionHandleOne && mSelectionHandleTwo ) )
1953   {
1954     // nothing to do if all handles are invisible or doesn't exist.
1955     return true;
1956   }
1957
1958   // Text scrolling
1959
1960   // Choose between the grab handle or the selection handles.
1961   Vector3& actualHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mActualGrabHandlePosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
1962   std::size_t& handlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCursorPosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
1963   Vector3& currentHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCurrentHandlePosition : mCurrentSelectionHandlePosition;
1964
1965   std::size_t newCursorPosition = 0;
1966   ReturnClosestIndex( actualHandlePosition.GetVectorXY(), newCursorPosition );
1967
1968   // Whether the handle's position is different of the previous one and in the case of the selection handle,
1969   // the new selection handle's position needs to be different of the other one.
1970   const bool differentSelectionHandles = ( mGrabHandleVisibility && mGrabHandle ) ? newCursorPosition != handlePosition :
1971                                          ( mCurrentSelectionId == HandleOne ) ? ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleTwoPosition ) :
1972                                                                                 ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleOnePosition );
1973
1974   if( differentSelectionHandles )
1975   {
1976     handlePosition = newCursorPosition;
1977
1978     const Vector3 actualPosition = GetActualPositionFromCharacterPosition( newCursorPosition );
1979
1980     Vector2 scrollDelta = ( actualPosition - currentHandlePosition ).GetVectorXY();
1981
1982     Vector2 scrollPosition = mDisplayedTextView.GetScrollPosition();
1983     scrollPosition += scrollDelta;
1984     SetScrollPosition( scrollPosition );
1985
1986     if( mDisplayedTextView.IsScrollPositionTrimmed() )
1987     {
1988       StopScrollTimer();
1989     }
1990
1991     currentHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition ).GetVectorXY();
1992   }
1993
1994   actualHandlePosition.x += mScrollDisplacement.x;
1995   actualHandlePosition.y += mScrollDisplacement.y;
1996
1997   return true;
1998 }
1999
2000 // Public Internal Methods (public for testing purpose)
2001
2002 void TextInput::SetUpTouchEvents()
2003 {
2004   if ( !mTapDetector )
2005   {
2006     mTapDetector = TapGestureDetector::New();
2007     // Attach the actors and connect the signal
2008     mTapDetector.Attach(Self());
2009
2010     // As contains children which may register for tap the default control detector is not used.
2011     mTapDetector.DetectedSignal().Connect(this, &TextInput::OnTextTap);
2012   }
2013
2014   if ( !mDoubleTapDetector )
2015   {
2016     mDoubleTapDetector = TapGestureDetector::New();
2017     mDoubleTapDetector.SetTapsRequired( 2 );
2018     mDoubleTapDetector.DetectedSignal().Connect(this, &TextInput::OnDoubleTap);
2019
2020     // Only attach and detach the actor to the double tap detector when we enter/leave edit mode
2021     // so that we do not, unnecessarily, have a double tap request all the time
2022   }
2023
2024   if ( !mPanGestureDetector )
2025   {
2026     mPanGestureDetector = PanGestureDetector::New();
2027     mPanGestureDetector.DetectedSignal().Connect(this, &TextInput::OnHandlePan);
2028   }
2029
2030   if ( !mLongPressDetector )
2031   {
2032     mLongPressDetector = LongPressGestureDetector::New();
2033     mLongPressDetector.DetectedSignal().Connect(this, &TextInput::OnLongPress);
2034     mLongPressDetector.Attach(Self());
2035   }
2036 }
2037
2038 void TextInput::CreateTextViewActor()
2039 {
2040   mDisplayedTextView = Toolkit::TextView::New();
2041   mDisplayedTextView.SetMarkupProcessingEnabled( mMarkUpEnabled );
2042   mDisplayedTextView.SetParentOrigin(ParentOrigin::TOP_LEFT);
2043   mDisplayedTextView.SetAnchorPoint(AnchorPoint::TOP_LEFT);
2044   mDisplayedTextView.SetMultilinePolicy(Toolkit::TextView::SplitByWord);
2045   mDisplayedTextView.SetWidthExceedPolicy( Toolkit::TextView::Original );
2046   mDisplayedTextView.SetHeightExceedPolicy( Toolkit::TextView::Original );
2047   mDisplayedTextView.SetLineJustification( Toolkit::TextView::Left );
2048   mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>( Toolkit::Alignment::HorizontalLeft | Toolkit::Alignment::VerticalTop ) );
2049   mDisplayedTextView.SetPosition( Vector3( 0.0f, 0.0f, DISPLAYED_TEXT_VIEW_Z_OFFSET ) );
2050   mDisplayedTextView.SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
2051
2052   mDisplayedTextView.ScrolledSignal().Connect( this, &TextInput::OnTextViewScrolled );
2053
2054   Self().Add( mDisplayedTextView );
2055 }
2056
2057 // Start a timer to initiate, used by the cursor to blink.
2058 void TextInput::StartCursorBlinkTimer()
2059 {
2060   if ( !mCursorBlinkTimer )
2061   {
2062     mCursorBlinkTimer = Timer::New( CURSOR_BLINK_INTERVAL );
2063     mCursorBlinkTimer.TickSignal().Connect( this, &TextInput::OnCursorBlinkTimerTick );
2064   }
2065
2066   if ( !mCursorBlinkTimer.IsRunning() )
2067   {
2068     mCursorBlinkTimer.Start();
2069   }
2070 }
2071
2072 // Start a timer to initiate, used by the cursor to blink.
2073 void TextInput::StopCursorBlinkTimer()
2074 {
2075   if ( mCursorBlinkTimer )
2076   {
2077     mCursorBlinkTimer.Stop();
2078   }
2079 }
2080
2081 void TextInput::StartEditMode()
2082 {
2083   DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput StartEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2084
2085   if(!mEditModeActive)
2086   {
2087     SetKeyInputFocus();
2088   }
2089
2090   if ( mDoubleTapDetector )
2091   {
2092     mDoubleTapDetector.Attach( Self() );
2093   }
2094 }
2095
2096 void TextInput::EndEditMode()
2097 {
2098   DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput EndEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2099
2100   ClearKeyInputFocus();
2101
2102   if ( mDoubleTapDetector )
2103   {
2104     mDoubleTapDetector.Detach( Self() );
2105   }
2106 }
2107
2108 void TextInput::ApplyPreEditStyle( std::size_t preEditStartPosition, std::size_t preEditStringLength )
2109 {
2110   if ( mPreEditFlag && ( preEditStringLength > 0 ) )
2111   {
2112     mUnderlinedPriorToPreEdit = mInputStyle.GetUnderline();
2113     TextStyle style;
2114     style.SetUnderline( true );
2115     ApplyStyleToRange( style, TextStyle::UNDERLINE , preEditStartPosition, preEditStartPosition + preEditStringLength -1 );
2116   }
2117 }
2118
2119 void TextInput::RemovePreEditStyle()
2120 {
2121   if ( !mUnderlinedPriorToPreEdit )
2122   {
2123     TextStyle style;
2124     style.SetUnderline( false );
2125     SetActiveStyle( style, TextStyle::UNDERLINE );
2126   }
2127 }
2128
2129 // IMF related methods
2130
2131
2132 ImfManager::ImfCallbackData TextInput::ImfEventReceived( Dali::ImfManager& imfManager, const ImfManager::ImfEventData&  imfEvent )
2133 {
2134   bool update( false );
2135   bool preeditResetRequired ( false );
2136
2137   if (imfEvent.eventName != ImfManager::GETSURROUNDING )
2138   {
2139     HidePopup(); // If Pop-up shown then hides it as editing text.
2140   }
2141
2142   switch ( imfEvent.eventName )
2143   {
2144     case ImfManager::PREEDIT:
2145     {
2146       mIgnoreFirstCommitFlag = false;
2147
2148       // 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
2149       if ( mHighlightMeshActor &&  (!imfEvent.predictiveString.empty()) )
2150       {
2151         // replaces highlighted text with new character
2152         DeleteHighlightedText( false );
2153       }
2154
2155       preeditResetRequired = PreEditReceived( imfEvent.predictiveString, imfEvent.cursorOffset );
2156
2157       if( IsScrollEnabled() )
2158       {
2159         // Calculates the new cursor position (in actor coordinates)
2160         const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2161         ScrollTextViewToMakeCursorVisible( cursorPosition );
2162       }
2163
2164       update = true;
2165
2166       break;
2167     }
2168     case ImfManager::COMMIT:
2169     {
2170       if( mIgnoreFirstCommitFlag )
2171       {
2172         // Do not commit in this case when keyboard sends a commit when shows for the first time (work-around for imf keyboard).
2173         mIgnoreFirstCommitFlag = false;
2174       }
2175       else
2176       {
2177         // A Commit message is a word that has been accepted, it may have been a pre-edit word previously but now commited.
2178
2179         // 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
2180         if ( mHighlightMeshActor &&  (!imfEvent.predictiveString.empty()) )
2181         {
2182           // replaces highlighted text with new character
2183           DeleteHighlightedText( false );
2184         }
2185
2186        // A PreEditReset can cause a commit message to be sent, the Ignore Commit flag is used in scenarios where the word is
2187        // not needed, one such scenario is when the pre-edit word is too long to fit.
2188        if ( !mIgnoreCommitFlag )
2189        {
2190          update = CommitReceived( imfEvent.predictiveString );
2191        }
2192        else
2193        {
2194          mIgnoreCommitFlag = false; // reset ignore flag so next commit is acted upon.
2195        }
2196       }
2197
2198       if( update )
2199       {
2200         if( IsScrollEnabled() )
2201         {
2202           // Calculates the new cursor position (in actor coordinates)
2203           const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2204
2205           ScrollTextViewToMakeCursorVisible( cursorPosition );
2206         }
2207       }
2208       break;
2209     }
2210     case ImfManager::DELETESURROUNDING:
2211     {
2212       DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - delete surrounding mPreEditFlag[%s] cursor offset[%d] characters to delete[%d] position to delete[%u] \n",
2213                      (mPreEditFlag)?"true":"false", imfEvent.cursorOffset, imfEvent.numberOfChars, static_cast<std::size_t>( mCursorPosition+imfEvent.cursorOffset) );
2214
2215       mPreEditFlag = false;
2216
2217       std::size_t toDelete = 0;
2218       std::size_t numberOfCharacters = 0;
2219
2220       if( mHighlightMeshActor )
2221       {
2222         // delete highlighted text.
2223         toDelete = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2224         numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - toDelete;
2225       }
2226       else
2227       {
2228         if( std::abs( imfEvent.cursorOffset ) < mCursorPosition )
2229         {
2230           toDelete = mCursorPosition + imfEvent.cursorOffset;
2231         }
2232         if( toDelete + imfEvent.numberOfChars > mStyledText.size() )
2233         {
2234           numberOfCharacters = mStyledText.size() - toDelete;
2235         }
2236         else
2237         {
2238           numberOfCharacters = imfEvent.numberOfChars;
2239         }
2240       }
2241       DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding pre-delete range mCursorPosition[%u] \n", mCursorPosition);
2242       DeleteRange( toDelete, numberOfCharacters );
2243
2244       mCursorPosition = toDelete;
2245       mNumberOfSurroundingCharactersDeleted = numberOfCharacters;
2246
2247       EmitTextModified();
2248
2249       DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding post-delete range mCursorPosition[%u] \n", mCursorPosition);
2250       break;
2251     }
2252     case ImfManager::GETSURROUNDING:
2253     {
2254       // 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
2255       // the next key pressed.  Instead the Select function sets the cursor position and surrounding text.
2256       if (! ( mHighlightMeshActor || mSelectingText ) )
2257       {
2258         std::string text( GetText() );
2259         DALI_LOG_INFO( gLogFilter, Debug::General, "OnKey - surrounding text - set text [%s] and cursor[%u] \n", text.c_str(), mCursorPosition );
2260
2261         imfManager.SetCursorPosition( mCursorPosition );
2262         imfManager.SetSurroundingText( text );
2263       }
2264
2265       if( 0 != mNumberOfSurroundingCharactersDeleted )
2266       {
2267         mDisplayedTextView.RemoveTextFrom( mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2268         mNumberOfSurroundingCharactersDeleted = 0;
2269
2270         if( mStyledText.empty() )
2271         {
2272           // Styled text is empty, so set the placeholder text.
2273           mDisplayedTextView.SetText( mStyledPlaceHolderText );
2274           mPlaceHolderSet = true;
2275         }
2276       }
2277       break;
2278     }
2279     case ImfManager::VOID:
2280     {
2281       DALI_ASSERT_DEBUG( false );
2282     }
2283   } // end switch
2284
2285   ImfManager::ImfCallbackData callbackData( update, mCursorPosition, GetText(), preeditResetRequired );
2286
2287   return callbackData;
2288 }
2289
2290 bool TextInput::PreEditReceived(const std::string& keyString, std::size_t cursorOffset )
2291 {
2292   mPreserveCursorPosition = false;  // As in pre-edit state we should have the cursor at the end of the word displayed not last touch position.
2293
2294   DALI_LOG_INFO(gLogFilter, Debug::General, ">>PreEditReceived preserveCursorPos[%d] mCursorPos[%d] mPreEditFlag[%d]\n",
2295                 mPreserveCursorPosition, mCursorPosition, mPreEditFlag );
2296
2297   bool preeditResetRequest ( false );
2298
2299   if( mPreEditFlag ) // Already in pre-edit state.
2300   {
2301     if( mStyledText.size() >= mMaxStringLength )
2302     {
2303       DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived styledTextSize >= mMaxStringLength \n");
2304       // Cannot fit these characters into field, clear pre-edit.
2305       if ( !mUnderlinedPriorToPreEdit )
2306       {
2307         TextStyle style;
2308         style.SetUnderline( false );
2309         ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
2310       }
2311       mIgnoreCommitFlag = true;
2312       preeditResetRequest = false; // this will reset the keyboard's predictive suggestions.
2313       mPreEditFlag = false;
2314       EmitMaxInputCharactersReachedSignal();
2315     }
2316     else
2317     {
2318       // delete existing pre-edit string
2319       const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2320
2321       // Store new pre-edit string
2322       mPreEditString.SetText( keyString );
2323
2324       if ( keyString.empty() )
2325       {
2326         mPreEditFlag = false;
2327         mCursorPosition = mPreEditStartPosition;
2328
2329         if( mStyledText.empty() )
2330         {
2331           // Styled text is empty, so set the placeholder text.
2332           mDisplayedTextView.SetText( mStyledPlaceHolderText );
2333           mPlaceHolderSet = true;
2334         }
2335         else
2336         {
2337           mDisplayedTextView.RemoveTextFrom( mPreEditStartPosition, numberOfCharactersToReplace );
2338         }
2339         GetTextLayoutInfo();
2340         EmitTextModified();
2341       }
2342       else
2343       {
2344         // Insert new pre-edit string. InsertAt updates the size and position table.
2345         mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersToReplace );
2346         // 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.
2347         mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2348         ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2349         DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] \n", mCursorPosition);
2350         EmitTextModified();
2351       }
2352       // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2353       DrawCursor();
2354     }
2355   }
2356   else  // mPreEditFlag not set
2357   {
2358     if ( !keyString.empty() ) // Imf can send an empty pre-edit followed by Backspace instead of a commit.
2359     {
2360       DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived Initial Pre-Edit string \n");
2361       // new pre-edit so move into pre-edit state by setting flag
2362       mPreEditFlag = true;
2363       mPreEditString.SetText( keyString ); // store new pre-edit string
2364       mPreEditStartPosition = mCursorPosition; // store starting cursor position of pre-edit so know where to re-start from
2365       mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, 0 );
2366       // 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.
2367       mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2368       ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2369       DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] mPreEditStartPosition[%u]\n", mCursorPosition, mPreEditStartPosition);
2370       // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2371       DrawCursor();
2372       EmitTextModified();
2373     }
2374     else
2375     {
2376       DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived with empty keyString\n");
2377     }
2378   }
2379
2380   return preeditResetRequest;
2381 }
2382
2383 bool TextInput::CommitReceived(const std::string& keyString )
2384 {
2385   DALI_LOG_INFO(gLogFilter, Debug::General, ">>CommitReceived preserveCursorPos[%d] mPreEditStartPosition [%d] mCursorPos[%d] mPreEditFlag[%d] mIgnoreCommitFlag[%s]\n",
2386       mPreserveCursorPosition, mPreEditStartPosition, mCursorPosition, mPreEditFlag, (mIgnoreCommitFlag)?"true":"false" );
2387
2388   bool update( false );
2389
2390   RemovePreEditStyle();
2391
2392   const std::size_t styledTextSize( mStyledText.size() );
2393   if( styledTextSize >= mMaxStringLength )
2394   {
2395     // Cannot fit these characters into field, clear pre-edit.
2396     if ( mPreEditFlag )
2397     {
2398       mIgnoreCommitFlag = true;
2399       mPreEditFlag = false;
2400     }
2401     EmitMaxInputCharactersReachedSignal();
2402   }
2403   else
2404   {
2405     if( mPreEditFlag )
2406     {
2407       // delete existing pre-edit string
2408       const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2409       mPreEditFlag = false;
2410
2411       DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived mPreserveCursorPosition[%s] mPreEditStartPosition[%u]\n",
2412                     (mPreserveCursorPosition)?"true":"false", mPreEditStartPosition );
2413
2414       if ( mPreserveCursorPosition ) // PreEditReset has been called triggering this commit.
2415       {
2416         // No need to update cursor position as Cursor location given by touch.
2417         InsertAt( Text( keyString ), mPreEditStartPosition, numberOfCharactersToReplace );
2418         mPreserveCursorPosition = false;
2419       }
2420       else
2421       {
2422         // Cursor not set by touch so needs to be re-positioned to input more text
2423         mCursorPosition = mPreEditStartPosition + InsertAt( Text(keyString), mPreEditStartPosition, numberOfCharactersToReplace ); // update cursor position as InsertAt, re-draw cursor with this
2424
2425         // If a space or enter caused the commit then our string is one longer than the string given to us by the commit key.
2426         if ( mCommitByKeyInput )
2427         {
2428           mCursorPosition = std::min ( mCursorPosition + 1, mStyledText.size() );
2429           mCommitByKeyInput = false;
2430         }
2431       }
2432
2433       EmitTextModified();
2434
2435       if ( mSelectTextOnCommit )
2436       {
2437         SelectText(mRequestedSelection.mStartOfSelection, mRequestedSelection.mEndOfSelection );
2438       }
2439
2440       update = true;
2441     }
2442     else // mPreEditFlag not set
2443     {
2444       if ( !mIgnoreCommitFlag ) // Check if this commit should be ignored.
2445       {
2446         if( mStyledText.empty() && mPlaceHolderSet )
2447         {
2448           // If the styled text is empty and the placeholder text is set, it needs to be cleared.
2449           mDisplayedTextView.SetText( "" );
2450           mNumberOfSurroundingCharactersDeleted = 0;
2451           mPlaceHolderSet = false;
2452         }
2453         mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2454         update = true;
2455         mNumberOfSurroundingCharactersDeleted = 0;
2456         EmitTextModified();
2457       }
2458       else
2459       {
2460         mIgnoreCommitFlag = false; // Reset flag so future commits will not be ignored.
2461       }
2462     }
2463   }
2464
2465   mSelectTextOnCommit = false;
2466
2467   DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived << mCursorPos[%d] mPreEditFlag[%d] update[%s] \n",
2468                                              mCursorPosition, mPreEditFlag, (update)?"true":"false" );
2469
2470   return update;
2471 }
2472
2473 // End of IMF related methods
2474
2475 std::size_t TextInput::DeletePreEdit()
2476 {
2477   DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeletePreEdit mPreEditFlag[%s] \n", (mPreEditFlag)?"true":"false");
2478
2479   DALI_ASSERT_DEBUG( mPreEditFlag );
2480
2481   const std::size_t preEditStringLength = mPreEditString.GetLength();
2482   const std::size_t styledTextSize = mStyledText.size();
2483
2484   std::size_t endPosition = mPreEditStartPosition + preEditStringLength;
2485
2486   // Prevents erase items outside mStyledText bounds.
2487   if( mPreEditStartPosition > styledTextSize )
2488   {
2489     DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. mPreEditStartPosition > mStyledText.size()" );
2490     mPreEditStartPosition = styledTextSize;
2491   }
2492
2493   if( ( endPosition > styledTextSize ) || ( endPosition < mPreEditStartPosition ) )
2494   {
2495     DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. ( endPosition > mStyledText.size() ) || ( endPosition < mPreEditStartPosition )" );
2496     endPosition = styledTextSize;
2497   }
2498
2499   mStyledText.erase( mStyledText.begin() + mPreEditStartPosition, mStyledText.begin() + endPosition );
2500
2501   // DeletePreEdit() doesn't remove characters from the text-view because may be followed by an InsertAt() which inserts characters,
2502   // in that case, the Insert should use the returned number of deleted characters and replace the text which helps the text-view to
2503   // reuse glyphs.
2504   // In case DeletePreEdit() is not followed by an InsertAt() characters must be deleted after this call.
2505
2506   return preEditStringLength;
2507 }
2508
2509 void TextInput::PreEditReset( bool preserveCursorPosition )
2510 {
2511   DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReset preserveCursorPos[%d] mCursorPos[%d] \n",
2512                 preserveCursorPosition, mCursorPosition);
2513
2514   // 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.
2515   mPreserveCursorPosition = preserveCursorPosition;
2516
2517   // Reset incase we are in a pre-edit state.
2518   ImfManager imfManager = ImfManager::Get();
2519   if ( imfManager )
2520   {
2521     imfManager.Reset(); // Will trigger a commit message
2522   }
2523 }
2524
2525 void TextInput::CursorUpdate()
2526 {
2527   DrawCursor();
2528
2529   ImfManager imfManager = ImfManager::Get();
2530   if ( imfManager )
2531   {
2532     std::string text( GetText() );
2533     imfManager.SetSurroundingText( text );  // Notifying IMF of a cursor change triggers a surrounding text request so updating it now.
2534     imfManager.SetCursorPosition ( mCursorPosition );
2535     imfManager.NotifyCursorPosition();
2536   }
2537 }
2538
2539 /* Delete highlighted characters redisplay*/
2540 void TextInput::DeleteHighlightedText( bool inheritStyle )
2541 {
2542   DALI_LOG_INFO( gLogFilter, Debug::General, "DeleteHighlightedText handlePosOne[%u] handlePosTwo[%u]\n", mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
2543
2544   if(mHighlightMeshActor)
2545   {
2546     mCursorPosition = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2547
2548     MarkupProcessor::StyledTextArray::iterator start = mStyledText.begin() + mCursorPosition;
2549     MarkupProcessor::StyledTextArray::iterator end =  mStyledText.begin() + std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2550
2551     // Get the styled text of the characters to be deleted as it may be needed if
2552     // the "exceed the text-input's boundaries" option is disabled.
2553     MarkupProcessor::StyledTextArray styledCharactersToDelete;
2554
2555     styledCharactersToDelete.insert( styledCharactersToDelete.begin(), start, end );
2556
2557     mStyledText.erase( start, end ); // erase range of characters
2558
2559     // Remove text from TextView.
2560
2561     if( mStyledText.empty() )
2562     {
2563       // Styled text is empty, so set the placeholder text.
2564       mDisplayedTextView.SetText( mStyledPlaceHolderText );
2565       mPlaceHolderSet = true;
2566     }
2567     else
2568     {
2569       const std::size_t numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - mCursorPosition;
2570
2571       mDisplayedTextView.RemoveTextFrom( mCursorPosition, numberOfCharacters );
2572
2573       // It may happen than after removing a white space or a new line character,
2574       // two words merge, this new word could be big enough to not fit in its
2575       // current line, so moved to the next one, and make some part of the text to
2576       // exceed the text-input's boundary.
2577       if( !mExceedEnabled )
2578       {
2579         // Get the new text layout after removing some characters.
2580         mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2581
2582         // Get text-input's size.
2583         const Vector3& size = GetControlSize();
2584
2585         if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2586             ( mTextLayoutInfo.mTextSize.height > size.height ) )
2587         {
2588           mDisplayedTextView.InsertTextAt( mCursorPosition, styledCharactersToDelete );
2589
2590           mStyledText.insert( mStyledText.begin() + mCursorPosition,
2591                               styledCharactersToDelete.begin(),
2592                               styledCharactersToDelete.end() );
2593         }
2594       }
2595     }
2596     GetTextLayoutInfo();
2597
2598     RemoveHighlight();
2599
2600     if( inheritStyle )
2601     {
2602       const TextStyle oldInputStyle( mInputStyle );
2603
2604       mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2605
2606       if( oldInputStyle != mInputStyle )
2607       {
2608         // Updates the line height accordingly with the input style.
2609         UpdateLineHeight();
2610
2611         EmitStyleChangedSignal();
2612       }
2613     }
2614   }
2615 }
2616
2617 void TextInput::DeleteRange( const std::size_t start, const std::size_t ncharacters )
2618 {
2619   DALI_ASSERT_DEBUG( start <= mStyledText.size() );
2620   DALI_ASSERT_DEBUG( !mStyledText.empty() );
2621
2622   DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeleteRange pre mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2623
2624
2625   if ( ( !mStyledText.empty()) && ( ( start + ncharacters ) <= mStyledText.size() ) )
2626   {
2627     MarkupProcessor::StyledTextArray::iterator itStart =  mStyledText.begin() + start;
2628     MarkupProcessor::StyledTextArray::iterator itEnd =  mStyledText.begin() + start + ncharacters;
2629
2630     mStyledText.erase(itStart, itEnd);
2631
2632     // update the selection handles if they are visible.
2633     if( mHighlightMeshActor )
2634     {
2635       std::size_t& minHandle = ( mSelectionHandleOnePosition <= mSelectionHandleTwoPosition ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition );
2636       std::size_t& maxHandle = ( mSelectionHandleTwoPosition > mSelectionHandleOnePosition ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition );
2637
2638       if( minHandle >= start + ncharacters )
2639       {
2640         minHandle -= ncharacters;
2641       }
2642       else if( ( minHandle > start ) && ( minHandle < start + ncharacters ) )
2643       {
2644         minHandle = start;
2645       }
2646
2647       if( maxHandle >= start + ncharacters )
2648       {
2649         maxHandle -= ncharacters;
2650       }
2651       else if( ( maxHandle > start ) && ( maxHandle < start + ncharacters ) )
2652       {
2653         maxHandle = start;
2654       }
2655     }
2656
2657     // 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.
2658   }
2659
2660   DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteRange<< post mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2661
2662   // Although mStyledText has been set to a new text string we no longer re-draw the text or notify the cursor change.
2663   // This is a performance decision as the use of this function often means the text is being replaced or just deleted.
2664   // Mean we do not re-draw the text more than we have too.
2665 }
2666
2667 /* Delete character at current cursor position and redisplay*/
2668 void TextInput::DeleteCharacter( std::size_t positionToDelete )
2669 {
2670   // Ensure positionToDelete is not out of bounds.
2671   DALI_ASSERT_DEBUG( positionToDelete <= mStyledText.size() );
2672   DALI_ASSERT_DEBUG( !mStyledText.empty() );
2673   DALI_ASSERT_DEBUG( positionToDelete > 0 );
2674
2675   DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteCharacter positionToDelete[%u]", positionToDelete );
2676
2677
2678   if ( ( !mStyledText.empty()) && ( positionToDelete > 0 ) && positionToDelete <= mStyledText.size() )  // don't try to delete if no characters left of cursor
2679   {
2680     MarkupProcessor::StyledTextArray::iterator it =  mStyledText.begin() + positionToDelete - 1;
2681
2682     // Get the styled text of the character to be deleted as it may be needed if
2683     // the "exceed the text-input's boundaries" option is disabled.
2684     const MarkupProcessor::StyledText styledCharacterToDelete( *it );
2685
2686     mStyledText.erase(it);  // erase the character left of positionToDelete
2687
2688     if( mStyledText.empty() )
2689     {
2690       // Styled text is empty, so set the placeholder text.
2691       mDisplayedTextView.SetText( mStyledPlaceHolderText );
2692       mPlaceHolderSet = true;
2693     }
2694     else
2695     {
2696       mDisplayedTextView.RemoveTextFrom( positionToDelete - 1, 1 );
2697
2698       const Character characterToDelete = styledCharacterToDelete.mText[0];
2699
2700       // It may happen than after removing a white space or a new line character,
2701       // two words merge, this new word could be big enough to not fit in its
2702       // current line, so moved to the next one, and make some part of the text to
2703       // exceed the text-input's boundary.
2704       if( !mExceedEnabled )
2705       {
2706         if( characterToDelete.IsWhiteSpace() || characterToDelete.IsNewLine() )
2707         {
2708           // Get the new text layout after removing one character.
2709           mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2710
2711           // Get text-input's size.
2712           const Vector3& size = GetControlSize();
2713
2714           if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2715               ( mTextLayoutInfo.mTextSize.height > size.height ) )
2716           {
2717             MarkupProcessor::StyledTextArray array;
2718             array.push_back( styledCharacterToDelete );
2719             mDisplayedTextView.InsertTextAt( positionToDelete - 1, array );
2720
2721             mStyledText.insert( mStyledText.begin() + ( positionToDelete - 1 ), styledCharacterToDelete );
2722           }
2723         }
2724       }
2725     }
2726     GetTextLayoutInfo();
2727
2728     ShowGrabHandleAndSetVisibility( false );
2729
2730     mCursorPosition = positionToDelete -1;
2731
2732     const TextStyle oldInputStyle( mInputStyle );
2733
2734     mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2735
2736     if( oldInputStyle != mInputStyle )
2737     {
2738       // Updates the line height accordingly with the input style.
2739       UpdateLineHeight();
2740
2741       EmitStyleChangedSignal();
2742     }
2743   }
2744 }
2745
2746 /*Insert new character into the string and (optionally) redisplay text-input*/
2747 std::size_t TextInput::InsertAt( const Text& newText, const std::size_t insertionPosition, const std::size_t numberOfCharactersToReplace )
2748 {
2749   DALI_LOG_INFO(gLogFilter, Debug::General, "InsertAt insertionPosition[%u]\n", insertionPosition );
2750
2751   // Ensure insertionPosition is not out of bounds.
2752   DALI_ASSERT_ALWAYS( insertionPosition <= mStyledText.size() );
2753
2754   bool textExceedsMaximunNumberOfCharacters = false;
2755   bool textExceedsBoundary = false;
2756   std::size_t insertedStringLength = DoInsertAt( newText, insertionPosition, numberOfCharactersToReplace, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
2757
2758   ShowGrabHandleAndSetVisibility( false );
2759
2760   if( textExceedsMaximunNumberOfCharacters || textExceedsBoundary )
2761   {
2762     if( mPreEditFlag )
2763     {
2764       mIgnoreCommitFlag = true;
2765       mPreEditFlag = false;
2766       // A PreEditReset( false ) should be triggered from here if the keyboards predictive suggestions must be cleared.
2767       // Although can not directly call PreEditReset() as it will cause a recursive emit loop.
2768     }
2769
2770     if( textExceedsMaximunNumberOfCharacters )
2771     {
2772       EmitMaxInputCharactersReachedSignal();
2773     }
2774
2775     if( textExceedsBoundary )
2776     {
2777       EmitInputTextExceedsBoundariesSignal();
2778       PreEditReset( false );
2779     }
2780   }
2781
2782   return insertedStringLength;
2783 }
2784
2785 ImageActor TextInput::CreateCursor( Image cursorImage, const Vector4& border )
2786 {
2787   ImageActor cursor;
2788
2789   if ( cursorImage )
2790   {
2791     cursor = ImageActor::New( cursorImage );
2792   }
2793   else
2794   {
2795     cursor = ImageActor::New( Image::New( DEFAULT_CURSOR ) );
2796   }
2797
2798   cursor.SetStyle(ImageActor::STYLE_NINE_PATCH);
2799   cursor.SetNinePatchBorder( border );
2800
2801   cursor.SetParentOrigin(ParentOrigin::TOP_LEFT);
2802   cursor.SetAnchorPoint(AnchorPoint::BOTTOM_CENTER);
2803   cursor.SetVisible(false);
2804
2805   return cursor;
2806 }
2807
2808 void TextInput::AdvanceCursor(bool reverse, std::size_t places)
2809 {
2810   // As cursor is not moving due to grab handle, handle should be hidden.
2811   ShowGrabHandleAndSetVisibility( false );
2812
2813   bool cursorPositionChanged = false;
2814   if (reverse)
2815   {
2816     if ( mCursorPosition >= places )
2817     {
2818       mCursorPosition = mCursorPosition - places;
2819       cursorPositionChanged = true;
2820     }
2821   }
2822   else
2823   {
2824     if ((mCursorPosition + places) <= mStyledText.size())
2825     {
2826       mCursorPosition = mCursorPosition + places;
2827       cursorPositionChanged = true;
2828     }
2829   }
2830
2831   if( cursorPositionChanged )
2832   {
2833     const std::size_t cursorPositionForStyle = ( 0 == mCursorPosition ? 0 : mCursorPosition - 1 );
2834
2835     const TextStyle oldInputStyle( mInputStyle );
2836     mInputStyle = GetStyleAt( cursorPositionForStyle ); // Inherit style from selected position.
2837
2838     DrawCursor();
2839
2840     if( oldInputStyle != mInputStyle )
2841     {
2842       // Updates the line height accordingly with the input style.
2843       UpdateLineHeight();
2844
2845       EmitStyleChangedSignal();
2846     }
2847
2848     ImfManager imfManager = ImfManager::Get();
2849     if ( imfManager )
2850     {
2851       imfManager.SetCursorPosition ( mCursorPosition );
2852       imfManager.NotifyCursorPosition();
2853     }
2854   }
2855 }
2856
2857 void TextInput::DrawCursor(const std::size_t nthChar)
2858 {
2859   // Get height of cursor and set its size
2860   Size size( CURSOR_THICKNESS, 0.0f );
2861   if (!mTextLayoutInfo.mCharacterLayoutInfoTable.empty())
2862   {
2863     size.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height;
2864   }
2865   else
2866   {
2867     // Measure Font so know how big text will be if no initial text to measure.
2868     size.height = mLineHeight;
2869   }
2870
2871   mCursor.SetSize(size);
2872
2873   // If the character is italic then the cursor also tilts.
2874   mCursor.SetRotation( mInputStyle.GetItalics() ? Degree( mInputStyle.GetItalicsAngle() - CURSOR_ANGLE_OFFSET ) : Degree( 0.f ), Vector3::ZAXIS );
2875
2876   DALI_ASSERT_DEBUG( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
2877
2878   if ( ( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) )
2879   {
2880     Vector3 altPosition;    // Alternate (i.e. opposite direction) cursor position.
2881     bool altPositionValid;  // Alternate cursor validity flag.
2882     bool directionRTL;      // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
2883     Vector3 position = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
2884
2885     SetAltCursorEnabled( altPositionValid );
2886
2887     if(!altPositionValid)
2888     {
2889       mCursor.SetPosition( position + UI_OFFSET );
2890     }
2891     else
2892     {
2893       size.height *= 0.5f;
2894       mCursor.SetSize(size);
2895       mCursor.SetPosition( position + UI_OFFSET - Vector3(0.0f, directionRTL ? 0.0f : size.height, 0.0f) );
2896
2897       // TODO: change this cursor pos, to be the one where the cursor is sourced from.
2898       Size rowSize = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) );
2899       size.height = rowSize.height * 0.5f;
2900       mCursorRTL.SetSize(size);
2901       mCursorRTL.SetPosition( altPosition + UI_OFFSET - Vector3(0.0f, directionRTL ? size.height : 0.0f, 0.0f) );
2902     }
2903
2904     if( IsScrollEnabled() )
2905     {
2906       // Whether cursor and grab handle are inside the boundaries of the text-input when text scroll is enabled.
2907       mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( position, size, GetControlSize() );
2908     }
2909   } // EditMode
2910 }
2911
2912 void TextInput::SetAltCursorEnabled( bool enabled )
2913 {
2914   mCursorRTLEnabled = enabled;
2915   mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2916 }
2917
2918 void TextInput::SetCursorVisibility( bool visible )
2919 {
2920   mCursorVisibility = visible;
2921   mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
2922   mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2923 }
2924
2925 void TextInput::CreateGrabHandle( Dali::Image image )
2926 {
2927   if ( !mGrabHandle )
2928   {
2929     if ( !image )
2930     {
2931       mGrabHandleImage = Image::New(DEFAULT_GRAB_HANDLE);
2932     }
2933     else
2934     {
2935       mGrabHandleImage = image;
2936     }
2937
2938     mGrabHandle = ImageActor::New(mGrabHandleImage);
2939     mGrabHandle.SetParentOrigin(ParentOrigin::TOP_LEFT);
2940     mGrabHandle.SetAnchorPoint(AnchorPoint::TOP_CENTER);
2941
2942     mGrabHandle.SetDrawMode(DrawMode::OVERLAY);
2943
2944     ShowGrabHandleAndSetVisibility( false );
2945
2946     CreateGrabArea( mGrabHandle );
2947
2948     mActiveLayer.Add(mGrabHandle);
2949   }
2950 }
2951
2952 void TextInput::CreateGrabArea( Actor& parent )
2953 {
2954   mGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
2955   mGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
2956   mGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE ) ) );  // grab area to be larger than text actor
2957   mGrabArea.TouchedSignal().Connect(this,&TextInput::OnPressDown);
2958   mTapDetector.Attach( mGrabArea );
2959   mPanGestureDetector.Attach( mGrabArea );
2960
2961   parent.Add(mGrabArea);
2962 }
2963
2964 Vector3 TextInput::MoveGrabHandle( const Vector2& displacement )
2965 {
2966   Vector3 actualHandlePosition;
2967
2968   if (mGrabHandle)
2969   {
2970     mActualGrabHandlePosition.x += displacement.x;
2971     mActualGrabHandlePosition.y += displacement.y;
2972
2973     // Grab handle should jump to the nearest character and take cursor with it
2974     std::size_t newCursorPosition = 0;
2975     ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY(), newCursorPosition );
2976
2977     actualHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition );
2978
2979     bool handleVisible = true;
2980
2981     if( IsScrollEnabled() )
2982     {
2983       const Vector3 controlSize = GetControlSize();
2984       const Size cursorSize = GetRowRectFromCharacterPosition( GetVisualPosition( newCursorPosition ) );
2985       // Scrolls the text if the handle is not in a visible position
2986       handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
2987                                                   cursorSize,
2988                                                   controlSize );
2989
2990       if( handleVisible )
2991       {
2992         StopScrollTimer();
2993         mCurrentHandlePosition = actualHandlePosition;
2994         mScrollDisplacement = Vector2::ZERO;
2995       }
2996       else
2997       {
2998         if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
2999         {
3000           mScrollDisplacement.x = -SCROLL_SPEED;
3001         }
3002         else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3003         {
3004           mScrollDisplacement.x = SCROLL_SPEED;
3005         }
3006         if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3007         {
3008           mScrollDisplacement.y = -SCROLL_SPEED;
3009         }
3010         else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3011         {
3012           mScrollDisplacement.y = SCROLL_SPEED;
3013         }
3014         StartScrollTimer();
3015       }
3016     }
3017
3018     if( handleVisible &&                           // Only redraw cursor and do updates if position changed
3019         ( newCursorPosition != mCursorPosition ) ) // and the new position is visible (if scroll is not enabled, it's always true).
3020     {
3021       mCursorPosition = newCursorPosition;
3022
3023       mGrabHandle.SetPosition( actualHandlePosition + UI_OFFSET );
3024
3025       const TextStyle oldInputStyle( mInputStyle );
3026
3027       mInputStyle = GetStyleAtCursor(); //Inherit style from cursor position
3028
3029       CursorUpdate();  // Let keyboard know the new cursor position so can 're-capture' for prediction.
3030
3031       if( oldInputStyle != mInputStyle )
3032       {
3033         // Updates the line height accordingly with the input style.
3034         UpdateLineHeight();
3035
3036         EmitStyleChangedSignal();
3037       }
3038     }
3039   }
3040
3041   return actualHandlePosition;
3042 }
3043
3044 void TextInput::ShowGrabHandle( bool visible )
3045 {
3046   if ( IsGrabHandleEnabled() )
3047   {
3048     if( mGrabHandle )
3049     {
3050       mGrabHandle.SetVisible( mGrabHandleVisibility );
3051     }
3052     StartMonitoringStageForTouch();
3053   }
3054 }
3055
3056 void TextInput::ShowGrabHandleAndSetVisibility( bool visible )
3057 {
3058   mGrabHandleVisibility = visible;
3059   ShowGrabHandle( visible );
3060 }
3061
3062 // Callbacks connected to be Property notifications for Boundary checking.
3063
3064 void TextInput::OnLeftBoundaryExceeded(PropertyNotification& source)
3065 {
3066   mIsSelectionHandleOneFlipped = true;
3067   mSelectionHandleOne.SetScale( -1.0f, 1.0f, 1.0f );
3068   mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3069 }
3070
3071 void TextInput::OnReturnToLeftBoundary(PropertyNotification& source)
3072 {
3073   mIsSelectionHandleOneFlipped = false;
3074   mSelectionHandleOne.SetScale( 1.0f, 1.0f, 1.0f );
3075   mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3076 }
3077
3078 void TextInput::OnRightBoundaryExceeded(PropertyNotification& source)
3079 {
3080   mIsSelectionHandleTwoFlipped = true;
3081   mSelectionHandleTwo.SetScale( -1.0f, 1.0f, 1.0f );
3082   mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3083 }
3084
3085 void TextInput::OnReturnToRightBoundary(PropertyNotification& source)
3086 {
3087   mIsSelectionHandleTwoFlipped = false;
3088   mSelectionHandleTwo.SetScale( 1.0f, 1.0f, 1.0f );
3089   mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3090 }
3091
3092 // todo change PropertyNotification signal definition to include Actor. Hence won't need duplicate functions.
3093 void TextInput::OnHandleOneLeavesBoundary( PropertyNotification& source)
3094 {
3095   mSelectionHandleOne.SetOpacity(0.0f);
3096 }
3097
3098 void TextInput::OnHandleOneWithinBoundary(PropertyNotification& source)
3099 {
3100   mSelectionHandleOne.SetOpacity(1.0f);
3101 }
3102
3103 void TextInput::OnHandleTwoLeavesBoundary( PropertyNotification& source)
3104 {
3105   mSelectionHandleTwo.SetOpacity(0.0f);
3106 }
3107
3108 void TextInput::OnHandleTwoWithinBoundary(PropertyNotification& source)
3109 {
3110   mSelectionHandleTwo.SetOpacity(1.0f);
3111 }
3112
3113 // End of Callbacks connected to be Property notifications for Boundary checking.
3114
3115 void TextInput::SetUpHandlePropertyNotifications()
3116 {
3117   /* Property notifications for handles exceeding the boundary and returning back within boundary */
3118
3119   Vector3 handlesize = GetSelectionHandleSize();
3120
3121   // Exceeding horizontal boundary
3122   PropertyNotification leftNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.x + handlesize.x) );
3123   leftNotification.NotifySignal().Connect( this, &TextInput::OnLeftBoundaryExceeded );
3124
3125   PropertyNotification rightNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.z - handlesize.x ) );
3126   rightNotification.NotifySignal().Connect( this, &TextInput::OnRightBoundaryExceeded );
3127
3128   // Within horizontal boundary
3129   PropertyNotification leftLeaveNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.x + 2*handlesize.x ) );
3130   leftLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToLeftBoundary );
3131
3132   PropertyNotification rightLeaveNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.z - 2*handlesize.x ) );
3133   rightLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToRightBoundary );
3134
3135   // Exceeding vertical boundary
3136   PropertyNotification verticalExceedNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3137                                                        OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3138                                                                          mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3139   verticalExceedNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneLeavesBoundary );
3140
3141   PropertyNotification verticalExceedNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3142                                                        OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3143                                                                          mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3144   verticalExceedNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoLeavesBoundary );
3145
3146   // Within vertical boundary
3147   PropertyNotification verticalWithinNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3148                                                        InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3149                                                                         mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3150   verticalWithinNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneWithinBoundary );
3151
3152   PropertyNotification verticalWithinNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3153                                                        InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3154                                                                         mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3155   verticalWithinNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoWithinBoundary );
3156 }
3157
3158 void TextInput::CreateSelectionHandles( std::size_t start, std::size_t end, Dali::Image handleOneImage,  Dali::Image handleTwoImage )
3159 {
3160   mSelectionHandleOnePosition = start;
3161   mSelectionHandleTwoPosition = end;
3162
3163   if ( !mSelectionHandleOne )
3164   {
3165     // create normal and pressed images
3166     mSelectionHandleOneImage = Image::New( DEFAULT_SELECTION_HANDLE_ONE );
3167     mSelectionHandleOneImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_ONE_PRESSED );
3168
3169     mSelectionHandleOne = ImageActor::New( mSelectionHandleOneImage );
3170     mSelectionHandleOne.SetName("SelectionHandleOne");
3171     mSelectionHandleOne.SetParentOrigin( ParentOrigin::TOP_LEFT );
3172     mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
3173     mIsSelectionHandleOneFlipped = false;
3174     mSelectionHandleOne.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text
3175
3176     mHandleOneGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3177     mHandleOneGrabArea.SetName("SelectionHandleOneGrabArea");
3178
3179     mHandleOneGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) );  // grab area to be larger than text actor
3180     mHandleOneGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3181
3182     mTapDetector.Attach( mHandleOneGrabArea );
3183     mPanGestureDetector.Attach( mHandleOneGrabArea );
3184
3185     mHandleOneGrabArea.TouchedSignal().Connect(this,&TextInput::OnHandleOneTouched);
3186
3187     mSelectionHandleOne.Add( mHandleOneGrabArea );
3188     mActiveLayer.Add( mSelectionHandleOne );
3189   }
3190
3191   if ( !mSelectionHandleTwo )
3192   {
3193     // create normal and pressed images
3194     mSelectionHandleTwoImage = Image::New( DEFAULT_SELECTION_HANDLE_TWO );
3195     mSelectionHandleTwoImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_TWO_PRESSED );
3196
3197     mSelectionHandleTwo = ImageActor::New( mSelectionHandleTwoImage );
3198     mSelectionHandleTwo.SetName("SelectionHandleTwo");
3199     mSelectionHandleTwo.SetParentOrigin( ParentOrigin::TOP_LEFT );
3200     mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT );
3201     mIsSelectionHandleTwoFlipped = false;
3202     mSelectionHandleTwo.SetDrawMode(DrawMode::OVERLAY); // ensure grab handle above text
3203
3204     mHandleTwoGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3205     mHandleTwoGrabArea.SetName("SelectionHandleTwoGrabArea");
3206     mHandleTwoGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) );  // grab area to be larger than text actor
3207     mHandleTwoGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3208
3209     mTapDetector.Attach( mHandleTwoGrabArea );
3210     mPanGestureDetector.Attach( mHandleTwoGrabArea );
3211
3212     mHandleTwoGrabArea.TouchedSignal().Connect(this, &TextInput::OnHandleTwoTouched);
3213
3214     mSelectionHandleTwo.Add( mHandleTwoGrabArea );
3215
3216     mActiveLayer.Add( mSelectionHandleTwo );
3217   }
3218
3219   SetUpHandlePropertyNotifications();
3220
3221   // update table as text may have changed.
3222   GetTextLayoutInfo();
3223
3224   mSelectionHandleOneActualPosition = GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition );
3225   mSelectionHandleTwoActualPosition = GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition );
3226
3227   mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
3228   mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
3229
3230   // Calculates and set the visibility if the scroll mode is enabled.
3231   bool isSelectionHandleOneVisible = true;
3232   bool isSelectionHandleTwoVisible = true;
3233   if( IsScrollEnabled() )
3234   {
3235     const Vector3& controlSize( GetControlSize() );
3236     isSelectionHandleOneVisible = IsPositionInsideBoundaries( mSelectionHandleOneActualPosition, Size::ZERO, controlSize );
3237     isSelectionHandleTwoVisible = IsPositionInsideBoundaries( mSelectionHandleTwoActualPosition, Size::ZERO, controlSize );
3238     mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
3239     mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
3240   }
3241
3242   CreateHighlight();  // function will only create highlight if not already created.
3243 }
3244
3245 Vector3 TextInput::MoveSelectionHandle( SelectionHandleId handleId, const Vector2& displacement )
3246 {
3247   Vector3 actualHandlePosition;
3248
3249   if ( mSelectionHandleOne && mSelectionHandleTwo )
3250   {
3251     const Vector3& controlSize = GetControlSize();
3252
3253     Size cursorSize( CURSOR_THICKNESS, 0.f );
3254
3255     // Get a reference of the wanted selection handle (handle one or two).
3256     Vector3& actualSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
3257
3258     // Get a reference for the current position of the handle and a copy of its pair
3259     std::size_t& currentSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3260     const std::size_t pairSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition;
3261
3262     // Get a handle of the selection handle actor
3263     ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3264
3265     // Selection handles should jump to the nearest character
3266     std::size_t newHandlePosition = 0;
3267     ReturnClosestIndex( actualSelectionHandlePosition.GetVectorXY(), newHandlePosition );
3268
3269     actualHandlePosition = GetActualPositionFromCharacterPosition( newHandlePosition );
3270
3271     bool handleVisible = true;
3272
3273     if( IsScrollEnabled() )
3274     {
3275       mCurrentSelectionId = handleId;
3276
3277       cursorSize.height = GetRowRectFromCharacterPosition( GetVisualPosition( newHandlePosition ) ).height;
3278       // Restricts the movement of the grab handle inside the boundaries of the text-input.
3279       handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3280                                                   cursorSize,
3281                                                   controlSize );
3282
3283       if( handleVisible )
3284       {
3285         StopScrollTimer();
3286         mCurrentSelectionHandlePosition = actualHandlePosition;
3287         mScrollDisplacement = Vector2::ZERO;
3288       }
3289       else
3290       {
3291         if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3292         {
3293           mScrollDisplacement.x = -SCROLL_SPEED;
3294         }
3295         else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3296         {
3297           mScrollDisplacement.x = SCROLL_SPEED;
3298         }
3299         if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3300         {
3301           mScrollDisplacement.y = -SCROLL_SPEED;
3302         }
3303         else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3304         {
3305           mScrollDisplacement.y = SCROLL_SPEED;
3306         }
3307         StartScrollTimer();
3308       }
3309     }
3310
3311     if ( handleVisible &&                                          // Ensure the handle is visible.
3312          ( newHandlePosition != pairSelectionHandlePosition ) &&   // Ensure handle one is not the same position as handle two.
3313          ( newHandlePosition != currentSelectionHandlePosition ) ) // Ensure the handle has moved.
3314     {
3315       currentSelectionHandlePosition = newHandlePosition;
3316
3317       Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3318       selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3319
3320       UpdateHighlight();
3321
3322       if ( handleId == HandleOne )
3323       {
3324         const TextStyle oldInputStyle( mInputStyle );
3325
3326         // Set Active Style to that of first character in selection
3327         if( mSelectionHandleOnePosition < mStyledText.size() )
3328         {
3329           mInputStyle = ( mStyledText.at( mSelectionHandleOnePosition ) ).mStyle;
3330         }
3331
3332         if( oldInputStyle != mInputStyle )
3333         {
3334           // Updates the line height accordingly with the input style.
3335           UpdateLineHeight();
3336
3337           EmitStyleChangedSignal();
3338         }
3339       }
3340     }
3341   }
3342
3343   return actualHandlePosition; // Returns Handle position passed in if new value not assigned.
3344 }
3345
3346 void TextInput::SetSelectionHandlePosition(SelectionHandleId handleId)
3347 {
3348
3349   const std::size_t selectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3350   ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3351
3352   if ( selectionHandleActor )
3353   {
3354     const Vector3 actualHandlePosition = GetActualPositionFromCharacterPosition( selectionHandlePosition );
3355     Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3356     selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3357
3358     if( IsScrollEnabled() )
3359     {
3360       const Size cursorSize( CURSOR_THICKNESS,
3361                              GetRowRectFromCharacterPosition( GetVisualPosition( selectionHandlePosition ) ).height );
3362       selectionHandleActor.SetVisible( IsPositionInsideBoundaries( actualHandlePosition,
3363                                                                    cursorSize,
3364                                                                    GetControlSize() ) );
3365     }
3366   }
3367 }
3368
3369 std::size_t TextInput::GetVisualPosition(std::size_t logicalPosition) const
3370 {
3371   // Note: we're allowing caller to request a logical position of size (i.e. end of string)
3372   // For now the visual position of end of logical string will be end of visual string.
3373   DALI_ASSERT_DEBUG( logicalPosition <= mTextLayoutInfo.mCharacterLogicalToVisualMap.size() );
3374
3375   return logicalPosition != mTextLayoutInfo.mCharacterLogicalToVisualMap.size() ? mTextLayoutInfo.mCharacterLogicalToVisualMap[logicalPosition] : mTextLayoutInfo.mCharacterLogicalToVisualMap.size();
3376 }
3377
3378 void TextInput::GetVisualTextSelection(std::vector<bool>& selectedVisualText, std::size_t startSelection, std::size_t endSelection)
3379 {
3380   std::vector<int>::iterator it = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin();
3381   std::vector<int>::iterator startSelectionIt = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::min(startSelection, endSelection);
3382   std::vector<int>::iterator endSelectionIt = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::max(startSelection, endSelection);
3383   std::vector<int>::iterator end = mTextLayoutInfo.mCharacterLogicalToVisualMap.end();
3384
3385   selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size() );
3386
3387   // Deselect text prior to startSelectionIt
3388   for(;it!=startSelectionIt;++it)
3389   {
3390     selectedVisualText[*it] = false;
3391   }
3392
3393   // Select text from startSelectionIt -> endSelectionIt
3394   for(;it!=endSelectionIt;++it)
3395   {
3396     selectedVisualText[*it] = true;
3397   }
3398
3399   // Deselect text after endSelection
3400   for(;it!=end;++it)
3401   {
3402     selectedVisualText[*it] = false;
3403   }
3404
3405   selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size(), false );
3406 }
3407
3408 // Calculate the dimensions of the quads they will make the highlight mesh
3409 TextInput::HighlightInfo TextInput::CalculateHighlightInfo()
3410 {
3411   // At the moment there is no public API to modify the block alignment option.
3412   const bool blockAlignEnabled = true;
3413
3414   mNewHighlightInfo.mQuadList.clear(); // clear last quad information.
3415
3416   if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
3417   {
3418     Toolkit::TextView::CharacterLayoutInfoContainer::iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3419     Toolkit::TextView::CharacterLayoutInfoContainer::iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
3420
3421     // Get vector of flags representing characters that are selected (true) vs unselected (false).
3422     std::vector<bool> selectedVisualText;
3423     GetVisualTextSelection(selectedVisualText, mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
3424     std::vector<bool>::iterator selectedIt(selectedVisualText.begin());
3425     std::vector<bool>::iterator selectedEndIt(selectedVisualText.end());
3426
3427     SelectionState selectionState = SelectionNone;          ///< Current selection status of cursor over entire text.
3428     float rowLeft = 0.0f;
3429     float rowRight = 0.0f;
3430     // Keep track of the TextView's min/max extents. Should be able to query this from TextView.
3431     float maxRowLeft = std::numeric_limits<float>::max();
3432     float maxRowRight = 0.0f;
3433
3434     Toolkit::TextView::CharacterLayoutInfoContainer::iterator lastIt = it;
3435
3436     // Scan through entire text.
3437     while(it != end)
3438     {
3439       // selectionState: None when not in selection, Started when in selection, and Ended when reached end of selection.
3440
3441       Toolkit::TextView::CharacterLayoutInfo& charInfo(*it);
3442       bool charSelected( false );
3443       if( selectedIt != selectedEndIt )
3444       {
3445         charSelected = *selectedIt++;
3446       }
3447
3448       if(selectionState == SelectionNone)
3449       {
3450         if(charSelected)
3451         {
3452           selectionState = SelectionStarted;
3453           rowLeft = charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3454           rowRight = rowLeft + charInfo.mSize.width;
3455         }
3456       }
3457       else if(selectionState == SelectionStarted)
3458       {
3459         // break selection on:
3460         // 1. new line causing selection break. (\n or wordwrap)
3461         // 2. character not selected.
3462         if(charInfo.mPosition.y - lastIt->mPosition.y > CHARACTER_THRESHOLD ||
3463            !charSelected)
3464         {
3465           // finished selection.
3466           // TODO: TextView should have a table of visual rows, and each character a reference to the row
3467           // that it resides on. That way this enumeration is not necessary.
3468           Vector2 min, max;
3469           if(lastIt->mIsNewLineChar)
3470           {
3471             // If the last character is a new line, then to get the row rect, we need to scan from the character before the new line.
3472             lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3473           }
3474           const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3475           maxRowLeft = std::min(maxRowLeft, min.x);
3476           maxRowRight = std::max(maxRowRight, max.x);
3477           float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3478           float rowTop = rowBottom - rowSize.height;
3479
3480           // Still selected, and block-align mode then set rowRight to max, so it can be clamped afterwards
3481           if(charSelected && blockAlignEnabled)
3482           {
3483             rowRight = std::numeric_limits<float>::max();
3484           }
3485           mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3486
3487           selectionState = SelectionNone;
3488
3489           // Still selected? start a new selection
3490           if( charSelected )
3491           {
3492             // if block-align mode then set rowLeft to min, so it can be clamped afterwards
3493             rowLeft = blockAlignEnabled ? 0.0f : charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3494             rowRight = ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width;
3495             selectionState = SelectionStarted;
3496           }
3497         }
3498         else
3499         {
3500           // build up highlight(s) with this selection data.
3501           rowLeft = std::min( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x, rowLeft );
3502           rowRight = std::max( ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width, rowRight );
3503         }
3504       }
3505
3506       lastIt = it++;
3507     }
3508
3509     // If reached end, and still on selection, then close selection.
3510     if(it == end)
3511     {
3512       if(selectionState == SelectionStarted)
3513       {
3514         // finished selection.
3515         Vector2 min, max;
3516         if(lastIt->mIsNewLineChar)
3517         {
3518           lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3519         }
3520         const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3521         maxRowLeft = std::min(maxRowLeft, min.x);
3522         maxRowRight = std::max(maxRowRight, max.x);
3523         float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3524         float rowTop = rowBottom - rowSize.height;
3525         mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3526       }
3527     }
3528
3529     // Get the top left and bottom right corners.
3530     const Toolkit::TextView::CharacterLayoutInfo& firstCharacter( *mTextLayoutInfo.mCharacterLayoutInfoTable.begin() );
3531     const Vector2 topLeft( maxRowLeft, firstCharacter.mPosition.y - firstCharacter.mSize.height );
3532     const Vector2 bottomRight( topLeft.x + mTextLayoutInfo.mTextSize.width, topLeft.y + mTextLayoutInfo.mTextSize.height );
3533
3534     // Clamp quads so they appear to clip to borders of the whole text.
3535     mNewHighlightInfo.Clamp2D( topLeft, bottomRight );
3536
3537     // For block-align align Further Clamp quads to max left and right extents
3538     if(blockAlignEnabled)
3539     {
3540       // BlockAlign: Will adjust highlight to block:
3541       // i.e.
3542       //   H[ello] (top row right = max of all rows right)
3543       // [--this-] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3544       // [is some] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3545       // [text] (bottom row left = min of all rows left)
3546       // (common in SMS messaging selection)
3547       //
3548       // As opposed to the default which is tight text highlighting.
3549       //   H[ello]
3550       //   [this]
3551       // [is some]
3552       // [text]
3553       // (common in regular text editors/web browser selection)
3554
3555       mNewHighlightInfo.Clamp2D( Vector2(maxRowLeft, topLeft.y), Vector2(maxRowRight, bottomRight.y ) );
3556     }
3557
3558     // Finally clamp quads again so they don't exceed the boundry of the control.
3559     const Vector3& controlSize = GetControlSize();
3560     mNewHighlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
3561   } // end if
3562
3563   return mNewHighlightInfo;
3564 }
3565
3566 void TextInput::UpdateHighlight()
3567 {
3568 //  Construct a Mesh with a texture to be used as the highlight 'box' for selected text
3569 //
3570 //  Example scenarios where mesh is made from 3, 1, 2, 2 ,3 or 3 quads.
3571 //
3572 //   [ TOP   ]  [ TOP ]      [TOP ]  [ TOP    ]      [ TOP  ]      [ TOP  ]
3573 //  [ MIDDLE ]             [BOTTOM]  [BOTTOM]      [ MIDDLE ]   [ MIDDLE  ]
3574 //  [ BOTTOM]                                      [ MIDDLE ]   [ MIDDLE  ]
3575 //                                                 [BOTTOM]     [ MIDDLE  ]
3576 //                                                              [BOTTOM]
3577 //
3578 //  Each quad is created as 2 triangles.
3579 //  Middle is just 1 quad regardless of its size.
3580 //
3581 //  (0,0)         (0,0)
3582 //     0*    *2     0*       *2
3583 //     TOP          TOP
3584 //     3*    *1     3*       *1
3585 //  4*       *1     4*     *6
3586 //     MIDDLE         BOTTOM
3587 //  6*       *5     7*     *5
3588 //  6*    *8
3589 //   BOTTOM
3590 //  9*    *7
3591 //
3592
3593   if ( mHighlightMeshActor )
3594   {
3595     // vertex and triangle buffers should always be present if MeshActor is alive.
3596     HighlightInfo newHighlightInfo = CalculateHighlightInfo();
3597     MeshData::VertexContainer vertices;
3598     Dali::MeshData::FaceIndices faceIndices;
3599
3600     if( !newHighlightInfo.mQuadList.empty() )
3601     {
3602       std::vector<QuadCoordinates>::iterator iter = newHighlightInfo.mQuadList.begin();
3603       std::vector<QuadCoordinates>::iterator endIter = newHighlightInfo.mQuadList.end();
3604
3605       // vertex position defaults to (0 0 0)
3606       MeshData::Vertex vertex;
3607       // set normal for all vertices as (0 0 1) pointing outward from TextInput Actor.
3608       vertex.nZ = 1.0f;
3609
3610       for(std::size_t v = 0; iter != endIter; ++iter,v+=4 )
3611       {
3612         // Add each quad geometry (a sub-selection) to the mesh data.
3613
3614         // 0-----1
3615         // |\    |
3616         // | \ A |
3617         // |  \  |
3618         // | B \ |
3619         // |    \|
3620         // 2-----3
3621
3622         QuadCoordinates& quad = *iter;
3623         // top-left (v+0)
3624         vertex.x = quad.min.x;
3625         vertex.y = quad.min.y;
3626         vertices.push_back( vertex );
3627
3628         // top-right (v+1)
3629         vertex.x = quad.max.x;
3630         vertex.y = quad.min.y;
3631         vertices.push_back( vertex );
3632
3633         // bottom-left (v+2)
3634         vertex.x = quad.min.x;
3635         vertex.y = quad.max.y;
3636         vertices.push_back( vertex );
3637
3638         // bottom-right (v+3)
3639         vertex.x = quad.max.x;
3640         vertex.y = quad.max.y;
3641         vertices.push_back( vertex );
3642
3643         // triangle A (3, 1, 0)
3644         faceIndices.push_back( v + 3 );
3645         faceIndices.push_back( v + 1 );
3646         faceIndices.push_back( v );
3647
3648         // triangle B (0, 2, 3)
3649         faceIndices.push_back( v );
3650         faceIndices.push_back( v + 2 );
3651         faceIndices.push_back( v + 3 );
3652
3653         mMeshData.SetFaceIndices( faceIndices );
3654       }
3655
3656       BoneContainer bones(0); // passed empty as bones not required
3657       mMeshData.SetData( vertices, faceIndices, bones, mCustomMaterial );
3658       mHighlightMesh.UpdateMeshData(mMeshData);
3659     }
3660   }
3661 }
3662
3663 void TextInput::ClearPopup()
3664 {
3665   mPopUpPanel.Clear();
3666 }
3667
3668 void TextInput::AddPopupOption(const std::string& name, const std::string& caption, const Image icon, bool finalOption)
3669 {
3670   mPopUpPanel.AddOption(name, caption, icon, finalOption);
3671 }
3672
3673 void TextInput::SetPopupPosition(const Vector3& position)
3674 {
3675   mPopUpPanel.Self().SetPosition( position );
3676 }
3677
3678 void TextInput::HidePopup(bool animate, bool signalFinished )
3679 {
3680   if ( ( mPopUpPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopUpPanel.GetState() == TextInputPopup::StateShown )  )
3681   {
3682     mPopUpPanel.Hide( animate );
3683
3684     if( animate && signalFinished )
3685     {
3686       mPopUpPanel.HideFinishedSignal().Connect( this, &TextInput::OnPopupHideFinished );
3687     }
3688   }
3689 }
3690
3691 void TextInput::ShowPopup(bool animate)
3692 {
3693   Vector3 position;
3694
3695   if(mHighlightMeshActor && mState == StateEdit)
3696   {
3697     Vector3 topHandle;
3698     Size rowSize;
3699     // When text is selected, show popup above top handle (and text), or below bottom handle.
3700     // topHandle: referring to the top most point of the handle or the top line of selection.
3701     if ( mSelectionHandleTwoActualPosition.y > mSelectionHandleOneActualPosition.y )
3702     {
3703       topHandle = mSelectionHandleOneActualPosition;
3704       rowSize= GetRowRectFromCharacterPosition( mSelectionHandleOnePosition );
3705     }
3706     else
3707     {
3708       topHandle = mSelectionHandleTwoActualPosition;
3709       rowSize = GetRowRectFromCharacterPosition( mSelectionHandleTwoPosition );
3710     }
3711     topHandle.y += TOP_HANDLE_TOP_OFFSET - rowSize.height;
3712     position = Vector3(topHandle.x, topHandle.y, 0.0f);
3713
3714     // bottomHandle: referring to the bottom most point of the handle or the bottom line of selection.
3715     Vector3 bottomHandle;
3716     bottomHandle.y = std::max ( mSelectionHandleTwoActualPosition.y , mSelectionHandleOneActualPosition.y );
3717     bottomHandle.y += GetSelectionHandleSize().y + BOTTOM_HANDLE_BOTTOM_OFFSET;
3718     mPopUpPanel.SetAlternativeOffset(Vector2(0.0f, bottomHandle.y - topHandle.y));
3719   }
3720   else
3721   {
3722     // When no text is selected, show popup at world position of grab handle or cursor
3723     position = GetActualPositionFromCharacterPosition( mCursorPosition );
3724     const Size rowSize = GetRowRectFromCharacterPosition( mCursorPosition );
3725     position.y -= rowSize.height;
3726     // if can't be positioned above, then position below row.
3727     Vector2 alternativePopUpPosition( 0.0f, position.y ); // default if no grab handle
3728     if ( mGrabHandle )
3729     {
3730       alternativePopUpPosition.y = rowSize.height + ( mGrabHandle.GetCurrentSize().height * DEFAULT_GRAB_HANDLE_RELATIVE_SIZE.y ) ;
3731       // If grab handle enabled then position pop-up below the grab handle.
3732     }
3733     mPopUpPanel.SetAlternativeOffset( alternativePopUpPosition );
3734   }
3735
3736   // reposition popup above the desired cursor posiiton.
3737   Vector3 textViewSize = mDisplayedTextView.GetCurrentSize();
3738   textViewSize.z = 0.0f;
3739   // World position = world position of ParentOrigin of cursor (i.e. top-left corner of TextView) + cursor position;
3740   Vector3 worldPosition = mDisplayedTextView.GetCurrentWorldPosition() - (textViewSize * 0.5f) + position;
3741
3742   SetPopupPosition( worldPosition );
3743
3744   // Show popup
3745   mPopUpPanel.Show(animate);
3746   StartMonitoringStageForTouch();
3747
3748   mPopUpPanel.PressedSignal().Connect( this, &TextInput::OnPopupButtonPressed );
3749 }
3750
3751 void TextInput::ShowPopupCutCopyPaste()
3752 {
3753   ClearPopup();
3754   // Check the selected text is whole text or not.
3755   if( IsTextSelected() && ( mStyledText.size() != GetSelectedText().size() ) )
3756   {
3757     Image selectAllIcon = Image::New( DEFAULT_ICON_SELECT_ALL );
3758     AddPopupOption( OPTION_SELECT_ALL, GET_LOCALE_TEXT("IDS_COM_BODY_SELECT_ALL"), selectAllIcon );
3759   }
3760
3761   if ( !mStyledText.empty() )
3762   {
3763     Image cutIcon = Image::New( DEFAULT_ICON_CUT );
3764     Image copyIcon = Image::New( DEFAULT_ICON_COPY );
3765     AddPopupOption( OPTION_CUT, GET_LOCALE_TEXT("IDS_COM_BODY_CUT"), cutIcon );
3766     AddPopupOption( OPTION_COPY, GET_LOCALE_TEXT("IDS_COM_BODY_COPY"), copyIcon, true );
3767   }
3768
3769   if(mClipboard.NumberOfItems())
3770   {
3771     Image pasteIcon = Image::New( DEFAULT_ICON_PASTE );
3772     Image clipboardIcon = Image::New( DEFAULT_ICON_CLIPBOARD );
3773     AddPopupOption( OPTION_PASTE, GET_LOCALE_TEXT("IDS_COM_BODY_PASTE"), pasteIcon );
3774     AddPopupOption( OPTION_CLIPBOARD, GET_LOCALE_TEXT("IDS_COM_BODY_CLIPBOARD"), clipboardIcon, true );
3775   }
3776
3777   mPopUpPanel.Hide(false);
3778   ShowPopup();
3779 }
3780
3781 void TextInput::SetUpPopUpSelection()
3782 {
3783   ClearPopup();
3784
3785   // If no text exists then don't offer to select
3786   if ( !mStyledText.empty() )
3787   {
3788     Image selectIcon = Image::New( DEFAULT_ICON_SELECT );
3789     Image selectAllIcon = Image::New( DEFAULT_ICON_SELECT_ALL );
3790     AddPopupOption( OPTION_SELECT_WORD, GET_LOCALE_TEXT("IDS_COM_SK_SELECT"), selectIcon );
3791     AddPopupOption( OPTION_SELECT_ALL, GET_LOCALE_TEXT("IDS_COM_BODY_SELECT_ALL"), selectAllIcon );
3792   }
3793   // if clipboard has valid contents then offer paste option
3794   if( mClipboard.NumberOfItems() )
3795   {
3796     Image pasteIcon = Image::New( DEFAULT_ICON_PASTE );
3797     Image clipboardIcon = Image::New( DEFAULT_ICON_CLIPBOARD );
3798     AddPopupOption( OPTION_PASTE, GET_LOCALE_TEXT("IDS_COM_BODY_PASTE"), pasteIcon, true );
3799     AddPopupOption( OPTION_CLIPBOARD, GET_LOCALE_TEXT("IDS_COM_BODY_CLIPBOARD"), clipboardIcon, true );
3800   }
3801
3802   mPopUpPanel.Hide(false);
3803 }
3804
3805 bool TextInput::ReturnClosestIndex(const Vector2& source, std::size_t& closestIndex )
3806 {
3807   bool found = false;
3808   closestIndex = 0;
3809
3810   std::vector<Toolkit::TextView::CharacterLayoutInfo> matchedCharacters;
3811   bool lastRightToLeftChar(false);          /// RTL state of previous character encountered (character on the left of touch point)
3812   bool rightToLeftChar(false);              /// RTL state of current character encountered (character on the right of touch point)
3813   float glyphIntersection(0.0f);            /// Glyph intersection, the point between the two nearest characters touched.
3814
3815   const Vector2 sourceScrollOffset( source + mTextLayoutInfo.mScrollOffset );
3816
3817   if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
3818   {
3819     float closestYdifference = std::numeric_limits<float>::max();
3820     std::size_t lineOffset = 0;                  /// Keep track of position of the first character on the matched line of interest.
3821     std::size_t numberOfMatchedCharacters = 0;
3822
3823     // 1. Find closest character line to y part of source, create vector of all entries in that Y position
3824     // TODO: There should be an easy call to enumerate through each visual line, instead of each character on all visual lines.
3825
3826     for( std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), endIt = mTextLayoutInfo.mCharacterLayoutInfoTable.end(); it != endIt; ++it )
3827     {
3828       const Toolkit::TextView::CharacterLayoutInfo& info( *it );
3829       float baselinePosition = info.mPosition.y - info.mDescender;
3830
3831       if( info.mIsVisible )
3832       {
3833         // store difference between source y point and the y position of the current character
3834         float currentYdifference = fabsf( sourceScrollOffset.y - ( baselinePosition ) );
3835
3836         if(  currentYdifference < closestYdifference  )
3837         {
3838           // closest so far; store this difference and clear previous matchedCharacters as no longer closest
3839           lineOffset = it - mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3840           closestYdifference = currentYdifference;
3841           matchedCharacters.clear();
3842           numberOfMatchedCharacters = 0; // reset count
3843         }
3844
3845         // add all characters that are on the same Y axis (within the CHARACTER_THRESHOLD) to the matched array.
3846         if( fabsf( closestYdifference - currentYdifference )  < CHARACTER_THRESHOLD )
3847         {
3848           // ignore new line character.
3849           if( !info.mIsNewLineChar )
3850           {
3851             matchedCharacters.push_back( info );
3852             numberOfMatchedCharacters++;
3853           }
3854         }
3855       }
3856     } // End of loop checking each character's y position in the character layout table
3857
3858     // Check if last character is a newline, if it is
3859     // then need pretend there is an imaginary line afterwards,
3860     // and check if user is touching below previous line.
3861     const Toolkit::TextView::CharacterLayoutInfo& lastInfo( mTextLayoutInfo.mCharacterLayoutInfoTable[mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1] );
3862
3863     if( ( lastInfo.mIsVisible ) && ( lastInfo.mIsNewLineChar ) && ( sourceScrollOffset.y > lastInfo.mPosition.y ) )
3864     {
3865       closestIndex = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
3866     }
3867     else
3868     {
3869       std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator it = matchedCharacters.begin();
3870       std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator endIt = matchedCharacters.end();
3871
3872       bool matched( false );
3873
3874       // 2 Iterate through matching list of y positions and find closest matching X position.
3875       for( ; it != endIt; ++it )
3876       {
3877         const Toolkit::TextView::CharacterLayoutInfo& info( *it );
3878
3879         if( info.mIsVisible )
3880         {
3881           // stop when on left side of character's center.
3882           const float characterMidPointPosition = info.mPosition.x  + ( info.mSize.width * 0.5f ) ;
3883           if( sourceScrollOffset.x < characterMidPointPosition )
3884           {
3885             if(info.mIsRightToLeftCharacter)
3886             {
3887               rightToLeftChar = true;
3888             }
3889             glyphIntersection = info.mPosition.x;
3890             matched = true;
3891             break;
3892           }
3893
3894           lastRightToLeftChar = info.mIsRightToLeftCharacter;
3895         }
3896       }
3897
3898       if( it == endIt )
3899       {
3900         rightToLeftChar = lastRightToLeftChar;
3901       }
3902
3903       std::size_t matchCharacterIndex = it - matchedCharacters.begin();
3904       closestIndex = lineOffset + matchCharacterIndex;
3905
3906       mClosestCursorPositionEOL = false; // reset
3907       if ( it == endIt && !matched )
3908       {
3909         mClosestCursorPositionEOL = true; // Reached end of matched characters in closest line but no match so cursor should be after last character.
3910       }
3911
3912       // For RTL characters, need to adjust closestIndex by 1 (as the inequality above would be reverse)
3913       if( rightToLeftChar && lastRightToLeftChar )
3914       {
3915         --closestIndex; // (-1 = numeric_limits<std::size_t>::max())
3916       }
3917     }
3918   }
3919
3920   // closestIndex is the visual index, need to convert it to the logical index
3921   if( !mTextLayoutInfo.mCharacterVisualToLogicalMap.empty() )
3922   {
3923     if( closestIndex < mTextLayoutInfo.mCharacterVisualToLogicalMap.size() )
3924     {
3925       // Checks for situations where user is touching between LTR and RTL
3926       // characters. To identify if the user means the end of a LTR string
3927       // or the beginning of an RTL string, and vice versa.
3928       if( closestIndex > 0 )
3929       {
3930         if( rightToLeftChar && !lastRightToLeftChar )
3931         {
3932           // [LTR] [RTL]
3933           //   |..|..|
3934           //   AAA BBB
3935           // A: In this touch range, the user is indicating that they wish to place
3936           // the cursor at the end of the LTR text.
3937           // B: In this touch range, the user is indicating that they wish to place
3938           // the cursor at the end of the RTL text.
3939
3940           // Result of touching A area:
3941           // [.....LTR]|[RTL......]+
3942           //
3943           // |: primary cursor (for typing LTR chars)
3944           // +: secondary cursor (for typing RTL chars)
3945
3946           // Result of touching B area:
3947           // [.....LTR]+[RTL......]|
3948           //
3949           // |: primary cursor (for typing RTL chars)
3950           // +: secondary cursor (for typing LTR chars)
3951
3952           if( sourceScrollOffset.x < glyphIntersection )
3953           {
3954             --closestIndex;
3955           }
3956         }
3957         else if( !rightToLeftChar && lastRightToLeftChar )
3958         {
3959           if( sourceScrollOffset.x < glyphIntersection )
3960           {
3961             --closestIndex;
3962           }
3963         }
3964       }
3965
3966       closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap[closestIndex];
3967       // If user touched a left-side of RTL char, and the character on the left was an LTR then position logical cursor
3968       // one further ahead
3969       if( rightToLeftChar && !lastRightToLeftChar )
3970       {
3971         ++closestIndex;
3972       }
3973     }
3974     else if( closestIndex == numeric_limits<std::size_t>::max() ) // -1 RTL (after last arabic character on line)
3975     {
3976       closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap.size();
3977     }
3978     else if( mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterVisualToLogicalMap[ closestIndex - 1 ] ].mIsRightToLeftCharacter ) // size() LTR (after last european character on line)
3979     {
3980       closestIndex = 0;
3981     }
3982   }
3983
3984   return found;
3985 }
3986
3987 float TextInput::GetLineJustificationPosition() const
3988 {
3989   const Vector3& size = mDisplayedTextView.GetCurrentSize();
3990   Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
3991   float alignmentOffset = 0.f;
3992
3993   // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
3994   if( alignment & Toolkit::Alignment::HorizontalLeft )
3995   {
3996     alignmentOffset = 0.f;
3997   }
3998   else if( alignment & Toolkit::Alignment::HorizontalCenter )
3999   {
4000     alignmentOffset = 0.5f * ( size.width - mTextLayoutInfo.mTextSize.width );
4001   }
4002   else if( alignment & Toolkit::Alignment::HorizontalRight )
4003   {
4004     alignmentOffset = size.width - mTextLayoutInfo.mTextSize.width;
4005   }
4006
4007   Toolkit::TextView::LineJustification justification = mDisplayedTextView.GetLineJustification();
4008   float justificationOffset = 0.f;
4009
4010   switch( justification )
4011   {
4012     case Toolkit::TextView::Left:
4013     {
4014       justificationOffset = 0.f;
4015       break;
4016     }
4017     case Toolkit::TextView::Center:
4018     {
4019       justificationOffset = 0.5f * mTextLayoutInfo.mTextSize.width;
4020       break;
4021     }
4022     case Toolkit::TextView::Right:
4023     {
4024       justificationOffset = mTextLayoutInfo.mTextSize.width;
4025       break;
4026     }
4027     case Toolkit::TextView::Justified:
4028     {
4029       justificationOffset = 0.f;
4030       break;
4031     }
4032     default:
4033     {
4034       DALI_ASSERT_ALWAYS( false );
4035     }
4036   } // end switch
4037
4038   return alignmentOffset + justificationOffset;
4039 }
4040
4041 Vector3 TextInput::PositionCursorAfterWordWrap( std::size_t characterPosition ) const
4042 {
4043   /* Word wrap occurs automatically in TextView when the exceed policy moves a word to the next line when not enough space on current.
4044      A newline character is not inserted in this case */
4045
4046   DALI_ASSERT_DEBUG( !(characterPosition <= 0 ));
4047
4048   Vector3 cursorPosition;
4049
4050   Toolkit::TextView::CharacterLayoutInfo currentCharInfo;
4051
4052   if ( characterPosition == mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4053   {
4054     // end character so use
4055     currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1 ];
4056     cursorPosition = Vector3(currentCharInfo.mPosition.x + currentCharInfo.mSize.width, currentCharInfo.mPosition.y, currentCharInfo.mPosition.z) ;
4057   }
4058   else
4059   {
4060     currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4061   }
4062
4063   Toolkit::TextView::CharacterLayoutInfo previousCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1];
4064
4065   // If previous character on a different line then use current characters position
4066   if ( fabsf( (currentCharInfo.mPosition.y - currentCharInfo.mDescender )  - ( previousCharInfo.mPosition.y - previousCharInfo.mDescender) ) > Math::MACHINE_EPSILON_1000 )
4067   {
4068     if ( mClosestCursorPositionEOL )
4069     {
4070       cursorPosition = Vector3(previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z) ;
4071     }
4072     else
4073     {
4074       cursorPosition = Vector3(currentCharInfo.mPosition);
4075     }
4076   }
4077   else
4078   {
4079     // Previous character is on same line so use position of previous character plus it's width.
4080     cursorPosition = Vector3(previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z) ;
4081   }
4082
4083   return cursorPosition;
4084 }
4085
4086 Vector3 TextInput::GetActualPositionFromCharacterPosition(std::size_t characterPosition) const
4087 {
4088   bool direction(false);
4089   Vector3 alternatePosition;
4090   bool alternatePositionValid(false);
4091
4092   return GetActualPositionFromCharacterPosition( characterPosition, direction, alternatePosition, alternatePositionValid );
4093 }
4094
4095 Vector3 TextInput::GetActualPositionFromCharacterPosition(std::size_t characterPosition, bool& directionRTL, Vector3& alternatePosition, bool& alternatePositionValid ) const
4096 {
4097   Vector3 cursorPosition( 0.f, 0.f, 0.f );
4098
4099   alternatePositionValid = false;
4100   directionRTL = false;
4101
4102   if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
4103   {
4104     std::size_t visualCharacterPosition;
4105
4106     // When cursor is not at beginning, consider possibility of
4107     // showing 2 cursors. (whereas at beginning we only ever show one cursor)
4108     if(characterPosition > 0)
4109     {
4110       // Cursor position should be the end of the last character.
4111       // If the last character is LTR, then the end is on the right side of the glyph.
4112       // If the last character is RTL, then the end is on the left side of the glyph.
4113       visualCharacterPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition - 1 ];
4114
4115       if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + visualCharacterPosition ) ).mIsVisible )
4116       {
4117         visualCharacterPosition = FindVisibleCharacter( Left, visualCharacterPosition );
4118       }
4119
4120       Toolkit::TextView::CharacterLayoutInfo info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4121       if( ( visualCharacterPosition > 0 ) && info.mIsNewLineChar && !IsScrollEnabled() )
4122       {
4123         // Prevents the cursor to exceed the boundary if the last visible character is a 'new line character' and the scroll is not enabled.
4124         const Vector3& size = GetControlSize();
4125
4126         if( info.mPosition.y + info.mSize.height - mDisplayedTextView.GetLineHeightOffset() > size.height )
4127         {
4128           --visualCharacterPosition;
4129         }
4130         info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4131       }
4132
4133       if(!info.mIsNewLineChar)
4134       {
4135         cursorPosition = PositionCursorAfterWordWrap( characterPosition ); // Get position of cursor/handles taking in account auto word wrap.
4136       }
4137       else
4138       {
4139         // When cursor points to first character on new line, position cursor at the start of this glyph.
4140         if(characterPosition < mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4141         {
4142           std::size_t visualCharacterNextPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition ];
4143           const Toolkit::TextView::CharacterLayoutInfo& infoNext = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterNextPosition ];
4144           const float start( infoNext.mIsRightToLeftCharacter ? infoNext.mSize.width : 0.0f );
4145
4146           cursorPosition.x = infoNext.mPosition.x + start;
4147           cursorPosition.y = infoNext.mPosition.y;
4148         }
4149         else
4150         {
4151           // If cursor points to the end of text, then can only position
4152           // cursor where the new line starts based on the line-justification position.
4153           cursorPosition.x = GetLineJustificationPosition();
4154
4155           if(characterPosition == mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4156           {
4157             // If this is after the last character, then we can assume that the new cursor
4158             // should be exactly one row below the current row.
4159
4160             const Size rowRect(GetRowRectFromCharacterPosition(characterPosition - 1));
4161             cursorPosition.y = info.mPosition.y + rowRect.height;
4162           }
4163           else
4164           {
4165             // If this is not after last character, then we can use this row's height.
4166             // should be exactly one row below the current row.
4167
4168             const Size rowRect(GetRowRectFromCharacterPosition(characterPosition));
4169             cursorPosition.y = info.mPosition.y + rowRect.height;
4170           }
4171         }
4172       }
4173
4174       directionRTL = info.mIsRightToLeftCharacter;
4175
4176       // 1. When the cursor is neither at the beginning or the end,
4177       // we can show multiple cursors under situations when the cursor is
4178       // between RTL and LTR text...
4179       if(characterPosition != mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4180       {
4181         std::size_t visualCharacterAltPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[characterPosition] - 1;
4182
4183         DALI_ASSERT_ALWAYS(visualCharacterAltPosition < mTextLayoutInfo.mCharacterLayoutInfoTable.size());
4184         const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterAltPosition ];
4185
4186         if(!info.mIsRightToLeftCharacter && infoAlt.mIsRightToLeftCharacter)
4187         {
4188           // Stuation occurs when cursor is at the end of English text (LTR) and beginning of Arabic (RTL)
4189           // Text:     [...LTR...]|[...RTL...]
4190           // Cursor pos:          ^
4191           // Alternate cursor pos:            ^
4192           // In which case we need to display an alternate cursor for the RTL text.
4193
4194           alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width;
4195           alternatePosition.y = infoAlt.mPosition.y;
4196           alternatePositionValid = true;
4197         }
4198         else if(info.mIsRightToLeftCharacter && !infoAlt.mIsRightToLeftCharacter)
4199         {
4200           // Situation occurs when cursor is at end of the Arabic text (LTR) and beginning of English (RTL)
4201           // Text:           |[...RTL...] [...LTR....]
4202           // Cursor pos:     ^
4203           // Alternate cursor pos:       ^
4204           // In which case we need to display an alternate cursor for the RTL text.
4205
4206           alternatePosition.x = infoAlt.mPosition.x;
4207           alternatePosition.y = infoAlt.mPosition.y;
4208           alternatePositionValid = true;
4209         }
4210       }
4211       else
4212       {
4213         // 2. When the cursor is at the end of the text,
4214         // and we have multi-directional text,
4215         // we can also consider showing mulitple cursors.
4216         // The rule here is:
4217         // If first and last characters on row are different
4218         // Directions, then two cursors need to be displayed.
4219
4220         // Get first logical glyph on row
4221         std::size_t startCharacterPosition = GetRowStartFromCharacterPosition( characterPosition - 1 );
4222
4223         std::size_t visualCharacterStartPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ startCharacterPosition ];
4224         const Toolkit::TextView::CharacterLayoutInfo& infoStart= mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterStartPosition ];
4225
4226         if(info.mIsRightToLeftCharacter && !infoStart.mIsRightToLeftCharacter)
4227         {
4228           // For text Starting as LTR and ending as RTL. End cursor position is as follows:
4229           // Text:     [...LTR...]|[...RTL...]
4230           // Cursor pos:          ^
4231           // Alternate cursor pos:            ^
4232           // In which case we need to display an alternate cursor for the RTL text, this cursor
4233           // should be at the end of the given line.
4234
4235           const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1 ];
4236           alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width;
4237           alternatePosition.y = infoAlt.mPosition.y;
4238           alternatePositionValid = true;
4239         }
4240         else if(!info.mIsRightToLeftCharacter && infoStart.mIsRightToLeftCharacter) // starting RTL
4241         {
4242           // For text Starting as RTL and ending as LTR. End cursor position is as follows:
4243           // Text:           |[...RTL...] [...LTR....]
4244           // Cursor pos:     ^
4245           // Alternate cursor pos:       ^
4246           // In which case we need to display an alternate cursor for the RTL text.
4247
4248           const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ startCharacterPosition ];
4249           alternatePosition.x = infoAlt.mPosition.x;
4250           alternatePosition.y = infoAlt.mPosition.y;
4251           alternatePositionValid = true;
4252         }
4253       }
4254     } // characterPosition > 0
4255     else if(characterPosition == 0)
4256     {
4257       // When the cursor position is at the beginning, it should be at the start of the current character.
4258       // If the current character is LTR, then the start is on the right side of the glyph.
4259       // If the current character is RTL, then the start is on the left side of the glyph.
4260       visualCharacterPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition ];
4261
4262       if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + visualCharacterPosition ) ).mIsVisible )
4263       {
4264          visualCharacterPosition = FindVisibleCharacter( Right, visualCharacterPosition );
4265       }
4266
4267       const Toolkit::TextView::CharacterLayoutInfo& info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4268       const float start(info.mIsRightToLeftCharacter ? info.mSize.width : 0.0f);
4269
4270       cursorPosition.x = info.mPosition.x + start;
4271       cursorPosition.y = info.mPosition.y;
4272       directionRTL = info.mIsRightToLeftCharacter;
4273     }
4274   }
4275   else
4276   {
4277     // If the character table is void, place the cursor accordingly the text alignment.
4278     const Vector3& size = GetControlSize();
4279
4280     Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4281     float alignmentOffset = 0.f;
4282
4283     // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4284     if( alignment & Toolkit::Alignment::HorizontalLeft )
4285     {
4286       alignmentOffset = 0.f;
4287     }
4288     else if( alignment & Toolkit::Alignment::HorizontalCenter )
4289     {
4290       alignmentOffset = 0.5f * ( size.width );
4291     }
4292     else if( alignment & Toolkit::Alignment::HorizontalRight )
4293     {
4294       alignmentOffset = size.width;
4295     }
4296
4297     // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4298     cursorPosition.x = alignmentOffset;
4299
4300     // Work out cursor 'y' position when there are any character accordingly with the text view alignment settings.
4301     if( alignment & Toolkit::Alignment::VerticalTop )
4302     {
4303       cursorPosition.y = mLineHeight;
4304     }
4305     else if( alignment & Toolkit::Alignment::VerticalCenter )
4306     {
4307       cursorPosition.y = 0.5f * ( size.height + mLineHeight );
4308     }
4309     else if( alignment & Toolkit::Alignment::VerticalBottom )
4310     {
4311       cursorPosition.y = size.height;
4312     }
4313   }
4314
4315   cursorPosition.x -= mTextLayoutInfo.mScrollOffset.x;
4316   cursorPosition.y -= mTextLayoutInfo.mScrollOffset.y;
4317   if( alternatePositionValid )
4318   {
4319     alternatePosition.x -= mTextLayoutInfo.mScrollOffset.x;
4320     alternatePosition.y -= mTextLayoutInfo.mScrollOffset.y;
4321   }
4322
4323   return cursorPosition;
4324 }
4325
4326 std::size_t TextInput::GetRowStartFromCharacterPosition(std::size_t logicalPosition) const
4327 {
4328   // scan string from current position to beginning of current line to note direction of line
4329   while(logicalPosition)
4330   {
4331     logicalPosition--;
4332     std::size_t visualPosition = GetVisualPosition(logicalPosition);
4333     if(mTextLayoutInfo.mCharacterLayoutInfoTable[visualPosition].mIsNewLineChar)
4334     {
4335       logicalPosition++;
4336       break;
4337     }
4338   }
4339
4340   return logicalPosition;
4341 }
4342
4343 Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition) const
4344 {
4345   Vector2 min, max;
4346
4347   return GetRowRectFromCharacterPosition( characterPosition, min, max );
4348 }
4349
4350 Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition, Vector2& min, Vector2& max) const
4351 {
4352   // if we have no text content, then return position 0,0 with width 0, and height the same as cursor height.
4353   if( mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4354   {
4355     min = Vector2::ZERO;
4356     max = Vector2(0.0f, mLineHeight);
4357     return max;
4358   }
4359
4360   // TODO: This info should be readily available from text-view, we should not have to search hard for it.
4361   Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator begin = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
4362   Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
4363
4364   // If cursor is pointing to end of line, then start from last character.
4365   characterPosition = std::min( characterPosition, static_cast<std::size_t>(mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1) );
4366
4367   Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4368
4369   // 0. Find first a visible character. Draw a cursor beyound text-input bounds is not wanted.
4370   if( !it->mIsVisible )
4371   {
4372     characterPosition = FindVisibleCharacter( Left, characterPosition );
4373     it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4374   }
4375
4376   // Scan characters left and right of cursor, stopping when end of line/string reached or
4377   // y position greater than threshold of reference line.
4378
4379   // 1. scan left until we reach the beginning or a different line.
4380   Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator validCharIt = it;
4381   float referenceLine = it->mPosition.y - CHARACTER_THRESHOLD;
4382   // min-x position is the left-most char's left (x)
4383   // max-x position is the right-most char's right (x)
4384   // min-y position is the minimum of all character's top (y)
4385   // max-y position is the maximum of all character's bottom (y+height)
4386   min.y = validCharIt->mPosition.y;
4387   max.y = validCharIt->mPosition.y + validCharIt->mSize.y;
4388
4389   while(true)
4390   {
4391     validCharIt = it;
4392     min.y = std::min(min.y, validCharIt->mPosition.y);
4393     max.y = std::max(max.y, validCharIt->mPosition.y + validCharIt->mSize.y);
4394
4395     if(it == begin)
4396     {
4397       break;
4398     }
4399
4400     --it;
4401
4402     if( (it->mPosition.y < referenceLine) ||
4403         (it->mIsNewLineChar) ||
4404         (!it->mIsVisible) )
4405     {
4406       break;
4407     }
4408   }
4409
4410   // info refers to the first character on this line.
4411   min.x = validCharIt->mPosition.x;
4412
4413   // 2. scan right until we reach end or a different line
4414   it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4415   referenceLine = it->mPosition.y + CHARACTER_THRESHOLD;
4416
4417   while(it != end)
4418   {
4419     if( (it->mPosition.y > referenceLine) ||
4420         (it->mIsNewLineChar) ||
4421         (!it->mIsVisible) )
4422     {
4423       break;
4424     }
4425
4426     validCharIt = it;
4427     min.y = std::min(min.y, validCharIt->mPosition.y);
4428     max.y = std::max(max.y, validCharIt->mPosition.y + validCharIt->mSize.y);
4429
4430     ++it;
4431   }
4432
4433   DALI_ASSERT_DEBUG ( validCharIt != end  && "validCharIt invalid")
4434
4435   if ( validCharIt != end )
4436   {
4437     // info refers to the last character on this line.
4438     max.x = validCharIt->mPosition.x + validCharIt->mSize.x;
4439   }
4440
4441   return Size( max.x - min.x, max.y - min.y );
4442 }
4443
4444 bool TextInput::WasTouchedCheck( const Actor& touchedActor ) const
4445 {
4446   Actor popUpPanel = mPopUpPanel.GetRootActor();
4447
4448   if ( ( touchedActor == Self() ) || ( touchedActor == popUpPanel ) )
4449   {
4450     return true;
4451   }
4452   else
4453   {
4454     Dali::Actor parent( touchedActor.GetParent() );
4455
4456     if ( parent )
4457     {
4458       return WasTouchedCheck( parent );
4459     }
4460   }
4461
4462   return false;
4463 }
4464
4465 void TextInput::StartMonitoringStageForTouch()
4466 {
4467   Stage stage = Stage::GetCurrent();
4468   stage.TouchedSignal().Connect( this, &TextInput::OnStageTouched );
4469 }
4470
4471 void TextInput::EndMonitoringStageForTouch()
4472 {
4473   Stage stage = Stage::GetCurrent();
4474   stage.TouchedSignal().Disconnect( this, &TextInput::OnStageTouched );
4475 }
4476
4477 void TextInput::OnStageTouched(const TouchEvent& event)
4478 {
4479   if( event.GetPointCount() > 0 )
4480   {
4481     if ( TouchPoint::Down == event.GetPoint(0).state )
4482     {
4483       const Actor touchedActor(event.GetPoint(0).hitActor);
4484
4485       bool popUpShown( false );
4486
4487       if ( ( mPopUpPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopUpPanel.GetState() == TextInputPopup::StateShown ) )
4488       {
4489         popUpShown = true;
4490       }
4491
4492       bool textInputTouched = (touchedActor && WasTouchedCheck( touchedActor ));
4493
4494       if ( ( mHighlightMeshActor || popUpShown ) && !textInputTouched )
4495       {
4496         EndMonitoringStageForTouch();
4497         HidePopup( true, false );
4498       }
4499
4500       if ( ( IsGrabHandleEnabled() && mGrabHandle  ) && !textInputTouched )
4501       {
4502         EndMonitoringStageForTouch();
4503         ShowGrabHandleAndSetVisibility( false );
4504       }
4505     }
4506   }
4507 }
4508
4509 void TextInput::SelectText(std::size_t start, std::size_t end)
4510 {
4511   DALI_LOG_INFO(gLogFilter, Debug::General, "SelectText mEditModeActive[%s] grabHandle[%s] start[%u] end[%u] size[%u]\n", mEditModeActive?"true":"false",
4512                                                                                                                           IsGrabHandleEnabled()?"true":"false",
4513                                                                                                                           start, end, mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
4514   DALI_ASSERT_ALWAYS( start <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText start out of max range" );
4515   DALI_ASSERT_ALWAYS( end <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText end out of max range" );
4516
4517   StartMonitoringStageForTouch();
4518
4519   if ( mEditModeActive ) // Only allow text selection when in edit mode
4520   {
4521     // When replacing highlighted text keyboard should ignore current word at cursor hence notify keyboard that the cursor is at the start of the highlight.
4522     mSelectingText = true;
4523
4524     mCursorPosition = std::min( start, end ); // Set cursor position to start of highlighted text.
4525
4526     ImfManager imfManager = ImfManager::Get();
4527     if ( imfManager )
4528     {
4529       imfManager.SetCursorPosition ( mCursorPosition );
4530       imfManager.SetSurroundingText( GetText() );
4531       imfManager.NotifyCursorPosition();
4532     }
4533     // As the imfManager has been notified of the new cursor position we do not need to reset the pre-edit as it will be updated instead.
4534
4535     // Hide grab handle when selecting.
4536     ShowGrabHandleAndSetVisibility( false );
4537
4538     if( start != end )  // something to select
4539     {
4540       SetCursorVisibility( false );
4541       StopCursorBlinkTimer();
4542
4543       CreateSelectionHandles(start, end);
4544       UpdateHighlight();
4545
4546       const TextStyle oldInputStyle( mInputStyle );
4547       mInputStyle = GetStyleAt( mCursorPosition ); // Inherit style from selected position.
4548
4549       if( oldInputStyle != mInputStyle )
4550       {
4551         // Updates the line height accordingly with the input style.
4552         UpdateLineHeight();
4553
4554         EmitStyleChangedSignal();
4555       }
4556
4557       HidePopup();
4558     }
4559
4560     mSelectingText = false;
4561   }
4562 }
4563
4564 MarkupProcessor::StyledTextArray TextInput::GetSelectedText()
4565 {
4566   MarkupProcessor::StyledTextArray currentSelectedText;
4567
4568   if ( IsTextSelected() )
4569   {
4570     MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4571     MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4572
4573     for(; it != end; ++it)
4574     {
4575       MarkupProcessor::StyledText& styledText( *it );
4576       currentSelectedText.push_back( styledText );
4577     }
4578   }
4579   return currentSelectedText;
4580 }
4581
4582 void TextInput::ApplyStyleToRange(const TextStyle& style, const TextStyle::Mask mask, const std::size_t begin, const std::size_t end)
4583 {
4584   const std::size_t beginIndex = std::min( begin, end );
4585   const std::size_t endIndex = std::max( begin, end );
4586
4587   // Apply the style
4588   MarkupProcessor::SetTextStyleToRange( mStyledText, style, mask, beginIndex, endIndex );
4589
4590   // Create a styled text array used to replace the text into the text-view.
4591   MarkupProcessor::StyledTextArray text;
4592   text.insert( text.begin(), mStyledText.begin() + beginIndex, mStyledText.begin() + endIndex + 1 );
4593
4594   mDisplayedTextView.ReplaceTextFromTo( beginIndex, ( endIndex - beginIndex ) + 1, text );
4595   GetTextLayoutInfo();
4596
4597   if( IsScrollEnabled() )
4598   {
4599     // Need to set the scroll position as the text's size may have changed.
4600     ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
4601   }
4602
4603   ShowGrabHandleAndSetVisibility( false );
4604
4605   DrawCursor();
4606
4607   UpdateHighlight();
4608
4609   // Set Handle positioning as the new style may have repositioned the characters.
4610   SetSelectionHandlePosition(HandleOne);
4611   SetSelectionHandlePosition(HandleTwo);
4612 }
4613
4614 void TextInput::KeyboardStatusChanged(bool keyboardShown)
4615 {
4616   // Just hide the grab handle when keyboard is hidden.
4617   if (!keyboardShown )
4618   {
4619     ShowGrabHandleAndSetVisibility( false );
4620
4621     // If the keyboard is not now being shown, then hide the popup panel
4622     mPopUpPanel.Hide( true );
4623   }
4624 }
4625
4626 // Removes highlight and resumes edit mode state
4627 void TextInput::RemoveHighlight()
4628 {
4629   DALI_LOG_INFO(gLogFilter, Debug::General, "RemoveHighlight\n");
4630
4631   if ( mHighlightMeshActor )
4632   {
4633     if ( mSelectionHandleOne )
4634     {
4635       mActiveLayer.Remove( mSelectionHandleOne );
4636       mSelectionHandleOne.Reset();
4637       mSelectionHandleOneOffset.x = 0.0f;
4638     }
4639     if ( mSelectionHandleTwo )
4640     {
4641       mActiveLayer.Remove( mSelectionHandleTwo );
4642       mSelectionHandleTwo.Reset();
4643       mSelectionHandleTwoOffset.x = 0.0f;
4644     }
4645
4646     mNewHighlightInfo.mQuadList.clear();
4647
4648     Self().Remove( mHighlightMeshActor );
4649
4650     SetCursorVisibility( true );
4651     StartCursorBlinkTimer();
4652
4653     mHighlightMeshActor.Reset();
4654     // NOTE: We cannot dereference mHighlightMesh, due
4655     // to a bug in how the scene-graph MeshRenderer uses the Mesh data incorrectly.
4656
4657     HidePopup();
4658   }
4659
4660   mSelectionHandleOnePosition = 0;
4661   mSelectionHandleTwoPosition = 0;
4662 }
4663
4664 void TextInput::CreateHighlight()
4665 {
4666   if ( !mHighlightMeshActor )
4667   {
4668     mMeshData = MeshData( );
4669     mMeshData.SetHasNormals( true );
4670
4671     mCustomMaterial = Material::New("CustomMaterial");
4672     mCustomMaterial.SetDiffuseColor( LIGHTBLUE );
4673
4674     mMeshData.SetMaterial( mCustomMaterial );
4675
4676     mHighlightMesh = Mesh::New( mMeshData );
4677
4678     mHighlightMeshActor = MeshActor::New( mHighlightMesh );
4679     mHighlightMeshActor.SetName( "HighlightMeshActor" );
4680     mHighlightMeshActor.SetInheritShaderEffect( false );
4681     mHighlightMeshActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
4682     mHighlightMeshActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
4683     mHighlightMeshActor.SetPosition( 0.0f, 0.0f, DISPLAYED_HIGHLIGHT_Z_OFFSET );
4684     mHighlightMeshActor.SetAffectedByLighting(false);
4685
4686     Self().Add(mHighlightMeshActor);
4687   }
4688 }
4689
4690
4691 bool TextInput::CopySelectedTextToClipboard()
4692 {
4693   mCurrentCopySelecton.clear();
4694
4695   mCurrentCopySelecton = GetSelectedText();
4696
4697   std::string stringToStore;
4698
4699   /* Create a StyledTextArray from the selected region so can use the MarkUpProcessor to produce
4700    * a marked up string.
4701    */
4702   MarkupProcessor::StyledTextArray selectedText(mCurrentCopySelecton.begin(),mCurrentCopySelecton.end());
4703   MarkupProcessor::GetPlainString( selectedText, stringToStore );
4704   bool success = mClipboard.SetItem( stringToStore );
4705   return success;
4706 }
4707
4708 void TextInput::PasteText( const Text& text )
4709 {
4710   // Update Flag, indicates whether to update the text-input contents or not.
4711   // Any key stroke that results in a visual change of the text-input should
4712   // set this flag to true.
4713   bool update = false;
4714   if( mHighlightMeshActor )
4715   {
4716     /* if highlighted, delete entire text, and position cursor at start of deleted text. */
4717     mCursorPosition = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4718
4719     ImfManager imfManager = ImfManager::Get();
4720     if ( imfManager )
4721     {
4722       imfManager.SetCursorPosition( mCursorPosition );
4723       imfManager.NotifyCursorPosition();
4724     }
4725     DeleteHighlightedText( true );
4726     update = true;
4727   }
4728
4729   bool textExceedsMaximunNumberOfCharacters = false;
4730   bool textExceedsBoundary = false;
4731
4732   std::size_t insertedStringLength = DoInsertAt( text, mCursorPosition, 0, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
4733
4734   mCursorPosition += insertedStringLength;
4735   ImfManager imfManager = ImfManager::Get();
4736   if ( imfManager )
4737   {
4738     imfManager.SetCursorPosition ( mCursorPosition );
4739     imfManager.NotifyCursorPosition();
4740   }
4741
4742   update = update || ( insertedStringLength > 0 );
4743   if( update )
4744   {
4745     CursorUpdate();
4746   }
4747
4748   if( insertedStringLength < text.GetLength() )
4749   {
4750     EmitMaxInputCharactersReachedSignal();
4751   }
4752
4753   if( textExceedsBoundary )
4754   {
4755     EmitInputTextExceedsBoundariesSignal();
4756   }
4757 }
4758
4759 void TextInput::SetTextDirection()
4760 {
4761   // Put the cursor to the right if we are empty and an RTL language is being used.
4762   if ( mStyledText.empty() )
4763   {
4764     VirtualKeyboard::TextDirection direction( VirtualKeyboard::GetTextDirection() );
4765
4766     // Get the current text alignment preserving the vertical alignment. Also preserve the horizontal center
4767     // alignment as we do not want to set the text direction if we've been asked to be in the center.
4768     //
4769     // TODO: Should split SetTextAlignment into two APIs to better handle this (sometimes apps just want to
4770     //       set vertical alignment but are being forced to set the horizontal alignment as well with the
4771     //       current API.
4772     int alignment( mDisplayedTextView.GetTextAlignment() &
4773                   ( Toolkit::Alignment::VerticalTop |
4774                     Toolkit::Alignment::VerticalCenter |
4775                     Toolkit::Alignment::VerticalBottom |
4776                     Toolkit::Alignment::HorizontalCenter ) );
4777     Toolkit::TextView::LineJustification justification( mDisplayedTextView.GetLineJustification() );
4778
4779     // If our alignment is in the center, then do not change.
4780     if ( !( alignment & Toolkit::Alignment::HorizontalCenter ) )
4781     {
4782       alignment |= ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight;
4783     }
4784
4785     // If our justification is in the center, then do not change.
4786     if ( justification != Toolkit::TextView::Center )
4787     {
4788       justification = ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::TextView::Left : Toolkit::TextView::Right;
4789     }
4790
4791     mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(alignment) );
4792     mDisplayedTextView.SetLineJustification( justification );
4793   }
4794 }
4795
4796 void TextInput::UpdateLineHeight()
4797 {
4798   Dali::Font font = Dali::Font::New( FontParameters( mInputStyle.GetFontName(), mInputStyle.GetFontStyle(), mInputStyle.GetFontPointSize() ) );
4799   mLineHeight = font.GetLineHeight();
4800
4801   // If the height exceed policy is shrink or exceed the boundaries of the text-input is not allowed, then modify the line height is needed.
4802
4803   const bool shrink = mDisplayedTextView && ( Toolkit::TextView::ShrinkToFit == mDisplayedTextView.GetHeightExceedPolicy() ) && mStyledText.empty();
4804
4805   if( !mExceedEnabled || shrink )
4806   {
4807     mLineHeight = std::min( mLineHeight, GetControlSize().height );
4808   }
4809 }
4810
4811 std::size_t TextInput::FindVisibleCharacter( const FindVisibleCharacterDirection direction , const std::size_t cursorPosition ) const
4812 {
4813   std::size_t position = 0;
4814
4815   const std::size_t tableSize = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
4816
4817   switch( direction )
4818   {
4819     case Left:
4820     {
4821       position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4822
4823       if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1 : position ) ) ).mIsVisible )
4824       {
4825         position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4826       }
4827       break;
4828     }
4829     case Right:
4830     {
4831       position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4832       if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1 : position ) ) ).mIsVisible )
4833       {
4834         position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4835       }
4836       break;
4837     }
4838     case ByEnd:
4839     {
4840       position = FindVisibleCharacterLeft( 0, mTextLayoutInfo.mCharacterLayoutInfoTable );
4841       break;
4842     }
4843     default:
4844     {
4845       DALI_ASSERT_ALWAYS( !"TextInput::FindVisibleCharacter() Unknown direction." );
4846     }
4847   }
4848
4849   return position;
4850 }
4851
4852 void TextInput::SetSortModifier( float depthOffset )
4853 {
4854   if(mDisplayedTextView)
4855   {
4856     mDisplayedTextView.SetSortModifier(depthOffset);
4857   }
4858 }
4859
4860 void TextInput::SetSnapshotModeEnabled( bool enable )
4861 {
4862   if(mDisplayedTextView)
4863   {
4864     mDisplayedTextView.SetSnapshotModeEnabled( enable );
4865   }
4866 }
4867
4868 bool TextInput::IsSnapshotModeEnabled() const
4869 {
4870   bool snapshotEnabled = false;
4871
4872   if(mDisplayedTextView)
4873   {
4874     snapshotEnabled = mDisplayedTextView.IsSnapshotModeEnabled();
4875   }
4876
4877   return snapshotEnabled;
4878 }
4879
4880 void TextInput::SetMarkupProcessingEnabled( bool enable )
4881 {
4882   mMarkUpEnabled = enable;
4883 }
4884
4885 bool TextInput::IsMarkupProcessingEnabled() const
4886 {
4887   return mMarkUpEnabled;
4888 }
4889
4890 void TextInput::SetScrollEnabled( bool enable )
4891 {
4892   if( mDisplayedTextView )
4893   {
4894     mDisplayedTextView.SetScrollEnabled( enable );
4895   }
4896
4897   if( !enable )
4898   {
4899     // Don't set cursor's and handle's visibility to false if they are outside the
4900     // boundaries of the text-input.
4901     mIsCursorInScrollArea = true;
4902     mIsGrabHandleInScrollArea = true;
4903     if( mSelectionHandleOne && mSelectionHandleTwo )
4904     {
4905       mSelectionHandleOne.SetVisible( true );
4906       mSelectionHandleTwo.SetVisible( true );
4907
4908       if( mHighlightMeshActor )
4909       {
4910         mHighlightMeshActor.SetVisible( true );
4911       }
4912     }
4913   }
4914 }
4915
4916 bool TextInput::IsScrollEnabled() const
4917 {
4918   bool scrollEnabled = false;
4919
4920   if( mDisplayedTextView )
4921   {
4922     scrollEnabled = mDisplayedTextView.IsScrollEnabled();
4923   }
4924
4925   return scrollEnabled;
4926 }
4927
4928 void TextInput::SetScrollPosition( const Vector2& position )
4929 {
4930   if( mDisplayedTextView )
4931   {
4932     mDisplayedTextView.SetScrollPosition( position );
4933   }
4934 }
4935
4936 Vector2 TextInput::GetScrollPosition() const
4937 {
4938   Vector2 scrollPosition;
4939
4940   if( mDisplayedTextView )
4941   {
4942     scrollPosition = mDisplayedTextView.GetScrollPosition();
4943   }
4944
4945   return scrollPosition;
4946 }
4947
4948 std::size_t TextInput::DoInsertAt( const Text& text, const std::size_t position, const std::size_t numberOfCharactersToReplace, bool& textExceedsMaximunNumberOfCharacters, bool& textExceedsBoundary )
4949 {
4950   // determine number of characters that we can write to style text buffer, this is the insertStringLength
4951   std::size_t insertedStringLength = std::min( text.GetLength(), mMaxStringLength - mStyledText.size() );
4952   textExceedsMaximunNumberOfCharacters = insertedStringLength < text.GetLength();
4953
4954   // Add style to the new input text.
4955   MarkupProcessor::StyledTextArray textToInsert;
4956   for( std::size_t i = 0; i < insertedStringLength; ++i )
4957   {
4958     const MarkupProcessor::StyledText newStyledCharacter( text[i], mInputStyle );
4959     textToInsert.push_back( newStyledCharacter );
4960   }
4961
4962   //Insert text to the TextView.
4963   const bool emptyTextView = mStyledText.empty();
4964   if( emptyTextView && mPlaceHolderSet )
4965   {
4966     // There is no text set so call to TextView::SetText() is needed in order to clear the placeholder text.
4967     mDisplayedTextView.SetText( textToInsert );
4968   }
4969   else
4970   {
4971     if( 0 == numberOfCharactersToReplace )
4972     {
4973       mDisplayedTextView.InsertTextAt( position, textToInsert );
4974     }
4975     else
4976     {
4977       mDisplayedTextView.ReplaceTextFromTo( position, numberOfCharactersToReplace, textToInsert );
4978     }
4979   }
4980   mPlaceHolderSet = false;
4981
4982   if( textToInsert.empty() )
4983   {
4984     // If no text has been inserted, GetTextLayoutInfo() need to be called to check whether mStyledText has some text.
4985     GetTextLayoutInfo();
4986   }
4987   else
4988   {
4989     // GetTextLayoutInfo() can't be used here as mStyledText is not updated yet.
4990     mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
4991   }
4992
4993   textExceedsBoundary = false;
4994
4995   if( !mExceedEnabled )
4996   {
4997     const Vector3& size = GetControlSize();
4998
4999     if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5000     {
5001       // If new text does not fit within TextView
5002       mDisplayedTextView.RemoveTextFrom( position, insertedStringLength );
5003       // previously inserted text has been removed. Call GetTextLayoutInfo() to check whether mStyledText has some text.
5004       GetTextLayoutInfo();
5005       textExceedsBoundary = true;
5006       insertedStringLength = 0;
5007     }
5008
5009     if( textExceedsBoundary )
5010     {
5011       // Add the part of the text which fits on the text-input.
5012
5013       // Split the text which doesn't fit in two halves.
5014       MarkupProcessor::StyledTextArray firstHalf;
5015       MarkupProcessor::StyledTextArray secondHalf;
5016       SplitText( textToInsert, firstHalf, secondHalf );
5017
5018       // Clear text. This text will be filled with the text inserted.
5019       textToInsert.clear();
5020
5021       // Where to insert the text.
5022       std::size_t positionToInsert = position;
5023
5024       bool end = text.GetLength() <= 1;
5025       while( !end )
5026       {
5027         // Insert text and check ...
5028         const std::size_t textLength = firstHalf.size();
5029         mDisplayedTextView.InsertTextAt( positionToInsert, firstHalf );
5030         mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5031
5032         if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5033         {
5034           // Inserted text doesn't fit.
5035
5036           // Remove inserted text
5037           mDisplayedTextView.RemoveTextFrom( positionToInsert, textLength );
5038           mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5039
5040           // The iteration finishes when only one character doesn't fit.
5041           end = textLength <= 1;
5042
5043           if( !end )
5044           {
5045             // Prepare next two halves for next iteration.
5046             MarkupProcessor::StyledTextArray copyText = firstHalf;
5047             SplitText( copyText, firstHalf, secondHalf );
5048           }
5049         }
5050         else
5051         {
5052           // Text fits.
5053
5054           // store text to be inserted in mStyledText.
5055           textToInsert.insert( textToInsert.end(), firstHalf.begin(), firstHalf.end() );
5056
5057           // Increase the inserted characters counter.
5058           insertedStringLength += textLength;
5059
5060           // Prepare next two halves for next iteration.
5061           MarkupProcessor::StyledTextArray copyText = secondHalf;
5062           SplitText( copyText, firstHalf, secondHalf );
5063
5064           // Update where next text has to be inserted
5065           positionToInsert += textLength;
5066         }
5067       }
5068     }
5069   }
5070
5071   if( textToInsert.empty() && emptyTextView )
5072   {
5073     // No character has been added and the text-view was empty.
5074     // Set the placeholder text.
5075     mDisplayedTextView.SetText( mStyledPlaceHolderText );
5076     mPlaceHolderSet = true;
5077   }
5078   else
5079   {
5080     MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + position;
5081     mStyledText.insert( it, textToInsert.begin(), textToInsert.end() );
5082     mPlaceHolderSet = false;
5083   }
5084
5085   return insertedStringLength;
5086 }
5087
5088 void TextInput::GetTextLayoutInfo()
5089 {
5090   if( mStyledText.empty() )
5091   {
5092     // The text-input has no text, clear the text-view's layout info.
5093     mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5094   }
5095   else
5096   {
5097     if( mDisplayedTextView )
5098     {
5099       mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5100     }
5101     else
5102     {
5103       // There is no text-view.
5104       mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5105     }
5106   }
5107 }
5108
5109 void TextInput::EmitStyleChangedSignal()
5110 {
5111   // emit signal if input style changes.
5112   Toolkit::TextInput handle( GetOwner() );
5113   mStyleChangedSignalV2.Emit( handle, mInputStyle );
5114 }
5115
5116 void TextInput::EmitTextModified()
5117 {
5118   // emit signal when text changes.
5119   Toolkit::TextInput handle( GetOwner() );
5120   mTextModifiedSignal.Emit( handle );
5121 }
5122
5123
5124 void TextInput::EmitMaxInputCharactersReachedSignal()
5125 {
5126   // emit signal if max characters is reached during text input.
5127   DALI_LOG_INFO(gLogFilter, Debug::General, "EmitMaxInputCharactersReachedSignal \n");
5128
5129   Toolkit::TextInput handle( GetOwner() );
5130   mMaxInputCharactersReachedSignalV2.Emit( handle );
5131 }
5132
5133 void TextInput::EmitInputTextExceedsBoundariesSignal()
5134 {
5135   // Emit a signal when the input text exceeds the boundaries of the text input.
5136
5137   Toolkit::TextInput handle( GetOwner() );
5138   mInputTextExceedBoundariesSignalV2.Emit( handle );
5139 }
5140
5141 } // namespace Internal
5142
5143 } // namespace Toolkit
5144
5145 } // namespace Dali