Moved ControlImpl to Internal namespace & renamed to Control
[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
448 void TextInput::SetText( const MarkupProcessor::StyledTextArray& styleText )
449 {
450   DALI_LOG_INFO(gLogFilter, Debug::General, "SetText markup text\n" );
451
452   mDisplayedTextView.SetText( styleText );
453   mPlaceHolderSet = false;
454
455   // If text alignment hasn't been manually set by application developer, then we
456   // automatically determine the alignment based on the content of the text i.e. what
457   // language the text begins with.
458   // TODO: This should determine different alignments for each line (broken by '\n') of text.
459   if(!mOverrideAutomaticAlignment)
460   {
461     // Determine bidi direction of first character (skipping past whitespace, numbers, and symbols)
462     bool leftToRight(true);
463
464     if( !styleText.empty() )
465     {
466       bool breakOut(false);
467
468       for( MarkupProcessor::StyledTextArray::const_iterator textIter = styleText.begin(), textEndIter = styleText.end(); ( textIter != textEndIter ) && ( !breakOut ); ++textIter )
469       {
470         const Text& text = textIter->mText;
471
472         for( std::size_t i = 0; i < text.GetLength(); ++i )
473         {
474           Character character( text[i] );
475           if( character.GetCharacterDirection() != Character::Neutral )
476           {
477             leftToRight = ( character.GetCharacterDirection() == Character::LeftToRight );
478             breakOut = true;
479             break;
480           }
481         }
482       }
483     }
484
485     // Based on this direction, either left or right align text if not manually set by application developer.
486     mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(
487                                            ( leftToRight ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight) |
488                                              Toolkit::Alignment::VerticalTop ) );
489     mDisplayedTextView.SetLineJustification( leftToRight ? Toolkit::TextView::Left : Toolkit::TextView::Right);
490   }
491 }
492
493 void TextInput::SetMaxCharacterLength(std::size_t maxChars)
494 {
495   mMaxStringLength = maxChars;
496 }
497
498 void TextInput::SetNumberOfLinesLimit(std::size_t maxLines)
499 {
500   DALI_ASSERT_DEBUG( maxLines > 0 )
501
502   if ( maxLines > 0)
503   {
504     mNumberOflinesLimit = maxLines;
505   }
506 }
507
508 std::size_t TextInput::GetNumberOfLinesLimit() const
509 {
510   return mNumberOflinesLimit;
511 }
512
513 std::size_t TextInput::GetNumberOfCharacters() const
514 {
515   return mStyledText.size();
516 }
517
518 Toolkit::TextInput::InputSignalV2& TextInput::InputStartedSignal()
519 {
520   return mInputStartedSignalV2;
521 }
522
523 Toolkit::TextInput::InputSignalV2& TextInput::InputFinishedSignal()
524 {
525   return mInputFinishedSignalV2;
526 }
527
528 Toolkit::TextInput::InputSignalV2& TextInput::CutAndPasteToolBarDisplayedSignal()
529 {
530   return mCutAndPasteToolBarDisplayedV2;
531 }
532
533 Toolkit::TextInput::StyleChangedSignalV2& TextInput::StyleChangedSignal()
534 {
535   return mStyleChangedSignalV2;
536 }
537
538 Toolkit::TextInput::MaxInputCharactersReachedSignalV2& TextInput::MaxInputCharactersReachedSignal()
539 {
540   return mMaxInputCharactersReachedSignalV2;
541 }
542
543 Toolkit::TextInput::InputTextExceedBoundariesSignalV2& TextInput::InputTextExceedBoundariesSignal()
544 {
545   return mInputTextExceedBoundariesSignalV2;
546 }
547
548 bool TextInput::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
549 {
550   Dali::BaseHandle handle( object );
551
552   bool connected( true );
553   Toolkit::TextInput textInput = Toolkit::TextInput::DownCast(handle);
554
555   if( Toolkit::TextInput::SIGNAL_START_INPUT == signalName )
556   {
557     textInput.InputStartedSignal().Connect( tracker, functor );
558   }
559   else if( Toolkit::TextInput::SIGNAL_END_INPUT == signalName )
560   {
561     textInput.InputFinishedSignal().Connect( tracker, functor );
562   }
563   else if( Toolkit::TextInput::SIGNAL_STYLE_CHANGED == signalName )
564   {
565     textInput.StyleChangedSignal().Connect( tracker, functor );
566   }
567   else if( Toolkit::TextInput::SIGNAL_MAX_INPUT_CHARACTERS_REACHED == signalName )
568   {
569     textInput.MaxInputCharactersReachedSignal().Connect( tracker, functor );
570   }
571   else if( Toolkit::TextInput::SIGNAL_TEXT_EXCEED_BOUNDARIES == signalName )
572   {
573     textInput.InputTextExceedBoundariesSignal().Connect( tracker, functor );
574   }
575   else
576   {
577     // signalName does not match any signal
578     connected = false;
579   }
580
581   return connected;
582 }
583
584 void TextInput::SetEditable(bool editMode, bool setCursorOnTouchPoint, const Vector2& touchPoint)
585 {
586   if(editMode)
587   {
588     // update line height before calculate the actual position.
589     UpdateLineHeight();
590
591     if(!mEditModeActive)
592     {
593       if( setCursorOnTouchPoint )
594       {
595         // Sets the cursor position for the given touch point.
596         ReturnClosestIndex( touchPoint, mCursorPosition );
597
598         // Creates the grab handle.
599         if( IsGrabHandleEnabled() )
600         {
601           const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
602
603           CreateGrabHandle();
604
605           mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
606           mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
607           mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
608           ShowGrabHandleAndSetVisibility( true );
609
610           // Scrolls the text-view if needed.
611           if( IsScrollEnabled() )
612           {
613             ScrollTextViewToMakeCursorVisible( cursorPosition );
614           }
615         }
616       }
617       else
618       {
619         mCursorPosition = mStyledText.size(); // Initially set cursor position to end of string.
620       }
621     }
622
623     StartEditMode();
624   }
625   else
626   {
627     EndEditMode();
628   }
629 }
630
631 bool TextInput::IsEditable() const
632 {
633   return mEditModeActive;
634 }
635
636 void TextInput::SetEditOnTouch( bool editOnTouch )
637 {
638   mEditOnTouch = editOnTouch;
639 }
640
641 bool TextInput::IsEditOnTouch() const
642 {
643   return mEditOnTouch;
644 }
645
646 void TextInput::SetTextSelectable( bool textSelectable )
647 {
648   mTextSelection = textSelectable;
649 }
650
651 bool TextInput::IsTextSelectable() const
652 {
653   return mTextSelection;
654 }
655
656 bool TextInput::IsTextSelected() const
657 {
658   return mHighlightMeshActor;
659 }
660
661 void TextInput::DeSelectText()
662 {
663   RemoveHighlight();
664   HidePopup();
665   CursorUpdate();
666 }
667
668 void TextInput::SetGrabHandleImage(Dali::Image image )
669 {
670   if (image)
671   {
672     CreateGrabHandle(image);
673   }
674 }
675
676 void TextInput::SetCursorImage(Dali::Image image, const Vector4& border )
677 {
678   DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
679
680   if ( image )
681   {
682     mCursor.SetImage( image );
683     mCursor.SetNinePatchBorder( border );
684   }
685 }
686
687 Vector3 TextInput::GetSelectionHandleSize()
688 {
689   return DEFAULT_SELECTION_HANDLE_SIZE;
690 }
691
692 void TextInput::SetRTLCursorImage(Dali::Image image, const Vector4& border )
693 {
694   DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
695
696   if ( image )
697   {
698     mCursorRTL.SetImage( image);
699     mCursorRTL.SetNinePatchBorder(  border );
700   }
701 }
702
703 void TextInput::EnableGrabHandle(bool toggle)
704 {
705   // enables grab handle with will in turn de-activate magnifier
706   mGrabHandleEnabled = toggle;
707 }
708
709 bool TextInput::IsGrabHandleEnabled()
710 {
711   // if false then magnifier will be shown instead.
712   return mGrabHandleEnabled;
713 }
714
715 void TextInput::EnableSelectionHandleFlip( bool toggle )
716 {
717   // Deprecated function.  To be removed.
718   mIsSelectionHandleFlipEnabled = toggle;
719 }
720
721 bool TextInput::IsSelectionHandleFlipEnabled()
722 {
723   // Deprecated function, To be removed. Returns true as handle flipping always enabled by default so handles do not exceed screen.
724   return true;
725 }
726
727 void TextInput::SetSelectionHandleFlipMargin( const Vector4& margin )
728 {
729   // Deprecated function, now just stores margin for retreival, remove completely once depricated Public API removed.
730   Vector3 textInputSize = mDisplayedTextView.GetCurrentSize();
731   const Vector4 flipBoundary( -margin.x, -margin.y, textInputSize.width + margin.z, textInputSize.height + margin.w );
732
733   mSelectionHandleFlipMargin = margin;
734 }
735
736 void TextInput::SetBoundingRectangle( const Rect<float>& boundingRectangle )
737 {
738   // Convert to world coordinates and store as a Vector4 to be compatiable with Property Notifications.
739   Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
740
741   const float originX = boundingRectangle.x - 0.5f * stageSize.width;
742   const float originY = boundingRectangle.y - 0.5f * stageSize.height;
743
744   const Vector4 boundary( originX,
745                           originY,
746                           originX + boundingRectangle.width,
747                           originY + boundingRectangle.height );
748
749   mBoundingRectangleWorldCoordinates = boundary;
750 }
751
752 const Rect<float> TextInput::GetBoundingRectangle() const
753 {
754   Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
755
756   const float originX = mBoundingRectangleWorldCoordinates.x + 0.5f * stageSize.width;
757   const float originY = mBoundingRectangleWorldCoordinates.y + 0.5f * stageSize.height;
758
759   Rect<float>boundingRect( originX, originY, mBoundingRectangleWorldCoordinates.z - mBoundingRectangleWorldCoordinates.x, mBoundingRectangleWorldCoordinates.w - mBoundingRectangleWorldCoordinates.y);
760
761   return boundingRect;
762 }
763
764 const Vector4& TextInput::GetSelectionHandleFlipMargin()
765 {
766   return mSelectionHandleFlipMargin;
767 }
768
769 void TextInput::SetTextColor( const Vector4& color )
770 {
771   mDisplayedTextView.SetColor( color );
772 }
773
774 void TextInput::SetActiveStyle( const TextStyle& style, const TextStyle::Mask mask )
775 {
776   if( style != mInputStyle )
777   {
778     // different style.
779     bool emitSignal = false;
780
781     // mask: modify style according to mask, if different emit signal.
782     const TextStyle oldInputStyle( mInputStyle );
783
784     // Copy the new style.
785     mInputStyle.Copy( style, mask );
786
787     // if style has changed, emit signal.
788     if( oldInputStyle != mInputStyle )
789     {
790       emitSignal = true;
791     }
792
793     // Updates the line height accordingly with the input style.
794     UpdateLineHeight();
795
796     // Changing font point size will require the cursor to be re-sized
797     DrawCursor();
798
799     if( emitSignal )
800     {
801       EmitStyleChangedSignal();
802     }
803   }
804 }
805
806 void TextInput::ApplyStyle( const TextStyle& style, const TextStyle::Mask mask )
807 {
808   if ( IsTextSelected() )
809   {
810     const std::size_t begin = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
811     const std::size_t end = std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition) - 1;
812
813     if( !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
814     {
815       ApplyStyleToRange(style, mask, mTextLayoutInfo.mCharacterLogicalToVisualMap[begin], mTextLayoutInfo.mCharacterLogicalToVisualMap[end]);
816     }
817
818     // Keeps the old style to be compared with the new one.
819     const TextStyle oldInputStyle( mInputStyle );
820
821     // Copy only those parameters from the style which are set in the mask.
822     mInputStyle.Copy( style, mask );
823
824     if( mInputStyle != oldInputStyle )
825     {
826       // Updates the line height accordingly with the input style.
827       UpdateLineHeight();
828
829       EmitStyleChangedSignal();
830     }
831   }
832 }
833
834 void TextInput::ApplyStyleToAll( const TextStyle& style, const TextStyle::Mask mask )
835 {
836   if( !mStyledText.empty() )
837   {
838     ApplyStyleToRange( style, mask, 0, mStyledText.size() - 1 );
839   }
840 }
841
842 TextStyle TextInput::GetStyleAtCursor() const
843 {
844   TextStyle style;
845
846   if ( !mStyledText.empty() && ( mCursorPosition > 0 ) )
847   {
848     DALI_ASSERT_DEBUG( ( 0 <= mCursorPosition-1 ) && ( mCursorPosition-1 < mStyledText.size() ) );
849     style = mStyledText.at( mCursorPosition-1 ).mStyle;
850   }
851   else // No text.
852   {
853     style = mInputStyle;
854
855     if ( mInputStyle.GetFontPointSize() <  Math::MACHINE_EPSILON_1000 )
856     {
857       Dali::Font defaultFont = Dali::Font::New();
858       style.SetFontPointSize( PointSize( defaultFont.GetPointSize()) );
859     }
860   }
861
862   return style;
863 }
864
865 TextStyle TextInput::GetStyleAt( std::size_t position ) const
866 {
867   DALI_ASSERT_DEBUG( ( 0 <= position ) && ( position <= mStyledText.size() ) );
868
869   if( position >= mStyledText.size() )
870   {
871     position = mStyledText.size() - 1;
872   }
873
874   return mStyledText.at( position ).mStyle;
875 }
876
877 void TextInput::SetTextAlignment( Toolkit::Alignment::Type align )
878 {
879   mDisplayedTextView.SetTextAlignment( align );
880   mOverrideAutomaticAlignment = true;
881 }
882
883 void TextInput::SetTextLineJustification( Toolkit::TextView::LineJustification justification )
884 {
885   mDisplayedTextView.SetLineJustification( justification );
886   mOverrideAutomaticAlignment = true;
887 }
888
889 void TextInput::SetFadeBoundary( const Toolkit::TextView::FadeBoundary& fadeBoundary )
890 {
891   mDisplayedTextView.SetFadeBoundary( fadeBoundary );
892 }
893
894 const Toolkit::TextView::FadeBoundary& TextInput::GetFadeBoundary() const
895 {
896   return mDisplayedTextView.GetFadeBoundary();
897 }
898
899 Toolkit::Alignment::Type TextInput::GetTextAlignment() const
900 {
901   return mDisplayedTextView.GetTextAlignment();
902 }
903
904 void TextInput::SetMultilinePolicy( Toolkit::TextView::MultilinePolicy policy )
905 {
906   mDisplayedTextView.SetMultilinePolicy( policy );
907 }
908
909 Toolkit::TextView::MultilinePolicy TextInput::GetMultilinePolicy() const
910 {
911   return mDisplayedTextView.GetMultilinePolicy();
912 }
913
914 void TextInput::SetWidthExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
915 {
916   mDisplayedTextView.SetWidthExceedPolicy( policy );
917 }
918
919 Toolkit::TextView::ExceedPolicy TextInput::GetWidthExceedPolicy() const
920 {
921   return mDisplayedTextView.GetWidthExceedPolicy();
922 }
923
924 void TextInput::SetHeightExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
925 {
926   mDisplayedTextView.SetHeightExceedPolicy( policy );
927 }
928
929 Toolkit::TextView::ExceedPolicy TextInput::GetHeightExceedPolicy() const
930 {
931   return mDisplayedTextView.GetHeightExceedPolicy();
932 }
933
934 void TextInput::SetExceedEnabled( bool enable )
935 {
936   mExceedEnabled = enable;
937 }
938
939 bool TextInput::GetExceedEnabled() const
940 {
941   return mExceedEnabled;
942 }
943
944 void TextInput::SetBackground(Dali::Image image )
945 {
946   // TODO Should add this function and add public api to match.
947 }
948
949 bool TextInput::OnTouchEvent(const TouchEvent& event)
950 {
951   return false;
952 }
953
954 bool TextInput::OnKeyEvent(const KeyEvent& event)
955 {
956   switch( event.state )
957   {
958     case KeyEvent::Down:
959     {
960       return OnKeyDownEvent(event);
961     }
962     break;
963
964     case KeyEvent::Up:
965     {
966       return OnKeyUpEvent(event);
967     }
968     break;
969
970     default:
971     {
972       return false;
973     }
974     break;
975   }
976 }
977
978 void TextInput::OnKeyInputFocusGained()
979 {
980   DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusGained\n" );
981
982   mEditModeActive = true;
983
984   mActiveLayer.RaiseToTop(); // Ensure layer holding handles is on top
985
986   mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
987
988   // Updates the line height accordingly with the input style.
989   UpdateLineHeight();
990
991   // Connect the signals to use in text input.
992   VirtualKeyboard::StatusChangedSignal().Connect( this, &TextInput::KeyboardStatusChanged );
993   VirtualKeyboard::LanguageChangedSignal().Connect( this, &TextInput::SetTextDirection );
994
995   // Set the text direction if empty and connect to the signal to ensure we change direction when the language changes.
996   SetTextDirection();
997
998   GetTextLayoutInfo();
999
1000   DrawCursor();
1001   SetCursorVisibility( true );
1002   StartCursorBlinkTimer();
1003
1004   Toolkit::TextInput handle( GetOwner() );
1005   mInputStartedSignalV2.Emit( handle );
1006
1007   ImfManager imfManager = ImfManager::Get();
1008
1009   if ( imfManager )
1010   {
1011     imfManager.EventReceivedSignal().Connect(this, &TextInput::ImfEventReceived);
1012
1013     // Notify that the text editing start.
1014     imfManager.Activate();
1015
1016     // When window gain lost focus, the imf manager is deactivated. Thus when window gain focus again, the imf manager must be activated.
1017     imfManager.SetRestoreAferFocusLost( true );
1018
1019     imfManager.SetCursorPosition( mCursorPosition );
1020     imfManager.NotifyCursorPosition();
1021   }
1022
1023   mClipboard = Clipboard::Get(); // Store handle to clipboard
1024
1025   // Now in edit mode we can accept string to paste from clipboard
1026   if( Adaptor::IsAvailable() )
1027   {
1028     ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1029     if ( notifier )
1030     {
1031       notifier.ContentSelectedSignal().Connect( this, &TextInput::OnClipboardTextSelected );
1032     }
1033   }
1034 }
1035
1036 void TextInput::OnKeyInputFocusLost()
1037 {
1038   DALI_LOG_INFO(gLogFilter, Debug::General, ">>OnKeyInputFocusLost\n" );
1039
1040   if( mPreEditFlag )
1041   {
1042     // If key input focus is lost, it removes the
1043     // underline from the last pre-edit text.
1044     RemovePreEditStyle();
1045     const std::size_t numberOfCharactersDeleted = DeletePreEdit();
1046     InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersDeleted );
1047   }
1048
1049   ImfManager imfManager = ImfManager::Get();
1050   if ( imfManager )
1051   {
1052     // The text editing is finished. Therefore the imf manager don't have restore activation.
1053     imfManager.SetRestoreAferFocusLost( false );
1054
1055     // Notify that the text editing finish.
1056     imfManager.Deactivate();
1057
1058     imfManager.EventReceivedSignal().Disconnect(this, &TextInput::ImfEventReceived);
1059   }
1060   // Disconnect signal used the text input.
1061   VirtualKeyboard::LanguageChangedSignal().Disconnect( this, &TextInput::SetTextDirection );
1062
1063   Toolkit::TextInput handle( GetOwner() );
1064   mInputFinishedSignalV2.Emit( handle );
1065   mEditModeActive = false;
1066   mPreEditFlag = false;
1067   RemoveHighlight();
1068   SetCursorVisibility( false );
1069   StopCursorBlinkTimer();
1070
1071   ShowGrabHandleAndSetVisibility( false );
1072
1073   mClipboard.Reset();
1074   // No longer in edit mode so do not want to receive string from clipboard
1075   if( Adaptor::IsAvailable() )
1076   {
1077     ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
1078     if ( notifier )
1079     {
1080       notifier.ContentSelectedSignal().Disconnect( this, &TextInput::OnClipboardTextSelected );
1081     }
1082     Clipboard clipboard = Clipboard::Get();
1083
1084     if ( clipboard )
1085     {
1086       clipboard.HideClipboard();
1087     }
1088   }
1089 }
1090
1091 void TextInput::OnControlStageConnection()
1092 {
1093   Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
1094
1095   if ( mBoundingRectangleWorldCoordinates == Vector4::ZERO )
1096   {
1097     SetBoundingRectangle( Rect<float>( 0.0f, 0.0f, stageSize.width, stageSize.height ));
1098   }
1099 }
1100
1101 void TextInput::CreateActiveLayer()
1102 {
1103   Actor self = Self();
1104   mActiveLayer = Layer::New();
1105
1106   mActiveLayer.SetAnchorPoint( AnchorPoint::CENTER);
1107   mActiveLayer.SetParentOrigin( ParentOrigin::CENTER);
1108   mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
1109
1110   self.Add( mActiveLayer );
1111   mActiveLayer.RaiseToTop();
1112 }
1113
1114 void TextInput::OnInitialize()
1115 {
1116   CreateTextViewActor();
1117
1118   SetUpTouchEvents();
1119
1120   // Create 2 cursors (standard LTR and RTL cursor for when text can be added at
1121   // different positions depending on language)
1122   Image mCursorImage = Image::New( DEFAULT_CURSOR );
1123   mCursor = CreateCursor( mCursorImage, DEFAULT_CURSOR_IMAGE_9_BORDER );
1124   mCursorRTL = CreateCursor( mCursorImage, DEFAULT_CURSOR_IMAGE_9_BORDER );
1125
1126   Actor self = Self();
1127   self.Add( mCursor );
1128   self.Add( mCursorRTL );
1129
1130   mCursorVisibility = false;
1131
1132   CreateActiveLayer(); // todo move this so layer only created when needed.
1133
1134   // Assign names to image actors
1135   mCursor.SetName("mainCursor");
1136   mCursorRTL.SetName("rtlCursor");
1137 }
1138
1139 void TextInput::OnControlSizeSet(const Vector3& targetSize)
1140 {
1141   mDisplayedTextView.SetSize( targetSize );
1142   GetTextLayoutInfo();
1143   mActiveLayer.SetSize(targetSize);
1144 }
1145
1146 void TextInput::OnRelaidOut( Vector2 size, ActorSizeContainer& container )
1147 {
1148   Relayout( mDisplayedTextView, size, container );
1149   GetTextLayoutInfo();
1150
1151   DrawCursor();
1152 }
1153
1154 Vector3 TextInput::GetNaturalSize()
1155 {
1156   Vector3 naturalSize = mDisplayedTextView.GetNaturalSize();
1157
1158   if( mEditModeActive && ( Vector3::ZERO == naturalSize ) )
1159   {
1160     // If the natural is zero, it means there is no text. Let's return the cursor height as the natural height.
1161     naturalSize.height = mLineHeight;
1162   }
1163
1164   return naturalSize;
1165 }
1166
1167 float TextInput::GetHeightForWidth( float width )
1168 {
1169   float height = mDisplayedTextView.GetHeightForWidth( width );
1170
1171   if( mEditModeActive && ( fabsf( height ) < Math::MACHINE_EPSILON_1000 ) )
1172   {
1173     // If the height is zero, it means there is no text. Let's return the cursor height.
1174     height = mLineHeight;
1175   }
1176
1177   return height;
1178 }
1179
1180 /*end of Virtual methods from parent*/
1181
1182 // Private Internal methods
1183
1184 void TextInput::OnHandlePan(Actor actor, PanGesture gesture)
1185 {
1186   switch (gesture.state)
1187   {
1188     case Gesture::Started:
1189     // fall through so code not duplicated
1190     case Gesture::Continuing:
1191     {
1192       if (actor == mGrabArea)
1193       {
1194         SetCursorVisibility( true );
1195         ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1196         MoveGrabHandle( gesture.displacement );
1197         HidePopup(); // Do not show popup whilst handle is moving
1198       }
1199       else if (actor == mHandleOneGrabArea)
1200       {
1201         // the displacement in PanGesture is affected by the actor's rotation.
1202         mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1203         mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1204
1205         MoveSelectionHandle( HandleOne, gesture.displacement );
1206
1207         mState = StateDraggingHandle;
1208         HidePopup();
1209       }
1210       else if (actor == mHandleTwoGrabArea)
1211       {
1212         // the displacement in PanGesture is affected by the actor's rotation.
1213         mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1214         mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1215
1216         MoveSelectionHandle( HandleTwo, gesture.displacement );
1217
1218         mState = StateDraggingHandle;
1219         HidePopup();
1220       }
1221     }
1222     break;
1223
1224     case Gesture::Finished:
1225     {
1226       // Revert back to non-pressed selection handle images
1227       if (actor == mGrabArea)
1228       {
1229         mActualGrabHandlePosition = MoveGrabHandle( gesture.displacement );
1230         SetCursorVisibility( true );
1231         SetUpPopUpSelection();
1232         ShowPopup();
1233       }
1234       if (actor == mHandleOneGrabArea)
1235       {
1236         // the displacement in PanGesture is affected by the actor's rotation.
1237         mSelectionHandleOneActualPosition.x += gesture.displacement.x * mSelectionHandleOne.GetCurrentScale().x;
1238         mSelectionHandleOneActualPosition.y += gesture.displacement.y * mSelectionHandleOne.GetCurrentScale().y;
1239
1240         mSelectionHandleOneActualPosition = MoveSelectionHandle( HandleOne, gesture.displacement );
1241
1242         mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1243         mState = StateEdit;
1244         ShowPopupCutCopyPaste();
1245       }
1246       if (actor == mHandleTwoGrabArea)
1247       {
1248         // the displacement in PanGesture is affected by the actor's rotation.
1249         mSelectionHandleTwoActualPosition.x += gesture.displacement.x * mSelectionHandleTwo.GetCurrentScale().x;
1250         mSelectionHandleTwoActualPosition.y += gesture.displacement.y * mSelectionHandleTwo.GetCurrentScale().y;
1251
1252         mSelectionHandleTwoActualPosition = MoveSelectionHandle( HandleTwo, gesture.displacement );
1253
1254         mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1255         mState = StateEdit;
1256         ShowPopupCutCopyPaste();
1257       }
1258     }
1259     break;
1260     default:
1261       break;
1262   }
1263 }
1264
1265 // Stop the flashing animation so easy to see when moved.
1266 bool TextInput::OnPressDown(Dali::Actor actor, const TouchEvent& touch)
1267 {
1268   if (touch.GetPoint(0).state == TouchPoint::Down)
1269   {
1270     SetCursorVisibility( true );
1271     StopCursorBlinkTimer();
1272   }
1273   else if (touch.GetPoint(0).state == TouchPoint::Up)
1274   {
1275     SetCursorVisibility( true );
1276     StartCursorBlinkTimer();
1277   }
1278   return false;
1279 }
1280
1281 // selection handle one
1282 bool TextInput::OnHandleOneTouched(Dali::Actor actor, const TouchEvent& touch)
1283 {
1284   if (touch.GetPoint(0).state == TouchPoint::Down)
1285   {
1286     mSelectionHandleOne.SetImage( mSelectionHandleOneImagePressed );
1287   }
1288   else if (touch.GetPoint(0).state == TouchPoint::Up)
1289   {
1290     mSelectionHandleOne.SetImage( mSelectionHandleOneImage );
1291   }
1292   return false;
1293 }
1294
1295 // selection handle two
1296 bool TextInput::OnHandleTwoTouched(Dali::Actor actor, const TouchEvent& touch)
1297 {
1298   if (touch.GetPoint(0).state == TouchPoint::Down)
1299   {
1300     mSelectionHandleTwo.SetImage( mSelectionHandleTwoImagePressed );
1301   }
1302   else if (touch.GetPoint(0).state == TouchPoint::Up)
1303   {
1304     mSelectionHandleTwo.SetImage( mSelectionHandleTwoImage );
1305   }
1306   return false;
1307 }
1308
1309 void TextInput::OnDoubleTap(Dali::Actor actor, Dali::TapGesture tap)
1310 {
1311    // If text exists then select nearest word.
1312    if ( !mStyledText.empty())
1313    {
1314      HidePopup();
1315
1316      ShowGrabHandleAndSetVisibility( false );
1317
1318
1319      if ( mPreEditFlag )
1320      {
1321        // PreEdit will be committed here without needing a commit from IMF.  Remove pre-edit underline and reset flags which
1322        // converts the pre-edit word being displayed to a committed word.
1323        if ( !mUnderlinedPriorToPreEdit )
1324        {
1325          TextStyle style;
1326          style.SetUnderline( false );
1327          ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1328        }
1329        mPreEditFlag = false;
1330        mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1331        // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1332        PreEditReset( false );
1333      }
1334      mCursorPosition = 0;
1335
1336      mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1337      ReturnClosestIndex( tap.localPoint, mCursorPosition );
1338
1339      ImfManager imfManager = ImfManager::Get();
1340      if ( imfManager )
1341      {
1342        imfManager.SetCursorPosition ( mCursorPosition );
1343        imfManager.NotifyCursorPosition();
1344      }
1345
1346      std::size_t start = 0;
1347      std::size_t end = 0;
1348      Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1349
1350      SelectText( start, end );
1351    }
1352    // if no text but clipboard has content then show paste option
1353    if ( mClipboard.NumberOfItems() || !mStyledText.empty() )
1354    {
1355      ShowPopupCutCopyPaste();
1356    }
1357
1358    // If no text and clipboard empty then do nothing
1359 }
1360
1361 // TODO: Change the function name to be more general.
1362 void TextInput::OnTextTap(Dali::Actor actor, Dali::TapGesture tap)
1363 {
1364   DALI_LOG_INFO( gLogFilter, Debug::General, "OnTap mPreEditFlag[%s] mEditOnTouch[%s] mEditModeActive[%s] ", (mPreEditFlag)?"true":"false"
1365                                                                                                            , (mEditOnTouch)?"true":"false"
1366                                                                                                            , (mEditModeActive)?"true":"false");
1367
1368   if( mHandleOneGrabArea == actor || mHandleTwoGrabArea == actor )
1369   {
1370     return;
1371   }
1372
1373   if( mGrabArea == actor )
1374   {
1375     if( mPopUpPanel.GetState() == TextInputPopup::StateHidden || mPopUpPanel.GetState() == TextInputPopup::StateHiding )
1376     {
1377       SetUpPopUpSelection();
1378       ShowPopup();
1379     }
1380
1381     return;
1382   }
1383
1384   HidePopup();
1385   RemoveHighlight();
1386
1387   mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1388
1389   // Initially don't create the grab handle.
1390   bool createGrabHandle = false;
1391
1392   if ( !mEditModeActive )
1393   {
1394     // update line height before calculate the actual position.
1395     UpdateLineHeight();
1396
1397     // Only start edit mode if TextInput configured to edit on touch
1398     if ( mEditOnTouch )
1399     {
1400       // Set the initial cursor position in the tap point.
1401       ReturnClosestIndex(tap.localPoint, mCursorPosition );
1402
1403       // Create the grab handle.
1404       // TODO Make this a re-usable function.
1405       if ( IsGrabHandleEnabled() )
1406       {
1407         const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1408
1409         CreateGrabHandle();
1410
1411         mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1412         mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1413         mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1414         ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1415
1416       }
1417
1418       // Edit mode started after grab handle created to ensure the signal InputStarted is sent last.
1419       // This is used to ensure if selecting text hides the grab handle then this code is run after grab handle is created,
1420       // otherwise the Grab handle will be shown when selecting.
1421
1422       StartEditMode();
1423     }
1424   }
1425   else
1426   {
1427     // Show the keyboard if it was hidden.
1428     if (!VirtualKeyboard::IsVisible())
1429     {
1430       VirtualKeyboard::Show();
1431     }
1432
1433     // Reset keyboard as tap event has occurred.
1434     // Set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1435     PreEditReset( true );
1436
1437     GetTextLayoutInfo();
1438
1439     if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() ) // If string empty we do not need a grab handle.
1440     {
1441       // As already in edit mode, reposition cursor near tap and show grab handle for cursor, if grab handle not enabled then magnifier will be used instead.
1442
1443       ReturnClosestIndex(tap.localPoint, mCursorPosition );
1444
1445       DALI_LOG_INFO( gLogFilter, Debug::General, "mCursorPosition[%u]", mCursorPosition );
1446
1447       // Notify keyboard so it can 're-capture' word for predictive text.
1448       // As we have done a reset, is this required, expect IMF keyboard to request this information.
1449       ImfManager imfManager = ImfManager::Get();
1450       if ( imfManager )
1451       {
1452         imfManager.SetCursorPosition ( mCursorPosition );
1453         imfManager.NotifyCursorPosition();
1454       }
1455       const TextStyle oldInputStyle( mInputStyle );
1456
1457       mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
1458
1459       DrawCursor();
1460
1461       // Create the grab handle.
1462       // Grab handle is created later.
1463       createGrabHandle = true;
1464
1465       if( oldInputStyle != mInputStyle )
1466       {
1467         // Updates the line height accordingly with the input style.
1468         UpdateLineHeight();
1469
1470         EmitStyleChangedSignal();
1471       }
1472     }
1473   }
1474
1475   if ( createGrabHandle && IsGrabHandleEnabled() )
1476   {
1477     const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1478
1479     CreateGrabHandle();
1480
1481     mActualGrabHandlePosition.x = cursorPosition.x; // Set grab handle to be at the cursor position
1482     mActualGrabHandlePosition.y = cursorPosition.y; // Set grab handle to be at the cursor position
1483     mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1484     ShowGrabHandleAndSetVisibility( mIsGrabHandleInScrollArea );
1485
1486   }
1487 }
1488
1489 void TextInput::OnLongPress(Dali::Actor actor, Dali::LongPressGesture longPress)
1490 {
1491   DALI_LOG_INFO( gLogFilter, Debug::General, "OnLongPress\n" );
1492
1493   if(longPress.state == Dali::Gesture::Started)
1494   {
1495     // Start edit mode on long press
1496     if ( !mEditModeActive )
1497     {
1498       StartEditMode();
1499     }
1500
1501     // If text exists then select nearest word.
1502     if ( !mStyledText.empty())
1503     {
1504       HidePopup();
1505
1506       ShowGrabHandleAndSetVisibility( false );
1507
1508
1509       if ( mPreEditFlag )
1510       {
1511         // PreEdit will be committed here without needing a commit from IMF.  Remove pre-edit underline and reset flags which
1512         // converts the pre-edit word being displayed to a committed word.
1513         if ( !mUnderlinedPriorToPreEdit )
1514         {
1515           TextStyle style;
1516           style.SetUnderline( false );
1517           ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
1518         }
1519         mPreEditFlag = false;
1520         mIgnoreCommitFlag = true; // Predictive word interrupted, text displayed will not change, no need to actually commit.
1521         // Reset keyboard and set true so cursor position is preserved. Otherwise cursor position will that of the committed text not new tap location.
1522         PreEditReset( false );
1523       }
1524       mCursorPosition = 0;
1525
1526       mTextLayoutInfo.mScrollOffset = mDisplayedTextView.GetScrollPosition();
1527       ReturnClosestIndex( longPress.localPoint, mCursorPosition );
1528
1529       ImfManager imfManager = ImfManager::Get();
1530       if ( imfManager )
1531       {
1532         imfManager.SetCursorPosition ( mCursorPosition );
1533         imfManager.NotifyCursorPosition();
1534       }
1535       std::size_t start = 0;
1536       std::size_t end = 0;
1537       Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1538
1539       SelectText( start, end );
1540     }
1541
1542     // if no text but clipboard has content then show paste option, if no text and clipboard empty then do nothing
1543     if ( mClipboard.NumberOfItems() || !mStyledText.empty() )
1544     {
1545       ShowPopupCutCopyPaste();
1546     }
1547   }
1548 }
1549
1550 void TextInput::OnClipboardTextSelected( ClipboardEventNotifier& notifier )
1551 {
1552   const Text clipboardText( notifier.GetContent() );
1553   PasteText( clipboardText );
1554
1555   SetCursorVisibility( true );
1556   StartCursorBlinkTimer();
1557
1558   ShowGrabHandleAndSetVisibility( false );
1559
1560
1561   HidePopup();
1562 }
1563
1564 bool TextInput::OnPopupButtonPressed( Toolkit::Button button )
1565 {
1566   mPopUpPanel.PressedSignal().Disconnect( this, &TextInput::OnPopupButtonPressed );
1567
1568   const std::string& name = button.GetName();
1569
1570   if(name == OPTION_SELECT_WORD)
1571   {
1572     std::size_t start = 0;
1573     std::size_t end = 0;
1574     Dali::Toolkit::Internal::TextProcessor::FindNearestWord( mStyledText, mCursorPosition, start, end );
1575
1576     SelectText( start, end );
1577   }
1578   else if(name == OPTION_SELECT_ALL)
1579   {
1580     SetCursorVisibility(false);
1581     StopCursorBlinkTimer();
1582
1583     std::size_t end = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
1584     std::size_t start = 0;
1585
1586     SelectText( start, end );
1587   }
1588   else if(name == OPTION_CUT)
1589   {
1590     bool ret = CopySelectedTextToClipboard();
1591
1592     if ( ret )
1593     {
1594       DeleteHighlightedText( true );
1595       CursorUpdate();
1596     }
1597
1598     SetCursorVisibility( true );
1599     StartCursorBlinkTimer();
1600
1601     HidePopup();
1602   }
1603   else if(name == OPTION_COPY)
1604   {
1605     CopySelectedTextToClipboard();
1606
1607     RemoveHighlight();
1608
1609     SetCursorVisibility( true );
1610     StartCursorBlinkTimer();
1611
1612     HidePopup();
1613   }
1614   else if(name == OPTION_PASTE)
1615   {
1616     const Text retrievedString( mClipboard.GetItem( 0 ) );  // currently can only get first item in clip board, index 0;
1617
1618     PasteText(retrievedString);
1619
1620     SetCursorVisibility( true );
1621     StartCursorBlinkTimer();
1622
1623     ShowGrabHandleAndSetVisibility( false );
1624
1625
1626     HidePopup();
1627   }
1628   else if(name == OPTION_CLIPBOARD)
1629   {
1630     // In the case of clipboard being shown we do not want to show updated pop-up after hide animation completes
1631     // Hence pass the false parameter for signalFinished.
1632     HidePopup( true, false );
1633     mClipboard.ShowClipboard();
1634   }
1635
1636   return false;
1637 }
1638
1639 bool TextInput::OnCursorBlinkTimerTick()
1640 {
1641   // Cursor blinking
1642   mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1643   if ( mCursorRTLEnabled )
1644   {
1645     mCursorRTL.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
1646   }
1647   mCursorBlinkStatus = !mCursorBlinkStatus;
1648
1649   return true;
1650 }
1651
1652 void TextInput::OnPopupHideFinished(TextInputPopup& popup)
1653 {
1654   popup.HideFinishedSignal().Disconnect( this, &TextInput::OnPopupHideFinished );
1655
1656   // Change Popup menu to Cut/Copy/Paste if text has been selected.
1657   if(mHighlightMeshActor && mState == StateEdit)
1658   {
1659     ShowPopupCutCopyPaste();
1660   }
1661 }
1662
1663 //FIXME this routine needs to be re-written as it contains too many branches.
1664 bool TextInput::OnKeyDownEvent(const KeyEvent& event)
1665 {
1666   std::string keyName = event.keyPressedName;
1667   std::string keyString = event.keyPressed;
1668
1669   DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyDownEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1670
1671   // Do not consume "Tab" and "Escape" keys.
1672   if(keyName == "Tab" || keyName == "Escape")
1673   {
1674     // Escape key to end the edit mode
1675     EndEditMode();
1676
1677     return false;
1678   }
1679
1680   HidePopup(); // If Pop-up shown then hides it as editing text.
1681
1682   // Update Flag, indicates whether to update the text-input contents or not.
1683   // Any key stroke that results in a visual change of the text-input should
1684   // set this flag to true.
1685   bool update(false);
1686
1687   // Whether to scroll text to cursor position.
1688   // Scroll is needed always the cursor is updated and after the pre-edit is received.
1689   bool scroll = false;
1690
1691   if (keyName == "Return")
1692   {
1693     if ( mNumberOflinesLimit > 1) // Prevents New line character / Return adding an extra line if limit set to 1
1694     {
1695       bool preEditFlagPreviouslySet( mPreEditFlag );
1696
1697       if (mHighlightMeshActor)
1698       {
1699         // replaces highlighted text with new line
1700         DeleteHighlightedText( false );
1701       }
1702       mCursorPosition = mCursorPosition + InsertAt( Text( NEWLINE ), mCursorPosition, 0 );
1703
1704       // If we are in pre-edit mode then pressing enter will cause a commit.  But the commit string does not include the
1705       // '\n' character so we need to ensure that the immediately following commit knows how it occurred.
1706       if ( mPreEditFlag )
1707       {
1708         mCommitByKeyInput = true;
1709       }
1710
1711       // If attempting to insert a new-line brings us out of PreEdit mode, then we should not ignore the next commit.
1712       if ( preEditFlagPreviouslySet && !mPreEditFlag )
1713       {
1714         mPreEditFlag = true;
1715         mIgnoreCommitFlag = false;
1716       }
1717
1718       update = true;
1719     }
1720     else
1721     {
1722       RemoveHighlight();
1723     }
1724   } // Return
1725   else if ( keyName == "space" )
1726   {
1727     if ( mHighlightMeshActor )
1728     {
1729       // Some text is selected so erase it before adding space.
1730       DeleteHighlightedText( true );
1731       update = true;
1732     }
1733
1734     mCursorPosition = mCursorPosition + InsertAt(Text(keyString), mCursorPosition, 0);
1735
1736     // If we are in pre-edit mode then pressing the space-bar will cause a commit.  But the commit string does not include the
1737     // ' ' character so we need to ensure that the immediately following commit knows how it occurred.
1738     if ( mPreEditFlag )
1739     {
1740       mCommitByKeyInput = true;
1741     }
1742
1743     update = true;
1744   } // space
1745   else if (keyName == "BackSpace")
1746   {
1747     if ( mHighlightMeshActor )
1748     {
1749       // Some text is selected so erase it
1750       DeleteHighlightedText( true );
1751       update = true;
1752     }
1753     else
1754     {
1755       if ( mCursorPosition > 0 )
1756       {
1757         DeleteCharacter( mCursorPosition );
1758         update = true;
1759       }
1760     }
1761   } // BackSpace
1762   else if (keyName == "Right")
1763   {
1764     AdvanceCursor();
1765     RemoveHighlight();
1766   }
1767   else if (keyName == "Left")
1768   {
1769     AdvanceCursor(true);
1770     RemoveHighlight();
1771   }
1772   else // event is a character
1773   {
1774     // Some text may be selected, hiding keyboard causes an empty keystring to be sent, we don't want to delete highlight in this case
1775     if ( !keyString.empty() )
1776     {
1777       if ( mHighlightMeshActor )
1778       {
1779         // replaces highlighted text with new character
1780         DeleteHighlightedText( false );
1781       }
1782
1783
1784       // Received key String
1785       mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, 0 );
1786       update = true;
1787     }
1788   }
1789
1790   // If key event has resulted in a change in the text/cursor, then trigger a relayout of text
1791   // as this is a costly operation.
1792   if(update)
1793   {
1794     CursorUpdate();
1795   }
1796
1797   if(update || scroll)
1798   {
1799     if( IsScrollEnabled() )
1800     {
1801       // Calculates the new cursor position (in actor coordinates)
1802       const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
1803
1804       ScrollTextViewToMakeCursorVisible( cursorPosition );
1805     }
1806   }
1807
1808   return true;
1809 }
1810
1811 bool TextInput::OnKeyUpEvent(const KeyEvent& event)
1812 {
1813   std::string keyName = event.keyPressedName;
1814   std::string keyString = event.keyPressed;
1815
1816   DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyUpEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1817
1818   // The selected text become deselected when the key code is DALI_KEY_BACK.
1819   if( IsTextSelected() && ( keyName == "XF86Stop" || keyName == "XF86Send") )
1820   {
1821     DeSelectText();
1822     return true;
1823   }
1824
1825   return false;
1826 }
1827
1828 void TextInput::OnTextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition )
1829 {
1830   // Updates the stored scroll position.
1831   mTextLayoutInfo.mScrollOffset = textView.GetScrollPosition();
1832
1833   const Vector3& controlSize = GetControlSize();
1834   Size cursorSize( CURSOR_THICKNESS, 0.f );
1835
1836   // Updates the cursor and grab handle position and visibility.
1837   if( mGrabHandle || mCursor )
1838   {
1839     cursorSize.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height;
1840     const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition);
1841
1842     mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( cursorPosition, cursorSize, controlSize );
1843
1844     mActualGrabHandlePosition = cursorPosition.GetVectorXY();
1845
1846     if( mGrabHandle )
1847     {
1848       ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1849       mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1850     }
1851
1852     if( mCursor )
1853     {
1854       mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
1855       mCursor.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1856     }
1857   }
1858
1859   // Updates the selection handles and highlighted text position and visibility.
1860   if( mSelectionHandleOne && mSelectionHandleTwo )
1861   {
1862     const Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition(mSelectionHandleOnePosition);
1863     const Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition(mSelectionHandleTwoPosition);
1864     cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleOnePosition ) ).mSize.height;
1865     const bool isSelectionHandleOneVisible = IsPositionInsideBoundaries( cursorPositionOne, cursorSize, controlSize );
1866     cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleTwoPosition ) ).mSize.height;
1867     const bool isSelectionHandleTwoVisible = IsPositionInsideBoundaries( cursorPositionTwo, cursorSize, controlSize );
1868
1869     mSelectionHandleOneActualPosition = cursorPositionOne.GetVectorXY();
1870     mSelectionHandleTwoActualPosition = cursorPositionTwo.GetVectorXY();
1871
1872     mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
1873     mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
1874     mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
1875     mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
1876
1877     if( mHighlightMeshActor )
1878     {
1879       mHighlightMeshActor.SetVisible( true );
1880       UpdateHighlight();
1881     }
1882   }
1883 }
1884
1885 void TextInput::ScrollTextViewToMakeCursorVisible( const Vector3& cursorPosition )
1886 {
1887   // Scroll the text to make the cursor visible.
1888   const Size cursorSize( CURSOR_THICKNESS,
1889                          GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height );
1890
1891   // Need to scroll the text to make the cursor visible and to cover the whole text-input area.
1892
1893   const Vector3& controlSize = GetControlSize();
1894
1895   // Calculates the new scroll position.
1896   Vector2 scrollOffset = mTextLayoutInfo.mScrollOffset;
1897   if( ( cursorPosition.x < 0.f ) || ( cursorPosition.x > controlSize.width ) )
1898   {
1899     scrollOffset.x += cursorPosition.x;
1900   }
1901
1902   if( cursorPosition.y - cursorSize.height < 0.f )
1903   {
1904     scrollOffset.y += ( cursorPosition.y - cursorSize.height );
1905   }
1906   else if( cursorPosition.y > controlSize.height )
1907   {
1908     scrollOffset.y += cursorPosition.y;
1909   }
1910
1911   // Sets the new scroll position.
1912   SetScrollPosition( Vector2::ZERO ); // TODO: need to reset to the zero position in order to make the scroll trim to work.
1913   SetScrollPosition( scrollOffset );
1914 }
1915
1916 void TextInput::StartScrollTimer()
1917 {
1918   if( !mScrollTimer )
1919   {
1920     mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
1921     mScrollTimer.TickSignal().Connect( this, &TextInput::OnScrollTimerTick );
1922   }
1923
1924   if( !mScrollTimer.IsRunning() )
1925   {
1926     mScrollTimer.Start();
1927   }
1928 }
1929
1930 void TextInput::StopScrollTimer()
1931 {
1932   if( mScrollTimer )
1933   {
1934     mScrollTimer.Stop();
1935   }
1936 }
1937
1938 bool TextInput::OnScrollTimerTick()
1939 {
1940   // TODO: need to set the new style accordingly the new handle position.
1941
1942   if( !( mGrabHandleVisibility && mGrabHandle ) && !( mSelectionHandleOne && mSelectionHandleTwo ) )
1943   {
1944     // nothing to do if all handles are invisible or doesn't exist.
1945     return true;
1946   }
1947
1948   // Text scrolling
1949
1950   // Choose between the grab handle or the selection handles.
1951   Vector3& actualHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mActualGrabHandlePosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
1952   std::size_t& handlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCursorPosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
1953   Vector3& currentHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCurrentHandlePosition : mCurrentSelectionHandlePosition;
1954
1955   std::size_t newCursorPosition = 0;
1956   ReturnClosestIndex( actualHandlePosition.GetVectorXY(), newCursorPosition );
1957
1958   // Whether the handle's position is different of the previous one and in the case of the selection handle,
1959   // the new selection handle's position needs to be different of the other one.
1960   const bool differentSelectionHandles = ( mGrabHandleVisibility && mGrabHandle ) ? newCursorPosition != handlePosition :
1961                                          ( mCurrentSelectionId == HandleOne ) ? ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleTwoPosition ) :
1962                                                                                 ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleOnePosition );
1963
1964   if( differentSelectionHandles )
1965   {
1966     handlePosition = newCursorPosition;
1967
1968     const Vector3 actualPosition = GetActualPositionFromCharacterPosition( newCursorPosition );
1969
1970     Vector2 scrollDelta = ( actualPosition - currentHandlePosition ).GetVectorXY();
1971
1972     Vector2 scrollPosition = mDisplayedTextView.GetScrollPosition();
1973     scrollPosition += scrollDelta;
1974     SetScrollPosition( scrollPosition );
1975
1976     if( mDisplayedTextView.IsScrollPositionTrimmed() )
1977     {
1978       StopScrollTimer();
1979     }
1980
1981     currentHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition ).GetVectorXY();
1982   }
1983
1984   actualHandlePosition.x += mScrollDisplacement.x;
1985   actualHandlePosition.y += mScrollDisplacement.y;
1986
1987   return true;
1988 }
1989
1990 // Public Internal Methods (public for testing purpose)
1991
1992 void TextInput::SetUpTouchEvents()
1993 {
1994   if ( !mTapDetector )
1995   {
1996     mTapDetector = TapGestureDetector::New();
1997     // Attach the actors and connect the signal
1998     mTapDetector.Attach(Self());
1999
2000     // As contains children which may register for tap the default control detector is not used.
2001     mTapDetector.DetectedSignal().Connect(this, &TextInput::OnTextTap);
2002   }
2003
2004   if ( !mDoubleTapDetector )
2005   {
2006     mDoubleTapDetector = TapGestureDetector::New();
2007     mDoubleTapDetector.SetTapsRequired( 2 );
2008     mDoubleTapDetector.DetectedSignal().Connect(this, &TextInput::OnDoubleTap);
2009
2010     // Only attach and detach the actor to the double tap detector when we enter/leave edit mode
2011     // so that we do not, unnecessarily, have a double tap request all the time
2012   }
2013
2014   if ( !mPanGestureDetector )
2015   {
2016     mPanGestureDetector = PanGestureDetector::New();
2017     mPanGestureDetector.DetectedSignal().Connect(this, &TextInput::OnHandlePan);
2018   }
2019
2020   if ( !mLongPressDetector )
2021   {
2022     mLongPressDetector = LongPressGestureDetector::New();
2023     mLongPressDetector.DetectedSignal().Connect(this, &TextInput::OnLongPress);
2024     mLongPressDetector.Attach(Self());
2025   }
2026 }
2027
2028 void TextInput::CreateTextViewActor()
2029 {
2030   mDisplayedTextView = Toolkit::TextView::New();
2031   mDisplayedTextView.SetMarkupProcessingEnabled( mMarkUpEnabled );
2032   mDisplayedTextView.SetParentOrigin(ParentOrigin::TOP_LEFT);
2033   mDisplayedTextView.SetAnchorPoint(AnchorPoint::TOP_LEFT);
2034   mDisplayedTextView.SetMultilinePolicy(Toolkit::TextView::SplitByWord);
2035   mDisplayedTextView.SetWidthExceedPolicy( Toolkit::TextView::Original );
2036   mDisplayedTextView.SetHeightExceedPolicy( Toolkit::TextView::Original );
2037   mDisplayedTextView.SetLineJustification( Toolkit::TextView::Left );
2038   mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>( Toolkit::Alignment::HorizontalLeft | Toolkit::Alignment::VerticalTop ) );
2039   mDisplayedTextView.SetPosition( Vector3( 0.0f, 0.0f, DISPLAYED_TEXT_VIEW_Z_OFFSET ) );
2040   mDisplayedTextView.SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
2041
2042   mDisplayedTextView.ScrolledSignal().Connect( this, &TextInput::OnTextViewScrolled );
2043
2044   Self().Add( mDisplayedTextView );
2045 }
2046
2047 // Start a timer to initiate, used by the cursor to blink.
2048 void TextInput::StartCursorBlinkTimer()
2049 {
2050   if ( !mCursorBlinkTimer )
2051   {
2052     mCursorBlinkTimer = Timer::New( CURSOR_BLINK_INTERVAL );
2053     mCursorBlinkTimer.TickSignal().Connect( this, &TextInput::OnCursorBlinkTimerTick );
2054   }
2055
2056   if ( !mCursorBlinkTimer.IsRunning() )
2057   {
2058     mCursorBlinkTimer.Start();
2059   }
2060 }
2061
2062 // Start a timer to initiate, used by the cursor to blink.
2063 void TextInput::StopCursorBlinkTimer()
2064 {
2065   if ( mCursorBlinkTimer )
2066   {
2067     mCursorBlinkTimer.Stop();
2068   }
2069 }
2070
2071 void TextInput::StartEditMode()
2072 {
2073   DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput StartEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2074
2075   if(!mEditModeActive)
2076   {
2077     SetKeyInputFocus();
2078   }
2079
2080   if ( mDoubleTapDetector )
2081   {
2082     mDoubleTapDetector.Attach( Self() );
2083   }
2084 }
2085
2086 void TextInput::EndEditMode()
2087 {
2088   DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput EndEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2089
2090   ClearKeyInputFocus();
2091
2092   if ( mDoubleTapDetector )
2093   {
2094     mDoubleTapDetector.Detach( Self() );
2095   }
2096 }
2097
2098 void TextInput::ApplyPreEditStyle( std::size_t preEditStartPosition, std::size_t preEditStringLength )
2099 {
2100   if ( mPreEditFlag && ( preEditStringLength > 0 ) )
2101   {
2102     mUnderlinedPriorToPreEdit = mInputStyle.GetUnderline();
2103     TextStyle style;
2104     style.SetUnderline( true );
2105     ApplyStyleToRange( style, TextStyle::UNDERLINE , preEditStartPosition, preEditStartPosition + preEditStringLength -1 );
2106   }
2107 }
2108
2109 void TextInput::RemovePreEditStyle()
2110 {
2111   if ( !mUnderlinedPriorToPreEdit )
2112   {
2113     TextStyle style;
2114     style.SetUnderline( false );
2115     SetActiveStyle( style, TextStyle::UNDERLINE );
2116   }
2117 }
2118
2119 // IMF related methods
2120
2121
2122 ImfManager::ImfCallbackData TextInput::ImfEventReceived( Dali::ImfManager& imfManager, const ImfManager::ImfEventData&  imfEvent )
2123 {
2124   bool update( false );
2125   bool preeditResetRequired ( false );
2126
2127   if (imfEvent.eventName != ImfManager::GETSURROUNDING )
2128   {
2129     HidePopup(); // If Pop-up shown then hides it as editing text.
2130   }
2131
2132   switch ( imfEvent.eventName )
2133   {
2134     case ImfManager::PREEDIT:
2135     {
2136       mIgnoreFirstCommitFlag = false;
2137
2138       // Some text may be selected, hiding keyboard causes an empty predictive string to be sent, we don't want to delete highlight in this case
2139       if ( mHighlightMeshActor &&  (!imfEvent.predictiveString.empty()) )
2140       {
2141         // replaces highlighted text with new character
2142         DeleteHighlightedText( false );
2143       }
2144
2145       preeditResetRequired = PreEditReceived( imfEvent.predictiveString, imfEvent.cursorOffset );
2146
2147       if( IsScrollEnabled() )
2148       {
2149         // Calculates the new cursor position (in actor coordinates)
2150         const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2151         ScrollTextViewToMakeCursorVisible( cursorPosition );
2152       }
2153
2154       update = true;
2155
2156       break;
2157     }
2158     case ImfManager::COMMIT:
2159     {
2160       if( mIgnoreFirstCommitFlag )
2161       {
2162         // Do not commit in this case when keyboard sends a commit when shows for the first time (work-around for imf keyboard).
2163         mIgnoreFirstCommitFlag = false;
2164       }
2165       else
2166       {
2167         // A Commit message is a word that has been accepted, it may have been a pre-edit word previously but now commited.
2168
2169         // Some text may be selected, hiding keyboard causes an empty predictive string to be sent, we don't want to delete highlight in this case
2170         if ( mHighlightMeshActor &&  (!imfEvent.predictiveString.empty()) )
2171         {
2172           // replaces highlighted text with new character
2173           DeleteHighlightedText( false );
2174         }
2175
2176        // A PreEditReset can cause a commit message to be sent, the Ignore Commit flag is used in scenarios where the word is
2177        // not needed, one such scenario is when the pre-edit word is too long to fit.
2178        if ( !mIgnoreCommitFlag )
2179        {
2180          update = CommitReceived( imfEvent.predictiveString );
2181        }
2182        else
2183        {
2184          mIgnoreCommitFlag = false; // reset ignore flag so next commit is acted upon.
2185        }
2186       }
2187
2188       if( update )
2189       {
2190         if( IsScrollEnabled() )
2191         {
2192           // Calculates the new cursor position (in actor coordinates)
2193           const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2194
2195           ScrollTextViewToMakeCursorVisible( cursorPosition );
2196         }
2197       }
2198       break;
2199     }
2200     case ImfManager::DELETESURROUNDING:
2201     {
2202       DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - delete surrounding mPreEditFlag[%s] cursor offset[%d] characters to delete[%d] position to delete[%u] \n",
2203                      (mPreEditFlag)?"true":"false", imfEvent.cursorOffset, imfEvent.numberOfChars, static_cast<std::size_t>( mCursorPosition+imfEvent.cursorOffset) );
2204
2205       mPreEditFlag = false;
2206
2207       std::size_t toDelete = 0;
2208       std::size_t numberOfCharacters = 0;
2209
2210       if( mHighlightMeshActor )
2211       {
2212         // delete highlighted text.
2213         toDelete = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2214         numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - toDelete;
2215       }
2216       else
2217       {
2218         if( std::abs( imfEvent.cursorOffset ) < mCursorPosition )
2219         {
2220           toDelete = mCursorPosition + imfEvent.cursorOffset;
2221         }
2222         if( toDelete + imfEvent.numberOfChars > mStyledText.size() )
2223         {
2224           numberOfCharacters = mStyledText.size() - toDelete;
2225         }
2226         else
2227         {
2228           numberOfCharacters = imfEvent.numberOfChars;
2229         }
2230       }
2231       DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding pre-delete range mCursorPosition[%u] \n", mCursorPosition);
2232       DeleteRange( toDelete, numberOfCharacters );
2233
2234       mCursorPosition = toDelete;
2235       mNumberOfSurroundingCharactersDeleted = numberOfCharacters;
2236
2237       DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding post-delete range mCursorPosition[%u] \n", mCursorPosition);
2238       break;
2239     }
2240     case ImfManager::GETSURROUNDING:
2241     {
2242       // If text is selected/highlighted and surrounding text received we do not want the keyboard to store the word at cursor and return it as a predictive word along with
2243       // the next key pressed.  Instead the Select function sets the cursor position and surrounding text.
2244       if (! ( mHighlightMeshActor || mSelectingText ) )
2245       {
2246         std::string text( GetText() );
2247         DALI_LOG_INFO( gLogFilter, Debug::General, "OnKey - surrounding text - set text [%s] and cursor[%u] \n", text.c_str(), mCursorPosition );
2248
2249         imfManager.SetCursorPosition( mCursorPosition );
2250         imfManager.SetSurroundingText( text );
2251       }
2252
2253       if( 0 != mNumberOfSurroundingCharactersDeleted )
2254       {
2255         mDisplayedTextView.RemoveTextFrom( mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2256         mNumberOfSurroundingCharactersDeleted = 0;
2257
2258         if( mStyledText.empty() )
2259         {
2260           // Styled text is empty, so set the placeholder text.
2261           mDisplayedTextView.SetText( mStyledPlaceHolderText );
2262           mPlaceHolderSet = true;
2263         }
2264       }
2265       break;
2266     }
2267     case ImfManager::VOID:
2268     {
2269       DALI_ASSERT_DEBUG( false );
2270     }
2271   } // end switch
2272
2273   ImfManager::ImfCallbackData callbackData( update, mCursorPosition, GetText(), preeditResetRequired );
2274
2275   return callbackData;
2276 }
2277
2278 bool TextInput::PreEditReceived(const std::string& keyString, std::size_t cursorOffset )
2279 {
2280   mPreserveCursorPosition = false;  // As in pre-edit state we should have the cursor at the end of the word displayed not last touch position.
2281
2282   DALI_LOG_INFO(gLogFilter, Debug::General, ">>PreEditReceived preserveCursorPos[%d] mCursorPos[%d] mPreEditFlag[%d]\n",
2283                 mPreserveCursorPosition, mCursorPosition, mPreEditFlag );
2284
2285   bool preeditResetRequest ( false );
2286
2287   if( mPreEditFlag ) // Already in pre-edit state.
2288   {
2289     if( mStyledText.size() >= mMaxStringLength )
2290     {
2291       DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived styledTextSize >= mMaxStringLength \n");
2292       // Cannot fit these characters into field, clear pre-edit.
2293       if ( !mUnderlinedPriorToPreEdit )
2294       {
2295         TextStyle style;
2296         style.SetUnderline( false );
2297         ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
2298       }
2299       mIgnoreCommitFlag = true;
2300       preeditResetRequest = false; // this will reset the keyboard's predictive suggestions.
2301       mPreEditFlag = false;
2302       EmitMaxInputCharactersReachedSignal();
2303     }
2304     else
2305     {
2306       // delete existing pre-edit string
2307       const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2308
2309       // Store new pre-edit string
2310       mPreEditString.SetText( keyString );
2311
2312       if ( keyString.empty() )
2313       {
2314         mPreEditFlag = false;
2315         mCursorPosition = mPreEditStartPosition;
2316
2317         if( mStyledText.empty() )
2318         {
2319           // Styled text is empty, so set the placeholder text.
2320           mDisplayedTextView.SetText( mStyledPlaceHolderText );
2321           mPlaceHolderSet = true;
2322         }
2323         else
2324         {
2325           mDisplayedTextView.RemoveTextFrom( mPreEditStartPosition, numberOfCharactersToReplace );
2326         }
2327         GetTextLayoutInfo();
2328       }
2329       else
2330       {
2331         // Insert new pre-edit string. InsertAt updates the size and position table.
2332         mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersToReplace );
2333         // If word was too long to be inserted then cursorOffset would be out of range as keyboard assumes there is not limit. Hence use of std::min.
2334         mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2335         ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2336         DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] \n", mCursorPosition);
2337       }
2338       // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2339       DrawCursor();
2340     }
2341   }
2342   else  // mPreEditFlag not set
2343   {
2344     if ( !keyString.empty() ) // Imf can send an empty pre-edit followed by Backspace instead of a commit.
2345     {
2346       DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived Initial Pre-Edit string \n");
2347       // new pre-edit so move into pre-edit state by setting flag
2348       mPreEditFlag = true;
2349       mPreEditString.SetText( keyString ); // store new pre-edit string
2350       mPreEditStartPosition = mCursorPosition; // store starting cursor position of pre-edit so know where to re-start from
2351       mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, 0 );
2352       // If word was too long to be inserted then cursorOffset would be out of range as keyboard assumes there is not limit. Hence use of std::min.
2353       mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2354       ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2355       DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] mPreEditStartPosition[%u]\n", mCursorPosition, mPreEditStartPosition);
2356
2357       // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2358       DrawCursor();
2359     }
2360     else
2361     {
2362       DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived with empty keyString\n");
2363     }
2364   }
2365
2366   return preeditResetRequest;
2367 }
2368
2369 bool TextInput::CommitReceived(const std::string& keyString )
2370 {
2371   DALI_LOG_INFO(gLogFilter, Debug::General, ">>CommitReceived preserveCursorPos[%d] mPreEditStartPosition [%d] mCursorPos[%d] mPreEditFlag[%d] mIgnoreCommitFlag[%s]\n",
2372       mPreserveCursorPosition, mPreEditStartPosition, mCursorPosition, mPreEditFlag, (mIgnoreCommitFlag)?"true":"false" );
2373
2374   bool update( false );
2375
2376   RemovePreEditStyle();
2377
2378   const std::size_t styledTextSize( mStyledText.size() );
2379   if( styledTextSize >= mMaxStringLength )
2380   {
2381     // Cannot fit these characters into field, clear pre-edit.
2382     if ( mPreEditFlag )
2383     {
2384       mIgnoreCommitFlag = true;
2385       mPreEditFlag = false;
2386     }
2387     EmitMaxInputCharactersReachedSignal();
2388   }
2389   else
2390   {
2391     if( mPreEditFlag )
2392     {
2393       // delete existing pre-edit string
2394       const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2395       mPreEditFlag = false;
2396
2397       DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived mPreserveCursorPosition[%s] mPreEditStartPosition[%u]\n",
2398                     (mPreserveCursorPosition)?"true":"false", mPreEditStartPosition );
2399
2400       if ( mPreserveCursorPosition ) // PreEditReset has been called triggering this commit.
2401       {
2402         // No need to update cursor position as Cursor location given by touch.
2403         InsertAt( Text( keyString ), mPreEditStartPosition, numberOfCharactersToReplace );
2404         mPreserveCursorPosition = false;
2405       }
2406       else
2407       {
2408         // Cursor not set by touch so needs to be re-positioned to input more text
2409         mCursorPosition = mPreEditStartPosition + InsertAt( Text(keyString), mPreEditStartPosition, numberOfCharactersToReplace ); // update cursor position as InsertAt, re-draw cursor with this
2410
2411         // If a space or enter caused the commit then our string is one longer than the string given to us by the commit key.
2412         if ( mCommitByKeyInput )
2413         {
2414           mCursorPosition = std::min ( mCursorPosition + 1, mStyledText.size() );
2415           mCommitByKeyInput = false;
2416         }
2417       }
2418
2419       if ( mSelectTextOnCommit )
2420       {
2421         SelectText(mRequestedSelection.mStartOfSelection, mRequestedSelection.mEndOfSelection );
2422       }
2423
2424       update = true;
2425     }
2426     else // mPreEditFlag not set
2427     {
2428       if ( !mIgnoreCommitFlag ) // Check if this commit should be ignored.
2429       {
2430         if( mStyledText.empty() && mPlaceHolderSet )
2431         {
2432           // If the styled text is empty and the placeholder text is set, it needs to be cleared.
2433           mDisplayedTextView.SetText( "" );
2434           mNumberOfSurroundingCharactersDeleted = 0;
2435           mPlaceHolderSet = false;
2436         }
2437         mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2438         update = true;
2439         mNumberOfSurroundingCharactersDeleted = 0;
2440       }
2441       else
2442       {
2443         mIgnoreCommitFlag = false; // Reset flag so future commits will not be ignored.
2444       }
2445     }
2446   }
2447
2448   mSelectTextOnCommit = false;
2449
2450   DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived << mCursorPos[%d] mPreEditFlag[%d] update[%s] \n",
2451                                              mCursorPosition, mPreEditFlag, (update)?"true":"false" );
2452
2453   return update;
2454 }
2455
2456 // End of IMF related methods
2457
2458 std::size_t TextInput::DeletePreEdit()
2459 {
2460   DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeletePreEdit mPreEditFlag[%s] \n", (mPreEditFlag)?"true":"false");
2461
2462   DALI_ASSERT_DEBUG( mPreEditFlag );
2463
2464   const std::size_t preEditStringLength = mPreEditString.GetLength();
2465   const std::size_t styledTextSize = mStyledText.size();
2466
2467   std::size_t endPosition = mPreEditStartPosition + preEditStringLength;
2468
2469   // Prevents erase items outside mStyledText bounds.
2470   if( mPreEditStartPosition > styledTextSize )
2471   {
2472     DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. mPreEditStartPosition > mStyledText.size()" );
2473     mPreEditStartPosition = styledTextSize;
2474   }
2475
2476   if( ( endPosition > styledTextSize ) || ( endPosition < mPreEditStartPosition ) )
2477   {
2478     DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. ( endPosition > mStyledText.size() ) || ( endPosition < mPreEditStartPosition )" );
2479     endPosition = styledTextSize;
2480   }
2481
2482   mStyledText.erase( mStyledText.begin() + mPreEditStartPosition, mStyledText.begin() + endPosition );
2483
2484   // DeletePreEdit() doesn't remove characters from the text-view because may be followed by an InsertAt() which inserts characters,
2485   // in that case, the Insert should use the returned number of deleted characters and replace the text which helps the text-view to
2486   // reuse glyphs.
2487   // In case DeletePreEdit() is not followed by an InsertAt() characters must be deleted after this call.
2488
2489   return preEditStringLength;
2490 }
2491
2492 void TextInput::PreEditReset( bool preserveCursorPosition )
2493 {
2494   DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReset preserveCursorPos[%d] mCursorPos[%d] \n",
2495                 preserveCursorPosition, mCursorPosition);
2496
2497   // Store flag to indicate that we do not want to lose the cursor position as the reset may have occurred due to touch event moving the cursor.
2498   mPreserveCursorPosition = preserveCursorPosition;
2499
2500   // Reset incase we are in a pre-edit state.
2501   ImfManager imfManager = ImfManager::Get();
2502   if ( imfManager )
2503   {
2504     imfManager.Reset(); // Will trigger a commit message
2505   }
2506 }
2507
2508 void TextInput::CursorUpdate()
2509 {
2510   DrawCursor();
2511
2512   ImfManager imfManager = ImfManager::Get();
2513   if ( imfManager )
2514   {
2515     std::string text( GetText() );
2516     imfManager.SetSurroundingText( text );  // Notifying IMF of a cursor change triggers a surrounding text request so updating it now.
2517     imfManager.SetCursorPosition ( mCursorPosition );
2518     imfManager.NotifyCursorPosition();
2519   }
2520 }
2521
2522 /* Delete highlighted characters redisplay*/
2523 void TextInput::DeleteHighlightedText( bool inheritStyle )
2524 {
2525   DALI_LOG_INFO( gLogFilter, Debug::General, "DeleteHighlightedText handlePosOne[%u] handlePosTwo[%u]\n", mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
2526
2527   if(mHighlightMeshActor)
2528   {
2529     mCursorPosition = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2530
2531     MarkupProcessor::StyledTextArray::iterator start = mStyledText.begin() + mCursorPosition;
2532     MarkupProcessor::StyledTextArray::iterator end =  mStyledText.begin() + std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2533
2534     // Get the styled text of the characters to be deleted as it may be needed if
2535     // the "exceed the text-input's boundaries" option is disabled.
2536     MarkupProcessor::StyledTextArray styledCharactersToDelete;
2537
2538     styledCharactersToDelete.insert( styledCharactersToDelete.begin(), start, end );
2539
2540     mStyledText.erase( start, end ); // erase range of characters
2541
2542     // Remove text from TextView.
2543
2544     if( mStyledText.empty() )
2545     {
2546       // Styled text is empty, so set the placeholder text.
2547       mDisplayedTextView.SetText( mStyledPlaceHolderText );
2548       mPlaceHolderSet = true;
2549     }
2550     else
2551     {
2552       const std::size_t numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - mCursorPosition;
2553
2554       mDisplayedTextView.RemoveTextFrom( mCursorPosition, numberOfCharacters );
2555
2556       // It may happen than after removing a white space or a new line character,
2557       // two words merge, this new word could be big enough to not fit in its
2558       // current line, so moved to the next one, and make some part of the text to
2559       // exceed the text-input's boundary.
2560       if( !mExceedEnabled )
2561       {
2562         // Get the new text layout after removing some characters.
2563         mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2564
2565         // Get text-input's size.
2566         const Vector3& size = GetControlSize();
2567
2568         if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2569             ( mTextLayoutInfo.mTextSize.height > size.height ) )
2570         {
2571           mDisplayedTextView.InsertTextAt( mCursorPosition, styledCharactersToDelete );
2572
2573           mStyledText.insert( mStyledText.begin() + mCursorPosition,
2574                               styledCharactersToDelete.begin(),
2575                               styledCharactersToDelete.end() );
2576         }
2577       }
2578     }
2579     GetTextLayoutInfo();
2580
2581     RemoveHighlight();
2582
2583     if( inheritStyle )
2584     {
2585       const TextStyle oldInputStyle( mInputStyle );
2586
2587       mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2588
2589       if( oldInputStyle != mInputStyle )
2590       {
2591         // Updates the line height accordingly with the input style.
2592         UpdateLineHeight();
2593
2594         EmitStyleChangedSignal();
2595       }
2596     }
2597   }
2598 }
2599
2600 void TextInput::DeleteRange( const std::size_t start, const std::size_t ncharacters )
2601 {
2602   DALI_ASSERT_DEBUG( start <= mStyledText.size() );
2603   DALI_ASSERT_DEBUG( !mStyledText.empty() );
2604
2605   DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeleteRange pre mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2606
2607
2608   if ( ( !mStyledText.empty()) && ( ( start + ncharacters ) <= mStyledText.size() ) )
2609   {
2610     MarkupProcessor::StyledTextArray::iterator itStart =  mStyledText.begin() + start;
2611     MarkupProcessor::StyledTextArray::iterator itEnd =  mStyledText.begin() + start + ncharacters;
2612
2613     mStyledText.erase(itStart, itEnd);
2614
2615     // update the selection handles if they are visible.
2616     if( mHighlightMeshActor )
2617     {
2618       std::size_t& minHandle = ( mSelectionHandleOnePosition <= mSelectionHandleTwoPosition ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition );
2619       std::size_t& maxHandle = ( mSelectionHandleTwoPosition > mSelectionHandleOnePosition ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition );
2620
2621       if( minHandle >= start + ncharacters )
2622       {
2623         minHandle -= ncharacters;
2624       }
2625       else if( ( minHandle > start ) && ( minHandle < start + ncharacters ) )
2626       {
2627         minHandle = start;
2628       }
2629
2630       if( maxHandle >= start + ncharacters )
2631       {
2632         maxHandle -= ncharacters;
2633       }
2634       else if( ( maxHandle > start ) && ( maxHandle < start + ncharacters ) )
2635       {
2636         maxHandle = start;
2637       }
2638     }
2639
2640     // Set text is not called here as currently it can not process the set text from deletion and then the set text from the in-coming pre-edit.
2641   }
2642
2643   DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteRange<< post mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2644
2645   // Although mStyledText has been set to a new text string we no longer re-draw the text or notify the cursor change.
2646   // This is a performance decision as the use of this function often means the text is being replaced or just deleted.
2647   // Mean we do not re-draw the text more than we have too.
2648 }
2649
2650 /* Delete character at current cursor position and redisplay*/
2651 void TextInput::DeleteCharacter( std::size_t positionToDelete )
2652 {
2653   // Ensure positionToDelete is not out of bounds.
2654   DALI_ASSERT_DEBUG( positionToDelete <= mStyledText.size() );
2655   DALI_ASSERT_DEBUG( !mStyledText.empty() );
2656   DALI_ASSERT_DEBUG( positionToDelete > 0 );
2657
2658   DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteCharacter positionToDelete[%u]", positionToDelete );
2659
2660
2661   if ( ( !mStyledText.empty()) && ( positionToDelete > 0 ) && positionToDelete <= mStyledText.size() )  // don't try to delete if no characters left of cursor
2662   {
2663     MarkupProcessor::StyledTextArray::iterator it =  mStyledText.begin() + positionToDelete - 1;
2664
2665     // Get the styled text of the character to be deleted as it may be needed if
2666     // the "exceed the text-input's boundaries" option is disabled.
2667     const MarkupProcessor::StyledText styledCharacterToDelete( *it );
2668
2669     mStyledText.erase(it);  // erase the character left of positionToDelete
2670
2671     if( mStyledText.empty() )
2672     {
2673       // Styled text is empty, so set the placeholder text.
2674       mDisplayedTextView.SetText( mStyledPlaceHolderText );
2675       mPlaceHolderSet = true;
2676     }
2677     else
2678     {
2679       mDisplayedTextView.RemoveTextFrom( positionToDelete - 1, 1 );
2680
2681       const Character characterToDelete = styledCharacterToDelete.mText[0];
2682
2683       // It may happen than after removing a white space or a new line character,
2684       // two words merge, this new word could be big enough to not fit in its
2685       // current line, so moved to the next one, and make some part of the text to
2686       // exceed the text-input's boundary.
2687       if( !mExceedEnabled )
2688       {
2689         if( characterToDelete.IsWhiteSpace() || characterToDelete.IsNewLine() )
2690         {
2691           // Get the new text layout after removing one character.
2692           mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2693
2694           // Get text-input's size.
2695           const Vector3& size = GetControlSize();
2696
2697           if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2698               ( mTextLayoutInfo.mTextSize.height > size.height ) )
2699           {
2700             MarkupProcessor::StyledTextArray array;
2701             array.push_back( styledCharacterToDelete );
2702             mDisplayedTextView.InsertTextAt( positionToDelete - 1, array );
2703
2704             mStyledText.insert( mStyledText.begin() + ( positionToDelete - 1 ), styledCharacterToDelete );
2705           }
2706         }
2707       }
2708     }
2709     GetTextLayoutInfo();
2710
2711     ShowGrabHandleAndSetVisibility( false );
2712
2713     mCursorPosition = positionToDelete -1;
2714
2715     const TextStyle oldInputStyle( mInputStyle );
2716
2717     mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2718
2719     if( oldInputStyle != mInputStyle )
2720     {
2721       // Updates the line height accordingly with the input style.
2722       UpdateLineHeight();
2723
2724       EmitStyleChangedSignal();
2725     }
2726   }
2727 }
2728
2729 /*Insert new character into the string and (optionally) redisplay text-input*/
2730 std::size_t TextInput::InsertAt( const Text& newText, const std::size_t insertionPosition, const std::size_t numberOfCharactersToReplace )
2731 {
2732   DALI_LOG_INFO(gLogFilter, Debug::General, "InsertAt insertionPosition[%u]\n", insertionPosition );
2733
2734   // Ensure insertionPosition is not out of bounds.
2735   DALI_ASSERT_ALWAYS( insertionPosition <= mStyledText.size() );
2736
2737   bool textExceedsMaximunNumberOfCharacters = false;
2738   bool textExceedsBoundary = false;
2739   std::size_t insertedStringLength = DoInsertAt( newText, insertionPosition, numberOfCharactersToReplace, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
2740
2741   ShowGrabHandleAndSetVisibility( false );
2742
2743   if( textExceedsMaximunNumberOfCharacters || textExceedsBoundary )
2744   {
2745     if( mPreEditFlag )
2746     {
2747       mIgnoreCommitFlag = true;
2748       mPreEditFlag = false;
2749       // A PreEditReset( false ) should be triggered from here if the keyboards predictive suggestions must be cleared.
2750       // Although can not directly call PreEditReset() as it will cause a recursive emit loop.
2751     }
2752
2753     if( textExceedsMaximunNumberOfCharacters )
2754     {
2755       EmitMaxInputCharactersReachedSignal();
2756     }
2757
2758     if( textExceedsBoundary )
2759     {
2760       EmitInputTextExceedsBoundariesSignal();
2761       PreEditReset( false );
2762     }
2763   }
2764
2765   return insertedStringLength;
2766 }
2767
2768 ImageActor TextInput::CreateCursor( Image cursorImage, const Vector4& border )
2769 {
2770   ImageActor cursor;
2771
2772   if ( cursorImage )
2773   {
2774     cursor = ImageActor::New( cursorImage );
2775   }
2776   else
2777   {
2778     cursor = ImageActor::New( Image::New( DEFAULT_CURSOR ) );
2779   }
2780
2781   cursor.SetStyle(ImageActor::STYLE_NINE_PATCH);
2782   cursor.SetNinePatchBorder( border );
2783
2784   cursor.SetParentOrigin(ParentOrigin::TOP_LEFT);
2785   cursor.SetAnchorPoint(AnchorPoint::BOTTOM_CENTER);
2786   cursor.SetVisible(false);
2787
2788   return cursor;
2789 }
2790
2791 void TextInput::AdvanceCursor(bool reverse, std::size_t places)
2792 {
2793   // As cursor is not moving due to grab handle, handle should be hidden.
2794   ShowGrabHandleAndSetVisibility( false );
2795
2796   bool cursorPositionChanged = false;
2797   if (reverse)
2798   {
2799     if ( mCursorPosition >= places )
2800     {
2801       mCursorPosition = mCursorPosition - places;
2802       cursorPositionChanged = true;
2803     }
2804   }
2805   else
2806   {
2807     if ((mCursorPosition + places) <= mStyledText.size())
2808     {
2809       mCursorPosition = mCursorPosition + places;
2810       cursorPositionChanged = true;
2811     }
2812   }
2813
2814   if( cursorPositionChanged )
2815   {
2816     const std::size_t cursorPositionForStyle = ( 0 == mCursorPosition ? 0 : mCursorPosition - 1 );
2817
2818     const TextStyle oldInputStyle( mInputStyle );
2819     mInputStyle = GetStyleAt( cursorPositionForStyle ); // Inherit style from selected position.
2820
2821     DrawCursor();
2822
2823     if( oldInputStyle != mInputStyle )
2824     {
2825       // Updates the line height accordingly with the input style.
2826       UpdateLineHeight();
2827
2828       EmitStyleChangedSignal();
2829     }
2830
2831     ImfManager imfManager = ImfManager::Get();
2832     if ( imfManager )
2833     {
2834       imfManager.SetCursorPosition ( mCursorPosition );
2835       imfManager.NotifyCursorPosition();
2836     }
2837   }
2838 }
2839
2840 void TextInput::DrawCursor(const std::size_t nthChar)
2841 {
2842   // Get height of cursor and set its size
2843   Size size( CURSOR_THICKNESS, 0.0f );
2844   if (!mTextLayoutInfo.mCharacterLayoutInfoTable.empty())
2845   {
2846     size.height = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) ).height;
2847   }
2848   else
2849   {
2850     // Measure Font so know how big text will be if no initial text to measure.
2851     size.height = mLineHeight;
2852   }
2853
2854   mCursor.SetSize(size);
2855
2856   // If the character is italic then the cursor also tilts.
2857   mCursor.SetRotation( mInputStyle.GetItalics() ? Degree( mInputStyle.GetItalicsAngle() - CURSOR_ANGLE_OFFSET ) : Degree( 0.f ), Vector3::ZAXIS );
2858
2859   DALI_ASSERT_DEBUG( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
2860
2861   if ( ( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) )
2862   {
2863     Vector3 altPosition;    // Alternate (i.e. opposite direction) cursor position.
2864     bool altPositionValid;  // Alternate cursor validity flag.
2865     bool directionRTL;      // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
2866     Vector3 position = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
2867
2868     SetAltCursorEnabled( altPositionValid );
2869
2870     if(!altPositionValid)
2871     {
2872       mCursor.SetPosition( position + UI_OFFSET );
2873     }
2874     else
2875     {
2876       size.height *= 0.5f;
2877       mCursor.SetSize(size);
2878       mCursor.SetPosition( position + UI_OFFSET - Vector3(0.0f, directionRTL ? 0.0f : size.height, 0.0f) );
2879
2880       // TODO: change this cursor pos, to be the one where the cursor is sourced from.
2881       Size rowSize = GetRowRectFromCharacterPosition( GetVisualPosition( mCursorPosition ) );
2882       size.height = rowSize.height * 0.5f;
2883       mCursorRTL.SetSize(size);
2884       mCursorRTL.SetPosition( altPosition + UI_OFFSET - Vector3(0.0f, directionRTL ? size.height : 0.0f, 0.0f) );
2885     }
2886
2887     if( IsScrollEnabled() )
2888     {
2889       // Whether cursor and grab handle are inside the boundaries of the text-input when text scroll is enabled.
2890       mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( position, size, GetControlSize() );
2891     }
2892   } // EditMode
2893 }
2894
2895 void TextInput::SetAltCursorEnabled( bool enabled )
2896 {
2897   mCursorRTLEnabled = enabled;
2898   mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2899 }
2900
2901 void TextInput::SetCursorVisibility( bool visible )
2902 {
2903   mCursorVisibility = visible;
2904   mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
2905   mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
2906 }
2907
2908 void TextInput::CreateGrabHandle( Dali::Image image )
2909 {
2910   if ( !mGrabHandle )
2911   {
2912     if ( !image )
2913     {
2914       mGrabHandleImage = Image::New(DEFAULT_GRAB_HANDLE);
2915     }
2916     else
2917     {
2918       mGrabHandleImage = image;
2919     }
2920
2921     mGrabHandle = ImageActor::New(mGrabHandleImage);
2922     mGrabHandle.SetParentOrigin(ParentOrigin::TOP_LEFT);
2923     mGrabHandle.SetAnchorPoint(AnchorPoint::TOP_CENTER);
2924
2925     mGrabHandle.SetDrawMode(DrawMode::OVERLAY);
2926
2927     ShowGrabHandleAndSetVisibility( false );
2928
2929     CreateGrabArea( mGrabHandle );
2930
2931     mActiveLayer.Add(mGrabHandle);
2932   }
2933 }
2934
2935 void TextInput::CreateGrabArea( Actor& parent )
2936 {
2937   mGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
2938   mGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
2939   mGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE ) ) );  // grab area to be larger than text actor
2940   mGrabArea.TouchedSignal().Connect(this,&TextInput::OnPressDown);
2941   mTapDetector.Attach( mGrabArea );
2942   mPanGestureDetector.Attach( mGrabArea );
2943
2944   parent.Add(mGrabArea);
2945 }
2946
2947 Vector3 TextInput::MoveGrabHandle( const Vector2& displacement )
2948 {
2949   Vector3 actualHandlePosition;
2950
2951   if (mGrabHandle)
2952   {
2953     mActualGrabHandlePosition.x += displacement.x;
2954     mActualGrabHandlePosition.y += displacement.y;
2955
2956     // Grab handle should jump to the nearest character and take cursor with it
2957     std::size_t newCursorPosition = 0;
2958     ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY(), newCursorPosition );
2959
2960     actualHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition );
2961
2962     bool handleVisible = true;
2963
2964     if( IsScrollEnabled() )
2965     {
2966       const Vector3 controlSize = GetControlSize();
2967       const Size cursorSize = GetRowRectFromCharacterPosition( GetVisualPosition( newCursorPosition ) );
2968       // Scrolls the text if the handle is not in a visible position
2969       handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
2970                                                   cursorSize,
2971                                                   controlSize );
2972
2973       if( handleVisible )
2974       {
2975         StopScrollTimer();
2976         mCurrentHandlePosition = actualHandlePosition;
2977         mScrollDisplacement = Vector2::ZERO;
2978       }
2979       else
2980       {
2981         if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
2982         {
2983           mScrollDisplacement.x = -SCROLL_SPEED;
2984         }
2985         else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
2986         {
2987           mScrollDisplacement.x = SCROLL_SPEED;
2988         }
2989         if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
2990         {
2991           mScrollDisplacement.y = -SCROLL_SPEED;
2992         }
2993         else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
2994         {
2995           mScrollDisplacement.y = SCROLL_SPEED;
2996         }
2997         StartScrollTimer();
2998       }
2999     }
3000
3001     if( handleVisible &&                           // Only redraw cursor and do updates if position changed
3002         ( newCursorPosition != mCursorPosition ) ) // and the new position is visible (if scroll is not enabled, it's always true).
3003     {
3004       mCursorPosition = newCursorPosition;
3005
3006       mGrabHandle.SetPosition( actualHandlePosition + UI_OFFSET );
3007
3008       const TextStyle oldInputStyle( mInputStyle );
3009
3010       mInputStyle = GetStyleAtCursor(); //Inherit style from cursor position
3011
3012       CursorUpdate();  // Let keyboard know the new cursor position so can 're-capture' for prediction.
3013
3014       if( oldInputStyle != mInputStyle )
3015       {
3016         // Updates the line height accordingly with the input style.
3017         UpdateLineHeight();
3018
3019         EmitStyleChangedSignal();
3020       }
3021     }
3022   }
3023
3024   return actualHandlePosition;
3025 }
3026
3027 void TextInput::ShowGrabHandle( bool visible )
3028 {
3029   if ( IsGrabHandleEnabled() )
3030   {
3031     if( mGrabHandle )
3032     {
3033       mGrabHandle.SetVisible( mGrabHandleVisibility );
3034     }
3035     StartMonitoringStageForTouch();
3036   }
3037 }
3038
3039 void TextInput::ShowGrabHandleAndSetVisibility( bool visible )
3040 {
3041   mGrabHandleVisibility = visible;
3042   ShowGrabHandle( visible );
3043 }
3044
3045 // Callbacks connected to be Property notifications for Boundary checking.
3046
3047 void TextInput::OnLeftBoundaryExceeded(PropertyNotification& source)
3048 {
3049   mIsSelectionHandleOneFlipped = true;
3050   mSelectionHandleOne.SetScale( -1.0f, 1.0f, 1.0f );
3051   mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3052 }
3053
3054 void TextInput::OnReturnToLeftBoundary(PropertyNotification& source)
3055 {
3056   mIsSelectionHandleOneFlipped = false;
3057   mSelectionHandleOne.SetScale( 1.0f, 1.0f, 1.0f );
3058   mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3059 }
3060
3061 void TextInput::OnRightBoundaryExceeded(PropertyNotification& source)
3062 {
3063   mIsSelectionHandleTwoFlipped = true;
3064   mSelectionHandleTwo.SetScale( -1.0f, 1.0f, 1.0f );
3065   mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3066 }
3067
3068 void TextInput::OnReturnToRightBoundary(PropertyNotification& source)
3069 {
3070   mIsSelectionHandleTwoFlipped = false;
3071   mSelectionHandleTwo.SetScale( 1.0f, 1.0f, 1.0f );
3072   mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3073 }
3074
3075 // todo change PropertyNotification signal definition to include Actor. Hence won't need duplicate functions.
3076 void TextInput::OnHandleOneLeavesBoundary( PropertyNotification& source)
3077 {
3078   mSelectionHandleOne.SetOpacity(0.0f);
3079 }
3080
3081 void TextInput::OnHandleOneWithinBoundary(PropertyNotification& source)
3082 {
3083   mSelectionHandleOne.SetOpacity(1.0f);
3084 }
3085
3086 void TextInput::OnHandleTwoLeavesBoundary( PropertyNotification& source)
3087 {
3088   mSelectionHandleTwo.SetOpacity(0.0f);
3089 }
3090
3091 void TextInput::OnHandleTwoWithinBoundary(PropertyNotification& source)
3092 {
3093   mSelectionHandleTwo.SetOpacity(1.0f);
3094 }
3095
3096 // End of Callbacks connected to be Property notifications for Boundary checking.
3097
3098 void TextInput::SetUpHandlePropertyNotifications()
3099 {
3100   /* Property notifications for handles exceeding the boundary and returning back within boundary */
3101
3102   Vector3 handlesize = GetSelectionHandleSize();
3103
3104   // Exceeding horizontal boundary
3105   PropertyNotification leftNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.x + handlesize.x) );
3106   leftNotification.NotifySignal().Connect( this, &TextInput::OnLeftBoundaryExceeded );
3107
3108   PropertyNotification rightNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.z - handlesize.x ) );
3109   rightNotification.NotifySignal().Connect( this, &TextInput::OnRightBoundaryExceeded );
3110
3111   // Within horizontal boundary
3112   PropertyNotification leftLeaveNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.x + 2*handlesize.x ) );
3113   leftLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToLeftBoundary );
3114
3115   PropertyNotification rightLeaveNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.z - 2*handlesize.x ) );
3116   rightLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToRightBoundary );
3117
3118   // Exceeding vertical boundary
3119   PropertyNotification verticalExceedNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3120                                                        OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3121                                                                          mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3122   verticalExceedNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneLeavesBoundary );
3123
3124   PropertyNotification verticalExceedNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3125                                                        OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3126                                                                          mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3127   verticalExceedNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoLeavesBoundary );
3128
3129   // Within vertical boundary
3130   PropertyNotification verticalWithinNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3131                                                        InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3132                                                                         mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3133   verticalWithinNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneWithinBoundary );
3134
3135   PropertyNotification verticalWithinNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3136                                                        InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3137                                                                         mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3138   verticalWithinNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoWithinBoundary );
3139 }
3140
3141 void TextInput::CreateSelectionHandles( std::size_t start, std::size_t end, Dali::Image handleOneImage,  Dali::Image handleTwoImage )
3142 {
3143   mSelectionHandleOnePosition = start;
3144   mSelectionHandleTwoPosition = end;
3145
3146   if ( !mSelectionHandleOne )
3147   {
3148     // create normal and pressed images
3149     mSelectionHandleOneImage = Image::New( DEFAULT_SELECTION_HANDLE_ONE );
3150     mSelectionHandleOneImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_ONE_PRESSED );
3151
3152     mSelectionHandleOne = ImageActor::New( mSelectionHandleOneImage );
3153     mSelectionHandleOne.SetName("SelectionHandleOne");
3154     mSelectionHandleOne.SetParentOrigin( ParentOrigin::TOP_LEFT );
3155     mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
3156     mIsSelectionHandleOneFlipped = false;
3157     mSelectionHandleOne.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text
3158
3159     mHandleOneGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3160     mHandleOneGrabArea.SetName("SelectionHandleOneGrabArea");
3161
3162     mHandleOneGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) );  // grab area to be larger than text actor
3163     mHandleOneGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3164
3165     mTapDetector.Attach( mHandleOneGrabArea );
3166     mPanGestureDetector.Attach( mHandleOneGrabArea );
3167
3168     mHandleOneGrabArea.TouchedSignal().Connect(this,&TextInput::OnHandleOneTouched);
3169
3170     mSelectionHandleOne.Add( mHandleOneGrabArea );
3171     mActiveLayer.Add( mSelectionHandleOne );
3172   }
3173
3174   if ( !mSelectionHandleTwo )
3175   {
3176     // create normal and pressed images
3177     mSelectionHandleTwoImage = Image::New( DEFAULT_SELECTION_HANDLE_TWO );
3178     mSelectionHandleTwoImagePressed = Image::New( DEFAULT_SELECTION_HANDLE_TWO_PRESSED );
3179
3180     mSelectionHandleTwo = ImageActor::New( mSelectionHandleTwoImage );
3181     mSelectionHandleTwo.SetName("SelectionHandleTwo");
3182     mSelectionHandleTwo.SetParentOrigin( ParentOrigin::TOP_LEFT );
3183     mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT );
3184     mIsSelectionHandleTwoFlipped = false;
3185     mSelectionHandleTwo.SetDrawMode(DrawMode::OVERLAY); // ensure grab handle above text
3186
3187     mHandleTwoGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3188     mHandleTwoGrabArea.SetName("SelectionHandleTwoGrabArea");
3189     mHandleTwoGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) );  // grab area to be larger than text actor
3190     mHandleTwoGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3191
3192     mTapDetector.Attach( mHandleTwoGrabArea );
3193     mPanGestureDetector.Attach( mHandleTwoGrabArea );
3194
3195     mHandleTwoGrabArea.TouchedSignal().Connect(this, &TextInput::OnHandleTwoTouched);
3196
3197     mSelectionHandleTwo.Add( mHandleTwoGrabArea );
3198
3199     mActiveLayer.Add( mSelectionHandleTwo );
3200   }
3201
3202   SetUpHandlePropertyNotifications();
3203
3204   // update table as text may have changed.
3205   GetTextLayoutInfo();
3206
3207   mSelectionHandleOneActualPosition = GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition );
3208   mSelectionHandleTwoActualPosition = GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition );
3209
3210   mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
3211   mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
3212
3213   // Calculates and set the visibility if the scroll mode is enabled.
3214   bool isSelectionHandleOneVisible = true;
3215   bool isSelectionHandleTwoVisible = true;
3216   if( IsScrollEnabled() )
3217   {
3218     const Vector3& controlSize( GetControlSize() );
3219     isSelectionHandleOneVisible = IsPositionInsideBoundaries( mSelectionHandleOneActualPosition, Size::ZERO, controlSize );
3220     isSelectionHandleTwoVisible = IsPositionInsideBoundaries( mSelectionHandleTwoActualPosition, Size::ZERO, controlSize );
3221     mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
3222     mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
3223   }
3224
3225   CreateHighlight();  // function will only create highlight if not already created.
3226 }
3227
3228 Vector3 TextInput::MoveSelectionHandle( SelectionHandleId handleId, const Vector2& displacement )
3229 {
3230   Vector3 actualHandlePosition;
3231
3232   if ( mSelectionHandleOne && mSelectionHandleTwo )
3233   {
3234     const Vector3& controlSize = GetControlSize();
3235
3236     Size cursorSize( CURSOR_THICKNESS, 0.f );
3237
3238     // Get a reference of the wanted selection handle (handle one or two).
3239     Vector3& actualSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
3240
3241     // Get a reference for the current position of the handle and a copy of its pair
3242     std::size_t& currentSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3243     const std::size_t pairSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition;
3244
3245     // Get a handle of the selection handle actor
3246     ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3247
3248     // Selection handles should jump to the nearest character
3249     std::size_t newHandlePosition = 0;
3250     ReturnClosestIndex( actualSelectionHandlePosition.GetVectorXY(), newHandlePosition );
3251
3252     actualHandlePosition = GetActualPositionFromCharacterPosition( newHandlePosition );
3253
3254     bool handleVisible = true;
3255
3256     if( IsScrollEnabled() )
3257     {
3258       mCurrentSelectionId = handleId;
3259
3260       cursorSize.height = GetRowRectFromCharacterPosition( GetVisualPosition( newHandlePosition ) ).height;
3261       // Restricts the movement of the grab handle inside the boundaries of the text-input.
3262       handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3263                                                   cursorSize,
3264                                                   controlSize );
3265
3266       if( handleVisible )
3267       {
3268         StopScrollTimer();
3269         mCurrentSelectionHandlePosition = actualHandlePosition;
3270         mScrollDisplacement = Vector2::ZERO;
3271       }
3272       else
3273       {
3274         if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3275         {
3276           mScrollDisplacement.x = -SCROLL_SPEED;
3277         }
3278         else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3279         {
3280           mScrollDisplacement.x = SCROLL_SPEED;
3281         }
3282         if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3283         {
3284           mScrollDisplacement.y = -SCROLL_SPEED;
3285         }
3286         else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3287         {
3288           mScrollDisplacement.y = SCROLL_SPEED;
3289         }
3290         StartScrollTimer();
3291       }
3292     }
3293
3294     if ( handleVisible &&                                          // Ensure the handle is visible.
3295          ( newHandlePosition != pairSelectionHandlePosition ) &&   // Ensure handle one is not the same position as handle two.
3296          ( newHandlePosition != currentSelectionHandlePosition ) ) // Ensure the handle has moved.
3297     {
3298       currentSelectionHandlePosition = newHandlePosition;
3299
3300       Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3301       selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3302
3303       UpdateHighlight();
3304
3305       if ( handleId == HandleOne )
3306       {
3307         const TextStyle oldInputStyle( mInputStyle );
3308
3309         // Set Active Style to that of first character in selection
3310         if( mSelectionHandleOnePosition < mStyledText.size() )
3311         {
3312           mInputStyle = ( mStyledText.at( mSelectionHandleOnePosition ) ).mStyle;
3313         }
3314
3315         if( oldInputStyle != mInputStyle )
3316         {
3317           // Updates the line height accordingly with the input style.
3318           UpdateLineHeight();
3319
3320           EmitStyleChangedSignal();
3321         }
3322       }
3323     }
3324   }
3325
3326   return actualHandlePosition; // Returns Handle position passed in if new value not assigned.
3327 }
3328
3329 void TextInput::SetSelectionHandlePosition(SelectionHandleId handleId)
3330 {
3331
3332   const std::size_t selectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3333   ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3334
3335   if ( selectionHandleActor )
3336   {
3337     const Vector3 actualHandlePosition = GetActualPositionFromCharacterPosition( selectionHandlePosition );
3338     Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3339     selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3340
3341     if( IsScrollEnabled() )
3342     {
3343       const Size cursorSize( CURSOR_THICKNESS,
3344                              GetRowRectFromCharacterPosition( GetVisualPosition( selectionHandlePosition ) ).height );
3345       selectionHandleActor.SetVisible( IsPositionInsideBoundaries( actualHandlePosition,
3346                                                                    cursorSize,
3347                                                                    GetControlSize() ) );
3348     }
3349   }
3350 }
3351
3352 std::size_t TextInput::GetVisualPosition(std::size_t logicalPosition) const
3353 {
3354   // Note: we're allowing caller to request a logical position of size (i.e. end of string)
3355   // For now the visual position of end of logical string will be end of visual string.
3356   DALI_ASSERT_DEBUG( logicalPosition <= mTextLayoutInfo.mCharacterLogicalToVisualMap.size() );
3357
3358   return logicalPosition != mTextLayoutInfo.mCharacterLogicalToVisualMap.size() ? mTextLayoutInfo.mCharacterLogicalToVisualMap[logicalPosition] : mTextLayoutInfo.mCharacterLogicalToVisualMap.size();
3359 }
3360
3361 void TextInput::GetVisualTextSelection(std::vector<bool>& selectedVisualText, std::size_t startSelection, std::size_t endSelection)
3362 {
3363   std::vector<int>::iterator it = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin();
3364   std::vector<int>::iterator startSelectionIt = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::min(startSelection, endSelection);
3365   std::vector<int>::iterator endSelectionIt = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::max(startSelection, endSelection);
3366   std::vector<int>::iterator end = mTextLayoutInfo.mCharacterLogicalToVisualMap.end();
3367
3368   selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size() );
3369
3370   // Deselect text prior to startSelectionIt
3371   for(;it!=startSelectionIt;++it)
3372   {
3373     selectedVisualText[*it] = false;
3374   }
3375
3376   // Select text from startSelectionIt -> endSelectionIt
3377   for(;it!=endSelectionIt;++it)
3378   {
3379     selectedVisualText[*it] = true;
3380   }
3381
3382   // Deselect text after endSelection
3383   for(;it!=end;++it)
3384   {
3385     selectedVisualText[*it] = false;
3386   }
3387
3388   selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size(), false );
3389 }
3390
3391 // Calculate the dimensions of the quads they will make the highlight mesh
3392 TextInput::HighlightInfo TextInput::CalculateHighlightInfo()
3393 {
3394   // At the moment there is no public API to modify the block alignment option.
3395   const bool blockAlignEnabled = true;
3396
3397   mNewHighlightInfo.mQuadList.clear(); // clear last quad information.
3398
3399   if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
3400   {
3401     Toolkit::TextView::CharacterLayoutInfoContainer::iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3402     Toolkit::TextView::CharacterLayoutInfoContainer::iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
3403
3404     // Get vector of flags representing characters that are selected (true) vs unselected (false).
3405     std::vector<bool> selectedVisualText;
3406     GetVisualTextSelection(selectedVisualText, mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
3407     std::vector<bool>::iterator selectedIt(selectedVisualText.begin());
3408     std::vector<bool>::iterator selectedEndIt(selectedVisualText.end());
3409
3410     SelectionState selectionState = SelectionNone;          ///< Current selection status of cursor over entire text.
3411     float rowLeft = 0.0f;
3412     float rowRight = 0.0f;
3413     // Keep track of the TextView's min/max extents. Should be able to query this from TextView.
3414     float maxRowLeft = std::numeric_limits<float>::max();
3415     float maxRowRight = 0.0f;
3416
3417     Toolkit::TextView::CharacterLayoutInfoContainer::iterator lastIt = it;
3418
3419     // Scan through entire text.
3420     while(it != end)
3421     {
3422       // selectionState: None when not in selection, Started when in selection, and Ended when reached end of selection.
3423
3424       Toolkit::TextView::CharacterLayoutInfo& charInfo(*it);
3425       bool charSelected( false );
3426       if( selectedIt != selectedEndIt )
3427       {
3428         charSelected = *selectedIt++;
3429       }
3430
3431       if(selectionState == SelectionNone)
3432       {
3433         if(charSelected)
3434         {
3435           selectionState = SelectionStarted;
3436           rowLeft = charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3437           rowRight = rowLeft + charInfo.mSize.width;
3438         }
3439       }
3440       else if(selectionState == SelectionStarted)
3441       {
3442         // break selection on:
3443         // 1. new line causing selection break. (\n or wordwrap)
3444         // 2. character not selected.
3445         if(charInfo.mPosition.y - lastIt->mPosition.y > CHARACTER_THRESHOLD ||
3446            !charSelected)
3447         {
3448           // finished selection.
3449           // TODO: TextView should have a table of visual rows, and each character a reference to the row
3450           // that it resides on. That way this enumeration is not necessary.
3451           Vector2 min, max;
3452           if(lastIt->mIsNewLineChar)
3453           {
3454             // 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.
3455             lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3456           }
3457           const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3458           maxRowLeft = std::min(maxRowLeft, min.x);
3459           maxRowRight = std::max(maxRowRight, max.x);
3460           float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3461           float rowTop = rowBottom - rowSize.height;
3462
3463           // Still selected, and block-align mode then set rowRight to max, so it can be clamped afterwards
3464           if(charSelected && blockAlignEnabled)
3465           {
3466             rowRight = std::numeric_limits<float>::max();
3467           }
3468           mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3469
3470           selectionState = SelectionNone;
3471
3472           // Still selected? start a new selection
3473           if( charSelected )
3474           {
3475             // if block-align mode then set rowLeft to min, so it can be clamped afterwards
3476             rowLeft = blockAlignEnabled ? 0.0f : charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3477             rowRight = ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width;
3478             selectionState = SelectionStarted;
3479           }
3480         }
3481         else
3482         {
3483           // build up highlight(s) with this selection data.
3484           rowLeft = std::min( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x, rowLeft );
3485           rowRight = std::max( ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width, rowRight );
3486         }
3487       }
3488
3489       lastIt = it++;
3490     }
3491
3492     // If reached end, and still on selection, then close selection.
3493     if(it == end)
3494     {
3495       if(selectionState == SelectionStarted)
3496       {
3497         // finished selection.
3498         Vector2 min, max;
3499         if(lastIt->mIsNewLineChar)
3500         {
3501           lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3502         }
3503         const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3504         maxRowLeft = std::min(maxRowLeft, min.x);
3505         maxRowRight = std::max(maxRowRight, max.x);
3506         float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3507         float rowTop = rowBottom - rowSize.height;
3508         mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3509       }
3510     }
3511
3512     // Get the top left and bottom right corners.
3513     const Toolkit::TextView::CharacterLayoutInfo& firstCharacter( *mTextLayoutInfo.mCharacterLayoutInfoTable.begin() );
3514     const Vector2 topLeft( maxRowLeft, firstCharacter.mPosition.y - firstCharacter.mSize.height );
3515     const Vector2 bottomRight( topLeft.x + mTextLayoutInfo.mTextSize.width, topLeft.y + mTextLayoutInfo.mTextSize.height );
3516
3517     // Clamp quads so they appear to clip to borders of the whole text.
3518     mNewHighlightInfo.Clamp2D( topLeft, bottomRight );
3519
3520     // For block-align align Further Clamp quads to max left and right extents
3521     if(blockAlignEnabled)
3522     {
3523       // BlockAlign: Will adjust highlight to block:
3524       // i.e.
3525       //   H[ello] (top row right = max of all rows right)
3526       // [--this-] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3527       // [is some] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3528       // [text] (bottom row left = min of all rows left)
3529       // (common in SMS messaging selection)
3530       //
3531       // As opposed to the default which is tight text highlighting.
3532       //   H[ello]
3533       //   [this]
3534       // [is some]
3535       // [text]
3536       // (common in regular text editors/web browser selection)
3537
3538       mNewHighlightInfo.Clamp2D( Vector2(maxRowLeft, topLeft.y), Vector2(maxRowRight, bottomRight.y ) );
3539     }
3540
3541     // Finally clamp quads again so they don't exceed the boundry of the control.
3542     const Vector3& controlSize = GetControlSize();
3543     mNewHighlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
3544   } // end if
3545
3546   return mNewHighlightInfo;
3547 }
3548
3549 void TextInput::UpdateHighlight()
3550 {
3551 //  Construct a Mesh with a texture to be used as the highlight 'box' for selected text
3552 //
3553 //  Example scenarios where mesh is made from 3, 1, 2, 2 ,3 or 3 quads.
3554 //
3555 //   [ TOP   ]  [ TOP ]      [TOP ]  [ TOP    ]      [ TOP  ]      [ TOP  ]
3556 //  [ MIDDLE ]             [BOTTOM]  [BOTTOM]      [ MIDDLE ]   [ MIDDLE  ]
3557 //  [ BOTTOM]                                      [ MIDDLE ]   [ MIDDLE  ]
3558 //                                                 [BOTTOM]     [ MIDDLE  ]
3559 //                                                              [BOTTOM]
3560 //
3561 //  Each quad is created as 2 triangles.
3562 //  Middle is just 1 quad regardless of its size.
3563 //
3564 //  (0,0)         (0,0)
3565 //     0*    *2     0*       *2
3566 //     TOP          TOP
3567 //     3*    *1     3*       *1
3568 //  4*       *1     4*     *6
3569 //     MIDDLE         BOTTOM
3570 //  6*       *5     7*     *5
3571 //  6*    *8
3572 //   BOTTOM
3573 //  9*    *7
3574 //
3575
3576   if ( mHighlightMeshActor )
3577   {
3578     // vertex and triangle buffers should always be present if MeshActor is alive.
3579     HighlightInfo newHighlightInfo = CalculateHighlightInfo();
3580     MeshData::VertexContainer vertices;
3581     Dali::MeshData::FaceIndices faceIndices;
3582
3583     if( !newHighlightInfo.mQuadList.empty() )
3584     {
3585       std::vector<QuadCoordinates>::iterator iter = newHighlightInfo.mQuadList.begin();
3586       std::vector<QuadCoordinates>::iterator endIter = newHighlightInfo.mQuadList.end();
3587
3588       // vertex position defaults to (0 0 0)
3589       MeshData::Vertex vertex;
3590       // set normal for all vertices as (0 0 1) pointing outward from TextInput Actor.
3591       vertex.nZ = 1.0f;
3592
3593       for(std::size_t v = 0; iter != endIter; ++iter,v+=4 )
3594       {
3595         // Add each quad geometry (a sub-selection) to the mesh data.
3596
3597         // 0-----1
3598         // |\    |
3599         // | \ A |
3600         // |  \  |
3601         // | B \ |
3602         // |    \|
3603         // 2-----3
3604
3605         QuadCoordinates& quad = *iter;
3606         // top-left (v+0)
3607         vertex.x = quad.min.x;
3608         vertex.y = quad.min.y;
3609         vertices.push_back( vertex );
3610
3611         // top-right (v+1)
3612         vertex.x = quad.max.x;
3613         vertex.y = quad.min.y;
3614         vertices.push_back( vertex );
3615
3616         // bottom-left (v+2)
3617         vertex.x = quad.min.x;
3618         vertex.y = quad.max.y;
3619         vertices.push_back( vertex );
3620
3621         // bottom-right (v+3)
3622         vertex.x = quad.max.x;
3623         vertex.y = quad.max.y;
3624         vertices.push_back( vertex );
3625
3626         // triangle A (3, 1, 0)
3627         faceIndices.push_back( v + 3 );
3628         faceIndices.push_back( v + 1 );
3629         faceIndices.push_back( v );
3630
3631         // triangle B (0, 2, 3)
3632         faceIndices.push_back( v );
3633         faceIndices.push_back( v + 2 );
3634         faceIndices.push_back( v + 3 );
3635
3636         mMeshData.SetFaceIndices( faceIndices );
3637       }
3638
3639       BoneContainer bones(0); // passed empty as bones not required
3640       mMeshData.SetData( vertices, faceIndices, bones, mCustomMaterial );
3641       mHighlightMesh.UpdateMeshData(mMeshData);
3642     }
3643   }
3644 }
3645
3646 void TextInput::ClearPopup()
3647 {
3648   mPopUpPanel.Clear();
3649 }
3650
3651 void TextInput::AddPopupOption(const std::string& name, const std::string& caption, const Image icon, bool finalOption)
3652 {
3653   mPopUpPanel.AddOption(name, caption, icon, finalOption);
3654 }
3655
3656 void TextInput::SetPopupPosition(const Vector3& position)
3657 {
3658   mPopUpPanel.Self().SetPosition( position );
3659 }
3660
3661 void TextInput::HidePopup(bool animate, bool signalFinished )
3662 {
3663   if ( ( mPopUpPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopUpPanel.GetState() == TextInputPopup::StateShown )  )
3664   {
3665     mPopUpPanel.Hide( animate );
3666
3667     if( animate && signalFinished )
3668     {
3669       mPopUpPanel.HideFinishedSignal().Connect( this, &TextInput::OnPopupHideFinished );
3670     }
3671   }
3672 }
3673
3674 void TextInput::ShowPopup(bool animate)
3675 {
3676   Vector3 position;
3677
3678   if(mHighlightMeshActor && mState == StateEdit)
3679   {
3680     Vector3 topHandle;
3681     Size rowSize;
3682     // When text is selected, show popup above top handle (and text), or below bottom handle.
3683     // topHandle: referring to the top most point of the handle or the top line of selection.
3684     if ( mSelectionHandleTwoActualPosition.y > mSelectionHandleOneActualPosition.y )
3685     {
3686       topHandle = mSelectionHandleOneActualPosition;
3687       rowSize= GetRowRectFromCharacterPosition( mSelectionHandleOnePosition );
3688     }
3689     else
3690     {
3691       topHandle = mSelectionHandleTwoActualPosition;
3692       rowSize = GetRowRectFromCharacterPosition( mSelectionHandleTwoPosition );
3693     }
3694     topHandle.y += TOP_HANDLE_TOP_OFFSET - rowSize.height;
3695     position = Vector3(topHandle.x, topHandle.y, 0.0f);
3696
3697     // bottomHandle: referring to the bottom most point of the handle or the bottom line of selection.
3698     Vector3 bottomHandle;
3699     bottomHandle.y = std::max ( mSelectionHandleTwoActualPosition.y , mSelectionHandleOneActualPosition.y );
3700     bottomHandle.y += GetSelectionHandleSize().y + BOTTOM_HANDLE_BOTTOM_OFFSET;
3701     mPopUpPanel.SetAlternativeOffset(Vector2(0.0f, bottomHandle.y - topHandle.y));
3702   }
3703   else
3704   {
3705     // When no text is selected, show popup at world position of grab handle or cursor
3706     position = GetActualPositionFromCharacterPosition( mCursorPosition );
3707     const Size rowSize = GetRowRectFromCharacterPosition( mCursorPosition );
3708     position.y -= rowSize.height;
3709     // if can't be positioned above, then position below row.
3710     Vector2 alternativePopUpPosition( 0.0f, position.y ); // default if no grab handle
3711     if ( mGrabHandle )
3712     {
3713       alternativePopUpPosition.y = rowSize.height + ( mGrabHandle.GetCurrentSize().height * DEFAULT_GRAB_HANDLE_RELATIVE_SIZE.y ) ;
3714       // If grab handle enabled then position pop-up below the grab handle.
3715     }
3716     mPopUpPanel.SetAlternativeOffset( alternativePopUpPosition );
3717   }
3718
3719   // reposition popup above the desired cursor posiiton.
3720   Vector3 textViewSize = mDisplayedTextView.GetCurrentSize();
3721   textViewSize.z = 0.0f;
3722   // World position = world position of ParentOrigin of cursor (i.e. top-left corner of TextView) + cursor position;
3723   Vector3 worldPosition = mDisplayedTextView.GetCurrentWorldPosition() - (textViewSize * 0.5f) + position;
3724
3725   SetPopupPosition( worldPosition );
3726
3727   // Show popup
3728   mPopUpPanel.Show(animate);
3729   StartMonitoringStageForTouch();
3730
3731   mPopUpPanel.PressedSignal().Connect( this, &TextInput::OnPopupButtonPressed );
3732 }
3733
3734 void TextInput::ShowPopupCutCopyPaste()
3735 {
3736   ClearPopup();
3737   // Check the selected text is whole text or not.
3738   if( IsTextSelected() && ( mStyledText.size() != GetSelectedText().size() ) )
3739   {
3740     Image selectAllIcon = Image::New( DEFAULT_ICON_SELECT_ALL );
3741     AddPopupOption( OPTION_SELECT_ALL, GET_LOCALE_TEXT("IDS_COM_BODY_SELECT_ALL"), selectAllIcon );
3742   }
3743
3744   if ( !mStyledText.empty() )
3745   {
3746     Image cutIcon = Image::New( DEFAULT_ICON_CUT );
3747     Image copyIcon = Image::New( DEFAULT_ICON_COPY );
3748     AddPopupOption( OPTION_CUT, GET_LOCALE_TEXT("IDS_COM_BODY_CUT"), cutIcon );
3749     AddPopupOption( OPTION_COPY, GET_LOCALE_TEXT("IDS_COM_BODY_COPY"), copyIcon, true );
3750   }
3751
3752   if(mClipboard.NumberOfItems())
3753   {
3754     Image pasteIcon = Image::New( DEFAULT_ICON_PASTE );
3755     Image clipboardIcon = Image::New( DEFAULT_ICON_CLIPBOARD );
3756     AddPopupOption( OPTION_PASTE, GET_LOCALE_TEXT("IDS_COM_BODY_PASTE"), pasteIcon );
3757     AddPopupOption( OPTION_CLIPBOARD, GET_LOCALE_TEXT("IDS_COM_BODY_CLIPBOARD"), clipboardIcon, true );
3758   }
3759
3760   mPopUpPanel.Hide(false);
3761   ShowPopup();
3762 }
3763
3764 void TextInput::SetUpPopUpSelection()
3765 {
3766   ClearPopup();
3767
3768   // If no text exists then don't offer to select
3769   if ( !mStyledText.empty() )
3770   {
3771     Image selectIcon = Image::New( DEFAULT_ICON_SELECT );
3772     Image selectAllIcon = Image::New( DEFAULT_ICON_SELECT_ALL );
3773     AddPopupOption( OPTION_SELECT_WORD, GET_LOCALE_TEXT("IDS_COM_SK_SELECT"), selectIcon );
3774     AddPopupOption( OPTION_SELECT_ALL, GET_LOCALE_TEXT("IDS_COM_BODY_SELECT_ALL"), selectAllIcon );
3775   }
3776   // if clipboard has valid contents then offer paste option
3777   if( mClipboard.NumberOfItems() )
3778   {
3779     Image pasteIcon = Image::New( DEFAULT_ICON_PASTE );
3780     Image clipboardIcon = Image::New( DEFAULT_ICON_CLIPBOARD );
3781     AddPopupOption( OPTION_PASTE, GET_LOCALE_TEXT("IDS_COM_BODY_PASTE"), pasteIcon, true );
3782     AddPopupOption( OPTION_CLIPBOARD, GET_LOCALE_TEXT("IDS_COM_BODY_CLIPBOARD"), clipboardIcon, true );
3783   }
3784
3785   mPopUpPanel.Hide(false);
3786 }
3787
3788 bool TextInput::ReturnClosestIndex(const Vector2& source, std::size_t& closestIndex )
3789 {
3790   bool found = false;
3791   closestIndex = 0;
3792
3793   std::vector<Toolkit::TextView::CharacterLayoutInfo> matchedCharacters;
3794   bool lastRightToLeftChar(false);          /// RTL state of previous character encountered (character on the left of touch point)
3795   bool rightToLeftChar(false);              /// RTL state of current character encountered (character on the right of touch point)
3796   float glyphIntersection(0.0f);            /// Glyph intersection, the point between the two nearest characters touched.
3797
3798   const Vector2 sourceScrollOffset( source + mTextLayoutInfo.mScrollOffset );
3799
3800   if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
3801   {
3802     float closestYdifference = std::numeric_limits<float>::max();
3803     std::size_t lineOffset = 0;                  /// Keep track of position of the first character on the matched line of interest.
3804     std::size_t numberOfMatchedCharacters = 0;
3805
3806     // 1. Find closest character line to y part of source, create vector of all entries in that Y position
3807     // TODO: There should be an easy call to enumerate through each visual line, instead of each character on all visual lines.
3808
3809     for( std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), endIt = mTextLayoutInfo.mCharacterLayoutInfoTable.end(); it != endIt; ++it )
3810     {
3811       const Toolkit::TextView::CharacterLayoutInfo& info( *it );
3812       float baselinePosition = info.mPosition.y - info.mDescender;
3813
3814       if( info.mIsVisible )
3815       {
3816         // store difference between source y point and the y position of the current character
3817         float currentYdifference = fabsf( sourceScrollOffset.y - ( baselinePosition ) );
3818
3819         if(  currentYdifference < closestYdifference  )
3820         {
3821           // closest so far; store this difference and clear previous matchedCharacters as no longer closest
3822           lineOffset = it - mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3823           closestYdifference = currentYdifference;
3824           matchedCharacters.clear();
3825           numberOfMatchedCharacters = 0; // reset count
3826         }
3827
3828         // add all characters that are on the same Y axis (within the CHARACTER_THRESHOLD) to the matched array.
3829         if( fabsf( closestYdifference - currentYdifference )  < CHARACTER_THRESHOLD )
3830         {
3831           // ignore new line character.
3832           if( !info.mIsNewLineChar )
3833           {
3834             matchedCharacters.push_back( info );
3835             numberOfMatchedCharacters++;
3836           }
3837         }
3838       }
3839     } // End of loop checking each character's y position in the character layout table
3840
3841     // Check if last character is a newline, if it is
3842     // then need pretend there is an imaginary line afterwards,
3843     // and check if user is touching below previous line.
3844     const Toolkit::TextView::CharacterLayoutInfo& lastInfo( mTextLayoutInfo.mCharacterLayoutInfoTable[mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1] );
3845
3846     if( ( lastInfo.mIsVisible ) && ( lastInfo.mIsNewLineChar ) && ( sourceScrollOffset.y > lastInfo.mPosition.y ) )
3847     {
3848       closestIndex = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
3849     }
3850     else
3851     {
3852       std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator it = matchedCharacters.begin();
3853       std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator endIt = matchedCharacters.end();
3854
3855       bool matched( false );
3856
3857       // 2 Iterate through matching list of y positions and find closest matching X position.
3858       for( ; it != endIt; ++it )
3859       {
3860         const Toolkit::TextView::CharacterLayoutInfo& info( *it );
3861
3862         if( info.mIsVisible )
3863         {
3864           // stop when on left side of character's center.
3865           const float characterMidPointPosition = info.mPosition.x  + ( info.mSize.width * 0.5f ) ;
3866           if( sourceScrollOffset.x < characterMidPointPosition )
3867           {
3868             if(info.mIsRightToLeftCharacter)
3869             {
3870               rightToLeftChar = true;
3871             }
3872             glyphIntersection = info.mPosition.x;
3873             matched = true;
3874             break;
3875           }
3876
3877           lastRightToLeftChar = info.mIsRightToLeftCharacter;
3878         }
3879       }
3880
3881       if( it == endIt )
3882       {
3883         rightToLeftChar = lastRightToLeftChar;
3884       }
3885
3886       std::size_t matchCharacterIndex = it - matchedCharacters.begin();
3887       closestIndex = lineOffset + matchCharacterIndex;
3888
3889       mClosestCursorPositionEOL = false; // reset
3890       if ( it == endIt && !matched )
3891       {
3892         mClosestCursorPositionEOL = true; // Reached end of matched characters in closest line but no match so cursor should be after last character.
3893       }
3894
3895       // For RTL characters, need to adjust closestIndex by 1 (as the inequality above would be reverse)
3896       if( rightToLeftChar && lastRightToLeftChar )
3897       {
3898         --closestIndex; // (-1 = numeric_limits<std::size_t>::max())
3899       }
3900     }
3901   }
3902
3903   // closestIndex is the visual index, need to convert it to the logical index
3904   if( !mTextLayoutInfo.mCharacterVisualToLogicalMap.empty() )
3905   {
3906     if( closestIndex < mTextLayoutInfo.mCharacterVisualToLogicalMap.size() )
3907     {
3908       // Checks for situations where user is touching between LTR and RTL
3909       // characters. To identify if the user means the end of a LTR string
3910       // or the beginning of an RTL string, and vice versa.
3911       if( closestIndex > 0 )
3912       {
3913         if( rightToLeftChar && !lastRightToLeftChar )
3914         {
3915           // [LTR] [RTL]
3916           //   |..|..|
3917           //   AAA BBB
3918           // A: In this touch range, the user is indicating that they wish to place
3919           // the cursor at the end of the LTR text.
3920           // B: In this touch range, the user is indicating that they wish to place
3921           // the cursor at the end of the RTL text.
3922
3923           // Result of touching A area:
3924           // [.....LTR]|[RTL......]+
3925           //
3926           // |: primary cursor (for typing LTR chars)
3927           // +: secondary cursor (for typing RTL chars)
3928
3929           // Result of touching B area:
3930           // [.....LTR]+[RTL......]|
3931           //
3932           // |: primary cursor (for typing RTL chars)
3933           // +: secondary cursor (for typing LTR chars)
3934
3935           if( sourceScrollOffset.x < glyphIntersection )
3936           {
3937             --closestIndex;
3938           }
3939         }
3940         else if( !rightToLeftChar && lastRightToLeftChar )
3941         {
3942           if( sourceScrollOffset.x < glyphIntersection )
3943           {
3944             --closestIndex;
3945           }
3946         }
3947       }
3948
3949       closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap[closestIndex];
3950       // If user touched a left-side of RTL char, and the character on the left was an LTR then position logical cursor
3951       // one further ahead
3952       if( rightToLeftChar && !lastRightToLeftChar )
3953       {
3954         ++closestIndex;
3955       }
3956     }
3957     else if( closestIndex == numeric_limits<std::size_t>::max() ) // -1 RTL (after last arabic character on line)
3958     {
3959       closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap.size();
3960     }
3961     else if( mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterVisualToLogicalMap[ closestIndex - 1 ] ].mIsRightToLeftCharacter ) // size() LTR (after last european character on line)
3962     {
3963       closestIndex = 0;
3964     }
3965   }
3966
3967   return found;
3968 }
3969
3970 float TextInput::GetLineJustificationPosition() const
3971 {
3972   const Vector3& size = mDisplayedTextView.GetCurrentSize();
3973   Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
3974   float alignmentOffset = 0.f;
3975
3976   // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
3977   if( alignment & Toolkit::Alignment::HorizontalLeft )
3978   {
3979     alignmentOffset = 0.f;
3980   }
3981   else if( alignment & Toolkit::Alignment::HorizontalCenter )
3982   {
3983     alignmentOffset = 0.5f * ( size.width - mTextLayoutInfo.mTextSize.width );
3984   }
3985   else if( alignment & Toolkit::Alignment::HorizontalRight )
3986   {
3987     alignmentOffset = size.width - mTextLayoutInfo.mTextSize.width;
3988   }
3989
3990   Toolkit::TextView::LineJustification justification = mDisplayedTextView.GetLineJustification();
3991   float justificationOffset = 0.f;
3992
3993   switch( justification )
3994   {
3995     case Toolkit::TextView::Left:
3996     {
3997       justificationOffset = 0.f;
3998       break;
3999     }
4000     case Toolkit::TextView::Center:
4001     {
4002       justificationOffset = 0.5f * mTextLayoutInfo.mTextSize.width;
4003       break;
4004     }
4005     case Toolkit::TextView::Right:
4006     {
4007       justificationOffset = mTextLayoutInfo.mTextSize.width;
4008       break;
4009     }
4010     case Toolkit::TextView::Justified:
4011     {
4012       justificationOffset = 0.f;
4013       break;
4014     }
4015     default:
4016     {
4017       DALI_ASSERT_ALWAYS( false );
4018     }
4019   } // end switch
4020
4021   return alignmentOffset + justificationOffset;
4022 }
4023
4024 Vector3 TextInput::PositionCursorAfterWordWrap( std::size_t characterPosition ) const
4025 {
4026   /* Word wrap occurs automatically in TextView when the exceed policy moves a word to the next line when not enough space on current.
4027      A newline character is not inserted in this case */
4028
4029   DALI_ASSERT_DEBUG( !(characterPosition <= 0 ));
4030
4031   Vector3 cursorPosition;
4032
4033   Toolkit::TextView::CharacterLayoutInfo currentCharInfo;
4034
4035   if ( characterPosition == mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4036   {
4037     // end character so use
4038     currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1 ];
4039     cursorPosition = Vector3(currentCharInfo.mPosition.x + currentCharInfo.mSize.width, currentCharInfo.mPosition.y, currentCharInfo.mPosition.z) ;
4040   }
4041   else
4042   {
4043     currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4044   }
4045
4046   Toolkit::TextView::CharacterLayoutInfo previousCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1];
4047
4048   // If previous character on a different line then use current characters position
4049   if ( fabsf( (currentCharInfo.mPosition.y - currentCharInfo.mDescender )  - ( previousCharInfo.mPosition.y - previousCharInfo.mDescender) ) > Math::MACHINE_EPSILON_1000 )
4050   {
4051     if ( mClosestCursorPositionEOL )
4052     {
4053       cursorPosition = Vector3(previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z) ;
4054     }
4055     else
4056     {
4057       cursorPosition = Vector3(currentCharInfo.mPosition);
4058     }
4059   }
4060   else
4061   {
4062     // Previous character is on same line so use position of previous character plus it's width.
4063     cursorPosition = Vector3(previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z) ;
4064   }
4065
4066   return cursorPosition;
4067 }
4068
4069 Vector3 TextInput::GetActualPositionFromCharacterPosition(std::size_t characterPosition) const
4070 {
4071   bool direction(false);
4072   Vector3 alternatePosition;
4073   bool alternatePositionValid(false);
4074
4075   return GetActualPositionFromCharacterPosition( characterPosition, direction, alternatePosition, alternatePositionValid );
4076 }
4077
4078 Vector3 TextInput::GetActualPositionFromCharacterPosition(std::size_t characterPosition, bool& directionRTL, Vector3& alternatePosition, bool& alternatePositionValid ) const
4079 {
4080   Vector3 cursorPosition( 0.f, 0.f, 0.f );
4081
4082   alternatePositionValid = false;
4083   directionRTL = false;
4084
4085   if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
4086   {
4087     std::size_t visualCharacterPosition;
4088
4089     // When cursor is not at beginning, consider possibility of
4090     // showing 2 cursors. (whereas at beginning we only ever show one cursor)
4091     if(characterPosition > 0)
4092     {
4093       // Cursor position should be the end of the last character.
4094       // If the last character is LTR, then the end is on the right side of the glyph.
4095       // If the last character is RTL, then the end is on the left side of the glyph.
4096       visualCharacterPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition - 1 ];
4097
4098       if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + visualCharacterPosition ) ).mIsVisible )
4099       {
4100         visualCharacterPosition = FindVisibleCharacter( Left, visualCharacterPosition );
4101       }
4102
4103       Toolkit::TextView::CharacterLayoutInfo info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4104       if( ( visualCharacterPosition > 0 ) && info.mIsNewLineChar && !IsScrollEnabled() )
4105       {
4106         // Prevents the cursor to exceed the boundary if the last visible character is a 'new line character' and the scroll is not enabled.
4107         const Vector3& size = GetControlSize();
4108
4109         if( info.mPosition.y + info.mSize.height - mDisplayedTextView.GetLineHeightOffset() > size.height )
4110         {
4111           --visualCharacterPosition;
4112         }
4113         info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4114       }
4115
4116       if(!info.mIsNewLineChar)
4117       {
4118         cursorPosition = PositionCursorAfterWordWrap( characterPosition ); // Get position of cursor/handles taking in account auto word wrap.
4119       }
4120       else
4121       {
4122         // When cursor points to first character on new line, position cursor at the start of this glyph.
4123         if(characterPosition < mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4124         {
4125           std::size_t visualCharacterNextPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition ];
4126           const Toolkit::TextView::CharacterLayoutInfo& infoNext = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterNextPosition ];
4127           const float start( infoNext.mIsRightToLeftCharacter ? infoNext.mSize.width : 0.0f );
4128
4129           cursorPosition.x = infoNext.mPosition.x + start;
4130           cursorPosition.y = infoNext.mPosition.y;
4131         }
4132         else
4133         {
4134           // If cursor points to the end of text, then can only position
4135           // cursor where the new line starts based on the line-justification position.
4136           cursorPosition.x = GetLineJustificationPosition();
4137
4138           if(characterPosition == mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4139           {
4140             // If this is after the last character, then we can assume that the new cursor
4141             // should be exactly one row below the current row.
4142
4143             const Size rowRect(GetRowRectFromCharacterPosition(characterPosition - 1));
4144             cursorPosition.y = info.mPosition.y + rowRect.height;
4145           }
4146           else
4147           {
4148             // If this is not after last character, then we can use this row's height.
4149             // should be exactly one row below the current row.
4150
4151             const Size rowRect(GetRowRectFromCharacterPosition(characterPosition));
4152             cursorPosition.y = info.mPosition.y + rowRect.height;
4153           }
4154         }
4155       }
4156
4157       directionRTL = info.mIsRightToLeftCharacter;
4158
4159       // 1. When the cursor is neither at the beginning or the end,
4160       // we can show multiple cursors under situations when the cursor is
4161       // between RTL and LTR text...
4162       if(characterPosition != mTextLayoutInfo.mCharacterLogicalToVisualMap.size())
4163       {
4164         std::size_t visualCharacterAltPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[characterPosition] - 1;
4165
4166         DALI_ASSERT_ALWAYS(visualCharacterAltPosition < mTextLayoutInfo.mCharacterLayoutInfoTable.size());
4167         const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterAltPosition ];
4168
4169         if(!info.mIsRightToLeftCharacter && infoAlt.mIsRightToLeftCharacter)
4170         {
4171           // Stuation occurs when cursor is at the end of English text (LTR) and beginning of Arabic (RTL)
4172           // Text:     [...LTR...]|[...RTL...]
4173           // Cursor pos:          ^
4174           // Alternate cursor pos:            ^
4175           // In which case we need to display an alternate cursor for the RTL text.
4176
4177           alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width;
4178           alternatePosition.y = infoAlt.mPosition.y;
4179           alternatePositionValid = true;
4180         }
4181         else if(info.mIsRightToLeftCharacter && !infoAlt.mIsRightToLeftCharacter)
4182         {
4183           // Situation occurs when cursor is at end of the Arabic text (LTR) and beginning of English (RTL)
4184           // Text:           |[...RTL...] [...LTR....]
4185           // Cursor pos:     ^
4186           // Alternate cursor pos:       ^
4187           // In which case we need to display an alternate cursor for the RTL text.
4188
4189           alternatePosition.x = infoAlt.mPosition.x;
4190           alternatePosition.y = infoAlt.mPosition.y;
4191           alternatePositionValid = true;
4192         }
4193       }
4194       else
4195       {
4196         // 2. When the cursor is at the end of the text,
4197         // and we have multi-directional text,
4198         // we can also consider showing mulitple cursors.
4199         // The rule here is:
4200         // If first and last characters on row are different
4201         // Directions, then two cursors need to be displayed.
4202
4203         // Get first logical glyph on row
4204         std::size_t startCharacterPosition = GetRowStartFromCharacterPosition( characterPosition - 1 );
4205
4206         std::size_t visualCharacterStartPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ startCharacterPosition ];
4207         const Toolkit::TextView::CharacterLayoutInfo& infoStart= mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterStartPosition ];
4208
4209         if(info.mIsRightToLeftCharacter && !infoStart.mIsRightToLeftCharacter)
4210         {
4211           // For text Starting as LTR and ending as RTL. End cursor position is as follows:
4212           // Text:     [...LTR...]|[...RTL...]
4213           // Cursor pos:          ^
4214           // Alternate cursor pos:            ^
4215           // In which case we need to display an alternate cursor for the RTL text, this cursor
4216           // should be at the end of the given line.
4217
4218           const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1 ];
4219           alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width;
4220           alternatePosition.y = infoAlt.mPosition.y;
4221           alternatePositionValid = true;
4222         }
4223         else if(!info.mIsRightToLeftCharacter && infoStart.mIsRightToLeftCharacter) // starting RTL
4224         {
4225           // For text Starting as RTL and ending as LTR. End cursor position is as follows:
4226           // Text:           |[...RTL...] [...LTR....]
4227           // Cursor pos:     ^
4228           // Alternate cursor pos:       ^
4229           // In which case we need to display an alternate cursor for the RTL text.
4230
4231           const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ startCharacterPosition ];
4232           alternatePosition.x = infoAlt.mPosition.x;
4233           alternatePosition.y = infoAlt.mPosition.y;
4234           alternatePositionValid = true;
4235         }
4236       }
4237     } // characterPosition > 0
4238     else if(characterPosition == 0)
4239     {
4240       // When the cursor position is at the beginning, it should be at the start of the current character.
4241       // If the current character is LTR, then the start is on the right side of the glyph.
4242       // If the current character is RTL, then the start is on the left side of the glyph.
4243       visualCharacterPosition = mTextLayoutInfo.mCharacterLogicalToVisualMap[ characterPosition ];
4244
4245       if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + visualCharacterPosition ) ).mIsVisible )
4246       {
4247          visualCharacterPosition = FindVisibleCharacter( Right, visualCharacterPosition );
4248       }
4249
4250       const Toolkit::TextView::CharacterLayoutInfo& info = mTextLayoutInfo.mCharacterLayoutInfoTable[ visualCharacterPosition ];
4251       const float start(info.mIsRightToLeftCharacter ? info.mSize.width : 0.0f);
4252
4253       cursorPosition.x = info.mPosition.x + start;
4254       cursorPosition.y = info.mPosition.y;
4255       directionRTL = info.mIsRightToLeftCharacter;
4256     }
4257   }
4258   else
4259   {
4260     // If the character table is void, place the cursor accordingly the text alignment.
4261     const Vector3& size = GetControlSize();
4262
4263     Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4264     float alignmentOffset = 0.f;
4265
4266     // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4267     if( alignment & Toolkit::Alignment::HorizontalLeft )
4268     {
4269       alignmentOffset = 0.f;
4270     }
4271     else if( alignment & Toolkit::Alignment::HorizontalCenter )
4272     {
4273       alignmentOffset = 0.5f * ( size.width );
4274     }
4275     else if( alignment & Toolkit::Alignment::HorizontalRight )
4276     {
4277       alignmentOffset = size.width;
4278     }
4279
4280     // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4281     cursorPosition.x = alignmentOffset;
4282
4283     // Work out cursor 'y' position when there are any character accordingly with the text view alignment settings.
4284     if( alignment & Toolkit::Alignment::VerticalTop )
4285     {
4286       cursorPosition.y = mLineHeight;
4287     }
4288     else if( alignment & Toolkit::Alignment::VerticalCenter )
4289     {
4290       cursorPosition.y = 0.5f * ( size.height + mLineHeight );
4291     }
4292     else if( alignment & Toolkit::Alignment::VerticalBottom )
4293     {
4294       cursorPosition.y = size.height;
4295     }
4296   }
4297
4298   cursorPosition.x -= mTextLayoutInfo.mScrollOffset.x;
4299   cursorPosition.y -= mTextLayoutInfo.mScrollOffset.y;
4300   if( alternatePositionValid )
4301   {
4302     alternatePosition.x -= mTextLayoutInfo.mScrollOffset.x;
4303     alternatePosition.y -= mTextLayoutInfo.mScrollOffset.y;
4304   }
4305
4306   return cursorPosition;
4307 }
4308
4309 std::size_t TextInput::GetRowStartFromCharacterPosition(std::size_t logicalPosition) const
4310 {
4311   // scan string from current position to beginning of current line to note direction of line
4312   while(logicalPosition)
4313   {
4314     logicalPosition--;
4315     std::size_t visualPosition = GetVisualPosition(logicalPosition);
4316     if(mTextLayoutInfo.mCharacterLayoutInfoTable[visualPosition].mIsNewLineChar)
4317     {
4318       logicalPosition++;
4319       break;
4320     }
4321   }
4322
4323   return logicalPosition;
4324 }
4325
4326 Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition) const
4327 {
4328   Vector2 min, max;
4329
4330   return GetRowRectFromCharacterPosition( characterPosition, min, max );
4331 }
4332
4333 Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition, Vector2& min, Vector2& max) const
4334 {
4335   // if we have no text content, then return position 0,0 with width 0, and height the same as cursor height.
4336   if( mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4337   {
4338     min = Vector2::ZERO;
4339     max = Vector2(0.0f, mLineHeight);
4340     return max;
4341   }
4342
4343   // TODO: This info should be readily available from text-view, we should not have to search hard for it.
4344   Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator begin = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
4345   Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
4346
4347   // If cursor is pointing to end of line, then start from last character.
4348   characterPosition = std::min( characterPosition, static_cast<std::size_t>(mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1) );
4349
4350   Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4351
4352   // 0. Find first a visible character. Draw a cursor beyound text-input bounds is not wanted.
4353   if( !it->mIsVisible )
4354   {
4355     characterPosition = FindVisibleCharacter( Left, characterPosition );
4356     it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4357   }
4358
4359   // Scan characters left and right of cursor, stopping when end of line/string reached or
4360   // y position greater than threshold of reference line.
4361
4362   // 1. scan left until we reach the beginning or a different line.
4363   Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator validCharIt = it;
4364   float referenceLine = it->mPosition.y - CHARACTER_THRESHOLD;
4365   // min-x position is the left-most char's left (x)
4366   // max-x position is the right-most char's right (x)
4367   // min-y position is the minimum of all character's top (y)
4368   // max-y position is the maximum of all character's bottom (y+height)
4369   min.y = validCharIt->mPosition.y;
4370   max.y = validCharIt->mPosition.y + validCharIt->mSize.y;
4371
4372   while(true)
4373   {
4374     validCharIt = it;
4375     min.y = std::min(min.y, validCharIt->mPosition.y);
4376     max.y = std::max(max.y, validCharIt->mPosition.y + validCharIt->mSize.y);
4377
4378     if(it == begin)
4379     {
4380       break;
4381     }
4382
4383     --it;
4384
4385     if( (it->mPosition.y < referenceLine) ||
4386         (it->mIsNewLineChar) ||
4387         (!it->mIsVisible) )
4388     {
4389       break;
4390     }
4391   }
4392
4393   // info refers to the first character on this line.
4394   min.x = validCharIt->mPosition.x;
4395
4396   // 2. scan right until we reach end or a different line
4397   it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition;
4398   referenceLine = it->mPosition.y + CHARACTER_THRESHOLD;
4399
4400   while(it != end)
4401   {
4402     if( (it->mPosition.y > referenceLine) ||
4403         (it->mIsNewLineChar) ||
4404         (!it->mIsVisible) )
4405     {
4406       break;
4407     }
4408
4409     validCharIt = it;
4410     min.y = std::min(min.y, validCharIt->mPosition.y);
4411     max.y = std::max(max.y, validCharIt->mPosition.y + validCharIt->mSize.y);
4412
4413     ++it;
4414   }
4415
4416   DALI_ASSERT_DEBUG ( validCharIt != end  && "validCharIt invalid")
4417
4418   if ( validCharIt != end )
4419   {
4420     // info refers to the last character on this line.
4421     max.x = validCharIt->mPosition.x + validCharIt->mSize.x;
4422   }
4423
4424   return Size( max.x - min.x, max.y - min.y );
4425 }
4426
4427 bool TextInput::WasTouchedCheck( const Actor& touchedActor ) const
4428 {
4429   Actor popUpPanel = mPopUpPanel.GetRootActor();
4430
4431   if ( ( touchedActor == Self() ) || ( touchedActor == popUpPanel ) )
4432   {
4433     return true;
4434   }
4435   else
4436   {
4437     Dali::Actor parent( touchedActor.GetParent() );
4438
4439     if ( parent )
4440     {
4441       return WasTouchedCheck( parent );
4442     }
4443   }
4444
4445   return false;
4446 }
4447
4448 void TextInput::StartMonitoringStageForTouch()
4449 {
4450   Stage stage = Stage::GetCurrent();
4451   stage.TouchedSignal().Connect( this, &TextInput::OnStageTouched );
4452 }
4453
4454 void TextInput::EndMonitoringStageForTouch()
4455 {
4456   Stage stage = Stage::GetCurrent();
4457   stage.TouchedSignal().Disconnect( this, &TextInput::OnStageTouched );
4458 }
4459
4460 void TextInput::OnStageTouched(const TouchEvent& event)
4461 {
4462   if( event.GetPointCount() > 0 )
4463   {
4464     if ( TouchPoint::Down == event.GetPoint(0).state )
4465     {
4466       const Actor touchedActor(event.GetPoint(0).hitActor);
4467
4468       bool popUpShown( false );
4469
4470       if ( ( mPopUpPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopUpPanel.GetState() == TextInputPopup::StateShown ) )
4471       {
4472         popUpShown = true;
4473       }
4474
4475       bool textInputTouched = (touchedActor && WasTouchedCheck( touchedActor ));
4476
4477       if ( ( mHighlightMeshActor || popUpShown ) && !textInputTouched )
4478       {
4479         EndMonitoringStageForTouch();
4480         HidePopup( true, false );
4481       }
4482
4483       if ( ( IsGrabHandleEnabled() && mGrabHandle  ) && !textInputTouched )
4484       {
4485         EndMonitoringStageForTouch();
4486         ShowGrabHandleAndSetVisibility( false );
4487       }
4488     }
4489   }
4490 }
4491
4492 void TextInput::SelectText(std::size_t start, std::size_t end)
4493 {
4494   DALI_LOG_INFO(gLogFilter, Debug::General, "SelectText mEditModeActive[%s] grabHandle[%s] start[%u] end[%u] size[%u]\n", mEditModeActive?"true":"false",
4495                                                                                                                           IsGrabHandleEnabled()?"true":"false",
4496                                                                                                                           start, end, mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
4497   DALI_ASSERT_ALWAYS( start <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText start out of max range" );
4498   DALI_ASSERT_ALWAYS( end <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText end out of max range" );
4499
4500   StartMonitoringStageForTouch();
4501
4502   if ( mEditModeActive ) // Only allow text selection when in edit mode
4503   {
4504     // When replacing highlighted text keyboard should ignore current word at cursor hence notify keyboard that the cursor is at the start of the highlight.
4505     mSelectingText = true;
4506
4507     mCursorPosition = std::min( start, end ); // Set cursor position to start of highlighted text.
4508
4509     ImfManager imfManager = ImfManager::Get();
4510     if ( imfManager )
4511     {
4512       imfManager.SetCursorPosition ( mCursorPosition );
4513       imfManager.SetSurroundingText( GetText() );
4514       imfManager.NotifyCursorPosition();
4515     }
4516     // 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.
4517
4518     // Hide grab handle when selecting.
4519     ShowGrabHandleAndSetVisibility( false );
4520
4521     if( start != end )  // something to select
4522     {
4523       SetCursorVisibility( false );
4524       StopCursorBlinkTimer();
4525
4526       CreateSelectionHandles(start, end);
4527       UpdateHighlight();
4528
4529       const TextStyle oldInputStyle( mInputStyle );
4530       mInputStyle = GetStyleAt( mCursorPosition ); // Inherit style from selected position.
4531
4532       if( oldInputStyle != mInputStyle )
4533       {
4534         // Updates the line height accordingly with the input style.
4535         UpdateLineHeight();
4536
4537         EmitStyleChangedSignal();
4538       }
4539
4540       HidePopup();
4541     }
4542
4543     mSelectingText = false;
4544   }
4545 }
4546
4547 MarkupProcessor::StyledTextArray TextInput::GetSelectedText()
4548 {
4549   MarkupProcessor::StyledTextArray currentSelectedText;
4550
4551   if ( IsTextSelected() )
4552   {
4553     MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4554     MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4555
4556     for(; it != end; ++it)
4557     {
4558       MarkupProcessor::StyledText& styledText( *it );
4559       currentSelectedText.push_back( styledText );
4560     }
4561   }
4562   return currentSelectedText;
4563 }
4564
4565 void TextInput::ApplyStyleToRange(const TextStyle& style, const TextStyle::Mask mask, const std::size_t begin, const std::size_t end)
4566 {
4567   const std::size_t beginIndex = std::min( begin, end );
4568   const std::size_t endIndex = std::max( begin, end );
4569
4570   // Apply the style
4571   MarkupProcessor::SetTextStyleToRange( mStyledText, style, mask, beginIndex, endIndex );
4572
4573   // Create a styled text array used to replace the text into the text-view.
4574   MarkupProcessor::StyledTextArray text;
4575   text.insert( text.begin(), mStyledText.begin() + beginIndex, mStyledText.begin() + endIndex + 1 );
4576
4577   mDisplayedTextView.ReplaceTextFromTo( beginIndex, ( endIndex - beginIndex ) + 1, text );
4578   GetTextLayoutInfo();
4579
4580   if( IsScrollEnabled() )
4581   {
4582     // Need to set the scroll position as the text's size may have changed.
4583     ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
4584   }
4585
4586   ShowGrabHandleAndSetVisibility( false );
4587
4588   DrawCursor();
4589
4590   UpdateHighlight();
4591
4592   // Set Handle positioning as the new style may have repositioned the characters.
4593   SetSelectionHandlePosition(HandleOne);
4594   SetSelectionHandlePosition(HandleTwo);
4595 }
4596
4597 void TextInput::KeyboardStatusChanged(bool keyboardShown)
4598 {
4599   // Just hide the grab handle when keyboard is hidden.
4600   if (!keyboardShown )
4601   {
4602     ShowGrabHandleAndSetVisibility( false );
4603
4604     // If the keyboard is not now being shown, then hide the popup panel
4605     mPopUpPanel.Hide( true );
4606   }
4607 }
4608
4609 // Removes highlight and resumes edit mode state
4610 void TextInput::RemoveHighlight()
4611 {
4612   DALI_LOG_INFO(gLogFilter, Debug::General, "RemoveHighlight\n");
4613
4614   if ( mHighlightMeshActor )
4615   {
4616     if ( mSelectionHandleOne )
4617     {
4618       mActiveLayer.Remove( mSelectionHandleOne );
4619       mSelectionHandleOne.Reset();
4620       mSelectionHandleOneOffset.x = 0.0f;
4621     }
4622     if ( mSelectionHandleTwo )
4623     {
4624       mActiveLayer.Remove( mSelectionHandleTwo );
4625       mSelectionHandleTwo.Reset();
4626       mSelectionHandleTwoOffset.x = 0.0f;
4627     }
4628
4629     mNewHighlightInfo.mQuadList.clear();
4630
4631     Self().Remove( mHighlightMeshActor );
4632
4633     SetCursorVisibility( true );
4634     StartCursorBlinkTimer();
4635
4636     mHighlightMeshActor.Reset();
4637     // NOTE: We cannot dereference mHighlightMesh, due
4638     // to a bug in how the scene-graph MeshRenderer uses the Mesh data incorrectly.
4639
4640     HidePopup();
4641   }
4642
4643   mSelectionHandleOnePosition = 0;
4644   mSelectionHandleTwoPosition = 0;
4645 }
4646
4647 void TextInput::CreateHighlight()
4648 {
4649   if ( !mHighlightMeshActor )
4650   {
4651     mMeshData = MeshData( );
4652     mMeshData.SetHasNormals( true );
4653
4654     mCustomMaterial = Material::New("CustomMaterial");
4655     mCustomMaterial.SetDiffuseColor( LIGHTBLUE );
4656
4657     mMeshData.SetMaterial( mCustomMaterial );
4658
4659     mHighlightMesh = Mesh::New( mMeshData );
4660
4661     mHighlightMeshActor = MeshActor::New( mHighlightMesh );
4662     mHighlightMeshActor.SetName( "HighlightMeshActor" );
4663     mHighlightMeshActor.SetInheritShaderEffect( false );
4664     mHighlightMeshActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
4665     mHighlightMeshActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
4666     mHighlightMeshActor.SetPosition( 0.0f, 0.0f, DISPLAYED_HIGHLIGHT_Z_OFFSET );
4667     mHighlightMeshActor.SetAffectedByLighting(false);
4668
4669     Self().Add(mHighlightMeshActor);
4670   }
4671 }
4672
4673
4674 bool TextInput::CopySelectedTextToClipboard()
4675 {
4676   mCurrentCopySelecton.clear();
4677
4678   mCurrentCopySelecton = GetSelectedText();
4679
4680   std::string stringToStore;
4681
4682   /* Create a StyledTextArray from the selected region so can use the MarkUpProcessor to produce
4683    * a marked up string.
4684    */
4685   MarkupProcessor::StyledTextArray selectedText(mCurrentCopySelecton.begin(),mCurrentCopySelecton.end());
4686   MarkupProcessor::GetPlainString( selectedText, stringToStore );
4687   bool success = mClipboard.SetItem( stringToStore );
4688   return success;
4689 }
4690
4691 void TextInput::PasteText( const Text& text )
4692 {
4693   // Update Flag, indicates whether to update the text-input contents or not.
4694   // Any key stroke that results in a visual change of the text-input should
4695   // set this flag to true.
4696   bool update = false;
4697   if( mHighlightMeshActor )
4698   {
4699     /* if highlighted, delete entire text, and position cursor at start of deleted text. */
4700     mCursorPosition = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4701
4702     ImfManager imfManager = ImfManager::Get();
4703     if ( imfManager )
4704     {
4705       imfManager.SetCursorPosition( mCursorPosition );
4706       imfManager.NotifyCursorPosition();
4707     }
4708     DeleteHighlightedText( true );
4709     update = true;
4710   }
4711
4712   bool textExceedsMaximunNumberOfCharacters = false;
4713   bool textExceedsBoundary = false;
4714
4715   std::size_t insertedStringLength = DoInsertAt( text, mCursorPosition, 0, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
4716
4717   mCursorPosition += insertedStringLength;
4718   ImfManager imfManager = ImfManager::Get();
4719   if ( imfManager )
4720   {
4721     imfManager.SetCursorPosition ( mCursorPosition );
4722     imfManager.NotifyCursorPosition();
4723   }
4724
4725   update = update || ( insertedStringLength > 0 );
4726   if( update )
4727   {
4728     CursorUpdate();
4729   }
4730
4731   if( insertedStringLength < text.GetLength() )
4732   {
4733     EmitMaxInputCharactersReachedSignal();
4734   }
4735
4736   if( textExceedsBoundary )
4737   {
4738     EmitInputTextExceedsBoundariesSignal();
4739   }
4740 }
4741
4742 void TextInput::SetTextDirection()
4743 {
4744   // Put the cursor to the right if we are empty and an RTL language is being used.
4745   if ( mStyledText.empty() )
4746   {
4747     VirtualKeyboard::TextDirection direction( VirtualKeyboard::GetTextDirection() );
4748
4749     // Get the current text alignment preserving the vertical alignment. Also preserve the horizontal center
4750     // alignment as we do not want to set the text direction if we've been asked to be in the center.
4751     //
4752     // TODO: Should split SetTextAlignment into two APIs to better handle this (sometimes apps just want to
4753     //       set vertical alignment but are being forced to set the horizontal alignment as well with the
4754     //       current API.
4755     int alignment( mDisplayedTextView.GetTextAlignment() &
4756                   ( Toolkit::Alignment::VerticalTop |
4757                     Toolkit::Alignment::VerticalCenter |
4758                     Toolkit::Alignment::VerticalBottom |
4759                     Toolkit::Alignment::HorizontalCenter ) );
4760     Toolkit::TextView::LineJustification justification( mDisplayedTextView.GetLineJustification() );
4761
4762     // If our alignment is in the center, then do not change.
4763     if ( !( alignment & Toolkit::Alignment::HorizontalCenter ) )
4764     {
4765       alignment |= ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight;
4766     }
4767
4768     // If our justification is in the center, then do not change.
4769     if ( justification != Toolkit::TextView::Center )
4770     {
4771       justification = ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::TextView::Left : Toolkit::TextView::Right;
4772     }
4773
4774     mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(alignment) );
4775     mDisplayedTextView.SetLineJustification( justification );
4776   }
4777 }
4778
4779 void TextInput::UpdateLineHeight()
4780 {
4781   Dali::Font font = Dali::Font::New( FontParameters( mInputStyle.GetFontName(), mInputStyle.GetFontStyle(), mInputStyle.GetFontPointSize() ) );
4782   mLineHeight = font.GetLineHeight();
4783
4784   // 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.
4785
4786   const bool shrink = mDisplayedTextView && ( Toolkit::TextView::ShrinkToFit == mDisplayedTextView.GetHeightExceedPolicy() ) && mStyledText.empty();
4787
4788   if( !mExceedEnabled || shrink )
4789   {
4790     mLineHeight = std::min( mLineHeight, GetControlSize().height );
4791   }
4792 }
4793
4794 std::size_t TextInput::FindVisibleCharacter( const FindVisibleCharacterDirection direction , const std::size_t cursorPosition ) const
4795 {
4796   std::size_t position = 0;
4797
4798   const std::size_t tableSize = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
4799
4800   switch( direction )
4801   {
4802     case Left:
4803     {
4804       position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4805
4806       if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1 : position ) ) ).mIsVisible )
4807       {
4808         position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4809       }
4810       break;
4811     }
4812     case Right:
4813     {
4814       position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4815       if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1 : position ) ) ).mIsVisible )
4816       {
4817         position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
4818       }
4819       break;
4820     }
4821     case ByEnd:
4822     {
4823       position = FindVisibleCharacterLeft( 0, mTextLayoutInfo.mCharacterLayoutInfoTable );
4824       break;
4825     }
4826     default:
4827     {
4828       DALI_ASSERT_ALWAYS( !"TextInput::FindVisibleCharacter() Unknown direction." );
4829     }
4830   }
4831
4832   return position;
4833 }
4834
4835 void TextInput::SetSortModifier( float depthOffset )
4836 {
4837   if(mDisplayedTextView)
4838   {
4839     mDisplayedTextView.SetSortModifier(depthOffset);
4840   }
4841 }
4842
4843 void TextInput::SetSnapshotModeEnabled( bool enable )
4844 {
4845   if(mDisplayedTextView)
4846   {
4847     mDisplayedTextView.SetSnapshotModeEnabled( enable );
4848   }
4849 }
4850
4851 bool TextInput::IsSnapshotModeEnabled() const
4852 {
4853   bool snapshotEnabled = false;
4854
4855   if(mDisplayedTextView)
4856   {
4857     snapshotEnabled = mDisplayedTextView.IsSnapshotModeEnabled();
4858   }
4859
4860   return snapshotEnabled;
4861 }
4862
4863 void TextInput::SetMarkupProcessingEnabled( bool enable )
4864 {
4865   mMarkUpEnabled = enable;
4866 }
4867
4868 bool TextInput::IsMarkupProcessingEnabled() const
4869 {
4870   return mMarkUpEnabled;
4871 }
4872
4873 void TextInput::SetScrollEnabled( bool enable )
4874 {
4875   if( mDisplayedTextView )
4876   {
4877     mDisplayedTextView.SetScrollEnabled( enable );
4878   }
4879
4880   if( !enable )
4881   {
4882     // Don't set cursor's and handle's visibility to false if they are outside the
4883     // boundaries of the text-input.
4884     mIsCursorInScrollArea = true;
4885     mIsGrabHandleInScrollArea = true;
4886     if( mSelectionHandleOne && mSelectionHandleTwo )
4887     {
4888       mSelectionHandleOne.SetVisible( true );
4889       mSelectionHandleTwo.SetVisible( true );
4890
4891       if( mHighlightMeshActor )
4892       {
4893         mHighlightMeshActor.SetVisible( true );
4894       }
4895     }
4896   }
4897 }
4898
4899 bool TextInput::IsScrollEnabled() const
4900 {
4901   bool scrollEnabled = false;
4902
4903   if( mDisplayedTextView )
4904   {
4905     scrollEnabled = mDisplayedTextView.IsScrollEnabled();
4906   }
4907
4908   return scrollEnabled;
4909 }
4910
4911 void TextInput::SetScrollPosition( const Vector2& position )
4912 {
4913   if( mDisplayedTextView )
4914   {
4915     mDisplayedTextView.SetScrollPosition( position );
4916   }
4917 }
4918
4919 Vector2 TextInput::GetScrollPosition() const
4920 {
4921   Vector2 scrollPosition;
4922
4923   if( mDisplayedTextView )
4924   {
4925     scrollPosition = mDisplayedTextView.GetScrollPosition();
4926   }
4927
4928   return scrollPosition;
4929 }
4930
4931 std::size_t TextInput::DoInsertAt( const Text& text, const std::size_t position, const std::size_t numberOfCharactersToReplace, bool& textExceedsMaximunNumberOfCharacters, bool& textExceedsBoundary )
4932 {
4933   // determine number of characters that we can write to style text buffer, this is the insertStringLength
4934   std::size_t insertedStringLength = std::min( text.GetLength(), mMaxStringLength - mStyledText.size() );
4935   textExceedsMaximunNumberOfCharacters = insertedStringLength < text.GetLength();
4936
4937   // Add style to the new input text.
4938   MarkupProcessor::StyledTextArray textToInsert;
4939   for( std::size_t i = 0; i < insertedStringLength; ++i )
4940   {
4941     const MarkupProcessor::StyledText newStyledCharacter( text[i], mInputStyle );
4942     textToInsert.push_back( newStyledCharacter );
4943   }
4944
4945   //Insert text to the TextView.
4946   const bool emptyTextView = mStyledText.empty();
4947   if( emptyTextView && mPlaceHolderSet )
4948   {
4949     // There is no text set so call to TextView::SetText() is needed in order to clear the placeholder text.
4950     mDisplayedTextView.SetText( textToInsert );
4951   }
4952   else
4953   {
4954     if( 0 == numberOfCharactersToReplace )
4955     {
4956       mDisplayedTextView.InsertTextAt( position, textToInsert );
4957     }
4958     else
4959     {
4960       mDisplayedTextView.ReplaceTextFromTo( position, numberOfCharactersToReplace, textToInsert );
4961     }
4962   }
4963   mPlaceHolderSet = false;
4964
4965   if( textToInsert.empty() )
4966   {
4967     // If no text has been inserted, GetTextLayoutInfo() need to be called to check whether mStyledText has some text.
4968     GetTextLayoutInfo();
4969   }
4970   else
4971   {
4972     // GetTextLayoutInfo() can't be used here as mStyledText is not updated yet.
4973     mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
4974   }
4975
4976   textExceedsBoundary = false;
4977
4978   if( !mExceedEnabled )
4979   {
4980     const Vector3& size = GetControlSize();
4981
4982     if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
4983     {
4984       // If new text does not fit within TextView
4985       mDisplayedTextView.RemoveTextFrom( position, insertedStringLength );
4986       // previously inserted text has been removed. Call GetTextLayoutInfo() to check whether mStyledText has some text.
4987       GetTextLayoutInfo();
4988       textExceedsBoundary = true;
4989       insertedStringLength = 0;
4990     }
4991
4992     if( textExceedsBoundary )
4993     {
4994       // Add the part of the text which fits on the text-input.
4995
4996       // Split the text which doesn't fit in two halves.
4997       MarkupProcessor::StyledTextArray firstHalf;
4998       MarkupProcessor::StyledTextArray secondHalf;
4999       SplitText( textToInsert, firstHalf, secondHalf );
5000
5001       // Clear text. This text will be filled with the text inserted.
5002       textToInsert.clear();
5003
5004       // Where to insert the text.
5005       std::size_t positionToInsert = position;
5006
5007       bool end = text.GetLength() <= 1;
5008       while( !end )
5009       {
5010         // Insert text and check ...
5011         const std::size_t textLength = firstHalf.size();
5012         mDisplayedTextView.InsertTextAt( positionToInsert, firstHalf );
5013         mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5014
5015         if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5016         {
5017           // Inserted text doesn't fit.
5018
5019           // Remove inserted text
5020           mDisplayedTextView.RemoveTextFrom( positionToInsert, textLength );
5021           mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5022
5023           // The iteration finishes when only one character doesn't fit.
5024           end = textLength <= 1;
5025
5026           if( !end )
5027           {
5028             // Prepare next two halves for next iteration.
5029             MarkupProcessor::StyledTextArray copyText = firstHalf;
5030             SplitText( copyText, firstHalf, secondHalf );
5031           }
5032         }
5033         else
5034         {
5035           // Text fits.
5036
5037           // store text to be inserted in mStyledText.
5038           textToInsert.insert( textToInsert.end(), firstHalf.begin(), firstHalf.end() );
5039
5040           // Increase the inserted characters counter.
5041           insertedStringLength += textLength;
5042
5043           // Prepare next two halves for next iteration.
5044           MarkupProcessor::StyledTextArray copyText = secondHalf;
5045           SplitText( copyText, firstHalf, secondHalf );
5046
5047           // Update where next text has to be inserted
5048           positionToInsert += textLength;
5049         }
5050       }
5051     }
5052   }
5053
5054   if( textToInsert.empty() && emptyTextView )
5055   {
5056     // No character has been added and the text-view was empty.
5057     // Set the placeholder text.
5058     mDisplayedTextView.SetText( mStyledPlaceHolderText );
5059     mPlaceHolderSet = true;
5060   }
5061   else
5062   {
5063     MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + position;
5064     mStyledText.insert( it, textToInsert.begin(), textToInsert.end() );
5065     mPlaceHolderSet = false;
5066   }
5067
5068   return insertedStringLength;
5069 }
5070
5071 void TextInput::GetTextLayoutInfo()
5072 {
5073   if( mStyledText.empty() )
5074   {
5075     // The text-input has no text, clear the text-view's layout info.
5076     mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5077   }
5078   else
5079   {
5080     if( mDisplayedTextView )
5081     {
5082       mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5083     }
5084     else
5085     {
5086       // There is no text-view.
5087       mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5088     }
5089   }
5090 }
5091
5092 void TextInput::EmitStyleChangedSignal()
5093 {
5094   // emit signal if input style changes.
5095
5096   Toolkit::TextInput handle( GetOwner() );
5097   mStyleChangedSignalV2.Emit( handle, mInputStyle );
5098 }
5099
5100 void TextInput::EmitMaxInputCharactersReachedSignal()
5101 {
5102   // emit signal if max characters is reached during text input.
5103   DALI_LOG_INFO(gLogFilter, Debug::General, "EmitMaxInputCharactersReachedSignal \n");
5104
5105   Toolkit::TextInput handle( GetOwner() );
5106   mMaxInputCharactersReachedSignalV2.Emit( handle );
5107 }
5108
5109 void TextInput::EmitInputTextExceedsBoundariesSignal()
5110 {
5111   // Emit a signal when the input text exceeds the boundaries of the text input.
5112
5113   Toolkit::TextInput handle( GetOwner() );
5114   mInputTextExceedBoundariesSignalV2.Emit( handle );
5115 }
5116
5117 } // namespace Internal
5118
5119 } // namespace Toolkit
5120
5121 } // namespace Dali