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