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