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