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