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