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