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