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