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