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