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