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