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