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