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