Update toolkit with ResourceImage/Image split
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / text-input / text-input-impl.cpp
1 /*
2  * Copyright (c) 2014 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the 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 #include <dali/public-api/images/resource-image.h>
34
35 // INTERNAL INCLUDES
36 #include <dali-toolkit/internal/controls/text-view/text-processor.h>
37 #include <dali-toolkit/public-api/controls/buttons/push-button.h>
38 #include <dali-toolkit/public-api/controls/alignment/alignment.h>
39 #include <dali-toolkit/public-api/controls/default-controls/solid-color-actor.h>
40
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::InputSignalType& TextInput::InputStartedSignal()
543 {
544   return mInputStartedSignal;
545 }
546
547 Toolkit::TextInput::InputSignalType& TextInput::InputFinishedSignal()
548 {
549   return mInputFinishedSignal;
550 }
551
552 Toolkit::TextInput::InputSignalType& TextInput::CutAndPasteToolBarDisplayedSignal()
553 {
554   return mCutAndPasteToolBarDisplayed;
555 }
556
557 Toolkit::TextInput::StyleChangedSignalType& TextInput::StyleChangedSignal()
558 {
559   return mStyleChangedSignal;
560 }
561
562 Toolkit::TextInput::TextModifiedSignalType& TextInput::TextModifiedSignal()
563 {
564   return mTextModifiedSignal;
565 }
566
567 Toolkit::TextInput::MaxInputCharactersReachedSignalType& TextInput::MaxInputCharactersReachedSignal()
568 {
569   return mMaxInputCharactersReachedSignal;
570 }
571
572 Toolkit::TextInput::InputTextExceedBoundariesSignalType& TextInput::InputTextExceedBoundariesSignal()
573 {
574   return mInputTextExceedBoundariesSignal;
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   mInputStartedSignal.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.SetRestoreAfterFocusLost( 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.SetRestoreAfterFocusLost( 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   mInputFinishedSignal.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::OnRelayout( const 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, const 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, const 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, const 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, const 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     }
1771
1772     mCursorPosition = mCursorPosition + InsertAt(Text(keyString), mCursorPosition, 0);
1773
1774     // If we are in pre-edit mode then pressing the space-bar will cause a commit.  But the commit string does not include the
1775     // ' ' character so we need to ensure that the immediately following commit knows how it occurred.
1776     if ( mPreEditFlag )
1777     {
1778       mCommitByKeyInput = true;
1779     }
1780     EmitTextModified();
1781     update = true;
1782   } // space
1783   else if (keyName == "BackSpace")
1784   {
1785     if ( mHighlightMeshActor )
1786     {
1787       // Some text is selected so erase it
1788       DeleteHighlightedText( true );
1789       update = true;
1790     }
1791     else
1792     {
1793       if ( mCursorPosition > 0 )
1794       {
1795         DeleteCharacter( mCursorPosition );
1796         update = true;
1797       }
1798     }
1799     EmitTextModified();
1800   } // BackSpace
1801   else if (keyName == "Right")
1802   {
1803     AdvanceCursor();
1804     RemoveHighlight();
1805   }
1806   else if (keyName == "Left")
1807   {
1808     AdvanceCursor(true);
1809     RemoveHighlight();
1810   }
1811   else // event is a character
1812   {
1813     // Some text may be selected, hiding keyboard causes an empty keystring to be sent, we don't want to delete highlight in this case
1814     if ( !keyString.empty() )
1815     {
1816       // replaces highlighted text with new character
1817       DeleteHighlightedText( false );
1818
1819       // Received key String
1820       mCursorPosition += InsertAt( Text( keyString ), mCursorPosition, 0 );
1821       update = true;
1822       EmitTextModified();
1823     }
1824   }
1825
1826   // If key event has resulted in a change in the text/cursor, then trigger a relayout of text
1827   // as this is a costly operation.
1828   if(update)
1829   {
1830     CursorUpdate();
1831   }
1832
1833   if(update || scroll)
1834   {
1835     if( IsScrollEnabled() )
1836     {
1837       // Calculates the new cursor position (in actor coordinates)
1838       const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
1839
1840       ScrollTextViewToMakeCursorVisible( cursorPosition );
1841     }
1842   }
1843
1844   return true;
1845 }
1846
1847 bool TextInput::OnKeyUpEvent(const KeyEvent& event)
1848 {
1849   std::string keyName = event.keyPressedName;
1850   std::string keyString = event.keyPressed;
1851
1852   DALI_LOG_INFO(gLogFilter, Debug::General, "OnKeyUpEvent keyName[%s] KeyString[%s]\n", keyName.c_str(), keyString.c_str() );
1853
1854   // The selected text become deselected when the key code is DALI_KEY_BACK.
1855   if( IsTextSelected() && ( keyName == "XF86Stop" || keyName == "XF86Send") )
1856   {
1857     DeSelectText();
1858     return true;
1859   }
1860
1861   return false;
1862 }
1863
1864 void TextInput::ChooseRtlSelectionHandlePosition( const Vector3& cursorPositionOne,
1865                                                   const Vector3& cursorPositionTwo,
1866                                                   bool altPositionValidOne,
1867                                                   bool altPositionValidTwo,
1868                                                   const Vector3& altPositionOne,
1869                                                   const Vector3& altPositionTwo )
1870 {
1871   // TODO VCC Valid for one line.
1872   // Try to place the selection handles. TODO think in something better. Probably need to know the direction of the paragraph.
1873   if( cursorPositionOne != cursorPositionTwo )
1874   {
1875     if( cursorPositionOne.x < cursorPositionTwo.x )
1876     {
1877       mSelectionHandleOneActualPosition = cursorPositionOne;
1878       mSelectionHandleTwoActualPosition = cursorPositionTwo;
1879     }
1880     else
1881     {
1882       mSelectionHandleOneActualPosition = cursorPositionTwo;
1883       mSelectionHandleTwoActualPosition = cursorPositionOne;
1884     }
1885   }
1886   else
1887   {
1888     mSelectionHandleOneActualPosition = cursorPositionOne;
1889     if( altPositionValidOne )
1890     {
1891       if( altPositionOne.x < mSelectionHandleOneActualPosition.x )
1892       {
1893         mSelectionHandleOneActualPosition = altPositionOne;
1894       }
1895     }
1896     if( altPositionValidTwo )
1897     {
1898       if(  altPositionTwo.x < mSelectionHandleOneActualPosition.x )
1899       {
1900         mSelectionHandleOneActualPosition = altPositionTwo;
1901       }
1902     }
1903
1904     mSelectionHandleTwoActualPosition = cursorPositionTwo;
1905     if( altPositionValidTwo )
1906     {
1907       if(  altPositionTwo.x > mSelectionHandleTwoActualPosition.x )
1908       {
1909         mSelectionHandleTwoActualPosition = altPositionTwo;
1910       }
1911     }
1912     if( altPositionValidOne )
1913     {
1914       if( altPositionOne.x > mSelectionHandleTwoActualPosition.x )
1915       {
1916         mSelectionHandleTwoActualPosition = altPositionOne;
1917       }
1918     }
1919   }
1920 }
1921
1922 void TextInput::OnTextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition )
1923 {
1924   // Updates the stored scroll position.
1925   mTextLayoutInfo.mScrollOffset = textView.GetScrollPosition();
1926
1927   const Vector3& controlSize = GetControlSize();
1928   Size cursorSize( CURSOR_THICKNESS, 0.f );
1929
1930   // Updates the cursor and grab handle position and visibility.
1931   if( mGrabHandle || mCursor )
1932   {
1933     cursorSize.height = GetRowRectFromCharacterPosition( mCursorPosition ).height;
1934
1935     Vector3 altPosition;    // Alternate (i.e. opposite direction) cursor position.
1936     bool altPositionValid;  // Alternate cursor validity flag.
1937     bool directionRTL;      // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
1938     Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
1939
1940     if( altPositionValid )
1941     {
1942       // Check which of the positions is the closest.
1943       if( fabsf( altPosition.x - mActualGrabHandlePosition.x ) < fabsf( cursorPosition.x - mActualGrabHandlePosition.x ) )
1944       {
1945         cursorPosition = altPosition;
1946       }
1947     }
1948
1949     mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( cursorPosition, cursorSize, controlSize );
1950
1951     mActualGrabHandlePosition = cursorPosition.GetVectorXY();
1952
1953     if( mGrabHandle )
1954     {
1955       ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
1956       mGrabHandle.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1957     }
1958
1959     if( mCursor )
1960     {
1961       mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
1962       mCursor.SetPosition( mActualGrabHandlePosition + UI_OFFSET );
1963     }
1964   }
1965
1966   // Updates the selection handles and highlighted text position and visibility.
1967   if( mSelectionHandleOne && mSelectionHandleTwo )
1968   {
1969     Vector3 altPositionOne;    // Alternate (i.e. opposite direction) cursor position.
1970     bool altPositionValidOne;  // Alternate cursor validity flag.
1971     bool directionRTLOne;      // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
1972     Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition, directionRTLOne, altPositionOne, altPositionValidOne );
1973
1974     Vector3 altPositionTwo;    // Alternate (i.e. opposite direction) cursor position.
1975     bool altPositionValidTwo;  // Alternate cursor validity flag.
1976     bool directionRTLTwo;      // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
1977     Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition, directionRTLTwo, altPositionTwo, altPositionValidTwo );
1978
1979     // VCC TODO: This method is a hack for one line.
1980     ChooseRtlSelectionHandlePosition( cursorPositionOne,
1981                                       cursorPositionTwo,
1982                                       altPositionValidOne,
1983                                       altPositionValidTwo,
1984                                       altPositionOne,
1985                                       altPositionTwo );
1986
1987     cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleOnePosition ) ).mSize.height;
1988     const bool isSelectionHandleOneVisible = IsPositionInsideBoundaries( cursorPositionOne, cursorSize, controlSize );
1989     cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleTwoPosition ) ).mSize.height;
1990     const bool isSelectionHandleTwoVisible = IsPositionInsideBoundaries( cursorPositionTwo, cursorSize, controlSize );
1991
1992     mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
1993     mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
1994     mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
1995     mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
1996
1997     if( mHighlightMeshActor )
1998     {
1999       mHighlightMeshActor.SetVisible( true );
2000       UpdateHighlight();
2001     }
2002   }
2003 }
2004
2005 void TextInput::ScrollTextViewToMakeCursorVisible( const Vector3& cursorPosition )
2006 {
2007   // Scroll the text to make the cursor visible.
2008   const Size cursorSize( CURSOR_THICKNESS,
2009                          GetRowRectFromCharacterPosition( mCursorPosition ).height );
2010
2011   // Need to scroll the text to make the cursor visible and to cover the whole text-input area.
2012
2013   const Vector3& controlSize = GetControlSize();
2014
2015   // Calculates the new scroll position.
2016   Vector2 scrollOffset = mTextLayoutInfo.mScrollOffset;
2017   if( ( cursorPosition.x < 0.f ) || ( cursorPosition.x > controlSize.width ) )
2018   {
2019     scrollOffset.x += cursorPosition.x;
2020   }
2021
2022   if( cursorPosition.y - cursorSize.height < 0.f || cursorPosition.y > controlSize.height )
2023   {
2024     scrollOffset.y += cursorPosition.y;
2025   }
2026
2027   // Sets the new scroll position.
2028   SetScrollPosition( Vector2::ZERO ); // TODO: need to reset to the zero position in order to make the scroll trim to work.
2029   SetScrollPosition( scrollOffset );
2030 }
2031
2032 void TextInput::StartScrollTimer()
2033 {
2034   if( !mScrollTimer )
2035   {
2036     mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
2037     mScrollTimer.TickSignal().Connect( this, &TextInput::OnScrollTimerTick );
2038   }
2039
2040   if( !mScrollTimer.IsRunning() )
2041   {
2042     mScrollTimer.Start();
2043   }
2044 }
2045
2046 void TextInput::StopScrollTimer()
2047 {
2048   if( mScrollTimer )
2049   {
2050     mScrollTimer.Stop();
2051   }
2052 }
2053
2054 bool TextInput::OnScrollTimerTick()
2055 {
2056   // TODO: need to set the new style accordingly the new handle position.
2057
2058   if( !( mGrabHandleVisibility && mGrabHandle ) && !( mSelectionHandleOne && mSelectionHandleTwo ) )
2059   {
2060     // nothing to do if all handles are invisible or doesn't exist.
2061     return true;
2062   }
2063
2064   // Text scrolling
2065
2066   // Choose between the grab handle or the selection handles.
2067   Vector3& actualHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mActualGrabHandlePosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
2068   std::size_t& handlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCursorPosition : ( mCurrentSelectionId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
2069   Vector3& currentHandlePosition = ( mGrabHandleVisibility && mGrabHandle ) ? mCurrentHandlePosition : mCurrentSelectionHandlePosition;
2070
2071   std::size_t newCursorPosition = 0;
2072   ReturnClosestIndex( actualHandlePosition.GetVectorXY(), newCursorPosition );
2073
2074   // Whether the handle's position is different of the previous one and in the case of the selection handle,
2075   // the new selection handle's position needs to be different of the other one.
2076   const bool differentSelectionHandles = ( mGrabHandleVisibility && mGrabHandle ) ? newCursorPosition != handlePosition :
2077                                          ( mCurrentSelectionId == HandleOne ) ? ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleTwoPosition ) :
2078                                                                                 ( newCursorPosition != handlePosition ) && ( newCursorPosition != mSelectionHandleOnePosition );
2079
2080   if( differentSelectionHandles )
2081   {
2082     handlePosition = newCursorPosition;
2083
2084     const Vector3 actualPosition = GetActualPositionFromCharacterPosition( newCursorPosition );
2085
2086     Vector2 scrollDelta = ( actualPosition - currentHandlePosition ).GetVectorXY();
2087
2088     Vector2 scrollPosition = mDisplayedTextView.GetScrollPosition();
2089     scrollPosition += scrollDelta;
2090     SetScrollPosition( scrollPosition );
2091
2092     if( mDisplayedTextView.IsScrollPositionTrimmed() )
2093     {
2094       StopScrollTimer();
2095     }
2096
2097     currentHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition ).GetVectorXY();
2098   }
2099
2100   actualHandlePosition.x += mScrollDisplacement.x;
2101   actualHandlePosition.y += mScrollDisplacement.y;
2102
2103   return true;
2104 }
2105
2106 // Public Internal Methods (public for testing purpose)
2107
2108 void TextInput::SetUpTouchEvents()
2109 {
2110   if ( !mTapDetector )
2111   {
2112     mTapDetector = TapGestureDetector::New();
2113     // Attach the actors and connect the signal
2114     mTapDetector.Attach(Self());
2115
2116     // As contains children which may register for tap the default control detector is not used.
2117     mTapDetector.DetectedSignal().Connect(this, &TextInput::OnTextTap);
2118   }
2119
2120   if ( !mDoubleTapDetector )
2121   {
2122     mDoubleTapDetector = TapGestureDetector::New();
2123     mDoubleTapDetector.SetTapsRequired( 2 );
2124     mDoubleTapDetector.DetectedSignal().Connect(this, &TextInput::OnDoubleTap);
2125
2126     // Only attach and detach the actor to the double tap detector when we enter/leave edit mode
2127     // so that we do not, unnecessarily, have a double tap request all the time
2128   }
2129
2130   if ( !mPanGestureDetector )
2131   {
2132     mPanGestureDetector = PanGestureDetector::New();
2133     mPanGestureDetector.DetectedSignal().Connect(this, &TextInput::OnHandlePan);
2134   }
2135
2136   if ( !mLongPressDetector )
2137   {
2138     mLongPressDetector = LongPressGestureDetector::New();
2139     mLongPressDetector.DetectedSignal().Connect(this, &TextInput::OnLongPress);
2140     mLongPressDetector.Attach(Self());
2141   }
2142 }
2143
2144 void TextInput::CreateTextViewActor()
2145 {
2146   mDisplayedTextView = Toolkit::TextView::New();
2147   mDisplayedTextView.SetName( "DisplayedTextView ");
2148   mDisplayedTextView.SetMarkupProcessingEnabled( mMarkUpEnabled );
2149   mDisplayedTextView.SetParentOrigin(ParentOrigin::TOP_LEFT);
2150   mDisplayedTextView.SetAnchorPoint(AnchorPoint::TOP_LEFT);
2151   mDisplayedTextView.SetMultilinePolicy(Toolkit::TextView::SplitByWord);
2152   mDisplayedTextView.SetWidthExceedPolicy( Toolkit::TextView::Original );
2153   mDisplayedTextView.SetHeightExceedPolicy( Toolkit::TextView::Original );
2154   mDisplayedTextView.SetLineJustification( Toolkit::TextView::Left );
2155   mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>( Toolkit::Alignment::HorizontalLeft | Toolkit::Alignment::VerticalTop ) );
2156   mDisplayedTextView.SetPosition( Vector3( 0.0f, 0.0f, DISPLAYED_TEXT_VIEW_Z_OFFSET ) );
2157   mDisplayedTextView.SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
2158
2159   mDisplayedTextView.ScrolledSignal().Connect( this, &TextInput::OnTextViewScrolled );
2160
2161   Self().Add( mDisplayedTextView );
2162 }
2163
2164 // Start a timer to initiate, used by the cursor to blink.
2165 void TextInput::StartCursorBlinkTimer()
2166 {
2167   if ( !mCursorBlinkTimer )
2168   {
2169     mCursorBlinkTimer = Timer::New( CURSOR_BLINK_INTERVAL );
2170     mCursorBlinkTimer.TickSignal().Connect( this, &TextInput::OnCursorBlinkTimerTick );
2171   }
2172
2173   if ( !mCursorBlinkTimer.IsRunning() )
2174   {
2175     mCursorBlinkTimer.Start();
2176   }
2177 }
2178
2179 // Start a timer to initiate, used by the cursor to blink.
2180 void TextInput::StopCursorBlinkTimer()
2181 {
2182   if ( mCursorBlinkTimer )
2183   {
2184     mCursorBlinkTimer.Stop();
2185   }
2186 }
2187
2188 void TextInput::StartEditMode()
2189 {
2190   DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput StartEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2191
2192   if(!mEditModeActive)
2193   {
2194     SetKeyInputFocus();
2195   }
2196
2197   if ( mDoubleTapDetector )
2198   {
2199     mDoubleTapDetector.Attach( Self() );
2200   }
2201 }
2202
2203 void TextInput::EndEditMode()
2204 {
2205   DALI_LOG_INFO(gLogFilter, Debug::General, "TextInput EndEditMode mEditModeActive[%s]\n", (mEditModeActive)?"true":"false" );
2206
2207   ClearKeyInputFocus();
2208
2209   if ( mDoubleTapDetector )
2210   {
2211     mDoubleTapDetector.Detach( Self() );
2212   }
2213 }
2214
2215 void TextInput::ApplyPreEditStyle( std::size_t preEditStartPosition, std::size_t preEditStringLength )
2216 {
2217   if ( mPreEditFlag && ( preEditStringLength > 0 ) )
2218   {
2219     mUnderlinedPriorToPreEdit = mInputStyle.IsUnderlineEnabled();
2220     TextStyle style;
2221     style.SetUnderline( true );
2222     ApplyStyleToRange( style, TextStyle::UNDERLINE , preEditStartPosition, preEditStartPosition + preEditStringLength -1 );
2223   }
2224 }
2225
2226 void TextInput::RemovePreEditStyle()
2227 {
2228   if ( !mUnderlinedPriorToPreEdit )
2229   {
2230     TextStyle style;
2231     style.SetUnderline( false );
2232     SetActiveStyle( style, TextStyle::UNDERLINE );
2233   }
2234 }
2235
2236 // IMF related methods
2237
2238
2239 ImfManager::ImfCallbackData TextInput::ImfEventReceived( Dali::ImfManager& imfManager, const ImfManager::ImfEventData&  imfEvent )
2240 {
2241   bool update( false );
2242   bool preeditResetRequired ( false );
2243
2244   if (imfEvent.eventName != ImfManager::GETSURROUNDING )
2245   {
2246     HidePopup(); // If Pop-up shown then hides it as editing text.
2247   }
2248
2249   switch ( imfEvent.eventName )
2250   {
2251     case ImfManager::PREEDIT:
2252     {
2253       mIgnoreFirstCommitFlag = false;
2254
2255       // 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
2256       if ( mHighlightMeshActor &&  (!imfEvent.predictiveString.empty()) )
2257       {
2258         // replaces highlighted text with new character
2259         DeleteHighlightedText( false );
2260       }
2261
2262       preeditResetRequired = PreEditReceived( imfEvent.predictiveString, imfEvent.cursorOffset );
2263
2264       if( IsScrollEnabled() )
2265       {
2266         // Calculates the new cursor position (in actor coordinates)
2267         const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2268         ScrollTextViewToMakeCursorVisible( cursorPosition );
2269       }
2270
2271       update = true;
2272
2273       break;
2274     }
2275     case ImfManager::COMMIT:
2276     {
2277       if( mIgnoreFirstCommitFlag )
2278       {
2279         // Do not commit in this case when keyboard sends a commit when shows for the first time (work-around for imf keyboard).
2280         mIgnoreFirstCommitFlag = false;
2281       }
2282       else
2283       {
2284         // A Commit message is a word that has been accepted, it may have been a pre-edit word previously but now commited.
2285
2286         // 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
2287         if ( mHighlightMeshActor &&  (!imfEvent.predictiveString.empty()) )
2288         {
2289           // replaces highlighted text with new character
2290           DeleteHighlightedText( false );
2291         }
2292
2293        // A PreEditReset can cause a commit message to be sent, the Ignore Commit flag is used in scenarios where the word is
2294        // not needed, one such scenario is when the pre-edit word is too long to fit.
2295        if ( !mIgnoreCommitFlag )
2296        {
2297          update = CommitReceived( imfEvent.predictiveString );
2298        }
2299        else
2300        {
2301          mIgnoreCommitFlag = false; // reset ignore flag so next commit is acted upon.
2302        }
2303       }
2304
2305       if( update )
2306       {
2307         if( IsScrollEnabled() )
2308         {
2309           // Calculates the new cursor position (in actor coordinates)
2310           const Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition );
2311
2312           ScrollTextViewToMakeCursorVisible( cursorPosition );
2313         }
2314       }
2315       break;
2316     }
2317     case ImfManager::DELETESURROUNDING:
2318     {
2319       DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - delete surrounding mPreEditFlag[%s] cursor offset[%d] characters to delete[%d] position to delete[%u] \n",
2320                      (mPreEditFlag)?"true":"false", imfEvent.cursorOffset, imfEvent.numberOfChars, static_cast<std::size_t>( mCursorPosition+imfEvent.cursorOffset) );
2321
2322       mPreEditFlag = false;
2323
2324       std::size_t toDelete = 0;
2325       std::size_t numberOfCharacters = 0;
2326
2327       if( mHighlightMeshActor )
2328       {
2329         // delete highlighted text.
2330         toDelete = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2331         numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - toDelete;
2332       }
2333       else
2334       {
2335         if( static_cast<std::size_t>(std::abs( imfEvent.cursorOffset )) < mCursorPosition )
2336         {
2337           toDelete = mCursorPosition + imfEvent.cursorOffset;
2338         }
2339         if( toDelete + imfEvent.numberOfChars > mStyledText.size() )
2340         {
2341           numberOfCharacters = mStyledText.size() - toDelete;
2342         }
2343         else
2344         {
2345           numberOfCharacters = imfEvent.numberOfChars;
2346         }
2347       }
2348       DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding pre-delete range mCursorPosition[%u] \n", mCursorPosition);
2349       DeleteRange( toDelete, numberOfCharacters );
2350
2351       mCursorPosition = toDelete;
2352       mNumberOfSurroundingCharactersDeleted = numberOfCharacters;
2353
2354       EmitTextModified();
2355
2356       DALI_LOG_INFO( gLogFilter, Debug::General, "ImfEventReceived - deleteSurrounding post-delete range mCursorPosition[%u] \n", mCursorPosition);
2357       break;
2358     }
2359     case ImfManager::GETSURROUNDING:
2360     {
2361       // 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
2362       // the next key pressed.  Instead the Select function sets the cursor position and surrounding text.
2363       if (! ( mHighlightMeshActor || mSelectingText ) )
2364       {
2365         std::string text( GetText() );
2366         DALI_LOG_INFO( gLogFilter, Debug::General, "OnKey - surrounding text - set text [%s] and cursor[%u] \n", text.c_str(), mCursorPosition );
2367
2368         imfManager.SetCursorPosition( mCursorPosition );
2369         imfManager.SetSurroundingText( text );
2370       }
2371
2372       if( 0 != mNumberOfSurroundingCharactersDeleted )
2373       {
2374         mDisplayedTextView.RemoveTextFrom( mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2375         mNumberOfSurroundingCharactersDeleted = 0;
2376
2377         if( mStyledText.empty() )
2378         {
2379           ShowPlaceholderText( mStyledPlaceHolderText );
2380         }
2381       }
2382       break;
2383     }
2384     case ImfManager::VOID:
2385     {
2386       DALI_ASSERT_DEBUG( false );
2387     }
2388   } // end switch
2389
2390   ImfManager::ImfCallbackData callbackData( update, mCursorPosition, GetText(), preeditResetRequired );
2391
2392   return callbackData;
2393 }
2394
2395 bool TextInput::PreEditReceived(const std::string& keyString, std::size_t cursorOffset )
2396 {
2397   mPreserveCursorPosition = false;  // As in pre-edit state we should have the cursor at the end of the word displayed not last touch position.
2398
2399   DALI_LOG_INFO(gLogFilter, Debug::General, ">>PreEditReceived preserveCursorPos[%d] mCursorPos[%d] mPreEditFlag[%d]\n",
2400                 mPreserveCursorPosition, mCursorPosition, mPreEditFlag );
2401
2402   bool preeditResetRequest ( false );
2403
2404   if( mPreEditFlag ) // Already in pre-edit state.
2405   {
2406     if( mStyledText.size() >= mMaxStringLength )
2407     {
2408       DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived styledTextSize >= mMaxStringLength \n");
2409       // Cannot fit these characters into field, clear pre-edit.
2410       if ( !mUnderlinedPriorToPreEdit )
2411       {
2412         TextStyle style;
2413         style.SetUnderline( false );
2414         ApplyStyleToRange( style, TextStyle::UNDERLINE , mPreEditStartPosition, mPreEditStartPosition + mPreEditLength -1 );
2415       }
2416       mIgnoreCommitFlag = true;
2417       preeditResetRequest = false; // this will reset the keyboard's predictive suggestions.
2418       mPreEditFlag = false;
2419       EmitMaxInputCharactersReachedSignal();
2420     }
2421     else
2422     {
2423       // delete existing pre-edit string
2424       const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2425
2426       // Store new pre-edit string
2427       mPreEditString.SetText( keyString );
2428
2429       if ( keyString.empty() )
2430       {
2431         mPreEditFlag = false;
2432         mCursorPosition = mPreEditStartPosition;
2433
2434         if( mStyledText.empty() )
2435         {
2436           ShowPlaceholderText( mStyledPlaceHolderText );
2437         }
2438         else
2439         {
2440           mDisplayedTextView.RemoveTextFrom( mPreEditStartPosition, numberOfCharactersToReplace );
2441         }
2442
2443         GetTextLayoutInfo();
2444         EmitTextModified();
2445       }
2446       else
2447       {
2448         // Insert new pre-edit string. InsertAt updates the size and position table.
2449         mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, numberOfCharactersToReplace );
2450         // 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.
2451         mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2452         ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2453         DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] \n", mCursorPosition);
2454         EmitTextModified();
2455       }
2456       // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2457       DrawCursor();
2458     }
2459   }
2460   else  // mPreEditFlag not set
2461   {
2462     if ( !keyString.empty() ) // Imf can send an empty pre-edit followed by Backspace instead of a commit.
2463     {
2464       DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived Initial Pre-Edit string \n");
2465       // new pre-edit so move into pre-edit state by setting flag
2466       mPreEditFlag = true;
2467       mPreEditString.SetText( keyString ); // store new pre-edit string
2468       mPreEditStartPosition = mCursorPosition; // store starting cursor position of pre-edit so know where to re-start from
2469       mPreEditLength = InsertAt( mPreEditString, mPreEditStartPosition, 0 );
2470       // 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.
2471       mCursorPosition = mPreEditStartPosition + std::min( cursorOffset, mPreEditLength );
2472       ApplyPreEditStyle( mPreEditStartPosition, mPreEditLength );
2473       DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived mCursorPosition[%u] mPreEditStartPosition[%u]\n", mCursorPosition, mPreEditStartPosition);
2474       // cursor update to keyboard is not done here as the keyboard knows the cursor position and provides the 'cursorOffset'.
2475       DrawCursor();
2476       EmitTextModified();
2477     }
2478     else
2479     {
2480       DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReceived with empty keyString\n");
2481     }
2482   }
2483
2484   return preeditResetRequest;
2485 }
2486
2487 bool TextInput::CommitReceived(const std::string& keyString )
2488 {
2489   DALI_LOG_INFO(gLogFilter, Debug::General, ">>CommitReceived preserveCursorPos[%d] mPreEditStartPosition [%d] mCursorPos[%d] mPreEditFlag[%d] mIgnoreCommitFlag[%s]\n",
2490       mPreserveCursorPosition, mPreEditStartPosition, mCursorPosition, mPreEditFlag, (mIgnoreCommitFlag)?"true":"false" );
2491
2492   bool update( false );
2493
2494   RemovePreEditStyle();
2495
2496   const std::size_t styledTextSize( mStyledText.size() );
2497   if( styledTextSize >= mMaxStringLength )
2498   {
2499     // Cannot fit these characters into field, clear pre-edit.
2500     if ( mPreEditFlag )
2501     {
2502       mIgnoreCommitFlag = true;
2503       mPreEditFlag = false;
2504     }
2505     EmitMaxInputCharactersReachedSignal();
2506   }
2507   else
2508   {
2509     if( mPreEditFlag )
2510     {
2511       // delete existing pre-edit string
2512       const std::size_t numberOfCharactersToReplace = DeletePreEdit();
2513       mPreEditFlag = false;
2514
2515       DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived mPreserveCursorPosition[%s] mPreEditStartPosition[%u]\n",
2516                     (mPreserveCursorPosition)?"true":"false", mPreEditStartPosition );
2517
2518       if ( mPreserveCursorPosition ) // PreEditReset has been called triggering this commit.
2519       {
2520         // No need to update cursor position as Cursor location given by touch.
2521         InsertAt( Text( keyString ), mPreEditStartPosition, numberOfCharactersToReplace );
2522         mPreserveCursorPosition = false;
2523       }
2524       else
2525       {
2526         // Cursor not set by touch so needs to be re-positioned to input more text
2527         mCursorPosition = mPreEditStartPosition + InsertAt( Text(keyString), mPreEditStartPosition, numberOfCharactersToReplace ); // update cursor position as InsertAt, re-draw cursor with this
2528
2529         // If a space or enter caused the commit then our string is one longer than the string given to us by the commit key.
2530         if ( mCommitByKeyInput )
2531         {
2532           mCursorPosition = std::min ( mCursorPosition + 1, mStyledText.size() );
2533           mCommitByKeyInput = false;
2534         }
2535       }
2536
2537       EmitTextModified();
2538
2539       if ( mSelectTextOnCommit )
2540       {
2541         SelectText(mRequestedSelection.mStartOfSelection, mRequestedSelection.mEndOfSelection );
2542       }
2543
2544       update = true;
2545     }
2546     else // mPreEditFlag not set
2547     {
2548       if ( !mIgnoreCommitFlag ) // Check if this commit should be ignored.
2549       {
2550         if( mStyledText.empty() && mPlaceHolderSet )
2551         {
2552           // If the styled text is empty and the placeholder text is set, it needs to be cleared.
2553           mDisplayedTextView.SetText( "" );
2554           mNumberOfSurroundingCharactersDeleted = 0;
2555           mPlaceHolderSet = false;
2556         }
2557         mCursorPosition = mCursorPosition + InsertAt( Text( keyString ), mCursorPosition, mNumberOfSurroundingCharactersDeleted );
2558         update = true;
2559         mNumberOfSurroundingCharactersDeleted = 0;
2560         EmitTextModified();
2561       }
2562       else
2563       {
2564         mIgnoreCommitFlag = false; // Reset flag so future commits will not be ignored.
2565       }
2566     }
2567   }
2568
2569   mSelectTextOnCommit = false;
2570
2571   DALI_LOG_INFO(gLogFilter, Debug::General, "CommitReceived << mCursorPos[%d] mPreEditFlag[%d] update[%s] \n",
2572                                              mCursorPosition, mPreEditFlag, (update)?"true":"false" );
2573
2574   return update;
2575 }
2576
2577 // End of IMF related methods
2578
2579 std::size_t TextInput::DeletePreEdit()
2580 {
2581   DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeletePreEdit mPreEditFlag[%s] \n", (mPreEditFlag)?"true":"false");
2582
2583   DALI_ASSERT_DEBUG( mPreEditFlag );
2584
2585   const std::size_t preEditStringLength = mPreEditString.GetLength();
2586   const std::size_t styledTextSize = mStyledText.size();
2587
2588   std::size_t endPosition = mPreEditStartPosition + preEditStringLength;
2589
2590   // Prevents erase items outside mStyledText bounds.
2591   if( mPreEditStartPosition > styledTextSize )
2592   {
2593     DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. mPreEditStartPosition > mStyledText.size()" );
2594     mPreEditStartPosition = styledTextSize;
2595   }
2596
2597   if( ( endPosition > styledTextSize ) || ( endPosition < mPreEditStartPosition ) )
2598   {
2599     DALI_ASSERT_DEBUG( !"TextInput::DeletePreEdit. ( endPosition > mStyledText.size() ) || ( endPosition < mPreEditStartPosition )" );
2600     endPosition = styledTextSize;
2601   }
2602
2603   mStyledText.erase( mStyledText.begin() + mPreEditStartPosition, mStyledText.begin() + endPosition );
2604
2605   // DeletePreEdit() doesn't remove characters from the text-view because may be followed by an InsertAt() which inserts characters,
2606   // in that case, the Insert should use the returned number of deleted characters and replace the text which helps the text-view to
2607   // reuse glyphs.
2608   // In case DeletePreEdit() is not followed by an InsertAt() characters must be deleted after this call.
2609
2610   return preEditStringLength;
2611 }
2612
2613 void TextInput::PreEditReset( bool preserveCursorPosition )
2614 {
2615   DALI_LOG_INFO(gLogFilter, Debug::General, "PreEditReset preserveCursorPos[%d] mCursorPos[%d] \n",
2616                 preserveCursorPosition, mCursorPosition);
2617
2618   // 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.
2619   mPreserveCursorPosition = preserveCursorPosition;
2620
2621   // Reset incase we are in a pre-edit state.
2622   ImfManager imfManager = ImfManager::Get();
2623   if ( imfManager )
2624   {
2625     imfManager.Reset(); // Will trigger a commit message
2626   }
2627 }
2628
2629 void TextInput::CursorUpdate()
2630 {
2631   DrawCursor();
2632
2633   ImfManager imfManager = ImfManager::Get();
2634   if ( imfManager )
2635   {
2636     std::string text( GetText() );
2637     imfManager.SetSurroundingText( text );  // Notifying IMF of a cursor change triggers a surrounding text request so updating it now.
2638     imfManager.SetCursorPosition ( mCursorPosition );
2639     imfManager.NotifyCursorPosition();
2640   }
2641 }
2642
2643 /* Delete highlighted characters redisplay*/
2644 void TextInput::DeleteHighlightedText( bool inheritStyle )
2645 {
2646   DALI_LOG_INFO( gLogFilter, Debug::General, "DeleteHighlightedText handlePosOne[%u] handlePosTwo[%u]\n", mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
2647
2648   if( mHighlightMeshActor )
2649   {
2650     mCursorPosition = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2651
2652     MarkupProcessor::StyledTextArray::iterator start = mStyledText.begin() + mCursorPosition;
2653     MarkupProcessor::StyledTextArray::iterator end =  mStyledText.begin() + std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
2654
2655     // Get the styled text of the characters to be deleted as it may be needed if
2656     // the "exceed the text-input's boundaries" option is disabled.
2657     MarkupProcessor::StyledTextArray styledCharactersToDelete;
2658
2659     styledCharactersToDelete.insert( styledCharactersToDelete.begin(), start, end );
2660
2661     mStyledText.erase( start, end ); // erase range of characters
2662
2663     // Remove text from TextView and update place holder text if required
2664
2665     // Set the placeholder text only if the styled text is empty.
2666     if( mStyledText.empty() )
2667     {
2668       ShowPlaceholderText( mStyledPlaceHolderText );
2669     }
2670     else
2671     {
2672       const std::size_t numberOfCharacters = std::max( mSelectionHandleOnePosition, mSelectionHandleTwoPosition ) - mCursorPosition;
2673
2674       mDisplayedTextView.RemoveTextFrom( mCursorPosition, numberOfCharacters );
2675
2676       // It may happen than after removing a white space or a new line character,
2677       // two words merge, this new word could be big enough to not fit in its
2678       // current line, so moved to the next one, and make some part of the text to
2679       // exceed the text-input's boundary.
2680       if( !mExceedEnabled )
2681       {
2682         // Get the new text layout after removing some characters.
2683         mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2684
2685         // Get text-input's size.
2686         const Vector3& size = GetControlSize();
2687
2688         if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2689             ( mTextLayoutInfo.mTextSize.height > size.height ) )
2690         {
2691           mDisplayedTextView.InsertTextAt( mCursorPosition, styledCharactersToDelete );
2692
2693           mStyledText.insert( mStyledText.begin() + mCursorPosition,
2694                               styledCharactersToDelete.begin(),
2695                               styledCharactersToDelete.end() );
2696         }
2697       }
2698     }
2699     GetTextLayoutInfo();
2700
2701     RemoveHighlight();
2702
2703     EmitTextModified();
2704
2705     if( inheritStyle )
2706     {
2707       const TextStyle oldInputStyle( mInputStyle );
2708
2709       mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2710
2711       if( oldInputStyle != mInputStyle )
2712       {
2713         // Updates the line height accordingly with the input style.
2714         UpdateLineHeight();
2715
2716         EmitStyleChangedSignal();
2717       }
2718     }
2719   }
2720 }
2721
2722 void TextInput::DeleteRange( const std::size_t start, const std::size_t ncharacters )
2723 {
2724   DALI_ASSERT_DEBUG( start <= mStyledText.size() );
2725   DALI_ASSERT_DEBUG( !mStyledText.empty() );
2726
2727   DALI_LOG_INFO(gLogFilter, Debug::General, ">>DeleteRange pre mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2728
2729
2730   if ( ( !mStyledText.empty()) && ( ( start + ncharacters ) <= mStyledText.size() ) )
2731   {
2732     MarkupProcessor::StyledTextArray::iterator itStart =  mStyledText.begin() + start;
2733     MarkupProcessor::StyledTextArray::iterator itEnd =  mStyledText.begin() + start + ncharacters;
2734
2735     mStyledText.erase(itStart, itEnd);
2736
2737     // update the selection handles if they are visible.
2738     if( mHighlightMeshActor )
2739     {
2740       std::size_t& minHandle = ( mSelectionHandleOnePosition <= mSelectionHandleTwoPosition ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition );
2741       std::size_t& maxHandle = ( mSelectionHandleTwoPosition > mSelectionHandleOnePosition ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition );
2742
2743       if( minHandle >= start + ncharacters )
2744       {
2745         minHandle -= ncharacters;
2746       }
2747       else if( ( minHandle > start ) && ( minHandle < start + ncharacters ) )
2748       {
2749         minHandle = start;
2750       }
2751
2752       if( maxHandle >= start + ncharacters )
2753       {
2754         maxHandle -= ncharacters;
2755       }
2756       else if( ( maxHandle > start ) && ( maxHandle < start + ncharacters ) )
2757       {
2758         maxHandle = start;
2759       }
2760     }
2761
2762     // 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.
2763   }
2764
2765   DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteRange<< post mStyledText[%s] mPreEditFlag[%s] \n", GetText().c_str(), (mPreEditFlag)?"true":"false");
2766
2767   // Although mStyledText has been set to a new text string we no longer re-draw the text or notify the cursor change.
2768   // This is a performance decision as the use of this function often means the text is being replaced or just deleted.
2769   // Mean we do not re-draw the text more than we have too.
2770 }
2771
2772 /* Delete character at current cursor position and redisplay*/
2773 void TextInput::DeleteCharacter( std::size_t positionToDelete )
2774 {
2775   // Ensure positionToDelete is not out of bounds.
2776   DALI_ASSERT_DEBUG( positionToDelete <= mStyledText.size() );
2777   DALI_ASSERT_DEBUG( !mStyledText.empty() );
2778   DALI_ASSERT_DEBUG( positionToDelete > 0 );
2779
2780   DALI_LOG_INFO(gLogFilter, Debug::General, "DeleteCharacter positionToDelete[%u]", positionToDelete );
2781
2782
2783   if ( ( !mStyledText.empty()) && ( positionToDelete > 0 ) && positionToDelete <= mStyledText.size() )  // don't try to delete if no characters left of cursor
2784   {
2785     MarkupProcessor::StyledTextArray::iterator it =  mStyledText.begin() + positionToDelete - 1;
2786
2787     // Get the styled text of the character to be deleted as it may be needed if
2788     // the "exceed the text-input's boundaries" option is disabled.
2789     const MarkupProcessor::StyledText styledCharacterToDelete( *it );
2790
2791     mStyledText.erase(it);  // erase the character left of positionToDelete
2792
2793     if( mStyledText.empty() )
2794     {
2795       ShowPlaceholderText( mStyledPlaceHolderText );
2796     }
2797     else
2798     {
2799       mDisplayedTextView.RemoveTextFrom( positionToDelete - 1, 1 );
2800
2801       const Character characterToDelete = styledCharacterToDelete.mText[0];
2802
2803       // It may happen than after removing a white space or a new line character,
2804       // two words merge, this new word could be big enough to not fit in its
2805       // current line, so moved to the next one, and make some part of the text to
2806       // exceed the text-input's boundary.
2807       if( !mExceedEnabled )
2808       {
2809         if( characterToDelete.IsWhiteSpace() || characterToDelete.IsNewLine() )
2810         {
2811           // Get the new text layout after removing one character.
2812           mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
2813
2814           // Get text-input's size.
2815           const Vector3& size = GetControlSize();
2816
2817           if( ( mTextLayoutInfo.mTextSize.width > size.width ) ||
2818               ( mTextLayoutInfo.mTextSize.height > size.height ) )
2819           {
2820             MarkupProcessor::StyledTextArray array;
2821             array.push_back( styledCharacterToDelete );
2822             mDisplayedTextView.InsertTextAt( positionToDelete - 1, array );
2823
2824             mStyledText.insert( mStyledText.begin() + ( positionToDelete - 1 ), styledCharacterToDelete );
2825           }
2826         }
2827       }
2828     }
2829     GetTextLayoutInfo();
2830
2831     ShowGrabHandleAndSetVisibility( false );
2832
2833     mCursorPosition = positionToDelete -1;
2834
2835     const TextStyle oldInputStyle( mInputStyle );
2836
2837     mInputStyle = GetStyleAtCursor(); // Inherit style from cursor position
2838
2839     if( oldInputStyle != mInputStyle )
2840     {
2841       // Updates the line height accordingly with the input style.
2842       UpdateLineHeight();
2843
2844       EmitStyleChangedSignal();
2845     }
2846   }
2847 }
2848
2849 /*Insert new character into the string and (optionally) redisplay text-input*/
2850 std::size_t TextInput::InsertAt( const Text& newText, const std::size_t insertionPosition, const std::size_t numberOfCharactersToReplace )
2851 {
2852   DALI_LOG_INFO(gLogFilter, Debug::General, "InsertAt insertionPosition[%u]\n", insertionPosition );
2853
2854   // Ensure insertionPosition is not out of bounds.
2855   DALI_ASSERT_ALWAYS( insertionPosition <= mStyledText.size() );
2856
2857   bool textExceedsMaximunNumberOfCharacters = false;
2858   bool textExceedsBoundary = false;
2859   std::size_t insertedStringLength = DoInsertAt( newText, insertionPosition, numberOfCharactersToReplace, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
2860
2861   ShowGrabHandleAndSetVisibility( false );
2862
2863   if( textExceedsMaximunNumberOfCharacters || textExceedsBoundary )
2864   {
2865     if( mPreEditFlag )
2866     {
2867       mIgnoreCommitFlag = true;
2868       mPreEditFlag = false;
2869       // A PreEditReset( false ) should be triggered from here if the keyboards predictive suggestions must be cleared.
2870       // Although can not directly call PreEditReset() as it will cause a recursive emit loop.
2871     }
2872
2873     if( textExceedsMaximunNumberOfCharacters )
2874     {
2875       EmitMaxInputCharactersReachedSignal();
2876     }
2877
2878     if( textExceedsBoundary )
2879     {
2880       EmitInputTextExceedsBoundariesSignal();
2881       PreEditReset( false );
2882     }
2883   }
2884
2885   return insertedStringLength;
2886 }
2887
2888 ImageActor TextInput::CreateCursor( const Vector4& color)
2889 {
2890   ImageActor cursor;
2891   cursor = CreateSolidColorActor(color);
2892   cursor.SetName( "Cursor" );
2893
2894   cursor.SetParentOrigin(ParentOrigin::TOP_LEFT);
2895   cursor.SetAnchorPoint(AnchorPoint::BOTTOM_LEFT);
2896   cursor.SetVisible(false);
2897
2898   return cursor;
2899 }
2900
2901 void TextInput::AdvanceCursor(bool reverse, std::size_t places)
2902 {
2903   // As cursor is not moving due to grab handle, handle should be hidden.
2904   ShowGrabHandleAndSetVisibility( false );
2905
2906   bool cursorPositionChanged = false;
2907   if (reverse)
2908   {
2909     if ( mCursorPosition >= places )
2910     {
2911       mCursorPosition = mCursorPosition - places;
2912       cursorPositionChanged = true;
2913     }
2914   }
2915   else
2916   {
2917     if ((mCursorPosition + places) <= mStyledText.size())
2918     {
2919       mCursorPosition = mCursorPosition + places;
2920       cursorPositionChanged = true;
2921     }
2922   }
2923
2924   if( cursorPositionChanged )
2925   {
2926     const std::size_t cursorPositionForStyle = ( 0 == mCursorPosition ? 0 : mCursorPosition - 1 );
2927
2928     const TextStyle oldInputStyle( mInputStyle );
2929     mInputStyle = GetStyleAt( cursorPositionForStyle ); // Inherit style from selected position.
2930
2931     DrawCursor();
2932
2933     if( oldInputStyle != mInputStyle )
2934     {
2935       // Updates the line height accordingly with the input style.
2936       UpdateLineHeight();
2937
2938       EmitStyleChangedSignal();
2939     }
2940
2941     ImfManager imfManager = ImfManager::Get();
2942     if ( imfManager )
2943     {
2944       imfManager.SetCursorPosition ( mCursorPosition );
2945       imfManager.NotifyCursorPosition();
2946     }
2947   }
2948 }
2949
2950 void TextInput::DrawCursor()
2951 {
2952   const Size rowRect = GetRowRectFromCharacterPosition( mCursorPosition );
2953
2954   // Get height of cursor and set its size
2955   Size size( CURSOR_THICKNESS, 0.0f );
2956   if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
2957   {
2958     size.height = rowRect.height;
2959   }
2960   else
2961   {
2962     // Measure Font so know how big text will be if no initial text to measure.
2963     size.height = mLineHeight;
2964   }
2965
2966   mCursor.SetSize(size);
2967
2968   // If the character is italic then the cursor also tilts.
2969   mCursor.SetRotation( mInputStyle.IsItalicsEnabled() ? Degree( mInputStyle.GetItalicsAngle() - CURSOR_ANGLE_OFFSET ) : Degree( 0.f ), Vector3::ZAXIS );
2970
2971   DALI_ASSERT_DEBUG( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
2972
2973   if( ( mCursorPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) )
2974   {
2975     Vector3 altPosition;    // Alternate (i.e. opposite direction) cursor position.
2976     bool altPositionValid;  // Alternate cursor validity flag.
2977     bool directionRTL;      // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
2978     Vector3 position = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid );
2979
2980     SetAltCursorEnabled( altPositionValid );
2981
2982     if( !altPositionValid )
2983     {
2984       mCursor.SetPosition( position + UI_OFFSET );
2985     }
2986     else
2987     {
2988       size.height *= 0.5f;
2989       mCursor.SetSize(size);
2990       mCursor.SetPosition( position + UI_OFFSET - Vector3( 0.0f, directionRTL ? 0.0f : size.height, 0.0f ) );
2991
2992       // TODO: change this cursor pos, to be the one where the cursor is sourced from.
2993       size.height = rowRect.height * 0.5f;
2994       mCursorRTL.SetSize(size);
2995       mCursorRTL.SetPosition( altPosition + UI_OFFSET - Vector3( 0.0f, directionRTL ? size.height : 0.0f, 0.0f ) );
2996     }
2997
2998     if( IsScrollEnabled() )
2999     {
3000       // Whether cursor and grab handle are inside the boundaries of the text-input when text scroll is enabled.
3001       mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( position, size, GetControlSize() );
3002     }
3003   } // EditMode
3004 }
3005
3006 void TextInput::SetAltCursorEnabled( bool enabled )
3007 {
3008   mCursorRTLEnabled = enabled;
3009   mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
3010 }
3011
3012 void TextInput::SetCursorVisibility( bool visible )
3013 {
3014   mCursorVisibility = visible;
3015   mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
3016   mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
3017 }
3018
3019 void TextInput::CreateGrabHandle( Dali::Image image )
3020 {
3021   if ( !mGrabHandle )
3022   {
3023     if ( !image )
3024     {
3025       mGrabHandleImage = ResourceImage::New(DEFAULT_GRAB_HANDLE);
3026     }
3027     else
3028     {
3029       mGrabHandleImage = image;
3030     }
3031
3032     mGrabHandle = ImageActor::New(mGrabHandleImage);
3033     mGrabHandle.SetParentOrigin(ParentOrigin::TOP_LEFT);
3034     mGrabHandle.SetAnchorPoint(AnchorPoint::TOP_CENTER);
3035
3036     mGrabHandle.SetDrawMode(DrawMode::OVERLAY);
3037
3038     ShowGrabHandleAndSetVisibility( false );
3039
3040     CreateGrabArea( mGrabHandle );
3041
3042     mActiveLayer.Add(mGrabHandle);
3043   }
3044 }
3045
3046 void TextInput::CreateGrabArea( Actor& parent )
3047 {
3048   mGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3049   mGrabArea.SetName( "GrabArea" );
3050   mGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3051   mGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE ) ) );  // grab area to be larger than text actor
3052   mGrabArea.TouchedSignal().Connect(this,&TextInput::OnPressDown);
3053   mTapDetector.Attach( mGrabArea );
3054   mPanGestureDetector.Attach( mGrabArea );
3055   mLongPressDetector.Attach( mGrabArea );
3056
3057   parent.Add(mGrabArea);
3058 }
3059
3060 Vector3 TextInput::MoveGrabHandle( const Vector2& displacement )
3061 {
3062   Vector3 actualHandlePosition;
3063
3064   if (mGrabHandle)
3065   {
3066     mActualGrabHandlePosition.x += displacement.x;
3067     mActualGrabHandlePosition.y += displacement.y;
3068
3069     // Grab handle should jump to the nearest character and take cursor with it
3070     std::size_t newCursorPosition = 0;
3071     ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY(), newCursorPosition );
3072
3073     Vector3 altPosition;    // Alternate (i.e. opposite direction) cursor position.
3074     bool altPositionValid;  // Alternate cursor validity flag.
3075     bool directionRTL;      // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
3076     actualHandlePosition = GetActualPositionFromCharacterPosition( newCursorPosition, directionRTL, altPosition, altPositionValid );
3077
3078     if( altPositionValid )
3079     {
3080       // Check which of the positions is the closest.
3081       if( fabsf( altPosition.x - mActualGrabHandlePosition.x ) < fabsf( actualHandlePosition.x - mActualGrabHandlePosition.x ) )
3082       {
3083         actualHandlePosition = altPosition;
3084       }
3085     }
3086
3087     bool handleVisible = true;
3088
3089     if( IsScrollEnabled() )
3090     {
3091       const Vector3 controlSize = GetControlSize();
3092       const Size cursorSize = GetRowRectFromCharacterPosition( newCursorPosition );
3093       // Scrolls the text if the handle is not in a visible position
3094       handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3095                                                   cursorSize,
3096                                                   controlSize );
3097
3098       if( handleVisible )
3099       {
3100         StopScrollTimer();
3101         mCurrentHandlePosition = actualHandlePosition;
3102         mScrollDisplacement = Vector2::ZERO;
3103       }
3104       else
3105       {
3106         if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3107         {
3108           mScrollDisplacement.x = -SCROLL_SPEED;
3109         }
3110         else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3111         {
3112           mScrollDisplacement.x = SCROLL_SPEED;
3113         }
3114         if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3115         {
3116           mScrollDisplacement.y = -SCROLL_SPEED;
3117         }
3118         else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3119         {
3120           mScrollDisplacement.y = SCROLL_SPEED;
3121         }
3122         StartScrollTimer();
3123       }
3124     }
3125
3126     if( handleVisible &&                           // Only redraw cursor and do updates if position changed
3127         ( newCursorPosition != mCursorPosition ) ) // and the new position is visible (if scroll is not enabled, it's always true).
3128     {
3129       mCursorPosition = newCursorPosition;
3130
3131       mGrabHandle.SetPosition( actualHandlePosition + UI_OFFSET );
3132
3133       const TextStyle oldInputStyle( mInputStyle );
3134
3135       mInputStyle = GetStyleAtCursor(); //Inherit style from cursor position
3136
3137       CursorUpdate();  // Let keyboard know the new cursor position so can 're-capture' for prediction.
3138
3139       if( oldInputStyle != mInputStyle )
3140       {
3141         // Updates the line height accordingly with the input style.
3142         UpdateLineHeight();
3143
3144         EmitStyleChangedSignal();
3145       }
3146     }
3147   }
3148
3149   return actualHandlePosition;
3150 }
3151
3152 void TextInput::ShowGrabHandle( bool visible )
3153 {
3154   if ( IsGrabHandleEnabled() )
3155   {
3156     if( mGrabHandle )
3157     {
3158       mGrabHandle.SetVisible( mGrabHandleVisibility );
3159     }
3160     StartMonitoringStageForTouch();
3161   }
3162 }
3163
3164 void TextInput::ShowGrabHandleAndSetVisibility( bool visible )
3165 {
3166   mGrabHandleVisibility = visible;
3167   ShowGrabHandle( visible );
3168 }
3169
3170 // Callbacks connected to be Property notifications for Boundary checking.
3171
3172 void TextInput::OnLeftBoundaryExceeded(PropertyNotification& source)
3173 {
3174   mIsSelectionHandleOneFlipped = true;
3175   mSelectionHandleOne.SetScale( -1.0f, 1.0f, 1.0f );
3176   mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3177 }
3178
3179 void TextInput::OnReturnToLeftBoundary(PropertyNotification& source)
3180 {
3181   mIsSelectionHandleOneFlipped = false;
3182   mSelectionHandleOne.SetScale( 1.0f, 1.0f, 1.0f );
3183   mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3184 }
3185
3186 void TextInput::OnRightBoundaryExceeded(PropertyNotification& source)
3187 {
3188   mIsSelectionHandleTwoFlipped = true;
3189   mSelectionHandleTwo.SetScale( -1.0f, 1.0f, 1.0f );
3190   mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
3191 }
3192
3193 void TextInput::OnReturnToRightBoundary(PropertyNotification& source)
3194 {
3195   mIsSelectionHandleTwoFlipped = false;
3196   mSelectionHandleTwo.SetScale( 1.0f, 1.0f, 1.0f );
3197   mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT);
3198 }
3199
3200 // todo change PropertyNotification signal definition to include Actor. Hence won't need duplicate functions.
3201 void TextInput::OnHandleOneLeavesBoundary( PropertyNotification& source)
3202 {
3203   mSelectionHandleOne.SetOpacity(0.0f);
3204 }
3205
3206 void TextInput::OnHandleOneWithinBoundary(PropertyNotification& source)
3207 {
3208   mSelectionHandleOne.SetOpacity(1.0f);
3209 }
3210
3211 void TextInput::OnHandleTwoLeavesBoundary( PropertyNotification& source)
3212 {
3213   mSelectionHandleTwo.SetOpacity(0.0f);
3214 }
3215
3216 void TextInput::OnHandleTwoWithinBoundary(PropertyNotification& source)
3217 {
3218   mSelectionHandleTwo.SetOpacity(1.0f);
3219 }
3220
3221 // End of Callbacks connected to be Property notifications for Boundary checking.
3222
3223 void TextInput::SetUpHandlePropertyNotifications()
3224 {
3225   /* Property notifications for handles exceeding the boundary and returning back within boundary */
3226
3227   Vector3 handlesize = GetSelectionHandleSize();
3228
3229   // Exceeding horizontal boundary
3230   PropertyNotification leftNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.x + handlesize.x) );
3231   leftNotification.NotifySignal().Connect( this, &TextInput::OnLeftBoundaryExceeded );
3232
3233   PropertyNotification rightNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.z - handlesize.x ) );
3234   rightNotification.NotifySignal().Connect( this, &TextInput::OnRightBoundaryExceeded );
3235
3236   // Within horizontal boundary
3237   PropertyNotification leftLeaveNotification = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.x + 2*handlesize.x ) );
3238   leftLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToLeftBoundary );
3239
3240   PropertyNotification rightLeaveNotification = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.z - 2*handlesize.x ) );
3241   rightLeaveNotification.NotifySignal().Connect( this, &TextInput::OnReturnToRightBoundary );
3242
3243   // Exceeding vertical boundary
3244   PropertyNotification verticalExceedNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3245                                                        OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3246                                                                          mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3247   verticalExceedNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneLeavesBoundary );
3248
3249   PropertyNotification verticalExceedNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3250                                                        OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3251                                                                          mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3252   verticalExceedNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoLeavesBoundary );
3253
3254   // Within vertical boundary
3255   PropertyNotification verticalWithinNotificationOne = mSelectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3256                                                        InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3257                                                                         mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3258   verticalWithinNotificationOne.NotifySignal().Connect( this, &TextInput::OnHandleOneWithinBoundary );
3259
3260   PropertyNotification verticalWithinNotificationTwo = mSelectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
3261                                                        InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
3262                                                                         mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
3263   verticalWithinNotificationTwo.NotifySignal().Connect( this, &TextInput::OnHandleTwoWithinBoundary );
3264 }
3265
3266 void TextInput::CreateSelectionHandles( std::size_t start, std::size_t end, Dali::Image handleOneImage,  Dali::Image handleTwoImage )
3267 {
3268   mSelectionHandleOnePosition = start;
3269   mSelectionHandleTwoPosition = end;
3270
3271   if ( !mSelectionHandleOne )
3272   {
3273     // create normal and pressed images
3274     mSelectionHandleOneImage = ResourceImage::New( DEFAULT_SELECTION_HANDLE_ONE );
3275     mSelectionHandleOneImagePressed = ResourceImage::New( DEFAULT_SELECTION_HANDLE_ONE_PRESSED );
3276
3277     mSelectionHandleOne = ImageActor::New( mSelectionHandleOneImage );
3278     mSelectionHandleOne.SetName("SelectionHandleOne");
3279     mSelectionHandleOne.SetParentOrigin( ParentOrigin::TOP_LEFT );
3280     mSelectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text.
3281     mIsSelectionHandleOneFlipped = false;
3282     mSelectionHandleOne.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text
3283
3284     mHandleOneGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3285     mHandleOneGrabArea.SetName("SelectionHandleOneGrabArea");
3286
3287     mHandleOneGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) );  // grab area to be larger than text actor
3288     mHandleOneGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3289
3290     mTapDetector.Attach( mHandleOneGrabArea );
3291     mPanGestureDetector.Attach( mHandleOneGrabArea );
3292
3293     mHandleOneGrabArea.TouchedSignal().Connect(this,&TextInput::OnHandleOneTouched);
3294
3295     mSelectionHandleOne.Add( mHandleOneGrabArea );
3296     mActiveLayer.Add( mSelectionHandleOne );
3297   }
3298
3299   if ( !mSelectionHandleTwo )
3300   {
3301     // create normal and pressed images
3302     mSelectionHandleTwoImage = ResourceImage::New( DEFAULT_SELECTION_HANDLE_TWO );
3303     mSelectionHandleTwoImagePressed = ResourceImage::New( DEFAULT_SELECTION_HANDLE_TWO_PRESSED );
3304
3305     mSelectionHandleTwo = ImageActor::New( mSelectionHandleTwoImage );
3306     mSelectionHandleTwo.SetName("SelectionHandleTwo");
3307     mSelectionHandleTwo.SetParentOrigin( ParentOrigin::TOP_LEFT );
3308     mSelectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT );
3309     mIsSelectionHandleTwoFlipped = false;
3310     mSelectionHandleTwo.SetDrawMode(DrawMode::OVERLAY); // ensure grab handle above text
3311
3312     mHandleTwoGrabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move
3313     mHandleTwoGrabArea.SetName("SelectionHandleTwoGrabArea");
3314     mHandleTwoGrabArea.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), RelativeToConstraint( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ) ) );  // grab area to be larger than text actor
3315     mHandleTwoGrabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
3316
3317     mTapDetector.Attach( mHandleTwoGrabArea );
3318     mPanGestureDetector.Attach( mHandleTwoGrabArea );
3319
3320     mHandleTwoGrabArea.TouchedSignal().Connect(this, &TextInput::OnHandleTwoTouched);
3321
3322     mSelectionHandleTwo.Add( mHandleTwoGrabArea );
3323
3324     mActiveLayer.Add( mSelectionHandleTwo );
3325   }
3326
3327   SetUpHandlePropertyNotifications();
3328
3329   // update table as text may have changed.
3330   GetTextLayoutInfo();
3331
3332   Vector3 altPositionOne;    // Alternate (i.e. opposite direction) cursor position.
3333   bool altPositionValidOne;  // Alternate cursor validity flag.
3334   bool directionRTLOne;      // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
3335   Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition, directionRTLOne, altPositionOne, altPositionValidOne );
3336
3337   Vector3 altPositionTwo;    // Alternate (i.e. opposite direction) cursor position.
3338   bool altPositionValidTwo;  // Alternate cursor validity flag.
3339   bool directionRTLTwo;      // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
3340   Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition, directionRTLTwo, altPositionTwo, altPositionValidTwo );
3341
3342   // VCC TODO: This method is a hack for one line.
3343   ChooseRtlSelectionHandlePosition( cursorPositionOne,
3344                                     cursorPositionTwo,
3345                                     altPositionValidOne,
3346                                     altPositionValidTwo,
3347                                     altPositionOne,
3348                                     altPositionTwo );
3349
3350   mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset );
3351   mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset );
3352
3353   // Calculates and set the visibility if the scroll mode is enabled.
3354   bool isSelectionHandleOneVisible = true;
3355   bool isSelectionHandleTwoVisible = true;
3356   if( IsScrollEnabled() )
3357   {
3358     const Vector3& controlSize( GetControlSize() );
3359     isSelectionHandleOneVisible = IsPositionInsideBoundaries( mSelectionHandleOneActualPosition, Size::ZERO, controlSize );
3360     isSelectionHandleTwoVisible = IsPositionInsideBoundaries( mSelectionHandleTwoActualPosition, Size::ZERO, controlSize );
3361     mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible );
3362     mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
3363   }
3364
3365   CreateHighlight();  // function will only create highlight if not already created.
3366 }
3367
3368 Vector3 TextInput::MoveSelectionHandle( SelectionHandleId handleId, const Vector2& displacement )
3369 {
3370   Vector3 actualHandlePosition;
3371
3372   if ( mSelectionHandleOne && mSelectionHandleTwo )
3373   {
3374     const Vector3& controlSize = GetControlSize();
3375
3376     Size cursorSize( CURSOR_THICKNESS, 0.f );
3377
3378     // Get a reference of the wanted selection handle (handle one or two).
3379     Vector3& actualSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOneActualPosition : mSelectionHandleTwoActualPosition;
3380
3381     // Get a reference for the current position of the handle and a copy of its pair
3382     std::size_t& currentSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3383     const std::size_t pairSelectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleTwoPosition : mSelectionHandleOnePosition;
3384
3385     // Get a handle of the selection handle actor
3386     ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3387
3388     // Selection handles should jump to the nearest character
3389     std::size_t newHandlePosition = 0;
3390     ReturnClosestIndex( actualSelectionHandlePosition.GetVectorXY(), newHandlePosition );
3391
3392     Vector3 altPosition;    // Alternate (i.e. opposite direction) cursor position.
3393     bool altPositionValid;  // Alternate cursor validity flag.
3394     bool directionRTL;      // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
3395     actualHandlePosition = GetActualPositionFromCharacterPosition( newHandlePosition, directionRTL, altPosition, altPositionValid );
3396     if( altPositionValid )
3397     {
3398       // Check which of the positions is the closest.
3399       if( fabsf( altPosition.x - actualSelectionHandlePosition.x ) < fabsf( actualHandlePosition.x - actualSelectionHandlePosition.x ) )
3400       {
3401         actualHandlePosition = altPosition;
3402       }
3403     }
3404
3405     bool handleVisible = true;
3406
3407     if( IsScrollEnabled() )
3408     {
3409       mCurrentSelectionId = handleId;
3410
3411       cursorSize.height = GetRowRectFromCharacterPosition( newHandlePosition ).height;
3412       // Restricts the movement of the grab handle inside the boundaries of the text-input.
3413       handleVisible = IsPositionInsideBoundaries( actualHandlePosition,
3414                                                   cursorSize,
3415                                                   controlSize );
3416
3417       if( handleVisible )
3418       {
3419         StopScrollTimer();
3420         mCurrentSelectionHandlePosition = actualHandlePosition;
3421         mScrollDisplacement = Vector2::ZERO;
3422       }
3423       else
3424       {
3425         if( ( actualHandlePosition.x < SCROLL_THRESHOLD ) && ( displacement.x <= 0.f ) )
3426         {
3427           mScrollDisplacement.x = -SCROLL_SPEED;
3428         }
3429         else if( ( actualHandlePosition.x > controlSize.width - SCROLL_THRESHOLD ) && ( displacement.x >= 0.f ) )
3430         {
3431           mScrollDisplacement.x = SCROLL_SPEED;
3432         }
3433         if( ( actualHandlePosition.y < SCROLL_THRESHOLD ) && ( displacement.y <= 0.f ) )
3434         {
3435           mScrollDisplacement.y = -SCROLL_SPEED;
3436         }
3437         else if( ( actualHandlePosition.y > controlSize.height - SCROLL_THRESHOLD ) && ( displacement.y >= 0.f ) )
3438         {
3439           mScrollDisplacement.y = SCROLL_SPEED;
3440         }
3441         StartScrollTimer();
3442       }
3443     }
3444
3445     if ( handleVisible &&                                          // Ensure the handle is visible.
3446          ( newHandlePosition != pairSelectionHandlePosition ) &&   // Ensure handle one is not the same position as handle two.
3447          ( newHandlePosition != currentSelectionHandlePosition ) ) // Ensure the handle has moved.
3448     {
3449       currentSelectionHandlePosition = newHandlePosition;
3450
3451       Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3452       selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3453
3454       UpdateHighlight();
3455
3456       if ( handleId == HandleOne )
3457       {
3458         const TextStyle oldInputStyle( mInputStyle );
3459
3460         // Set Active Style to that of first character in selection
3461         if( mSelectionHandleOnePosition < mStyledText.size() )
3462         {
3463           mInputStyle = ( mStyledText.at( mSelectionHandleOnePosition ) ).mStyle;
3464         }
3465
3466         if( oldInputStyle != mInputStyle )
3467         {
3468           // Updates the line height accordingly with the input style.
3469           UpdateLineHeight();
3470
3471           EmitStyleChangedSignal();
3472         }
3473       }
3474     }
3475   }
3476
3477   return actualHandlePosition; // Returns Handle position passed in if new value not assigned.
3478 }
3479
3480 void TextInput::SetSelectionHandlePosition(SelectionHandleId handleId)
3481 {
3482   const std::size_t selectionHandlePosition = ( handleId == HandleOne ) ? mSelectionHandleOnePosition : mSelectionHandleTwoPosition;
3483   ImageActor selectionHandleActor = ( handleId == HandleOne ) ? mSelectionHandleOne : mSelectionHandleTwo;
3484
3485   if ( selectionHandleActor )
3486   {
3487     const Vector3 actualHandlePosition = GetActualPositionFromCharacterPosition( selectionHandlePosition );
3488     Vector3 selectionHandleOffset = ( handleId == HandleOne ) ? mSelectionHandleOneOffset : mSelectionHandleTwoOffset;
3489     selectionHandleActor.SetPosition( actualHandlePosition + UI_OFFSET + selectionHandleOffset );
3490
3491     if( IsScrollEnabled() )
3492     {
3493       const Size cursorSize( CURSOR_THICKNESS,
3494                              GetRowRectFromCharacterPosition( selectionHandlePosition ).height );
3495       selectionHandleActor.SetVisible( IsPositionInsideBoundaries( actualHandlePosition,
3496                                                                    cursorSize,
3497                                                                    GetControlSize() ) );
3498     }
3499   }
3500 }
3501
3502 void TextInput::GetVisualTextSelection( std::vector<bool>& selectedVisualText, std::size_t startSelection, std::size_t endSelection )
3503 {
3504   selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size(), false );
3505
3506   // VCC Set true/false in logical order. TODO : It needs to be checked.
3507
3508   if( startSelection > endSelection )
3509   {
3510     std::swap( startSelection, endSelection );
3511   }
3512   std::size_t index = 0u;
3513   for( std::vector<bool>::iterator it = selectedVisualText.begin(), endIt = selectedVisualText.end(); it != endIt; ++it, ++index )
3514   {
3515     if( ( index < startSelection ) || ( endSelection <= index ) )
3516     {
3517       *it = false;
3518     }
3519     else
3520     {
3521       *it = true;
3522     }
3523   }
3524 }
3525
3526 // Calculate the dimensions of the quads they will make the highlight mesh
3527 TextInput::HighlightInfo TextInput::CalculateHighlightInfo()
3528 {
3529   // At the moment there is no public API to modify the block alignment option.
3530
3531   mNewHighlightInfo.mQuadList.clear(); // clear last quad information.
3532
3533   if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
3534   {
3535     Toolkit::TextView::CharacterLayoutInfoContainer::iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3536     Toolkit::TextView::CharacterLayoutInfoContainer::iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
3537
3538     // Get vector of flags representing characters that are selected (true) vs unselected (false).
3539     std::vector<bool> selectedVisualText;
3540     GetVisualTextSelection( selectedVisualText, mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
3541     std::vector<bool>::iterator selectedIt = selectedVisualText.begin();
3542     std::vector<bool>::iterator selectedEndIt = selectedVisualText.end();
3543
3544     SelectionState selectionState = SelectionNone;          ///< Current selection status of cursor over entire text.
3545     float rowLeft = 0.0f;
3546     float rowRight = 0.0f;
3547     // Keep track of the TextView's min/max extents. Should be able to query this from TextView.
3548     float maxRowLeft = std::numeric_limits<float>::max();
3549     float maxRowRight = 0.0f;
3550
3551     Toolkit::TextView::CharacterLayoutInfoContainer::iterator lastIt = it;
3552
3553     // Scan through entire text.
3554     while(it != end)
3555     {
3556       // selectionState: None when not in selection, Started when in selection, and Ended when reached end of selection.
3557
3558       Toolkit::TextView::CharacterLayoutInfo& charInfo(*it);
3559       bool charSelected = false;
3560       if( selectedIt != selectedEndIt )
3561       {
3562         charSelected = *selectedIt++;
3563       }
3564
3565       if( selectionState == SelectionNone )
3566       {
3567         if( charSelected )
3568         {
3569           selectionState = SelectionStarted;
3570           rowLeft = charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3571           rowRight = rowLeft + charInfo.mSize.width;
3572         }
3573       }
3574       else if( selectionState == SelectionStarted )
3575       {
3576         // break selection on:
3577         // 1. new line causing selection break. (\n or wordwrap)
3578         // 2. character not selected.
3579         if( !charSelected ||
3580             ( charInfo.mPosition.y - lastIt->mPosition.y > CHARACTER_THRESHOLD ) )
3581         {
3582           // finished selection.
3583           // TODO: TextView should have a table of visual rows, and each character a reference to the row
3584           // that it resides on. That way this enumeration is not necessary.
3585           Vector2 min, max;
3586           if(lastIt->mIsNewParagraphChar)
3587           {
3588             // 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.
3589             lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3590           }
3591           const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3592           maxRowLeft = std::min(maxRowLeft, min.x);
3593           maxRowRight = std::max(maxRowRight, max.x);
3594           float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3595           float rowTop = rowBottom - rowSize.height;
3596
3597           // Still selected, and block-align mode then set rowRight to max, so it can be clamped afterwards
3598           if(charSelected)
3599           {
3600             rowRight = std::numeric_limits<float>::max();
3601           }
3602           mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3603
3604           selectionState = SelectionNone;
3605
3606           // Still selected? start a new selection
3607           if( charSelected )
3608           {
3609             // if block-align mode then set rowLeft to min, so it can be clamped afterwards
3610             rowLeft = 0.0f;
3611             rowRight = ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width;
3612             selectionState = SelectionStarted;
3613           }
3614         }
3615         else
3616         {
3617           // build up highlight(s) with this selection data.
3618           rowLeft = std::min( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x, rowLeft );
3619           rowRight = std::max( ( charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x ) + charInfo.mSize.width, rowRight );
3620         }
3621       }
3622
3623       lastIt = it++;
3624     }
3625
3626     // If reached end, and still on selection, then close selection.
3627     if(it == end)
3628     {
3629       if(selectionState == SelectionStarted)
3630       {
3631         // finished selection.
3632         Vector2 min, max;
3633         if(lastIt->mIsNewParagraphChar)
3634         {
3635           lastIt = std::max( mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), lastIt - 1 );
3636         }
3637         const Size rowSize( GetRowRectFromCharacterPosition( lastIt - mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), min, max ) );
3638         maxRowLeft = std::min(maxRowLeft, min.x);
3639         maxRowRight = std::max(maxRowRight, max.x);
3640         float rowBottom = lastIt->mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3641         float rowTop = rowBottom - rowSize.height;
3642         mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3643       }
3644     }
3645
3646     // Get the top left and bottom right corners.
3647     const Toolkit::TextView::CharacterLayoutInfo& firstCharacter( *mTextLayoutInfo.mCharacterLayoutInfoTable.begin() );
3648     const Vector2 topLeft( maxRowLeft, firstCharacter.mPosition.y - firstCharacter.mSize.height );
3649     const Vector2 bottomRight( topLeft.x + mTextLayoutInfo.mTextSize.width, topLeft.y + mTextLayoutInfo.mTextSize.height );
3650
3651     // Clamp quads so they appear to clip to borders of the whole text.
3652     mNewHighlightInfo.Clamp2D( topLeft, bottomRight );
3653
3654     // For block-align align Further Clamp quads to max left and right extents
3655       // BlockAlign: Will adjust highlight to block:
3656       // i.e.
3657       //   H[ello] (top row right = max of all rows right)
3658       // [--this-] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3659       // [is some] (middle rows' left = min of all rows left, middle rows' right = max of all rows right)
3660       // [text] (bottom row left = min of all rows left)
3661       // (common in SMS messaging selection)
3662       //
3663       // As opposed to the default which is tight text highlighting.
3664       //   H[ello]
3665       //   [this]
3666       // [is some]
3667       // [text]
3668       // (common in regular text editors/web browser selection)
3669       mNewHighlightInfo.Clamp2D( Vector2(maxRowLeft, topLeft.y), Vector2(maxRowRight, bottomRight.y ) );
3670
3671     // Finally clamp quads again so they don't exceed the boundry of the control.
3672     const Vector3& controlSize = GetControlSize();
3673     mNewHighlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
3674   } // end if
3675
3676   return mNewHighlightInfo;
3677 }
3678
3679 // VCC TODO: two methods are not needed. this one is a quick hack to fix PLMs. Should implement one which support both directions.
3680 // This method creates one quad per character so different selection boxes for a mix of LTR and RTL languages are created.
3681 TextInput::HighlightInfo TextInput::CalculateHighlightInfoRtl()
3682 {
3683   // At the moment there is no public API to modify the block alignment option.
3684
3685   mNewHighlightInfo.mQuadList.clear(); // clear last quad information.
3686
3687   if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() )
3688   {
3689     Toolkit::TextView::CharacterLayoutInfoContainer::iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
3690     Toolkit::TextView::CharacterLayoutInfoContainer::iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end();
3691
3692     // Get vector of flags representing characters that are selected (true) vs unselected (false).
3693     std::vector<bool> selectedVisualText;
3694     GetVisualTextSelection( selectedVisualText, mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
3695     std::vector<bool>::iterator selectedIt = selectedVisualText.begin();
3696     std::vector<bool>::iterator selectedEndIt = selectedVisualText.end();
3697
3698     // SelectionState selectionState = SelectionNone;          ///< Current selection status of cursor over entire text.
3699     float rowLeft = 0.0f;
3700     float rowRight = 0.0f;
3701
3702     // VCC TODO this is valid for one line.
3703     Vector2 min, max;
3704     const Size rowSize = GetRowRectFromCharacterPosition( 0, min, max );
3705
3706     // Scan through entire text.
3707     while(it != end)
3708     {
3709       // selectionState: None when not in selection, Started when in selection, and Ended when reached end of selection.
3710
3711       Toolkit::TextView::CharacterLayoutInfo& charInfo(*it);
3712       bool charSelected = false;
3713       if( selectedIt != selectedEndIt )
3714       {
3715         charSelected = *selectedIt++;
3716       }
3717
3718       if( charSelected )
3719       {
3720         rowLeft = charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x;
3721         rowRight = rowLeft + charInfo.mSize.width;
3722
3723         float rowBottom = charInfo.mPosition.y - mTextLayoutInfo.mScrollOffset.y;
3724         float rowTop = rowBottom - rowSize.height;
3725         mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom );
3726       }
3727
3728       ++it;
3729     }
3730
3731     // Finally clamp quads again so they don't exceed the boundry of the control.
3732     const Vector3& controlSize = GetControlSize();
3733     mNewHighlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
3734   } // end if
3735
3736   return mNewHighlightInfo;
3737 }
3738
3739 void TextInput::UpdateHighlight()
3740 {
3741 //  Construct a Mesh with a texture to be used as the highlight 'box' for selected text
3742 //
3743 //  Example scenarios where mesh is made from 3, 1, 2, 2 ,3 or 3 quads.
3744 //
3745 //   [ TOP   ]  [ TOP ]      [TOP ]  [ TOP    ]      [ TOP  ]      [ TOP  ]
3746 //  [ MIDDLE ]             [BOTTOM]  [BOTTOM]      [ MIDDLE ]   [ MIDDLE  ]
3747 //  [ BOTTOM]                                      [ MIDDLE ]   [ MIDDLE  ]
3748 //                                                 [BOTTOM]     [ MIDDLE  ]
3749 //                                                              [BOTTOM]
3750 //
3751 //  Each quad is created as 2 triangles.
3752 //  Middle is just 1 quad regardless of its size.
3753 //
3754 //  (0,0)         (0,0)
3755 //     0*    *2     0*       *2
3756 //     TOP          TOP
3757 //     3*    *1     3*       *1
3758 //  4*       *1     4*     *6
3759 //     MIDDLE         BOTTOM
3760 //  6*       *5     7*     *5
3761 //  6*    *8
3762 //   BOTTOM
3763 //  9*    *7
3764 //
3765
3766   if ( mHighlightMeshActor )
3767   {
3768     // vertex and triangle buffers should always be present if MeshActor is alive.
3769     HighlightInfo newHighlightInfo = CalculateHighlightInfoRtl();
3770     MeshData::VertexContainer vertices;
3771     Dali::MeshData::FaceIndices faceIndices;
3772
3773     if( !newHighlightInfo.mQuadList.empty() )
3774     {
3775       std::vector<QuadCoordinates>::iterator iter = newHighlightInfo.mQuadList.begin();
3776       std::vector<QuadCoordinates>::iterator endIter = newHighlightInfo.mQuadList.end();
3777
3778       // vertex position defaults to (0 0 0)
3779       MeshData::Vertex vertex;
3780       // set normal for all vertices as (0 0 1) pointing outward from TextInput Actor.
3781       vertex.nZ = 1.0f;
3782
3783       for(std::size_t v = 0; iter != endIter; ++iter,v+=4 )
3784       {
3785         // Add each quad geometry (a sub-selection) to the mesh data.
3786
3787         // 0-----1
3788         // |\    |
3789         // | \ A |
3790         // |  \  |
3791         // | B \ |
3792         // |    \|
3793         // 2-----3
3794
3795         QuadCoordinates& quad = *iter;
3796         // top-left (v+0)
3797         vertex.x = quad.min.x;
3798         vertex.y = quad.min.y;
3799         vertices.push_back( vertex );
3800
3801         // top-right (v+1)
3802         vertex.x = quad.max.x;
3803         vertex.y = quad.min.y;
3804         vertices.push_back( vertex );
3805
3806         // bottom-left (v+2)
3807         vertex.x = quad.min.x;
3808         vertex.y = quad.max.y;
3809         vertices.push_back( vertex );
3810
3811         // bottom-right (v+3)
3812         vertex.x = quad.max.x;
3813         vertex.y = quad.max.y;
3814         vertices.push_back( vertex );
3815
3816         // triangle A (3, 1, 0)
3817         faceIndices.push_back( v + 3 );
3818         faceIndices.push_back( v + 1 );
3819         faceIndices.push_back( v );
3820
3821         // triangle B (0, 2, 3)
3822         faceIndices.push_back( v );
3823         faceIndices.push_back( v + 2 );
3824         faceIndices.push_back( v + 3 );
3825
3826         mMeshData.SetFaceIndices( faceIndices );
3827       }
3828
3829       BoneContainer bones(0); // passed empty as bones not required
3830       mMeshData.SetData( vertices, faceIndices, bones, mCustomMaterial );
3831       mHighlightMesh.UpdateMeshData(mMeshData);
3832     }
3833   }
3834 }
3835
3836 void TextInput::ClearPopup()
3837 {
3838   mPopupPanel.Clear();
3839 }
3840
3841 void TextInput::AddPopupOptions()
3842 {
3843   mPopupPanel.AddPopupOptions();
3844 }
3845
3846 void TextInput::SetPopupPosition( const Vector3& position, const Vector2& alternativePosition )
3847 {
3848   const Vector3& visiblePopUpSize = mPopupPanel.GetVisibileSize();
3849
3850   Vector3 clampedPosition ( position );
3851   Vector3 tailOffsetPosition ( position );
3852
3853   float xOffSet( 0.0f );
3854
3855   Actor self = Self();
3856   const Vector3 textViewTopLeftWorldPosition = self.GetCurrentWorldPosition() - self.GetCurrentSize()*0.5f;
3857
3858   const float popUpLeft = textViewTopLeftWorldPosition.x + position.x - visiblePopUpSize.width*0.5f;
3859   const float popUpTop = textViewTopLeftWorldPosition.y + position.y - visiblePopUpSize.height;
3860
3861   // Clamp to left or right or of boundary
3862   if( popUpLeft < mBoundingRectangleWorldCoordinates.x )
3863   {
3864     xOffSet = mBoundingRectangleWorldCoordinates.x - popUpLeft ;
3865   }
3866   else if ( popUpLeft + visiblePopUpSize.width > mBoundingRectangleWorldCoordinates.z )
3867   {
3868     xOffSet = mBoundingRectangleWorldCoordinates.z - ( popUpLeft + visiblePopUpSize.width );
3869   }
3870
3871   clampedPosition.x = position.x + xOffSet;
3872   tailOffsetPosition.x = -xOffSet;
3873
3874   // Check if top left of PopUp outside of top bounding rectangle, if so then flip to lower position.
3875   bool flipTail( false );
3876
3877   if ( popUpTop < mBoundingRectangleWorldCoordinates.y )
3878   {
3879     clampedPosition.y = alternativePosition.y + visiblePopUpSize.height;
3880     flipTail = true;
3881   }
3882
3883   mPopupPanel.GetRootActor().SetPosition( clampedPosition );
3884   mPopupPanel.SetTailPosition( tailOffsetPosition, flipTail );
3885 }
3886
3887 void TextInput::HidePopup(bool animate, bool signalFinished )
3888 {
3889   if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown )  )
3890   {
3891     mPopupPanel.Hide( animate );
3892
3893     if( animate && signalFinished )
3894     {
3895       mPopupPanel.HideFinishedSignal().Connect( this, &TextInput::OnPopupHideFinished );
3896     }
3897   }
3898 }
3899
3900 void TextInput::ShowPopup( bool animate )
3901 {
3902   Vector3 position;
3903   Vector2 alternativePopupPosition;
3904
3905   if(mHighlightMeshActor && mState == StateEdit)
3906   {
3907     Vector3 topHandle;
3908     Vector3 bottomHandle; // referring to the bottom most point of the handle or the bottom line of selection.
3909     Size rowSize;
3910     // When text is selected, show popup above top handle (and text), or below bottom handle.
3911     // topHandle: referring to the top most point of the handle or the top line of selection.
3912     if ( mSelectionHandleTwoActualPosition.y > mSelectionHandleOneActualPosition.y )
3913     {
3914       topHandle = mSelectionHandleOneActualPosition;
3915       bottomHandle = mSelectionHandleTwoActualPosition;
3916       rowSize= GetRowRectFromCharacterPosition( mSelectionHandleOnePosition );
3917     }
3918     else
3919     {
3920       topHandle = mSelectionHandleTwoActualPosition;
3921       bottomHandle = mSelectionHandleOneActualPosition;
3922       rowSize = GetRowRectFromCharacterPosition( mSelectionHandleTwoPosition );
3923     }
3924     topHandle.y += -mPopupOffsetFromText.y - rowSize.height;
3925     position = Vector3(topHandle.x, topHandle.y, 0.0f);
3926
3927     float xPosition = ( fabsf( topHandle.x - bottomHandle.x ) )*0.5f + std::min( mSelectionHandleOneActualPosition.x , mSelectionHandleTwoActualPosition.x );
3928
3929     position.x = xPosition;
3930
3931     // Alternative position if no upper space
3932     bottomHandle.y += GetSelectionHandleSize().y + mPopupOffsetFromText.w;
3933     alternativePopupPosition = Vector2 ( position.x, bottomHandle.y );
3934   }
3935   else
3936   {
3937     // When no text is selected, show popup at world position of grab handle or cursor
3938     position = GetActualPositionFromCharacterPosition( mCursorPosition );
3939     const Size rowSize = GetRowRectFromCharacterPosition( mCursorPosition );
3940     position.y -= ( mPopupOffsetFromText.y + rowSize.height );
3941     // if can't be positioned above, then position below row.
3942     alternativePopupPosition = Vector2( position.x, position.y ); // default if no grab handle
3943     if ( mGrabHandle )
3944     {
3945       // If grab handle enabled then position pop-up below the grab handle.
3946       alternativePopupPosition.y = rowSize.height + mGrabHandle.GetCurrentSize().height + mPopupOffsetFromText.w +50.0f;
3947     }
3948   }
3949
3950   SetPopupPosition( position, alternativePopupPosition );
3951
3952   // Show popup
3953   mPopupPanel.Show( Self(), animate );
3954   StartMonitoringStageForTouch();
3955
3956   mPopupPanel.PressedSignal().Connect( this, &TextInput::OnPopupButtonPressed );
3957 }
3958
3959 void TextInput::ShowPopupCutCopyPaste()
3960 {
3961   ClearPopup();
3962
3963   mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3964   // Check the selected text is whole text or not.
3965   if( IsTextSelected() && ( mStyledText.size() != GetSelectedText().size() ) )
3966   {
3967     mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3968   }
3969
3970   if ( !mStyledText.empty() && IsTextSelected() )
3971   {
3972     mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCopy, true );
3973     mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, true );
3974   }
3975
3976   if( mClipboard && mClipboard.NumberOfItems() )
3977   {
3978     mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
3979     mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
3980   }
3981
3982   AddPopupOptions();
3983
3984   mPopupPanel.Hide(false);
3985   ShowPopup();
3986 }
3987
3988 void TextInput::SetUpPopupSelection( bool showCutButton )
3989 {
3990   ClearPopup();
3991   mPopupPanel.CreateOrderedListOfOptions(); // todo Move this so only run when order has changed
3992   // If no text exists then don't offer to select
3993   if ( !mStyledText.empty() )
3994   {
3995     mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelectAll, true );
3996     mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsSelect, true );
3997     mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsCut, ( showCutButton && IsTextSelected() ) );
3998   }
3999   // if clipboard has valid contents then offer paste option
4000   if( mClipboard && mClipboard.NumberOfItems() )
4001   {
4002     mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsPaste, true );
4003     mPopupPanel.TogglePopupButtonOnOff( TextInputPopup::ButtonsClipboard, true );
4004   }
4005
4006   AddPopupOptions();
4007
4008   mPopupPanel.Hide(false);
4009 }
4010
4011 bool TextInput::ReturnClosestIndex(const Vector2& source, std::size_t& closestIndex )
4012 {
4013   bool found = false;
4014   closestIndex = 0;
4015
4016   std::vector<Toolkit::TextView::CharacterLayoutInfo> matchedCharacters;
4017   bool lastRightToLeftChar(false);          /// RTL state of previous character encountered (character on the left of touch point)
4018   bool rightToLeftChar(false);              /// RTL state of current character encountered (character on the right of touch point)
4019   float glyphIntersection(0.0f);            /// Glyph intersection, the point between the two nearest characters touched.
4020
4021   const Vector2 sourceScrollOffset( source + mTextLayoutInfo.mScrollOffset );
4022
4023   if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4024   {
4025     float closestYdifference = std::numeric_limits<float>::max();
4026     std::size_t lineOffset = 0;                  /// Keep track of position of the first character on the matched line of interest.
4027     std::size_t numberOfMatchedCharacters = 0;
4028
4029     // 1. Find closest character line to y part of source, create vector of all entries in that Y position
4030     // TODO: There should be an easy call to enumerate through each visual line, instead of each character on all visual lines.
4031
4032     for( std::vector<Toolkit::TextView::CharacterLayoutInfo>::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin(), endIt = mTextLayoutInfo.mCharacterLayoutInfoTable.end(); it != endIt; ++it )
4033     {
4034       const Toolkit::TextView::CharacterLayoutInfo& info( *it );
4035       float baselinePosition = info.mPosition.y - info.mDescender;
4036
4037       if( info.mIsVisible )
4038       {
4039         // store difference between source y point and the y position of the current character
4040         float currentYdifference = fabsf( sourceScrollOffset.y - ( baselinePosition ) );
4041
4042         if(  currentYdifference < closestYdifference  )
4043         {
4044           // closest so far; store this difference and clear previous matchedCharacters as no longer closest
4045           lineOffset = it - mTextLayoutInfo.mCharacterLayoutInfoTable.begin();
4046           closestYdifference = currentYdifference;
4047           matchedCharacters.clear();
4048           numberOfMatchedCharacters = 0; // reset count
4049         }
4050
4051         // add all characters that are on the same Y axis (within the CHARACTER_THRESHOLD) to the matched array.
4052         if( fabsf( closestYdifference - currentYdifference )  < CHARACTER_THRESHOLD )
4053         {
4054           // ignore new line character.
4055           if( !info.mIsNewParagraphChar )
4056           {
4057             matchedCharacters.push_back( info );
4058             numberOfMatchedCharacters++;
4059           }
4060         }
4061       }
4062     } // End of loop checking each character's y position in the character layout table
4063
4064     // Check if last character is a newline, if it is
4065     // then need pretend there is an imaginary line afterwards,
4066     // and check if user is touching below previous line.
4067     const Toolkit::TextView::CharacterLayoutInfo& lastInfo( mTextLayoutInfo.mCharacterLayoutInfoTable[mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1] );
4068
4069     if( ( lastInfo.mIsVisible ) && ( lastInfo.mIsNewParagraphChar ) && ( sourceScrollOffset.y > lastInfo.mPosition.y ) )
4070     {
4071       closestIndex = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
4072     }
4073     else
4074     {
4075       // 2 Iterate through matching list of y positions and find closest matching X position.
4076
4077       bool matched( false );
4078
4079       // Traverse the characters in the visual order. VCC TODO: check for more than one line.
4080       std::size_t visualIndex = 0u;
4081       const std::size_t matchedCharactersSize = matchedCharacters.size();
4082       for( ; visualIndex < matchedCharactersSize; ++visualIndex )
4083       {
4084         const Toolkit::TextView::CharacterLayoutInfo& info( *( matchedCharacters.begin() + mTextLayoutInfo.mCharacterVisualToLogicalMap[visualIndex] ) );
4085
4086         if( info.mIsVisible )
4087         {
4088           // stop when on left side of character's center.
4089           const float characterMidPointPosition = info.mPosition.x  + ( info.mSize.width * 0.5f ) ;
4090           if( sourceScrollOffset.x < characterMidPointPosition )
4091           {
4092             if(info.mIsRightToLeftCharacter)
4093             {
4094               rightToLeftChar = true;
4095             }
4096             glyphIntersection = info.mPosition.x;
4097             matched = true;
4098             break;
4099           }
4100
4101           lastRightToLeftChar = info.mIsRightToLeftCharacter;
4102         }
4103       }
4104
4105       if( visualIndex == matchedCharactersSize )
4106       {
4107         rightToLeftChar = lastRightToLeftChar;
4108       }
4109
4110       closestIndex = lineOffset + visualIndex;
4111
4112       mClosestCursorPositionEOL = false; // reset
4113       if( ( visualIndex == matchedCharactersSize ) && !matched )
4114       {
4115         mClosestCursorPositionEOL = true; // Reached end of matched characters in closest line but no match so cursor should be after last character.
4116       }
4117
4118       // For RTL characters, need to adjust closestIndex by 1 (as the inequality above would be reverse)
4119       if( rightToLeftChar && lastRightToLeftChar )
4120       {
4121         --closestIndex; // (-1 = numeric_limits<std::size_t>::max())
4122       }
4123     }
4124   }
4125
4126   // closestIndex is the visual index, need to convert it to the logical index
4127   if( !mTextLayoutInfo.mCharacterVisualToLogicalMap.empty() )
4128   {
4129     if( closestIndex < mTextLayoutInfo.mCharacterVisualToLogicalMap.size() )
4130     {
4131       // Checks for situations where user is touching between LTR and RTL
4132       // characters. To identify if the user means the end of a LTR string
4133       // or the beginning of an RTL string, and vice versa.
4134       if( closestIndex > 0 )
4135       {
4136         if( rightToLeftChar && !lastRightToLeftChar )
4137         {
4138           // [LTR] [RTL]
4139           //   |..|..|
4140           //   AAA BBB
4141           // A: In this touch range, the user is indicating that they wish to place
4142           // the cursor at the end of the LTR text.
4143           // B: In this touch range, the user is indicating that they wish to place
4144           // the cursor at the end of the RTL text.
4145
4146           // Result of touching A area:
4147           // [.....LTR]|[RTL......]+
4148           //
4149           // |: primary cursor (for typing LTR chars)
4150           // +: secondary cursor (for typing RTL chars)
4151
4152           // Result of touching B area:
4153           // [.....LTR]+[RTL......]|
4154           //
4155           // |: primary cursor (for typing RTL chars)
4156           // +: secondary cursor (for typing LTR chars)
4157
4158           if( sourceScrollOffset.x < glyphIntersection )
4159           {
4160             --closestIndex;
4161           }
4162         }
4163         else if( !rightToLeftChar && lastRightToLeftChar )
4164         {
4165           if( sourceScrollOffset.x < glyphIntersection )
4166           {
4167             --closestIndex;
4168           }
4169         }
4170       }
4171
4172       closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap[closestIndex];
4173       // If user touched a left-side of RTL char, and the character on the left was an LTR then position logical cursor
4174       // one further ahead
4175       if( rightToLeftChar && !lastRightToLeftChar )
4176       {
4177         ++closestIndex;
4178       }
4179     }
4180     else if( closestIndex == std::numeric_limits<std::size_t>::max() ) // -1 RTL (after last arabic character on line)
4181     {
4182       closestIndex = mTextLayoutInfo.mCharacterVisualToLogicalMap.size();
4183     }
4184     else if( mTextLayoutInfo.mCharacterLayoutInfoTable[ mTextLayoutInfo.mCharacterVisualToLogicalMap[ closestIndex - 1 ] ].mIsRightToLeftCharacter ) // size() LTR (after last european character on line)
4185     {
4186       closestIndex = 0;
4187     }
4188   }
4189
4190   return found;
4191 }
4192
4193 float TextInput::GetLineJustificationPosition() const
4194 {
4195   const Vector3& size = mDisplayedTextView.GetCurrentSize();
4196   Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4197   float alignmentOffset = 0.f;
4198
4199   // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4200   if( alignment & Toolkit::Alignment::HorizontalLeft )
4201   {
4202     alignmentOffset = 0.f;
4203   }
4204   else if( alignment & Toolkit::Alignment::HorizontalCenter )
4205   {
4206     alignmentOffset = 0.5f * ( size.width - mTextLayoutInfo.mTextSize.width );
4207   }
4208   else if( alignment & Toolkit::Alignment::HorizontalRight )
4209   {
4210     alignmentOffset = size.width - mTextLayoutInfo.mTextSize.width;
4211   }
4212
4213   Toolkit::TextView::LineJustification justification = mDisplayedTextView.GetLineJustification();
4214   float justificationOffset = 0.f;
4215
4216   switch( justification )
4217   {
4218     case Toolkit::TextView::Left:
4219     {
4220       justificationOffset = 0.f;
4221       break;
4222     }
4223     case Toolkit::TextView::Center:
4224     {
4225       justificationOffset = 0.5f * mTextLayoutInfo.mTextSize.width;
4226       break;
4227     }
4228     case Toolkit::TextView::Right:
4229     {
4230       justificationOffset = mTextLayoutInfo.mTextSize.width;
4231       break;
4232     }
4233     case Toolkit::TextView::Justified:
4234     {
4235       justificationOffset = 0.f;
4236       break;
4237     }
4238     default:
4239     {
4240       DALI_ASSERT_ALWAYS( false );
4241     }
4242   } // end switch
4243
4244   return alignmentOffset + justificationOffset;
4245 }
4246
4247 Vector3 TextInput::PositionCursorAfterWordWrap( std::size_t characterPosition ) const
4248 {
4249   /* Word wrap occurs automatically in TextView when the exceed policy moves a word to the next line when not enough space on current.
4250      A newline character is not inserted in this case */
4251
4252   Vector3 cursorPosition;
4253
4254   Toolkit::TextView::CharacterLayoutInfo currentCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4255
4256   bool noWrap = true;
4257
4258   if( characterPosition > 0u )
4259   {
4260     Toolkit::TextView::CharacterLayoutInfo previousCharInfo = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition - 1u ];
4261
4262     // If previous character on a different line then use current characters position
4263     if( fabsf( (currentCharInfo.mPosition.y - currentCharInfo.mDescender )  - ( previousCharInfo.mPosition.y - previousCharInfo.mDescender) ) > Math::MACHINE_EPSILON_1000 )
4264     {
4265       // VCC TODO: PositionCursorAfterWordWrap currently doesn't work for multiline. Need to check this branch.
4266       if ( mClosestCursorPositionEOL )
4267       {
4268         cursorPosition = Vector3( previousCharInfo.mPosition.x + previousCharInfo.mSize.width, previousCharInfo.mPosition.y, previousCharInfo.mPosition.z ) ;
4269       }
4270       else
4271       {
4272         cursorPosition = Vector3( currentCharInfo.mPosition );
4273       }
4274
4275       noWrap = false;
4276     }
4277   }
4278
4279   if( noWrap )
4280   {
4281     // If the character is left to right, the position is the character's position plus its width.
4282     const float ltrOffset = !currentCharInfo.mIsRightToLeftCharacter ? currentCharInfo.mSize.width : 0.f;
4283
4284     cursorPosition.x = currentCharInfo.mPosition.x  + ltrOffset;
4285     cursorPosition.y = currentCharInfo.mPosition.y;
4286   }
4287
4288   return cursorPosition;
4289 }
4290
4291 Vector3 TextInput::GetActualPositionFromCharacterPosition( std::size_t characterPosition ) const
4292 {
4293   bool direction = false;
4294   Vector3 alternatePosition;
4295   bool alternatePositionValid = false;
4296
4297   return GetActualPositionFromCharacterPosition( characterPosition, direction, alternatePosition, alternatePositionValid );
4298 }
4299
4300 Vector3 TextInput::GetActualPositionFromCharacterPosition( std::size_t characterPosition, bool& directionRTL, Vector3& alternatePosition, bool& alternatePositionValid ) const
4301 {
4302   DALI_ASSERT_DEBUG( ( mTextLayoutInfo.mCharacterLayoutInfoTable.size() == mTextLayoutInfo.mCharacterLogicalToVisualMap.size() ) &&
4303                      ( mTextLayoutInfo.mCharacterLayoutInfoTable.size() == mTextLayoutInfo.mCharacterVisualToLogicalMap.size() ) &&
4304                      "TextInput::GetActualPositionFromCharacterPosition. All layout tables must have the same size." );
4305
4306   Vector3 cursorPosition( 0.f, 0.f, 0.f );
4307
4308   alternatePositionValid = false;
4309   directionRTL = false;
4310
4311   if( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4312   {
4313     if( characterPosition == 0u )
4314     {
4315       // When the cursor position is at the beginning, it should be at the start of the current character.
4316       // If the current character is LTR, then the start is on the right side of the glyph.
4317       // If the current character is RTL, then the start is on the left side of the glyph.
4318
4319       if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() ) ).mIsVisible )
4320       {
4321          characterPosition = FindVisibleCharacter( Right, 0u );
4322       }
4323
4324       const Toolkit::TextView::CharacterLayoutInfo& info = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4325       const float rtlOffset = info.mIsRightToLeftCharacter ? info.mSize.width : 0.0f;
4326
4327       cursorPosition.x = info.mPosition.x + rtlOffset;
4328       cursorPosition.y = info.mPosition.y;
4329       directionRTL = info.mIsRightToLeftCharacter;
4330     }
4331     else if( characterPosition > 0u )
4332     {
4333       // Get the direction of the paragraph.
4334       const std::size_t startCharacterPosition = GetRowStartFromCharacterPosition( characterPosition );
4335       const bool isParagraphRightToLeft = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + startCharacterPosition ) ).mIsRightToLeftCharacter;
4336
4337       // When cursor is not at beginning, consider possibility of
4338       // showing 2 cursors. (whereas at beginning we only ever show one cursor)
4339
4340       // Cursor position should be the end of the last character.
4341       // If the last character is LTR, then the end is on the right side of the glyph.
4342       // If the last character is RTL, then the end is on the left side of the glyph.
4343
4344       --characterPosition;
4345
4346       if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + characterPosition ) ).mIsVisible )
4347       {
4348         characterPosition = FindVisibleCharacter( Left, characterPosition );
4349       }
4350
4351       Toolkit::TextView::CharacterLayoutInfo info = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4352       if( ( characterPosition > 0u ) && info.mIsNewParagraphChar && !IsScrollEnabled() )
4353       {
4354         // VCC TODO : check for a new paragraph character.
4355
4356         // Prevents the cursor to exceed the boundary if the last visible character is a 'new line character' and the scroll is not enabled.
4357         const Vector3& size = GetControlSize();
4358
4359         if( info.mPosition.y + info.mSize.height - mDisplayedTextView.GetLineHeightOffset() > size.height )
4360         {
4361           --characterPosition;
4362         }
4363         info = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4364       }
4365
4366       if( !info.mIsNewParagraphChar )
4367       {
4368         cursorPosition = PositionCursorAfterWordWrap( characterPosition ); // Get position of cursor/handles taking in account auto word wrap.
4369       }
4370       else
4371       {
4372         // VCC TODO : check for a new paragraph character.
4373
4374         // When cursor points to first character on new line, position cursor at the start of this glyph.
4375         if( characterPosition < mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4376         {
4377           const Toolkit::TextView::CharacterLayoutInfo& infoNext = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterPosition ];
4378           const float start( infoNext.mIsRightToLeftCharacter ? infoNext.mSize.width : 0.0f );
4379
4380           cursorPosition.x = infoNext.mPosition.x + start;
4381           cursorPosition.y = infoNext.mPosition.y;
4382         }
4383         else
4384         {
4385           // If cursor points to the end of text, then can only position
4386           // cursor where the new line starts based on the line-justification position.
4387           cursorPosition.x = GetLineJustificationPosition();
4388
4389           if( characterPosition == mTextLayoutInfo.mCharacterLogicalToVisualMap.size() )
4390           {
4391             // If this is after the last character, then we can assume that the new cursor
4392             // should be exactly one row below the current row.
4393
4394             const Size rowRect = GetRowRectFromCharacterPosition( characterPosition - 1u );
4395             cursorPosition.y = info.mPosition.y + rowRect.height;
4396           }
4397           else
4398           {
4399             // If this is not after last character, then we can use this row's height.
4400             // should be exactly one row below the current row.
4401
4402             const Size rowRect = GetRowRectFromCharacterPosition( characterPosition );
4403             cursorPosition.y = info.mPosition.y + rowRect.height;
4404           }
4405         }
4406       }
4407
4408       directionRTL = info.mIsRightToLeftCharacter;
4409
4410       if( 1u < mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4411       {
4412         // 1. When the cursor is neither at the beginning or the end,
4413         // we can show multiple cursors under situations when the cursor is
4414         // between RTL and LTR text...
4415         if( characterPosition + 1u < mTextLayoutInfo.mCharacterLayoutInfoTable.size() )
4416         {
4417           std::size_t characterAltPosition = characterPosition + 1u;
4418
4419           const Toolkit::TextView::CharacterLayoutInfo& infoAlt = mTextLayoutInfo.mCharacterLayoutInfoTable[ characterAltPosition ];
4420
4421           if(!info.mIsRightToLeftCharacter && infoAlt.mIsRightToLeftCharacter)
4422           {
4423             // Stuation occurs when cursor is at the end of English text (LTR) and beginning of Arabic (RTL)
4424             // Text:     [...LTR...]|[...RTL...]
4425             // Cursor pos:          ^
4426             // Alternate cursor pos:            ^
4427             // In which case we need to display an alternate cursor for the RTL text.
4428
4429             alternatePosition.x = infoAlt.mPosition.x + infoAlt.mSize.width;
4430             alternatePosition.y = infoAlt.mPosition.y;
4431             alternatePositionValid = true;
4432           }
4433           else if(info.mIsRightToLeftCharacter && !infoAlt.mIsRightToLeftCharacter)
4434           {
4435             // Situation occurs when cursor is at end of the Arabic text (LTR) and beginning of English (RTL)
4436             // Text:           |[...RTL...] [...LTR....]
4437             // Cursor pos:     ^
4438             // Alternate cursor pos:       ^
4439             // In which case we need to display an alternate cursor for the RTL text.
4440
4441             alternatePosition.x = infoAlt.mPosition.x;
4442             alternatePosition.y = infoAlt.mPosition.y;
4443             alternatePositionValid = true;
4444           }
4445         }
4446         else
4447         {
4448           // 2. When the cursor is at the end of the text,
4449           // and we have multi-directional text,
4450           // we can also consider showing mulitple cursors.
4451           // The rule here is:
4452           // If first and last characters on row are different
4453           // Directions, then two cursors need to be displayed.
4454
4455           if( info.mIsRightToLeftCharacter != isParagraphRightToLeft )
4456           {
4457             // The last character's direction is differernt than the first one of current paragraph.
4458
4459             // Get first
4460             const Toolkit::TextView::CharacterLayoutInfo& infoStart= mTextLayoutInfo.mCharacterLayoutInfoTable[ GetFirstCharacterWithSameDirection( characterPosition ) ];
4461
4462             if(info.mIsRightToLeftCharacter)
4463             {
4464               // For text Starting as LTR and ending as RTL. End cursor position is as follows:
4465               // Text:     [...LTR...]|[...RTL...]
4466               // Cursor pos:          ^
4467               // Alternate cursor pos:            ^
4468               // In which case we need to display an alternate cursor for the RTL text, this cursor
4469               // should be at the end of the given line.
4470
4471               alternatePosition.x = infoStart.mPosition.x + infoStart.mSize.width;
4472               alternatePosition.y = infoStart.mPosition.y;
4473               alternatePositionValid = true;
4474             }
4475             else if(!info.mIsRightToLeftCharacter) // starting RTL
4476             {
4477               // For text Starting as RTL and ending as LTR. End cursor position is as follows:
4478               // Text:           |[...RTL...] [...LTR....]
4479               // Cursor pos:     ^
4480               // Alternate cursor pos:       ^
4481               // In which case we need to display an alternate cursor for the RTL text.
4482
4483               alternatePosition.x = infoStart.mPosition.x;
4484               alternatePosition.y = infoStart.mPosition.y;
4485               alternatePositionValid = true;
4486             }
4487           }
4488         }
4489       }
4490     } // characterPosition > 0
4491   }
4492   else
4493   {
4494     // If the character table is void, place the cursor accordingly the text alignment.
4495     const Vector3& size = GetControlSize();
4496
4497     Toolkit::Alignment::Type alignment = mDisplayedTextView.GetTextAlignment();
4498     float alignmentOffset = 0.f;
4499
4500     // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4501     if( alignment & Toolkit::Alignment::HorizontalLeft )
4502     {
4503       alignmentOffset = 0.f;
4504     }
4505     else if( alignment & Toolkit::Alignment::HorizontalCenter )
4506     {
4507       alignmentOffset = 0.5f * ( size.width );
4508     }
4509     else if( alignment & Toolkit::Alignment::HorizontalRight )
4510     {
4511       alignmentOffset = size.width;
4512     }
4513
4514     // Work out cursor 'x' position when there are any character accordingly with the text view alignment settings.
4515     cursorPosition.x = alignmentOffset;
4516
4517     // Work out cursor 'y' position when there are any character accordingly with the text view alignment settings.
4518     if( alignment & Toolkit::Alignment::VerticalTop )
4519     {
4520       cursorPosition.y = mLineHeight;
4521     }
4522     else if( alignment & Toolkit::Alignment::VerticalCenter )
4523     {
4524       cursorPosition.y = 0.5f * ( size.height + mLineHeight );
4525     }
4526     else if( alignment & Toolkit::Alignment::VerticalBottom )
4527     {
4528       cursorPosition.y = size.height;
4529     }
4530   }
4531
4532   cursorPosition.x -= mTextLayoutInfo.mScrollOffset.x;
4533   cursorPosition.y -= mTextLayoutInfo.mScrollOffset.y;
4534
4535   if( alternatePositionValid )
4536   {
4537     alternatePosition.x -= mTextLayoutInfo.mScrollOffset.x;
4538     alternatePosition.y -= mTextLayoutInfo.mScrollOffset.y;
4539   }
4540
4541   return cursorPosition;
4542 }
4543
4544 std::size_t TextInput::GetRowStartFromCharacterPosition( std::size_t logicalPosition ) const
4545 {
4546   // scan string from current position to beginning of current line to note direction of line
4547   while( logicalPosition )
4548   {
4549     logicalPosition--;
4550     if( mTextLayoutInfo.mCharacterLayoutInfoTable[logicalPosition].mIsNewParagraphChar )
4551     {
4552       logicalPosition++;
4553       break;
4554     }
4555   }
4556
4557   return logicalPosition;
4558 }
4559
4560 std::size_t TextInput::GetFirstCharacterWithSameDirection( std::size_t logicalPosition ) const
4561 {
4562   const bool isRightToLeft = mTextLayoutInfo.mCharacterLayoutInfoTable[logicalPosition].mIsRightToLeftCharacter;
4563
4564   while( logicalPosition )
4565   {
4566     logicalPosition--;
4567     if( isRightToLeft != mTextLayoutInfo.mCharacterLayoutInfoTable[logicalPosition].mIsRightToLeftCharacter )
4568     {
4569       logicalPosition++;
4570       break;
4571     }
4572   }
4573
4574   return logicalPosition;
4575 }
4576
4577 Size TextInput::GetRowRectFromCharacterPosition(std::size_t characterPosition) const
4578 {
4579   Vector2 min, max;
4580
4581   return GetRowRectFromCharacterPosition( characterPosition, min, max );
4582 }
4583
4584 Size TextInput::GetRowRectFromCharacterPosition( std::size_t characterPosition, Vector2& min, Vector2& max ) const
4585 {
4586   // if we have no text content, then return position 0,0 with width 0, and height the same as cursor height.
4587   if( mTextLayoutInfo.mCharacterLayoutInfoTable.empty() )
4588   {
4589     min = Vector2::ZERO;
4590     max = Vector2(0.0f, mLineHeight);
4591     return max;
4592   }
4593
4594   DALI_ASSERT_DEBUG( characterPosition <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
4595
4596   // Initializes the min and max position.
4597   const std::size_t initialPosition = ( characterPosition == mTextLayoutInfo.mCharacterLayoutInfoTable.size() ) ? characterPosition - 1u : characterPosition;
4598   min = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + initialPosition ) ).mPosition.GetVectorXY();
4599   max = min;
4600
4601   bool found = false;
4602   // 1) Find the line where the character is laid-out.
4603   for( Toolkit::TextView::LineLayoutInfoContainer::const_iterator lineIt = mTextLayoutInfo.mLines.begin(), lineEndIt = mTextLayoutInfo.mLines.end();
4604          !found && ( lineIt != mTextLayoutInfo.mLines.end() );
4605        ++lineIt )
4606   {
4607     const Toolkit::TextView::LineLayoutInfo& lineInfo( *lineIt );
4608
4609     // Index within the whole text to the last character of the current line.
4610     std::size_t lastCharacterOfLine = 0u;
4611
4612     Toolkit::TextView::LineLayoutInfoContainer::const_iterator lineNextIt = lineIt + 1u;
4613     if( lineNextIt != lineEndIt )
4614     {
4615       lastCharacterOfLine = (*lineNextIt).mCharacterGlobalIndex - 1u;
4616     }
4617     else
4618     {
4619       lastCharacterOfLine = mTextLayoutInfo.mCharacterLayoutInfoTable.size() - 1u;
4620     }
4621
4622     // Check if the given chracter position is within the line.
4623     if( ( lineInfo.mCharacterGlobalIndex <= initialPosition ) && ( initialPosition <= lastCharacterOfLine ) )
4624     {
4625       // 2) Get the row rect of all laid-out characters on the line.
4626
4627       // Need to scan all characters of the line because they are in the logical position.
4628       for( Toolkit::TextView::CharacterLayoutInfoContainer::const_iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + lineInfo.mCharacterGlobalIndex,
4629              endIt = mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + lastCharacterOfLine + 1u;
4630            it != endIt;
4631            ++it )
4632       {
4633         const Toolkit::TextView::CharacterLayoutInfo& characterInfo( *it );
4634
4635         min.x = std::min( min.x, characterInfo.mPosition.x );
4636         min.y = std::min( min.y, characterInfo.mPosition.y );
4637         max.x = std::max( max.x, characterInfo.mPosition.x + characterInfo.mSize.width );
4638         max.y = std::max( max.y, characterInfo.mPosition.y + characterInfo.mSize.height );
4639       }
4640
4641       found = true;
4642     }
4643   }
4644
4645   return Size( max.x - min.x, max.y - min.y );
4646 }
4647
4648 bool TextInput::WasTouchedCheck( const Actor& touchedActor ) const
4649 {
4650   Actor popUpPanel = mPopupPanel.GetRootActor();
4651
4652   if ( ( touchedActor == Self() ) || ( touchedActor == popUpPanel ) )
4653   {
4654     return true;
4655   }
4656   else
4657   {
4658     Dali::Actor parent( touchedActor.GetParent() );
4659
4660     if ( parent )
4661     {
4662       return WasTouchedCheck( parent );
4663     }
4664   }
4665
4666   return false;
4667 }
4668
4669 void TextInput::StartMonitoringStageForTouch()
4670 {
4671   Stage stage = Stage::GetCurrent();
4672   stage.TouchedSignal().Connect( this, &TextInput::OnStageTouched );
4673 }
4674
4675 void TextInput::EndMonitoringStageForTouch()
4676 {
4677   Stage stage = Stage::GetCurrent();
4678   stage.TouchedSignal().Disconnect( this, &TextInput::OnStageTouched );
4679 }
4680
4681 void TextInput::OnStageTouched(const TouchEvent& event)
4682 {
4683   if( event.GetPointCount() > 0 )
4684   {
4685     if ( TouchPoint::Down == event.GetPoint(0).state )
4686     {
4687       const Actor touchedActor(event.GetPoint(0).hitActor);
4688
4689       bool popUpShown( false );
4690
4691       if ( ( mPopupPanel.GetState() == TextInputPopup::StateShowing ) || ( mPopupPanel.GetState() == TextInputPopup::StateShown ) )
4692       {
4693         popUpShown = true;
4694       }
4695
4696       bool textInputTouched = (touchedActor && WasTouchedCheck( touchedActor ));
4697
4698       if ( ( mHighlightMeshActor || popUpShown ) && !textInputTouched )
4699       {
4700         EndMonitoringStageForTouch();
4701         HidePopup( true, false );
4702       }
4703
4704       if ( ( IsGrabHandleEnabled() && mGrabHandle  ) && !textInputTouched )
4705       {
4706         EndMonitoringStageForTouch();
4707         ShowGrabHandleAndSetVisibility( false );
4708       }
4709     }
4710   }
4711 }
4712
4713 void TextInput::SelectText(std::size_t start, std::size_t end)
4714 {
4715   DALI_LOG_INFO(gLogFilter, Debug::General, "SelectText mEditModeActive[%s] grabHandle[%s] start[%u] end[%u] size[%u]\n", mEditModeActive?"true":"false",
4716                                                                                                                           IsGrabHandleEnabled()?"true":"false",
4717                                                                                                                           start, end, mTextLayoutInfo.mCharacterLayoutInfoTable.size() );
4718   DALI_ASSERT_ALWAYS( start <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText start out of max range" );
4719   DALI_ASSERT_ALWAYS( end <= mTextLayoutInfo.mCharacterLayoutInfoTable.size() && "TextInput::SelectText end out of max range" );
4720
4721   StartMonitoringStageForTouch();
4722
4723   if ( mEditModeActive ) // Only allow text selection when in edit mode
4724   {
4725     // When replacing highlighted text keyboard should ignore current word at cursor hence notify keyboard that the cursor is at the start of the highlight.
4726     mSelectingText = true;
4727
4728     std::size_t selectionStartPosition = std::min( start, end );
4729
4730     // Hide grab handle when selecting.
4731     ShowGrabHandleAndSetVisibility( false );
4732
4733     if( start != end )  // something to select
4734     {
4735       SetCursorVisibility( false );
4736       StopCursorBlinkTimer();
4737
4738       CreateSelectionHandles(start, end);
4739       UpdateHighlight();
4740
4741       const TextStyle oldInputStyle( mInputStyle );
4742       mInputStyle = GetStyleAt( selectionStartPosition ); // Inherit style from selected position.
4743
4744       if( oldInputStyle != mInputStyle )
4745       {
4746         // Updates the line height accordingly with the input style.
4747         UpdateLineHeight();
4748
4749         EmitStyleChangedSignal();
4750       }
4751
4752       HidePopup();
4753     }
4754
4755     mSelectingText = false;
4756   }
4757 }
4758
4759 MarkupProcessor::StyledTextArray TextInput::GetSelectedText()
4760 {
4761   MarkupProcessor::StyledTextArray currentSelectedText;
4762
4763   if ( IsTextSelected() )
4764   {
4765     MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4766     MarkupProcessor::StyledTextArray::iterator end = mStyledText.begin() + std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4767
4768     for(; it != end; ++it)
4769     {
4770       MarkupProcessor::StyledText& styledText( *it );
4771       currentSelectedText.push_back( styledText );
4772     }
4773   }
4774   return currentSelectedText;
4775 }
4776
4777 void TextInput::ApplyStyleToRange(const TextStyle& style, const TextStyle::Mask mask, const std::size_t begin, const std::size_t end)
4778 {
4779   const std::size_t beginIndex = std::min( begin, end );
4780   const std::size_t endIndex = std::max( begin, end );
4781
4782   // Apply the style
4783   MarkupProcessor::SetTextStyleToRange( mStyledText, style, mask, beginIndex, endIndex );
4784
4785   // Create a styled text array used to replace the text into the text-view.
4786   MarkupProcessor::StyledTextArray text;
4787   text.insert( text.begin(), mStyledText.begin() + beginIndex, mStyledText.begin() + endIndex + 1 );
4788
4789   mDisplayedTextView.ReplaceTextFromTo( beginIndex, ( endIndex - beginIndex ) + 1, text );
4790   GetTextLayoutInfo();
4791
4792   if( IsScrollEnabled() )
4793   {
4794     // Need to set the scroll position as the text's size may have changed.
4795     ScrollTextViewToMakeCursorVisible( Vector3( mTextLayoutInfo.mScrollOffset.x, mTextLayoutInfo.mScrollOffset.y, 0.f ) );
4796   }
4797
4798   ShowGrabHandleAndSetVisibility( false );
4799
4800   DrawCursor();
4801
4802   UpdateHighlight();
4803
4804   // Set Handle positioning as the new style may have repositioned the characters.
4805   SetSelectionHandlePosition(HandleOne);
4806   SetSelectionHandlePosition(HandleTwo);
4807 }
4808
4809 void TextInput::KeyboardStatusChanged(bool keyboardShown)
4810 {
4811   // Just hide the grab handle when keyboard is hidden.
4812   if (!keyboardShown )
4813   {
4814     ShowGrabHandleAndSetVisibility( false );
4815
4816     // If the keyboard is not now being shown, then hide the popup panel
4817     mPopupPanel.Hide( true );
4818   }
4819 }
4820
4821 // Removes highlight and resumes edit mode state
4822 void TextInput::RemoveHighlight( bool hidePopup )
4823 {
4824   DALI_LOG_INFO(gLogFilter, Debug::General, "RemoveHighlight\n");
4825
4826   if ( mHighlightMeshActor )
4827   {
4828     if ( mSelectionHandleOne )
4829     {
4830       mActiveLayer.Remove( mSelectionHandleOne );
4831       mSelectionHandleOne.Reset();
4832       mSelectionHandleOneOffset.x = 0.0f;
4833     }
4834     if ( mSelectionHandleTwo )
4835     {
4836       mActiveLayer.Remove( mSelectionHandleTwo );
4837       mSelectionHandleTwo.Reset();
4838       mSelectionHandleTwoOffset.x = 0.0f;
4839     }
4840
4841     mNewHighlightInfo.mQuadList.clear();
4842
4843     Self().Remove( mHighlightMeshActor );
4844
4845     SetCursorVisibility( true );
4846     StartCursorBlinkTimer();
4847
4848     mHighlightMeshActor.Reset();
4849     // NOTE: We cannot dereference mHighlightMesh, due
4850     // to a bug in how the scene-graph MeshRenderer uses the Mesh data incorrectly.
4851
4852     if ( hidePopup )
4853     {
4854       HidePopup();
4855     }
4856   }
4857
4858   mSelectionHandleOnePosition = 0;
4859   mSelectionHandleTwoPosition = 0;
4860 }
4861
4862 void TextInput::CreateHighlight()
4863 {
4864   if ( !mHighlightMeshActor )
4865   {
4866     mMeshData = MeshData( );
4867     mMeshData.SetHasNormals( true );
4868
4869     mCustomMaterial = Material::New("CustomMaterial");
4870     mCustomMaterial.SetDiffuseColor( mMaterialColor );
4871
4872     mMeshData.SetMaterial( mCustomMaterial );
4873
4874     mHighlightMesh = Mesh::New( mMeshData );
4875
4876     mHighlightMeshActor = MeshActor::New( mHighlightMesh );
4877     mHighlightMeshActor.SetName( "HighlightMeshActor" );
4878     mHighlightMeshActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
4879     mHighlightMeshActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
4880     mHighlightMeshActor.SetPosition( 0.0f, 0.0f, DISPLAYED_HIGHLIGHT_Z_OFFSET );
4881     mHighlightMeshActor.SetAffectedByLighting(false);
4882
4883     Self().Add(mHighlightMeshActor);
4884   }
4885 }
4886
4887
4888 bool TextInput::CopySelectedTextToClipboard()
4889 {
4890   mCurrentCopySelecton.clear();
4891
4892   mCurrentCopySelecton = GetSelectedText();
4893
4894   std::string stringToStore;
4895
4896   /* Create a StyledTextArray from the selected region so can use the MarkUpProcessor to produce
4897    * a marked up string.
4898    */
4899   MarkupProcessor::StyledTextArray selectedText(mCurrentCopySelecton.begin(),mCurrentCopySelecton.end());
4900   MarkupProcessor::GetPlainString( selectedText, stringToStore );
4901
4902   bool success = mClipboard.SetItem( stringToStore );
4903   return success;
4904 }
4905
4906 void TextInput::PasteText( const Text& text )
4907 {
4908   // Update Flag, indicates whether to update the text-input contents or not.
4909   // Any key stroke that results in a visual change of the text-input should
4910   // set this flag to true.
4911   bool update = false;
4912   if( mHighlightMeshActor )
4913   {
4914     /* if highlighted, delete entire text, and position cursor at start of deleted text. */
4915     mCursorPosition = std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
4916
4917     ImfManager imfManager = ImfManager::Get();
4918     if ( imfManager )
4919     {
4920       imfManager.SetCursorPosition( mCursorPosition );
4921       imfManager.NotifyCursorPosition();
4922     }
4923     DeleteHighlightedText( true );
4924     update = true;
4925   }
4926
4927   bool textExceedsMaximunNumberOfCharacters = false;
4928   bool textExceedsBoundary = false;
4929
4930   std::size_t insertedStringLength = DoInsertAt( text, mCursorPosition, 0, textExceedsMaximunNumberOfCharacters, textExceedsBoundary );
4931
4932   mCursorPosition += insertedStringLength;
4933   ImfManager imfManager = ImfManager::Get();
4934   if ( imfManager )
4935   {
4936     imfManager.SetCursorPosition ( mCursorPosition );
4937     imfManager.NotifyCursorPosition();
4938   }
4939
4940   update = update || ( insertedStringLength > 0 );
4941   if( update )
4942   {
4943     CursorUpdate();
4944     EmitTextModified();
4945   }
4946
4947   if( insertedStringLength < text.GetLength() )
4948   {
4949     EmitMaxInputCharactersReachedSignal();
4950   }
4951
4952   if( textExceedsBoundary )
4953   {
4954     EmitInputTextExceedsBoundariesSignal();
4955   }
4956 }
4957
4958 void TextInput::SetTextDirection()
4959 {
4960   // Put the cursor to the right if we are empty and an RTL language is being used.
4961   if ( mStyledText.empty() )
4962   {
4963     VirtualKeyboard::TextDirection direction( VirtualKeyboard::GetTextDirection() );
4964
4965     // Get the current text alignment preserving the vertical alignment. Also preserve the horizontal center
4966     // alignment as we do not want to set the text direction if we've been asked to be in the center.
4967     //
4968     // TODO: Should split SetTextAlignment into two APIs to better handle this (sometimes apps just want to
4969     //       set vertical alignment but are being forced to set the horizontal alignment as well with the
4970     //       current API.
4971     int alignment( mDisplayedTextView.GetTextAlignment() &
4972                   ( Toolkit::Alignment::VerticalTop |
4973                     Toolkit::Alignment::VerticalCenter |
4974                     Toolkit::Alignment::VerticalBottom |
4975                     Toolkit::Alignment::HorizontalCenter ) );
4976     Toolkit::TextView::LineJustification justification( mDisplayedTextView.GetLineJustification() );
4977
4978     // If our alignment is in the center, then do not change.
4979     if ( !( alignment & Toolkit::Alignment::HorizontalCenter ) )
4980     {
4981       alignment |= ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::Alignment::HorizontalLeft : Toolkit::Alignment::HorizontalRight;
4982     }
4983
4984     // If our justification is in the center, then do not change.
4985     if ( justification != Toolkit::TextView::Center )
4986     {
4987       justification = ( direction == VirtualKeyboard::LeftToRight ) ? Toolkit::TextView::Left : Toolkit::TextView::Right;
4988     }
4989
4990     mDisplayedTextView.SetTextAlignment( static_cast<Toolkit::Alignment::Type>(alignment) );
4991     mDisplayedTextView.SetLineJustification( justification );
4992   }
4993 }
4994
4995 void TextInput::UpdateLineHeight()
4996 {
4997   Dali::Font font = Dali::Font::New( FontParameters( mInputStyle.GetFontName(), mInputStyle.GetFontStyle(), mInputStyle.GetFontPointSize() ) );
4998   mLineHeight = font.GetLineHeight();
4999
5000   // 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.
5001
5002   const bool shrink = mDisplayedTextView && ( Toolkit::TextView::ShrinkToFit == mDisplayedTextView.GetHeightExceedPolicy() ) && mStyledText.empty();
5003
5004   if( !mExceedEnabled || shrink )
5005   {
5006     mLineHeight = std::min( mLineHeight, GetControlSize().height );
5007   }
5008 }
5009
5010 std::size_t TextInput::FindVisibleCharacter( FindVisibleCharacterDirection direction , std::size_t cursorPosition ) const
5011 {
5012   // VCC check if we need do this in the visual order ...
5013   std::size_t position = 0u;
5014
5015   const std::size_t tableSize = mTextLayoutInfo.mCharacterLayoutInfoTable.size();
5016
5017   switch( direction )
5018   {
5019     case Left:
5020     {
5021       position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
5022
5023       if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1u : position ) ) ).mIsVisible )
5024       {
5025         position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
5026       }
5027       break;
5028     }
5029     case Right:
5030     {
5031       position = FindVisibleCharacterRight( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
5032       if( !( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + ( tableSize == position ? position - 1u : position ) ) ).mIsVisible )
5033       {
5034         position = FindVisibleCharacterLeft( cursorPosition, mTextLayoutInfo.mCharacterLayoutInfoTable );
5035       }
5036       break;
5037     }
5038     case ByEnd:
5039     {
5040       position = FindVisibleCharacterLeft( 0u, mTextLayoutInfo.mCharacterLayoutInfoTable );
5041       break;
5042     }
5043     default:
5044     {
5045       DALI_ASSERT_ALWAYS( !"TextInput::FindVisibleCharacter() Unknown direction." );
5046     }
5047   }
5048
5049   return position;
5050 }
5051
5052 void TextInput::SetSortModifier( float depthOffset )
5053 {
5054   if(mDisplayedTextView)
5055   {
5056     mDisplayedTextView.SetSortModifier(depthOffset);
5057   }
5058 }
5059
5060 void TextInput::SetSnapshotModeEnabled( bool enable )
5061 {
5062   if(mDisplayedTextView)
5063   {
5064     mDisplayedTextView.SetSnapshotModeEnabled( enable );
5065   }
5066 }
5067
5068 bool TextInput::IsSnapshotModeEnabled() const
5069 {
5070   bool snapshotEnabled = false;
5071
5072   if(mDisplayedTextView)
5073   {
5074     snapshotEnabled = mDisplayedTextView.IsSnapshotModeEnabled();
5075   }
5076
5077   return snapshotEnabled;
5078 }
5079
5080 void TextInput::SetMarkupProcessingEnabled( bool enable )
5081 {
5082   mMarkUpEnabled = enable;
5083 }
5084
5085 bool TextInput::IsMarkupProcessingEnabled() const
5086 {
5087   return mMarkUpEnabled;
5088 }
5089
5090 void TextInput::SetScrollEnabled( bool enable )
5091 {
5092   if( mDisplayedTextView )
5093   {
5094     mDisplayedTextView.SetScrollEnabled( enable );
5095   }
5096
5097   if( !enable )
5098   {
5099     // Don't set cursor's and handle's visibility to false if they are outside the
5100     // boundaries of the text-input.
5101     mIsCursorInScrollArea = true;
5102     mIsGrabHandleInScrollArea = true;
5103     if( mSelectionHandleOne && mSelectionHandleTwo )
5104     {
5105       mSelectionHandleOne.SetVisible( true );
5106       mSelectionHandleTwo.SetVisible( true );
5107
5108       if( mHighlightMeshActor )
5109       {
5110         mHighlightMeshActor.SetVisible( true );
5111       }
5112     }
5113   }
5114 }
5115
5116 bool TextInput::IsScrollEnabled() const
5117 {
5118   bool scrollEnabled = false;
5119
5120   if( mDisplayedTextView )
5121   {
5122     scrollEnabled = mDisplayedTextView.IsScrollEnabled();
5123   }
5124
5125   return scrollEnabled;
5126 }
5127
5128 void TextInput::SetScrollPosition( const Vector2& position )
5129 {
5130   if( mDisplayedTextView )
5131   {
5132     mDisplayedTextView.SetScrollPosition( position );
5133   }
5134 }
5135
5136 Vector2 TextInput::GetScrollPosition() const
5137 {
5138   Vector2 scrollPosition;
5139
5140   if( mDisplayedTextView )
5141   {
5142     scrollPosition = mDisplayedTextView.GetScrollPosition();
5143   }
5144
5145   return scrollPosition;
5146 }
5147
5148 std::size_t TextInput::DoInsertAt( const Text& text, const std::size_t position, const std::size_t numberOfCharactersToReplace, bool& textExceedsMaximunNumberOfCharacters, bool& textExceedsBoundary )
5149 {
5150   // determine number of characters that we can write to style text buffer, this is the insertStringLength
5151   std::size_t insertedStringLength = std::min( text.GetLength(), mMaxStringLength - mStyledText.size() );
5152   textExceedsMaximunNumberOfCharacters = insertedStringLength < text.GetLength();
5153
5154   // Add style to the new input text.
5155   MarkupProcessor::StyledTextArray textToInsert;
5156   for( std::size_t i = 0; i < insertedStringLength; ++i )
5157   {
5158     const MarkupProcessor::StyledText newStyledCharacter( text[i], mInputStyle );
5159     textToInsert.push_back( newStyledCharacter );
5160   }
5161
5162   //Insert text to the TextView.
5163   const bool emptyTextView = mStyledText.empty();
5164   if( emptyTextView && mPlaceHolderSet )
5165   {
5166     // There is no text set so call to TextView::SetText() is needed in order to clear the placeholder text.
5167     mDisplayedTextView.SetText( textToInsert );
5168   }
5169   else
5170   {
5171     if( 0 == numberOfCharactersToReplace )
5172     {
5173       mDisplayedTextView.InsertTextAt( position, textToInsert );
5174     }
5175     else
5176     {
5177       mDisplayedTextView.ReplaceTextFromTo( position, numberOfCharactersToReplace, textToInsert );
5178     }
5179   }
5180   mPlaceHolderSet = false;
5181
5182   if( textToInsert.empty() )
5183   {
5184     // If no text has been inserted, GetTextLayoutInfo() need to be called to check whether mStyledText has some text.
5185     GetTextLayoutInfo();
5186   }
5187   else
5188   {
5189     // GetTextLayoutInfo() can't be used here as mStyledText is not updated yet.
5190     mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5191   }
5192
5193   textExceedsBoundary = false;
5194
5195   if( !mExceedEnabled )
5196   {
5197     const Vector3& size = GetControlSize();
5198
5199     if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5200     {
5201       // If new text does not fit within TextView
5202       mDisplayedTextView.RemoveTextFrom( position, insertedStringLength );
5203       // previously inserted text has been removed. Call GetTextLayoutInfo() to check whether mStyledText has some text.
5204       GetTextLayoutInfo();
5205       textExceedsBoundary = true;
5206       insertedStringLength = 0;
5207     }
5208
5209     if( textExceedsBoundary )
5210     {
5211       // Add the part of the text which fits on the text-input.
5212
5213       // Split the text which doesn't fit in two halves.
5214       MarkupProcessor::StyledTextArray firstHalf;
5215       MarkupProcessor::StyledTextArray secondHalf;
5216       SplitText( textToInsert, firstHalf, secondHalf );
5217
5218       // Clear text. This text will be filled with the text inserted.
5219       textToInsert.clear();
5220
5221       // Where to insert the text.
5222       std::size_t positionToInsert = position;
5223
5224       bool end = text.GetLength() <= 1;
5225       while( !end )
5226       {
5227         // Insert text and check ...
5228         const std::size_t textLength = firstHalf.size();
5229         mDisplayedTextView.InsertTextAt( positionToInsert, firstHalf );
5230         mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5231
5232         if( ( mTextLayoutInfo.mTextSize.width > size.width ) || ( mTextLayoutInfo.mTextSize.height > size.height ) )
5233         {
5234           // Inserted text doesn't fit.
5235
5236           // Remove inserted text
5237           mDisplayedTextView.RemoveTextFrom( positionToInsert, textLength );
5238           mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5239
5240           // The iteration finishes when only one character doesn't fit.
5241           end = textLength <= 1;
5242
5243           if( !end )
5244           {
5245             // Prepare next two halves for next iteration.
5246             MarkupProcessor::StyledTextArray copyText = firstHalf;
5247             SplitText( copyText, firstHalf, secondHalf );
5248           }
5249         }
5250         else
5251         {
5252           // Text fits.
5253
5254           // store text to be inserted in mStyledText.
5255           textToInsert.insert( textToInsert.end(), firstHalf.begin(), firstHalf.end() );
5256
5257           // Increase the inserted characters counter.
5258           insertedStringLength += textLength;
5259
5260           // Prepare next two halves for next iteration.
5261           MarkupProcessor::StyledTextArray copyText = secondHalf;
5262           SplitText( copyText, firstHalf, secondHalf );
5263
5264           // Update where next text has to be inserted
5265           positionToInsert += textLength;
5266         }
5267       }
5268     }
5269   }
5270
5271   if( textToInsert.empty() && emptyTextView )
5272   {
5273     // No character has been added and the text-view was empty.
5274     // Show the placeholder text.
5275     ShowPlaceholderText( mStyledPlaceHolderText );
5276   }
5277   else
5278   {
5279     MarkupProcessor::StyledTextArray::iterator it = mStyledText.begin() + position;
5280     mStyledText.insert( it, textToInsert.begin(), textToInsert.end() );
5281     mPlaceHolderSet = false;
5282   }
5283
5284   return insertedStringLength;
5285 }
5286
5287 void TextInput::GetTextLayoutInfo()
5288 {
5289   if( mStyledText.empty() )
5290   {
5291     // The text-input has no text, clear the text-view's layout info.
5292     mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5293   }
5294   else
5295   {
5296     if( mDisplayedTextView )
5297     {
5298       mDisplayedTextView.GetTextLayoutInfo( mTextLayoutInfo );
5299     }
5300     else
5301     {
5302       // There is no text-view.
5303       mTextLayoutInfo = Toolkit::TextView::TextLayoutInfo();
5304     }
5305   }
5306 }
5307
5308 void TextInput::SetOffsetFromText( const Vector4& offset )
5309 {
5310   mPopupOffsetFromText = offset;
5311 }
5312
5313 const Vector4& TextInput::GetOffsetFromText() const
5314 {
5315   return mPopupOffsetFromText;
5316 }
5317
5318 void TextInput::SetProperty( BaseObject* object, Property::Index propertyIndex, const Property::Value& value )
5319 {
5320   Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5321
5322   if ( textInput )
5323   {
5324     TextInput& textInputImpl( GetImpl( textInput ) );
5325
5326     switch ( propertyIndex )
5327     {
5328       case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5329       {
5330         textInputImpl.SetMaterialDiffuseColor( value.Get< Vector4 >() );
5331         break;
5332       }
5333       case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5334       {
5335         textInputImpl.mPopupPanel.SetCutPastePopupColor( value.Get< Vector4 >() );
5336         break;
5337       }
5338       case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5339       {
5340         textInputImpl.mPopupPanel.SetCutPastePopupPressedColor( value.Get< Vector4 >() );
5341         break;
5342       }
5343       case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY:
5344       {
5345         textInputImpl.mPopupPanel.SetCutPastePopupBorderColor( value.Get< Vector4 >() );
5346         break;
5347       }
5348       case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5349       {
5350         textInputImpl.mPopupPanel.SetCutPastePopupIconColor( value.Get< Vector4 >() );
5351         break;
5352       }
5353       case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5354       {
5355         textInputImpl.mPopupPanel.SetCutPastePopupIconPressedColor( value.Get< Vector4 >() );
5356         break;
5357       }
5358       case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5359       {
5360         textInputImpl.mPopupPanel.SetCutPastePopupTextColor( value.Get< Vector4 >() );
5361         break;
5362       }
5363       case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5364       {
5365         textInputImpl.mPopupPanel.SetCutPastePopupTextPressedColor( value.Get< Vector4 >() );
5366         break;
5367       }
5368       case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5369       {
5370         textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCut, value.Get<unsigned int>() );
5371         break;
5372       }
5373       case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5374       {
5375         textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsCopy, value.Get<unsigned int>() );
5376         break;
5377       }
5378       case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5379       {
5380         textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsPaste, value.Get<unsigned int>() );
5381         break;
5382       }
5383       case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5384       {
5385         textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelect, value.Get<unsigned int>() );
5386         break;
5387       }
5388       case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5389       {
5390         textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll, value.Get<unsigned int>() );
5391         break;
5392       }
5393       case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5394       {
5395         textInputImpl.mPopupPanel.SetButtonPriorityPosition( TextInputPopup::ButtonsClipboard, value.Get<unsigned int>() );
5396         break;
5397       }
5398       case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5399       {
5400         textInputImpl.SetOffsetFromText( value.Get< Vector4 >() );
5401         break;
5402       }
5403       case Toolkit::TextInput::CURSOR_COLOR_PROPERTY:
5404       {
5405         textInputImpl.mCursor.SetColor( value.Get< Vector4 >() );
5406       }
5407     }
5408   }
5409 }
5410
5411 Property::Value TextInput::GetProperty( BaseObject* object, Property::Index propertyIndex )
5412 {
5413   Property::Value value;
5414
5415   Toolkit::TextInput textInput = Toolkit::TextInput::DownCast( Dali::BaseHandle( object ) );
5416
5417   if ( textInput )
5418   {
5419     TextInput& textInputImpl( GetImpl( textInput ) );
5420
5421     switch ( propertyIndex )
5422     {
5423       case Toolkit::TextInput::HIGHLIGHT_COLOR_PROPERTY:
5424       {
5425         value = textInputImpl.GetMaterialDiffuseColor();
5426         break;
5427       }
5428       case Toolkit::TextInput::CUT_AND_PASTE_COLOR_PROPERTY:
5429       {
5430         value = textInputImpl.mPopupPanel.GetCutPastePopupColor();
5431         break;
5432       }
5433       case Toolkit::TextInput::CUT_AND_PASTE_PRESSED_COLOR_PROPERTY:
5434       {
5435         value = textInputImpl.mPopupPanel.GetCutPastePopupPressedColor();
5436         break;
5437       }
5438       case Toolkit::TextInput::CUT_AND_PASTE_BORDER_COLOR_PROPERTY :
5439       {
5440         value = textInputImpl.mPopupPanel.GetCutPastePopupBorderColor();
5441         break;
5442       }
5443       case Toolkit::TextInput::CUT_AND_PASTE_ICON_COLOR_PROPERTY:
5444       {
5445         value = textInputImpl.mPopupPanel.GetCutPastePopupIconColor();
5446         break;
5447       }
5448       case Toolkit::TextInput::CUT_AND_PASTE_ICON_PRESSED_COLOR_PROPERTY:
5449       {
5450         value = textInputImpl.mPopupPanel.GetCutPastePopupIconPressedColor();
5451         break;
5452       }
5453       case Toolkit::TextInput::CUT_AND_PASTE_TEXT_COLOR_PROPERTY:
5454       {
5455         value = textInputImpl.mPopupPanel.GetCutPastePopupTextColor();
5456         break;
5457       }
5458       case Toolkit::TextInput::CUT_AND_PASTE_TEXT_PRESSED_COLOR_PROPERTY:
5459       {
5460         value = textInputImpl.mPopupPanel.GetCutPastePopupTextPressedColor();
5461         break;
5462       }
5463       case Toolkit::TextInput::CUT_BUTTON_POSITION_PRIORITY_PROPERTY:
5464       {
5465         value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCut );
5466         break;
5467       }
5468       case Toolkit::TextInput::COPY_BUTTON_POSITION_PRIORITY_PROPERTY:
5469       {
5470         value =  textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsCopy );
5471         break;
5472       }
5473       case Toolkit::TextInput::PASTE_BUTTON_POSITION_PRIORITY_PROPERTY:
5474       {
5475         value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsPaste );
5476         break;
5477       }
5478       case Toolkit::TextInput::SELECT_BUTTON_POSITION_PRIORITY_PROPERTY:
5479       {
5480         value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelect );
5481         break;
5482       }
5483       case Toolkit::TextInput::SELECT_ALL_BUTTON_POSITION_PRIORITY_PROPERTY:
5484       {
5485         value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsSelectAll );
5486         break;
5487       }
5488       case Toolkit::TextInput::CLIPBOARD_BUTTON_POSITION_PRIORITY_PROPERTY:
5489       {
5490         value = textInputImpl.mPopupPanel.GetButtonPriorityPosition( TextInputPopup::ButtonsClipboard );
5491         break;
5492       }
5493       case Toolkit::TextInput::POP_UP_OFFSET_FROM_TEXT_PROPERTY:
5494       {
5495         value = textInputImpl.GetOffsetFromText();
5496         break;
5497       }
5498       case Toolkit::TextInput::CURSOR_COLOR_PROPERTY:
5499       {
5500         value = textInputImpl.mCursor.GetCurrentColor();
5501       }
5502     }
5503   }
5504   return value;
5505 }
5506
5507 void TextInput::EmitStyleChangedSignal()
5508 {
5509   // emit signal if input style changes.
5510   Toolkit::TextInput handle( GetOwner() );
5511   mStyleChangedSignal.Emit( handle, mInputStyle );
5512 }
5513
5514 void TextInput::EmitTextModified()
5515 {
5516   // emit signal when text changes.
5517   Toolkit::TextInput handle( GetOwner() );
5518   mTextModifiedSignal.Emit( handle );
5519 }
5520
5521
5522 void TextInput::EmitMaxInputCharactersReachedSignal()
5523 {
5524   // emit signal if max characters is reached during text input.
5525   DALI_LOG_INFO(gLogFilter, Debug::General, "EmitMaxInputCharactersReachedSignal \n");
5526
5527   Toolkit::TextInput handle( GetOwner() );
5528   mMaxInputCharactersReachedSignal.Emit( handle );
5529 }
5530
5531 void TextInput::EmitInputTextExceedsBoundariesSignal()
5532 {
5533   // Emit a signal when the input text exceeds the boundaries of the text input.
5534
5535   Toolkit::TextInput handle( GetOwner() );
5536   mInputTextExceedBoundariesSignal.Emit( handle );
5537 }
5538
5539 } // namespace Internal
5540
5541 } // namespace Toolkit
5542
5543 } // namespace Dali