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