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