Merge "Changed to use ImfManager for virtual keyboard APIs" into devel/master
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-controller-impl.cpp
1 /*
2  * Copyright (c) 2016 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/text/text-controller-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/public-api/adaptor-framework/key.h>
23 #include <dali/integration-api/debug.h>
24 #include <limits>
25
26 // INTERNAL INCLUDES
27 #include <dali-toolkit/internal/text/bidirectional-support.h>
28 #include <dali-toolkit/internal/text/character-set-conversion.h>
29 #include <dali-toolkit/internal/text/color-segmentation.h>
30 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
31 #include <dali-toolkit/internal/text/multi-language-support.h>
32 #include <dali-toolkit/internal/text/segmentation.h>
33 #include <dali-toolkit/internal/text/shaper.h>
34 #include <dali-toolkit/internal/text/text-control-interface.h>
35 #include <dali-toolkit/internal/text/text-run-container.h>
36
37 namespace
38 {
39
40 /**
41  * @brief Struct used to calculate the selection box.
42  */
43 struct SelectionBoxInfo
44 {
45   float lineOffset;
46   float lineHeight;
47   float minX;
48   float maxX;
49 };
50
51 #if defined(DEBUG_ENABLED)
52   Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
53 #endif
54
55 const float MAX_FLOAT = std::numeric_limits<float>::max();
56 const float MIN_FLOAT = std::numeric_limits<float>::min();
57 const Dali::Toolkit::Text::CharacterDirection LTR = false; ///< Left To Right direction
58 const uint32_t STAR = 0x2A;
59
60 } // namespace
61
62 namespace Dali
63 {
64
65 namespace Toolkit
66 {
67
68 namespace Text
69 {
70
71 EventData::EventData( DecoratorPtr decorator )
72 : mDecorator( decorator ),
73   mImfManager(),
74   mPlaceholderTextActive(),
75   mPlaceholderTextInactive(),
76   mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ),
77   mEventQueue(),
78   mInputStyleChangedQueue(),
79   mPreviousState( INACTIVE ),
80   mState( INACTIVE ),
81   mPrimaryCursorPosition( 0u ),
82   mLeftSelectionPosition( 0u ),
83   mRightSelectionPosition( 0u ),
84   mPreEditStartPosition( 0u ),
85   mPreEditLength( 0u ),
86   mCursorHookPositionX( 0.f ),
87   mIsShowingPlaceholderText( false ),
88   mPreEditFlag( false ),
89   mDecoratorUpdated( false ),
90   mCursorBlinkEnabled( true ),
91   mGrabHandleEnabled( true ),
92   mGrabHandlePopupEnabled( true ),
93   mSelectionEnabled( true ),
94   mUpdateCursorHookPosition( false ),
95   mUpdateCursorPosition( false ),
96   mUpdateGrabHandlePosition( false ),
97   mUpdateLeftSelectionPosition( false ),
98   mUpdateRightSelectionPosition( false ),
99   mIsLeftHandleSelected( false ),
100   mUpdateHighlightBox( false ),
101   mScrollAfterUpdatePosition( false ),
102   mScrollAfterDelete( false ),
103   mAllTextSelected( false ),
104   mUpdateInputStyle( false ),
105   mPasswordInput( false )
106 {
107   mImfManager = ImfManager::Get();
108 }
109
110 EventData::~EventData()
111 {}
112
113 bool Controller::Impl::ProcessInputEvents()
114 {
115   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
116   if( NULL == mEventData )
117   {
118     // Nothing to do if there is no text input.
119     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
120     return false;
121   }
122
123   if( mEventData->mDecorator )
124   {
125     for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
126          iter != mEventData->mEventQueue.end();
127          ++iter )
128     {
129       switch( iter->type )
130       {
131         case Event::CURSOR_KEY_EVENT:
132         {
133           OnCursorKeyEvent( *iter );
134           break;
135         }
136         case Event::TAP_EVENT:
137         {
138           OnTapEvent( *iter );
139           break;
140         }
141         case Event::LONG_PRESS_EVENT:
142         {
143           OnLongPressEvent( *iter );
144           break;
145         }
146         case Event::PAN_EVENT:
147         {
148           OnPanEvent( *iter );
149           break;
150         }
151         case Event::GRAB_HANDLE_EVENT:
152         case Event::LEFT_SELECTION_HANDLE_EVENT:
153         case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
154         {
155           OnHandleEvent( *iter );
156           break;
157         }
158         case Event::SELECT:
159         {
160           OnSelectEvent( *iter );
161           break;
162         }
163         case Event::SELECT_ALL:
164         {
165           OnSelectAllEvent();
166           break;
167         }
168       }
169     }
170   }
171
172   if( mEventData->mUpdateCursorPosition ||
173       mEventData->mUpdateHighlightBox )
174   {
175     NotifyImfManager();
176   }
177
178   // The cursor must also be repositioned after inserts into the model
179   if( mEventData->mUpdateCursorPosition )
180   {
181     // Updates the cursor position and scrolls the text to make it visible.
182     CursorInfo cursorInfo;
183     // Calculate the cursor position from the new cursor index.
184     GetCursorPosition( mEventData->mPrimaryCursorPosition,
185                        cursorInfo );
186
187     if( mEventData->mUpdateCursorHookPosition )
188     {
189       // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'.
190       mEventData->mCursorHookPositionX = cursorInfo.primaryPosition.x;
191       mEventData->mUpdateCursorHookPosition = false;
192     }
193
194     // Scroll first the text after delete ...
195     if( mEventData->mScrollAfterDelete )
196     {
197       ScrollTextToMatchCursor( cursorInfo );
198     }
199
200     // ... then, text can be scrolled to make the cursor visible.
201     if( mEventData->mScrollAfterUpdatePosition )
202     {
203       const Vector2 currentCursorPosition( cursorInfo.primaryPosition.x, cursorInfo.lineOffset );
204       ScrollToMakePositionVisible( currentCursorPosition, cursorInfo.lineHeight );
205     }
206     mEventData->mScrollAfterUpdatePosition = false;
207     mEventData->mScrollAfterDelete = false;
208
209     UpdateCursorPosition( cursorInfo );
210
211     mEventData->mDecoratorUpdated = true;
212     mEventData->mUpdateCursorPosition = false;
213     mEventData->mUpdateGrabHandlePosition = false;
214   }
215   else
216   {
217     CursorInfo leftHandleInfo;
218     CursorInfo rightHandleInfo;
219
220     if( mEventData->mUpdateHighlightBox )
221     {
222       GetCursorPosition( mEventData->mLeftSelectionPosition,
223                          leftHandleInfo );
224
225       GetCursorPosition( mEventData->mRightSelectionPosition,
226                          rightHandleInfo );
227
228       if( mEventData->mScrollAfterUpdatePosition && ( mEventData->mIsLeftHandleSelected ? mEventData->mUpdateLeftSelectionPosition : mEventData->mUpdateRightSelectionPosition ) )
229       {
230         CursorInfo& info = mEventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo;
231
232         const Vector2 currentCursorPosition( info.primaryPosition.x, info.lineOffset );
233         ScrollToMakePositionVisible( currentCursorPosition, info.lineHeight );
234       }
235     }
236
237     if( mEventData->mUpdateLeftSelectionPosition )
238     {
239       UpdateSelectionHandle( LEFT_SELECTION_HANDLE,
240                              leftHandleInfo );
241
242       SetPopupButtons();
243       mEventData->mDecoratorUpdated = true;
244       mEventData->mUpdateLeftSelectionPosition = false;
245     }
246
247     if( mEventData->mUpdateRightSelectionPosition )
248     {
249       UpdateSelectionHandle( RIGHT_SELECTION_HANDLE,
250                              rightHandleInfo );
251
252       SetPopupButtons();
253       mEventData->mDecoratorUpdated = true;
254       mEventData->mUpdateRightSelectionPosition = false;
255     }
256
257     if( mEventData->mUpdateHighlightBox )
258     {
259       RepositionSelectionHandles();
260
261       mEventData->mUpdateLeftSelectionPosition = false;
262       mEventData->mUpdateRightSelectionPosition = false;
263       mEventData->mUpdateHighlightBox = false;
264     }
265
266     mEventData->mScrollAfterUpdatePosition = false;
267   }
268
269   if( mEventData->mUpdateInputStyle )
270   {
271     // Keep a copy of the current input style.
272     InputStyle currentInputStyle;
273     currentInputStyle.Copy( mEventData->mInputStyle );
274
275     // Set the default style first.
276     RetrieveDefaultInputStyle( mEventData->mInputStyle );
277
278     // Get the character index from the cursor index.
279     const CharacterIndex styleIndex = ( mEventData->mPrimaryCursorPosition > 0u ) ? mEventData->mPrimaryCursorPosition - 1u : 0u;
280
281     // Retrieve the style from the style runs stored in the logical model.
282     mModel->mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle );
283
284     // Compare if the input style has changed.
285     const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
286
287     if( hasInputStyleChanged )
288     {
289       const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
290       // Queue the input style changed signal.
291       mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
292     }
293
294     mEventData->mUpdateInputStyle = false;
295   }
296
297   mEventData->mEventQueue.clear();
298
299   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
300
301   const bool decoratorUpdated = mEventData->mDecoratorUpdated;
302   mEventData->mDecoratorUpdated = false;
303
304   return decoratorUpdated;
305 }
306
307 void Controller::Impl::NotifyImfManager()
308 {
309   if( mEventData && mEventData->mImfManager )
310   {
311     CharacterIndex cursorPosition = GetLogicalCursorPosition();
312
313     const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces( 0u );
314
315     // Update the cursor position by removing the initial white spaces.
316     if( cursorPosition < numberOfWhiteSpaces )
317     {
318       cursorPosition = 0u;
319     }
320     else
321     {
322       cursorPosition -= numberOfWhiteSpaces;
323     }
324
325     mEventData->mImfManager.SetCursorPosition( cursorPosition );
326     mEventData->mImfManager.NotifyCursorPosition();
327   }
328 }
329
330 void Controller::Impl::NotifyImfMultiLineStatus()
331 {
332   if ( mEventData )
333   {
334     Text::Layout::Engine::Type layout = mLayoutEngine.GetLayout();
335     mEventData->mImfManager.NotifyTextInputMultiLine( layout == Text::Layout::Engine::MULTI_LINE_BOX );
336   }
337 }
338
339 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
340 {
341   CharacterIndex cursorPosition = 0u;
342
343   if( mEventData )
344   {
345     if( ( EventData::SELECTING == mEventData->mState ) ||
346         ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState ) )
347     {
348       cursorPosition = std::min( mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition );
349     }
350     else
351     {
352       cursorPosition = mEventData->mPrimaryCursorPosition;
353     }
354   }
355
356   return cursorPosition;
357 }
358
359 Length Controller::Impl::GetNumberOfWhiteSpaces( CharacterIndex index ) const
360 {
361   Length numberOfWhiteSpaces = 0u;
362
363   // Get the buffer to the text.
364   Character* utf32CharacterBuffer = mModel->mLogicalModel->mText.Begin();
365
366   const Length totalNumberOfCharacters = mModel->mLogicalModel->mText.Count();
367   for( ; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces )
368   {
369     if( !TextAbstraction::IsWhiteSpace( *( utf32CharacterBuffer + index ) ) )
370     {
371       break;
372     }
373   }
374
375   return numberOfWhiteSpaces;
376 }
377
378 void Controller::Impl::GetText( CharacterIndex index, std::string& text ) const
379 {
380   // Get the total number of characters.
381   Length numberOfCharacters = mModel->mLogicalModel->mText.Count();
382
383   // Retrieve the text.
384   if( 0u != numberOfCharacters )
385   {
386     Utf32ToUtf8( mModel->mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text );
387   }
388 }
389
390 void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters )
391 {
392   mTextUpdateInfo.mParagraphCharacterIndex = 0u;
393   mTextUpdateInfo.mStartGlyphIndex = 0u;
394   mTextUpdateInfo.mStartLineIndex = 0u;
395   numberOfCharacters = 0u;
396
397   const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
398   if( 0u == numberOfParagraphs )
399   {
400     mTextUpdateInfo.mParagraphCharacterIndex = 0u;
401     numberOfCharacters = 0u;
402
403     mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
404
405     // Nothing else to do if there are no paragraphs.
406     return;
407   }
408
409   // Find the paragraphs to be updated.
410   Vector<ParagraphRunIndex> paragraphsToBeUpdated;
411   if( mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters )
412   {
413     // Text is being added at the end of the current text.
414     if( mTextUpdateInfo.mIsLastCharacterNewParagraph )
415     {
416       // Text is being added in a new paragraph after the last character of the text.
417       mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
418       numberOfCharacters = 0u;
419       mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
420
421       mTextUpdateInfo.mStartGlyphIndex = mModel->mVisualModel->mGlyphs.Count();
422       mTextUpdateInfo.mStartLineIndex = mModel->mVisualModel->mLines.Count() - 1u;
423
424       // Nothing else to do;
425       return;
426     }
427
428     paragraphsToBeUpdated.PushBack( numberOfParagraphs - 1u );
429   }
430   else
431   {
432     Length numberOfCharactersToUpdate = 0u;
433     if( mTextUpdateInfo.mFullRelayoutNeeded )
434     {
435       numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
436     }
437     else
438     {
439       numberOfCharactersToUpdate = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
440     }
441     mModel->mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex,
442                                            numberOfCharactersToUpdate,
443                                            paragraphsToBeUpdated );
444   }
445
446   if( 0u != paragraphsToBeUpdated.Count() )
447   {
448     const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() );
449     const ParagraphRun& firstParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex );
450     mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
451
452     ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u );
453     const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex );
454
455     if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) &&                                            // Some character are removed.
456         ( lastParagraphIndex < numberOfParagraphs - 1u ) &&                                                // There is a next paragraph.
457         ( ( lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters ) == // The last removed character is the new paragraph character.
458           ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) )
459     {
460       // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
461       const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u );
462
463       numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
464     }
465     else
466     {
467       numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
468     }
469   }
470
471   mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
472   mTextUpdateInfo.mStartGlyphIndex = *( mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex );
473 }
474
475 void Controller::Impl::ClearFullModelData( OperationsMask operations )
476 {
477   if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
478   {
479     mModel->mLogicalModel->mLineBreakInfo.Clear();
480     mModel->mLogicalModel->mParagraphInfo.Clear();
481   }
482
483   if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
484   {
485     mModel->mLogicalModel->mLineBreakInfo.Clear();
486   }
487
488   if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
489   {
490     mModel->mLogicalModel->mScriptRuns.Clear();
491   }
492
493   if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
494   {
495     mModel->mLogicalModel->mFontRuns.Clear();
496   }
497
498   if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
499   {
500     if( NO_OPERATION != ( BIDI_INFO & operations ) )
501     {
502       mModel->mLogicalModel->mBidirectionalParagraphInfo.Clear();
503       mModel->mLogicalModel->mCharacterDirections.Clear();
504     }
505
506     if( NO_OPERATION != ( REORDER & operations ) )
507     {
508       // Free the allocated memory used to store the conversion table in the bidirectional line info run.
509       for( Vector<BidirectionalLineInfoRun>::Iterator it = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(),
510              endIt = mModel->mLogicalModel->mBidirectionalLineInfo.End();
511            it != endIt;
512            ++it )
513       {
514         BidirectionalLineInfoRun& bidiLineInfo = *it;
515
516         free( bidiLineInfo.visualToLogicalMap );
517         bidiLineInfo.visualToLogicalMap = NULL;
518       }
519       mModel->mLogicalModel->mBidirectionalLineInfo.Clear();
520     }
521   }
522
523   if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
524   {
525     mModel->mVisualModel->mGlyphs.Clear();
526     mModel->mVisualModel->mGlyphsToCharacters.Clear();
527     mModel->mVisualModel->mCharactersToGlyph.Clear();
528     mModel->mVisualModel->mCharactersPerGlyph.Clear();
529     mModel->mVisualModel->mGlyphsPerCharacter.Clear();
530     mModel->mVisualModel->mGlyphPositions.Clear();
531   }
532
533   if( NO_OPERATION != ( LAYOUT & operations ) )
534   {
535     mModel->mVisualModel->mLines.Clear();
536   }
537
538   if( NO_OPERATION != ( COLOR & operations ) )
539   {
540     mModel->mVisualModel->mColorIndices.Clear();
541   }
542 }
543
544 void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
545 {
546   const CharacterIndex endIndexPlusOne = endIndex + 1u;
547
548   if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
549   {
550     // Clear the line break info.
551     LineBreakInfo* lineBreakInfoBuffer = mModel->mLogicalModel->mLineBreakInfo.Begin();
552
553     mModel->mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex,
554                                                  lineBreakInfoBuffer + endIndexPlusOne );
555
556     // Clear the paragraphs.
557     ClearCharacterRuns( startIndex,
558                         endIndex,
559                         mModel->mLogicalModel->mParagraphInfo );
560   }
561
562   if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
563   {
564     // Clear the word break info.
565     WordBreakInfo* wordBreakInfoBuffer = mModel->mLogicalModel->mWordBreakInfo.Begin();
566
567     mModel->mLogicalModel->mWordBreakInfo.Erase( wordBreakInfoBuffer + startIndex,
568                                          wordBreakInfoBuffer + endIndexPlusOne );
569   }
570
571   if( NO_OPERATION != ( GET_SCRIPTS & operations ) )
572   {
573     // Clear the scripts.
574     ClearCharacterRuns( startIndex,
575                         endIndex,
576                         mModel->mLogicalModel->mScriptRuns );
577   }
578
579   if( NO_OPERATION != ( VALIDATE_FONTS & operations ) )
580   {
581     // Clear the fonts.
582     ClearCharacterRuns( startIndex,
583                         endIndex,
584                         mModel->mLogicalModel->mFontRuns );
585   }
586
587   if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() )
588   {
589     if( NO_OPERATION != ( BIDI_INFO & operations ) )
590     {
591       // Clear the bidirectional paragraph info.
592       ClearCharacterRuns( startIndex,
593                           endIndex,
594                           mModel->mLogicalModel->mBidirectionalParagraphInfo );
595
596       // Clear the character's directions.
597       CharacterDirection* characterDirectionsBuffer = mModel->mLogicalModel->mCharacterDirections.Begin();
598
599       mModel->mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex,
600                                                          characterDirectionsBuffer + endIndexPlusOne );
601     }
602
603     if( NO_OPERATION != ( REORDER & operations ) )
604     {
605       uint32_t startRemoveIndex = mModel->mLogicalModel->mBidirectionalLineInfo.Count();
606       uint32_t endRemoveIndex = startRemoveIndex;
607       ClearCharacterRuns( startIndex,
608                           endIndex,
609                           mModel->mLogicalModel->mBidirectionalLineInfo,
610                           startRemoveIndex,
611                           endRemoveIndex );
612
613       BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mModel->mLogicalModel->mBidirectionalLineInfo.Begin();
614
615       // Free the allocated memory used to store the conversion table in the bidirectional line info run.
616       for( Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
617              endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
618            it != endIt;
619            ++it )
620       {
621         BidirectionalLineInfoRun& bidiLineInfo = *it;
622
623         free( bidiLineInfo.visualToLogicalMap );
624         bidiLineInfo.visualToLogicalMap = NULL;
625       }
626
627       mModel->mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex,
628                                                            bidirectionalLineInfoBuffer + endRemoveIndex );
629     }
630   }
631 }
632
633 void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
634 {
635   const CharacterIndex endIndexPlusOne = endIndex + 1u;
636   const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
637
638   // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
639   GlyphIndex* charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
640   Length* glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
641
642   const GlyphIndex endGlyphIndexPlusOne = *( charactersToGlyphBuffer + endIndex ) + *( glyphsPerCharacterBuffer + endIndex );
643   const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
644
645   if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
646   {
647     // Update the character to glyph indices.
648     for( Vector<GlyphIndex>::Iterator it =  charactersToGlyphBuffer + endIndexPlusOne,
649            endIt =  charactersToGlyphBuffer + mModel->mVisualModel->mCharactersToGlyph.Count();
650          it != endIt;
651          ++it )
652     {
653       CharacterIndex& index = *it;
654       index -= numberOfGlyphsRemoved;
655     }
656
657     // Clear the character to glyph conversion table.
658     mModel->mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex,
659                                                     charactersToGlyphBuffer + endIndexPlusOne );
660
661     // Clear the glyphs per character table.
662     mModel->mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex,
663                                                      glyphsPerCharacterBuffer + endIndexPlusOne );
664
665     // Clear the glyphs buffer.
666     GlyphInfo* glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
667     mModel->mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
668                                          glyphsBuffer + endGlyphIndexPlusOne );
669
670     CharacterIndex* glyphsToCharactersBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
671
672     // Update the glyph to character indices.
673     for( Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
674            endIt = glyphsToCharactersBuffer + mModel->mVisualModel->mGlyphsToCharacters.Count();
675          it != endIt;
676          ++it )
677     {
678       CharacterIndex& index = *it;
679       index -= numberOfCharactersRemoved;
680     }
681
682     // Clear the glyphs to characters buffer.
683     mModel->mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
684                                                      glyphsToCharactersBuffer  + endGlyphIndexPlusOne );
685
686     // Clear the characters per glyph buffer.
687     Length* charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
688     mModel->mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
689                                                      charactersPerGlyphBuffer + endGlyphIndexPlusOne );
690
691     // Clear the positions buffer.
692     Vector2* positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
693     mModel->mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
694                                                  positionsBuffer + endGlyphIndexPlusOne );
695   }
696
697   if( NO_OPERATION != ( LAYOUT & operations ) )
698   {
699     // Clear the lines.
700     uint32_t startRemoveIndex = mModel->mVisualModel->mLines.Count();
701     uint32_t endRemoveIndex = startRemoveIndex;
702     ClearCharacterRuns( startIndex,
703                         endIndex,
704                         mModel->mVisualModel->mLines,
705                         startRemoveIndex,
706                         endRemoveIndex );
707
708     // Will update the glyph runs.
709     startRemoveIndex = mModel->mVisualModel->mLines.Count();
710     endRemoveIndex = startRemoveIndex;
711     ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex,
712                     endGlyphIndexPlusOne - 1u,
713                     mModel->mVisualModel->mLines,
714                     startRemoveIndex,
715                     endRemoveIndex );
716
717     // Set the line index from where to insert the new laid-out lines.
718     mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
719
720     LineRun* linesBuffer = mModel->mVisualModel->mLines.Begin();
721     mModel->mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex,
722                                         linesBuffer + endRemoveIndex );
723   }
724
725   if( NO_OPERATION != ( COLOR & operations ) )
726   {
727     if( 0u != mModel->mVisualModel->mColorIndices.Count() )
728     {
729       ColorIndex* colorIndexBuffer = mModel->mVisualModel->mColorIndices.Begin();
730       mModel->mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
731                                                  colorIndexBuffer + endGlyphIndexPlusOne );
732     }
733   }
734 }
735
736 void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations )
737 {
738   if( mTextUpdateInfo.mClearAll ||
739       ( ( 0u == startIndex ) &&
740         ( mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u ) ) )
741   {
742     ClearFullModelData( operations );
743   }
744   else
745   {
746     // Clear the model data related with characters.
747     ClearCharacterModelData( startIndex, endIndex, operations );
748
749     // Clear the model data related with glyphs.
750     ClearGlyphModelData( startIndex, endIndex, operations );
751   }
752
753   // The estimated number of lines. Used to avoid reallocations when layouting.
754   mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
755
756   mModel->mVisualModel->ClearCaches();
757 }
758
759 bool Controller::Impl::UpdateModel( OperationsMask operationsRequired )
760 {
761   DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" );
762
763   // Calculate the operations to be done.
764   const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
765
766   if( NO_OPERATION == operations )
767   {
768     // Nothing to do if no operations are pending and required.
769     return false;
770   }
771
772   Vector<Character> utf32CharactersStar;
773   const Length characterCount = mModel->mLogicalModel->mText.Count();
774   const bool isPasswordInput = ( mEventData != NULL && mEventData->mPasswordInput &&
775         !mEventData->mIsShowingPlaceholderText && characterCount > 0 );
776
777   if (isPasswordInput)
778   {
779     utf32CharactersStar.Resize( characterCount );
780
781     uint32_t* begin = utf32CharactersStar.Begin();
782     uint32_t* end = begin + characterCount;
783     while ( begin < end )
784     {
785       *begin++ = STAR;
786     }
787   }
788
789   Vector<Character>& utf32Characters = isPasswordInput ? utf32CharactersStar : mModel->mLogicalModel->mText;
790   const Length numberOfCharacters = utf32Characters.Count();
791
792   // Index to the first character of the first paragraph to be updated.
793   CharacterIndex startIndex = 0u;
794   // Number of characters of the paragraphs to be removed.
795   Length paragraphCharacters = 0u;
796
797   CalculateTextUpdateIndices( paragraphCharacters );
798   startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
799
800   if( mTextUpdateInfo.mClearAll ||
801       ( 0u != paragraphCharacters ) )
802   {
803     ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operations );
804   }
805
806   mTextUpdateInfo.mClearAll = false;
807
808   // Whether the model is updated.
809   bool updated = false;
810
811   Vector<LineBreakInfo>& lineBreakInfo = mModel->mLogicalModel->mLineBreakInfo;
812   const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
813
814   if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) )
815   {
816     // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
817     // calculate the bidirectional info for each 'paragraph'.
818     // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
819     // is not shaped together).
820     lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
821
822     SetLineBreakInfo( utf32Characters,
823                       startIndex,
824                       requestedNumberOfCharacters,
825                       lineBreakInfo );
826
827     // Create the paragraph info.
828     mModel->mLogicalModel->CreateParagraphInfo( startIndex,
829                                                 requestedNumberOfCharacters );
830     updated = true;
831   }
832
833   Vector<WordBreakInfo>& wordBreakInfo = mModel->mLogicalModel->mWordBreakInfo;
834   if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) )
835   {
836     // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
837     wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
838
839     SetWordBreakInfo( utf32Characters,
840                       startIndex,
841                       requestedNumberOfCharacters,
842                       wordBreakInfo );
843     updated = true;
844   }
845
846   const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations );
847   const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations );
848
849   Vector<ScriptRun>& scripts = mModel->mLogicalModel->mScriptRuns;
850   Vector<FontRun>& validFonts = mModel->mLogicalModel->mFontRuns;
851
852   if( getScripts || validateFonts )
853   {
854     // Validates the fonts assigned by the application or assigns default ones.
855     // It makes sure all the characters are going to be rendered by the correct font.
856     MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
857
858     if( getScripts )
859     {
860       // Retrieves the scripts used in the text.
861       multilanguageSupport.SetScripts( utf32Characters,
862                                        startIndex,
863                                        requestedNumberOfCharacters,
864                                        scripts );
865     }
866
867     if( validateFonts )
868     {
869       // Validate the fonts set through the mark-up string.
870       Vector<FontDescriptionRun>& fontDescriptionRuns = mModel->mLogicalModel->mFontDescriptionRuns;
871
872       // Get the default font's description.
873       TextAbstraction::FontDescription defaultFontDescription;
874       TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
875       if( NULL != mFontDefaults )
876       {
877         defaultFontDescription = mFontDefaults->mFontDescription;
878         defaultPointSize = mFontDefaults->mDefaultPointSize * 64u;
879       }
880
881       // Validates the fonts. If there is a character with no assigned font it sets a default one.
882       // After this call, fonts are validated.
883       multilanguageSupport.ValidateFonts( utf32Characters,
884                                           scripts,
885                                           fontDescriptionRuns,
886                                           defaultFontDescription,
887                                           defaultPointSize,
888                                           startIndex,
889                                           requestedNumberOfCharacters,
890                                           validFonts );
891     }
892     updated = true;
893   }
894
895   Vector<Character> mirroredUtf32Characters;
896   bool textMirrored = false;
897   const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
898   if( NO_OPERATION != ( BIDI_INFO & operations ) )
899   {
900     Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mModel->mLogicalModel->mBidirectionalParagraphInfo;
901     bidirectionalInfo.Reserve( numberOfParagraphs );
902
903     // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
904     SetBidirectionalInfo( utf32Characters,
905                           scripts,
906                           lineBreakInfo,
907                           startIndex,
908                           requestedNumberOfCharacters,
909                           bidirectionalInfo );
910
911     if( 0u != bidirectionalInfo.Count() )
912     {
913       // Only set the character directions if there is right to left characters.
914       Vector<CharacterDirection>& directions = mModel->mLogicalModel->mCharacterDirections;
915       GetCharactersDirection( bidirectionalInfo,
916                               numberOfCharacters,
917                               startIndex,
918                               requestedNumberOfCharacters,
919                               directions );
920
921       // This paragraph has right to left text. Some characters may need to be mirrored.
922       // TODO: consider if the mirrored string can be stored as well.
923
924       textMirrored = GetMirroredText( utf32Characters,
925                                       directions,
926                                       bidirectionalInfo,
927                                       startIndex,
928                                       requestedNumberOfCharacters,
929                                       mirroredUtf32Characters );
930     }
931     else
932     {
933       // There is no right to left characters. Clear the directions vector.
934       mModel->mLogicalModel->mCharacterDirections.Clear();
935     }
936     updated = true;
937   }
938
939   Vector<GlyphInfo>& glyphs = mModel->mVisualModel->mGlyphs;
940   Vector<CharacterIndex>& glyphsToCharactersMap = mModel->mVisualModel->mGlyphsToCharacters;
941   Vector<Length>& charactersPerGlyph = mModel->mVisualModel->mCharactersPerGlyph;
942   Vector<GlyphIndex> newParagraphGlyphs;
943   newParagraphGlyphs.Reserve( numberOfParagraphs );
944
945   const Length currentNumberOfGlyphs = glyphs.Count();
946   if( NO_OPERATION != ( SHAPE_TEXT & operations ) )
947   {
948     const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
949     // Shapes the text.
950     ShapeText( textToShape,
951                lineBreakInfo,
952                scripts,
953                validFonts,
954                startIndex,
955                mTextUpdateInfo.mStartGlyphIndex,
956                requestedNumberOfCharacters,
957                glyphs,
958                glyphsToCharactersMap,
959                charactersPerGlyph,
960                newParagraphGlyphs );
961
962     // Create the 'number of glyphs' per character and the glyph to character conversion tables.
963     mModel->mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
964     mModel->mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters );
965     updated = true;
966   }
967
968   const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
969
970   if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) )
971   {
972     GlyphInfo* glyphsBuffer = glyphs.Begin();
973     mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs );
974
975     // Update the width and advance of all new paragraph characters.
976     for( Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it )
977     {
978       const GlyphIndex index = *it;
979       GlyphInfo& glyph = *( glyphsBuffer + index );
980
981       glyph.xBearing = 0.f;
982       glyph.width = 0.f;
983       glyph.advance = 0.f;
984     }
985     updated = true;
986   }
987
988   if( NO_OPERATION != ( COLOR & operations ) )
989   {
990     // Set the color runs in glyphs.
991     SetColorSegmentationInfo( mModel->mLogicalModel->mColorRuns,
992                               mModel->mVisualModel->mCharactersToGlyph,
993                               mModel->mVisualModel->mGlyphsPerCharacter,
994                               startIndex,
995                               mTextUpdateInfo.mStartGlyphIndex,
996                               requestedNumberOfCharacters,
997                               mModel->mVisualModel->mColors,
998                               mModel->mVisualModel->mColorIndices );
999
1000     updated = true;
1001   }
1002
1003   if( ( NULL != mEventData ) &&
1004       mEventData->mPreEditFlag &&
1005       ( 0u != mModel->mVisualModel->mCharactersToGlyph.Count() ) )
1006   {
1007     // Add the underline for the pre-edit text.
1008     const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1009     const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
1010
1011     const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mEventData->mPreEditStartPosition );
1012     const CharacterIndex lastPreEditCharacter = mEventData->mPreEditStartPosition + ( ( mEventData->mPreEditLength > 0u ) ? mEventData->mPreEditLength - 1u : 0u );
1013     const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter );
1014     const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u );
1015
1016     GlyphRun underlineRun;
1017     underlineRun.glyphIndex = glyphStart;
1018     underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart;
1019
1020     // TODO: At the moment the underline runs are only for pre-edit.
1021     mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
1022   }
1023
1024   // The estimated number of lines. Used to avoid reallocations when layouting.
1025   mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() );
1026
1027   // Set the previous number of characters for the next time the text is updated.
1028   mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
1029
1030   return updated;
1031 }
1032
1033 void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle )
1034 {
1035   // Sets the default text's color.
1036   inputStyle.textColor = mTextColor;
1037   inputStyle.isDefaultColor = true;
1038
1039   inputStyle.familyName.clear();
1040   inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1041   inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1042   inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1043   inputStyle.size = 0.f;
1044
1045   inputStyle.lineSpacing = 0.f;
1046
1047   inputStyle.underlineProperties.clear();
1048   inputStyle.shadowProperties.clear();
1049   inputStyle.embossProperties.clear();
1050   inputStyle.outlineProperties.clear();
1051
1052   inputStyle.isFamilyDefined = false;
1053   inputStyle.isWeightDefined = false;
1054   inputStyle.isWidthDefined = false;
1055   inputStyle.isSlantDefined = false;
1056   inputStyle.isSizeDefined = false;
1057
1058   inputStyle.isLineSpacingDefined = false;
1059
1060   inputStyle.isUnderlineDefined = false;
1061   inputStyle.isShadowDefined = false;
1062   inputStyle.isEmbossDefined = false;
1063   inputStyle.isOutlineDefined = false;
1064
1065   // Sets the default font's family name, weight, width, slant and size.
1066   if( mFontDefaults )
1067   {
1068     if( mFontDefaults->familyDefined )
1069     {
1070       inputStyle.familyName = mFontDefaults->mFontDescription.family;
1071       inputStyle.isFamilyDefined = true;
1072     }
1073
1074     if( mFontDefaults->weightDefined )
1075     {
1076       inputStyle.weight = mFontDefaults->mFontDescription.weight;
1077       inputStyle.isWeightDefined = true;
1078     }
1079
1080     if( mFontDefaults->widthDefined )
1081     {
1082       inputStyle.width = mFontDefaults->mFontDescription.width;
1083       inputStyle.isWidthDefined = true;
1084     }
1085
1086     if( mFontDefaults->slantDefined )
1087     {
1088       inputStyle.slant = mFontDefaults->mFontDescription.slant;
1089       inputStyle.isSlantDefined = true;
1090     }
1091
1092     if( mFontDefaults->sizeDefined )
1093     {
1094       inputStyle.size = mFontDefaults->mDefaultPointSize;
1095       inputStyle.isSizeDefined = true;
1096     }
1097   }
1098 }
1099
1100 float Controller::Impl::GetDefaultFontLineHeight()
1101 {
1102   FontId defaultFontId = 0u;
1103   if( NULL == mFontDefaults )
1104   {
1105     TextAbstraction::FontDescription fontDescription;
1106     defaultFontId = mFontClient.GetFontId( fontDescription );
1107   }
1108   else
1109   {
1110     defaultFontId = mFontDefaults->GetFontId( mFontClient );
1111   }
1112
1113   Text::FontMetrics fontMetrics;
1114   mMetrics->GetFontMetrics( defaultFontId, fontMetrics );
1115
1116   return( fontMetrics.ascender - fontMetrics.descender );
1117 }
1118
1119 void Controller::Impl::OnCursorKeyEvent( const Event& event )
1120 {
1121   if( NULL == mEventData )
1122   {
1123     // Nothing to do if there is no text input.
1124     return;
1125   }
1126
1127   int keyCode = event.p1.mInt;
1128
1129   if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
1130   {
1131     if( mEventData->mPrimaryCursorPosition > 0u )
1132     {
1133       mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
1134     }
1135   }
1136   else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
1137   {
1138     if( mModel->mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
1139     {
1140       mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
1141     }
1142   }
1143   else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
1144   {
1145     // Get first the line index of the current cursor position index.
1146     CharacterIndex characterIndex = 0u;
1147
1148     if( mEventData->mPrimaryCursorPosition > 0u )
1149     {
1150       characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1151     }
1152
1153     const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1154
1155     if( lineIndex > 0u )
1156     {
1157       // Retrieve the cursor position info.
1158       CursorInfo cursorInfo;
1159       GetCursorPosition( mEventData->mPrimaryCursorPosition,
1160                          cursorInfo );
1161
1162       // Get the line above.
1163       const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + ( lineIndex - 1u ) );
1164
1165       // Get the next hit 'y' point.
1166       const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
1167
1168       // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1169       mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1170                                                                         mModel->mLogicalModel,
1171                                                                         mMetrics,
1172                                                                         mEventData->mCursorHookPositionX,
1173                                                                         hitPointY );
1174     }
1175   }
1176   else if(   Dali::DALI_KEY_CURSOR_DOWN == keyCode )
1177   {
1178     // Get first the line index of the current cursor position index.
1179     CharacterIndex characterIndex = 0u;
1180
1181     if( mEventData->mPrimaryCursorPosition > 0u )
1182     {
1183       characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1184     }
1185
1186     const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1187
1188     if( lineIndex + 1u < mModel->mVisualModel->mLines.Count() )
1189     {
1190       // Retrieve the cursor position info.
1191       CursorInfo cursorInfo;
1192       GetCursorPosition( mEventData->mPrimaryCursorPosition,
1193                          cursorInfo );
1194
1195       // Get the line below.
1196       const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + lineIndex + 1u );
1197
1198       // Get the next hit 'y' point.
1199       const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
1200
1201       // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1202       mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1203                                                                         mModel->mLogicalModel,
1204                                                                         mMetrics,
1205                                                                         mEventData->mCursorHookPositionX,
1206                                                                         hitPointY );
1207     }
1208   }
1209
1210   mEventData->mUpdateCursorPosition = true;
1211   mEventData->mUpdateInputStyle = true;
1212   mEventData->mScrollAfterUpdatePosition = true;
1213 }
1214
1215 void Controller::Impl::OnTapEvent( const Event& event )
1216 {
1217   if( NULL != mEventData )
1218   {
1219     const unsigned int tapCount = event.p1.mUint;
1220
1221     if( 1u == tapCount )
1222     {
1223       if( IsShowingRealText() )
1224       {
1225         // Convert from control's coords to text's coords.
1226         const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1227         const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1228
1229         // Keep the tap 'x' position. Used to move the cursor.
1230         mEventData->mCursorHookPositionX = xPosition;
1231
1232         mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1233                                                                           mModel->mLogicalModel,
1234                                                                           mMetrics,
1235                                                                           xPosition,
1236                                                                           yPosition );
1237
1238         // When the cursor position is changing, delay cursor blinking
1239         mEventData->mDecorator->DelayCursorBlink();
1240       }
1241       else
1242       {
1243         mEventData->mPrimaryCursorPosition = 0u;
1244       }
1245
1246       mEventData->mUpdateCursorPosition = true;
1247       mEventData->mUpdateGrabHandlePosition = true;
1248       mEventData->mScrollAfterUpdatePosition = true;
1249       mEventData->mUpdateInputStyle = true;
1250
1251       // Notify the cursor position to the imf manager.
1252       if( mEventData->mImfManager )
1253       {
1254         mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1255         mEventData->mImfManager.NotifyCursorPosition();
1256       }
1257     }
1258   }
1259 }
1260
1261 void Controller::Impl::OnPanEvent( const Event& event )
1262 {
1263   if( NULL == mEventData )
1264   {
1265     // Nothing to do if there is no text input.
1266     return;
1267   }
1268
1269   const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled();
1270   const bool isVerticalScrollEnabled = mEventData->mDecorator->IsVerticalScrollEnabled();
1271
1272   if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled )
1273   {
1274     // Nothing to do if scrolling is not enabled.
1275     return;
1276   }
1277
1278   const int state = event.p1.mInt;
1279
1280   switch( state )
1281   {
1282     case Gesture::Started:
1283     {
1284       // Will remove the cursor, handles or text's popup, ...
1285       ChangeState( EventData::TEXT_PANNING );
1286       break;
1287     }
1288     case Gesture::Continuing:
1289     {
1290       const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1291       const Vector2 currentScroll = mModel->mScrollPosition;
1292
1293       if( isHorizontalScrollEnabled )
1294       {
1295         const float displacementX = event.p2.mFloat;
1296         mModel->mScrollPosition.x += displacementX;
1297
1298         ClampHorizontalScroll( layoutSize );
1299       }
1300
1301       if( isVerticalScrollEnabled )
1302       {
1303         const float displacementY = event.p3.mFloat;
1304         mModel->mScrollPosition.y += displacementY;
1305
1306         ClampVerticalScroll( layoutSize );
1307       }
1308
1309       mEventData->mDecorator->UpdatePositions( mModel->mScrollPosition - currentScroll );
1310       break;
1311     }
1312     case Gesture::Finished:
1313     case Gesture::Cancelled: // FALLTHROUGH
1314     {
1315       // Will go back to the previous state to show the cursor, handles, the text's popup, ...
1316       ChangeState( mEventData->mPreviousState );
1317       break;
1318     }
1319     default:
1320       break;
1321   }
1322 }
1323
1324 void Controller::Impl::OnLongPressEvent( const Event& event )
1325 {
1326   DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
1327
1328   if( EventData::EDITING == mEventData->mState )
1329   {
1330     ChangeState ( EventData::EDITING_WITH_POPUP );
1331     mEventData->mDecoratorUpdated = true;
1332     mEventData->mUpdateInputStyle = true;
1333   }
1334 }
1335
1336 void Controller::Impl::OnHandleEvent( const Event& event )
1337 {
1338   if( NULL == mEventData )
1339   {
1340     // Nothing to do if there is no text input.
1341     return;
1342   }
1343
1344   const unsigned int state = event.p1.mUint;
1345   const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1346   const bool isSmoothHandlePanEnabled = mEventData->mDecorator->IsSmoothHandlePanEnabled();
1347
1348   if( HANDLE_PRESSED == state )
1349   {
1350     // Convert from decorator's coords to text's coords.
1351     const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1352     const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1353
1354     // Need to calculate the handle's new position.
1355     const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1356                                                                           mModel->mLogicalModel,
1357                                                                           mMetrics,
1358                                                                           xPosition,
1359                                                                           yPosition );
1360
1361     if( Event::GRAB_HANDLE_EVENT == event.type )
1362     {
1363       ChangeState ( EventData::GRAB_HANDLE_PANNING );
1364
1365       if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1366       {
1367         // Updates the cursor position if the handle's new position is different than the current one.
1368         mEventData->mUpdateCursorPosition = true;
1369         // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
1370         mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1371         mEventData->mPrimaryCursorPosition = handleNewPosition;
1372       }
1373
1374       // Updates the decorator if the soft handle panning is enabled. It triggers a relayout in the decorator and the new position of the handle is set.
1375       mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1376     }
1377     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1378     {
1379       ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1380
1381       if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1382           ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1383       {
1384         // Updates the highlight box if the handle's new position is different than the current one.
1385         mEventData->mUpdateHighlightBox = true;
1386         // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1387         mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1388         mEventData->mLeftSelectionPosition = handleNewPosition;
1389       }
1390
1391       // Updates the decorator if the soft handle panning is enabled. It triggers a relayout in the decorator and the new position of the handle is set.
1392       mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1393
1394       // Will define the order to scroll the text to match the handle position.
1395       mEventData->mIsLeftHandleSelected = true;
1396     }
1397     else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1398     {
1399       ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1400
1401       if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1402           ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1403       {
1404         // Updates the highlight box if the handle's new position is different than the current one.
1405         mEventData->mUpdateHighlightBox = true;
1406         // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1407         mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1408         mEventData->mRightSelectionPosition = handleNewPosition;
1409       }
1410
1411       // Updates the decorator if the soft handle panning is enabled. It triggers a relayout in the decorator and the new position of the handle is set.
1412       mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1413
1414       // Will define the order to scroll the text to match the handle position.
1415       mEventData->mIsLeftHandleSelected = false;
1416     }
1417   } // end ( HANDLE_PRESSED == state )
1418   else if( ( HANDLE_RELEASED == state ) ||
1419            handleStopScrolling )
1420   {
1421     CharacterIndex handlePosition = 0u;
1422     if( handleStopScrolling || isSmoothHandlePanEnabled )
1423     {
1424       // Convert from decorator's coords to text's coords.
1425       const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1426       const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1427
1428       handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1429                                                     mModel->mLogicalModel,
1430                                                     mMetrics,
1431                                                     xPosition,
1432                                                     yPosition );
1433     }
1434
1435     if( Event::GRAB_HANDLE_EVENT == event.type )
1436     {
1437       mEventData->mUpdateCursorPosition = true;
1438       mEventData->mUpdateGrabHandlePosition = true;
1439       mEventData->mUpdateInputStyle = true;
1440
1441       if( !IsClipboardEmpty() )
1442       {
1443         ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1444       }
1445
1446       if( handleStopScrolling || isSmoothHandlePanEnabled )
1447       {
1448         mEventData->mScrollAfterUpdatePosition = true;
1449         mEventData->mPrimaryCursorPosition = handlePosition;
1450       }
1451     }
1452     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1453     {
1454       ChangeState( EventData::SELECTING );
1455
1456       mEventData->mUpdateHighlightBox = true;
1457       mEventData->mUpdateLeftSelectionPosition = true;
1458       mEventData->mUpdateRightSelectionPosition = true;
1459
1460       if( handleStopScrolling || isSmoothHandlePanEnabled )
1461       {
1462         mEventData->mScrollAfterUpdatePosition = true;
1463
1464         if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1465             ( handlePosition != mEventData->mLeftSelectionPosition ) )
1466         {
1467           mEventData->mLeftSelectionPosition = handlePosition;
1468         }
1469       }
1470     }
1471     else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1472     {
1473       ChangeState( EventData::SELECTING );
1474
1475       mEventData->mUpdateHighlightBox = true;
1476       mEventData->mUpdateRightSelectionPosition = true;
1477       mEventData->mUpdateLeftSelectionPosition = true;
1478
1479       if( handleStopScrolling || isSmoothHandlePanEnabled )
1480       {
1481         mEventData->mScrollAfterUpdatePosition = true;
1482         if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1483             ( handlePosition != mEventData->mLeftSelectionPosition ) )
1484         {
1485           mEventData->mRightSelectionPosition = handlePosition;
1486         }
1487       }
1488     }
1489
1490     mEventData->mDecoratorUpdated = true;
1491   } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1492   else if( HANDLE_SCROLLING == state )
1493   {
1494     const float xSpeed = event.p2.mFloat;
1495     const float ySpeed = event.p3.mFloat;
1496     const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1497     const Vector2 currentScrollPosition = mModel->mScrollPosition;
1498
1499     mModel->mScrollPosition.x += xSpeed;
1500     mModel->mScrollPosition.y += ySpeed;
1501
1502     ClampHorizontalScroll( layoutSize );
1503     ClampVerticalScroll( layoutSize );
1504
1505     bool endOfScroll = false;
1506     if( Vector2::ZERO == ( currentScrollPosition - mModel->mScrollPosition ) )
1507     {
1508       // Notify the decorator there is no more text to scroll.
1509       // The decorator won't send more scroll events.
1510       mEventData->mDecorator->NotifyEndOfScroll();
1511       // Still need to set the position of the handle.
1512       endOfScroll = true;
1513     }
1514
1515     // Set the position of the handle.
1516     const bool scrollRightDirection = xSpeed > 0.f;
1517     const bool scrollBottomDirection = ySpeed > 0.f;
1518     const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1519     const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1520
1521     if( Event::GRAB_HANDLE_EVENT == event.type )
1522     {
1523       ChangeState( EventData::GRAB_HANDLE_PANNING );
1524
1525       // Get the grab handle position in decorator coords.
1526       Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1527
1528       if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1529       {
1530         // Position the grag handle close to either the left or right edge.
1531         position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1532       }
1533
1534       if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1535       {
1536         position.x = mEventData->mCursorHookPositionX;
1537
1538         // Position the grag handle close to either the top or bottom edge.
1539         position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1540       }
1541
1542       // Get the new handle position.
1543       // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1544       const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1545                                                                          mModel->mLogicalModel,
1546                                                                          mMetrics,
1547                                                                          position.x - mModel->mScrollPosition.x,
1548                                                                          position.y - mModel->mScrollPosition.y );
1549
1550       if( mEventData->mPrimaryCursorPosition != handlePosition )
1551       {
1552         mEventData->mUpdateCursorPosition = true;
1553         mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1554         mEventData->mScrollAfterUpdatePosition = true;
1555         mEventData->mPrimaryCursorPosition = handlePosition;
1556       }
1557       mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1558
1559       // Updates the decorator if the soft handle panning is enabled.
1560       mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1561     }
1562     else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1563     {
1564       ChangeState( EventData::SELECTION_HANDLE_PANNING );
1565
1566       // Get the selection handle position in decorator coords.
1567       Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1568
1569       if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1570       {
1571         // Position the selection handle close to either the left or right edge.
1572         position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1573       }
1574
1575       if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1576       {
1577         position.x = mEventData->mCursorHookPositionX;
1578
1579         // Position the grag handle close to either the top or bottom edge.
1580         position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1581       }
1582
1583       // Get the new handle position.
1584       // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1585       const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1586                                                                          mModel->mLogicalModel,
1587                                                                          mMetrics,
1588                                                                          position.x - mModel->mScrollPosition.x,
1589                                                                          position.y - mModel->mScrollPosition.y );
1590
1591       if( leftSelectionHandleEvent )
1592       {
1593         const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1594
1595         if( differentHandles || endOfScroll )
1596         {
1597           mEventData->mUpdateHighlightBox = true;
1598           mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1599           mEventData->mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1600           mEventData->mLeftSelectionPosition = handlePosition;
1601         }
1602       }
1603       else
1604       {
1605         const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1606         if( differentHandles || endOfScroll )
1607         {
1608           mEventData->mUpdateHighlightBox = true;
1609           mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1610           mEventData->mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1611           mEventData->mRightSelectionPosition = handlePosition;
1612         }
1613       }
1614
1615       if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1616       {
1617         RepositionSelectionHandles();
1618
1619         mEventData->mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1620       }
1621     }
1622     mEventData->mDecoratorUpdated = true;
1623   } // end ( HANDLE_SCROLLING == state )
1624 }
1625
1626 void Controller::Impl::OnSelectEvent( const Event& event )
1627 {
1628   if( NULL == mEventData )
1629   {
1630     // Nothing to do if there is no text.
1631     return;
1632   }
1633
1634   if( mEventData->mSelectionEnabled )
1635   {
1636     // Convert from control's coords to text's coords.
1637     const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1638     const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1639
1640     // Calculates the logical position from the x,y coords.
1641     RepositionSelectionHandles( xPosition,
1642                                 yPosition );
1643   }
1644 }
1645
1646 void Controller::Impl::OnSelectAllEvent()
1647 {
1648   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
1649
1650   if( NULL == mEventData )
1651   {
1652     // Nothing to do if there is no text.
1653     return;
1654   }
1655
1656   if( mEventData->mSelectionEnabled )
1657   {
1658     ChangeState( EventData::SELECTING );
1659
1660     mEventData->mLeftSelectionPosition = 0u;
1661     mEventData->mRightSelectionPosition = mModel->mLogicalModel->mText.Count();
1662
1663     mEventData->mScrollAfterUpdatePosition = true;
1664     mEventData->mUpdateLeftSelectionPosition = true;
1665     mEventData->mUpdateRightSelectionPosition = true;
1666     mEventData->mUpdateHighlightBox = true;
1667   }
1668 }
1669
1670 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1671 {
1672   if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1673   {
1674     // Nothing to select if handles are in the same place.
1675     selectedText.clear();
1676     return;
1677   }
1678
1679   const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1680
1681   //Get start and end position of selection
1682   const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1683   const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1684
1685   Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
1686   const Length numberOfCharacters = utf32Characters.Count();
1687
1688   // Validate the start and end selection points
1689   if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1690   {
1691     //Get text as a UTF8 string
1692     Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1693
1694     if( deleteAfterRetrieval ) // Only delete text if copied successfully
1695     {
1696       // Keep a copy of the current input style.
1697       InputStyle currentInputStyle;
1698       currentInputStyle.Copy( mEventData->mInputStyle );
1699
1700       // Set as input style the style of the first deleted character.
1701       mModel->mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1702
1703       // Compare if the input style has changed.
1704       const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
1705
1706       if( hasInputStyleChanged )
1707       {
1708         const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
1709         // Queue the input style changed signal.
1710         mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
1711       }
1712
1713       mModel->mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1714
1715       // Mark the paragraphs to be updated.
1716       mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1717       mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1718
1719       // Delete text between handles
1720       Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1721       Vector<Character>::Iterator last  = first + lengthOfSelectedText;
1722       utf32Characters.Erase( first, last );
1723
1724       // Will show the cursor at the first character of the selection.
1725       mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1726     }
1727     else
1728     {
1729       // Will show the cursor at the last character of the selection.
1730       mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1731     }
1732
1733     mEventData->mDecoratorUpdated = true;
1734   }
1735 }
1736
1737 void Controller::Impl::ShowClipboard()
1738 {
1739   if( mClipboard )
1740   {
1741     mClipboard.ShowClipboard();
1742   }
1743 }
1744
1745 void Controller::Impl::HideClipboard()
1746 {
1747   if( mClipboard && mClipboardHideEnabled )
1748   {
1749     mClipboard.HideClipboard();
1750   }
1751 }
1752
1753 void Controller::Impl::SetClipboardHideEnable(bool enable)
1754 {
1755   mClipboardHideEnabled = enable;
1756 }
1757
1758 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1759 {
1760   //Send string to clipboard
1761   return ( mClipboard && mClipboard.SetItem( source ) );
1762 }
1763
1764 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1765 {
1766   std::string selectedText;
1767   RetrieveSelection( selectedText, deleteAfterSending );
1768   CopyStringToClipboard( selectedText );
1769   ChangeState( EventData::EDITING );
1770 }
1771
1772 void Controller::Impl::RequestGetTextFromClipboard()
1773 {
1774   if ( mClipboard )
1775   {
1776     mClipboard.RequestItem();
1777   }
1778 }
1779
1780 void Controller::Impl::RepositionSelectionHandles()
1781 {
1782   CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1783   CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1784
1785   if( selectionStart == selectionEnd )
1786   {
1787     // Nothing to select if handles are in the same place.
1788     return;
1789   }
1790
1791   mEventData->mDecorator->ClearHighlights();
1792
1793   const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1794   const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
1795   const GlyphInfo* const glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
1796   const Vector2* const positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
1797   const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
1798   const CharacterIndex* const glyphToCharacterBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
1799   const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mModel->mLogicalModel->mCharacterDirections.Count() ) ? mModel->mLogicalModel->mCharacterDirections.Begin() : NULL;
1800
1801   const bool isLastCharacter = selectionEnd >= mModel->mLogicalModel->mText.Count();
1802   const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
1803   const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
1804
1805   // Swap the indices if the start is greater than the end.
1806   const bool indicesSwapped = selectionStart > selectionEnd;
1807
1808   // Tell the decorator to flip the selection handles if needed.
1809   mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
1810
1811   if( indicesSwapped )
1812   {
1813     std::swap( selectionStart, selectionEnd );
1814   }
1815
1816   // Get the indices to the first and last selected glyphs.
1817   const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
1818   const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
1819   const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
1820   const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
1821
1822   // Get the lines where the glyphs are laid-out.
1823   const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
1824
1825   LineIndex lineIndex = 0u;
1826   Length numberOfLines = 0u;
1827   mModel->mVisualModel->GetNumberOfLines( glyphStart,
1828                                           1u + glyphEnd - glyphStart,
1829                                           lineIndex,
1830                                           numberOfLines );
1831   const LineIndex firstLineIndex = lineIndex;
1832
1833   // Create the structure to store some selection box info.
1834   Vector<SelectionBoxInfo> selectionBoxLinesInfo;
1835   selectionBoxLinesInfo.Resize( numberOfLines );
1836
1837   SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
1838   selectionBoxInfo->minX = MAX_FLOAT;
1839   selectionBoxInfo->maxX = MIN_FLOAT;
1840
1841   // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
1842   float minHighlightX = std::numeric_limits<float>::max();
1843   float maxHighlightX = std::numeric_limits<float>::min();
1844   Size highLightSize;
1845   Vector2 highLightPosition; // The highlight position in decorator's coords.
1846
1847   // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
1848
1849   // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
1850   selectionBoxInfo->lineOffset = CalculateLineOffset( mModel->mVisualModel->mLines,
1851                                                       firstLineIndex );
1852
1853   // Transform to decorator's (control) coords.
1854   selectionBoxInfo->lineOffset += mModel->mScrollPosition.y;
1855
1856   lineRun += firstLineIndex;
1857
1858   // The line height is the addition of the line ascender and the line descender.
1859   // However, the line descender has a negative value, hence the subtraction.
1860   selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1861
1862   GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1863
1864   // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
1865   const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
1866   bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionStart ) );
1867
1868   // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
1869   const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
1870   bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionEndMinusOne ) );
1871
1872   // The number of quads of the selection box.
1873   const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
1874   mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
1875
1876   // Count the actual number of quads.
1877   unsigned int actualNumberOfQuads = 0u;
1878   Vector4 quad;
1879
1880   // Traverse the glyphs.
1881   for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
1882   {
1883     const GlyphInfo& glyph = *( glyphsBuffer + index );
1884     const Vector2& position = *( positionsBuffer + index );
1885
1886     if( splitStartGlyph )
1887     {
1888       // If the first glyph is a ligature that must be broken it may be needed to add only part of the glyph to the highlight box.
1889
1890       const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
1891       const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
1892       // Get the direction of the character.
1893       CharacterDirection isCurrentRightToLeft = false;
1894       if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1895       {
1896         isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
1897       }
1898
1899       // The end point could be in the middle of the ligature.
1900       // Calculate the number of characters selected.
1901       const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
1902
1903       quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
1904       quad.y = selectionBoxInfo->lineOffset;
1905       quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
1906       quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
1907
1908       // Store the min and max 'x' for each line.
1909       selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1910       selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1911
1912       mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
1913       ++actualNumberOfQuads;
1914
1915       splitStartGlyph = false;
1916       continue;
1917     }
1918
1919     if( splitEndGlyph && ( index == glyphEnd ) )
1920     {
1921       // Equally, if the last glyph is a ligature that must be broken it may be needed to add only part of the glyph to the highlight box.
1922
1923       const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
1924       const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
1925       // Get the direction of the character.
1926       CharacterDirection isCurrentRightToLeft = false;
1927       if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1928       {
1929         isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
1930       }
1931
1932       const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
1933
1934       quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
1935       quad.y = selectionBoxInfo->lineOffset;
1936       quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
1937       quad.w = quad.y + selectionBoxInfo->lineHeight;
1938
1939       // Store the min and max 'x' for each line.
1940       selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1941       selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1942
1943       mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1944                                             quad );
1945       ++actualNumberOfQuads;
1946
1947       splitEndGlyph = false;
1948       continue;
1949     }
1950
1951     quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x;
1952     quad.y = selectionBoxInfo->lineOffset;
1953     quad.z = quad.x + glyph.advance;
1954     quad.w = quad.y + selectionBoxInfo->lineHeight;
1955
1956     // Store the min and max 'x' for each line.
1957     selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1958     selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1959
1960     mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1961                                           quad );
1962     ++actualNumberOfQuads;
1963
1964     // Whether to retrieve the next line.
1965     if( index == lastGlyphOfLine )
1966     {
1967       // Retrieve the next line.
1968       ++lineRun;
1969
1970       // Get the last glyph of the new line.
1971       lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1972
1973       ++lineIndex;
1974       if( lineIndex < firstLineIndex + numberOfLines )
1975       {
1976         // Keep the offset and height of the current selection box.
1977         const float currentLineOffset = selectionBoxInfo->lineOffset;
1978         const float currentLineHeight = selectionBoxInfo->lineHeight;
1979
1980         // Get the selection box info for the next line.
1981         ++selectionBoxInfo;
1982
1983         selectionBoxInfo->minX = MAX_FLOAT;
1984         selectionBoxInfo->maxX = MIN_FLOAT;
1985
1986         // Update the line's vertical offset.
1987         selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
1988
1989         // The line height is the addition of the line ascender and the line descender.
1990         // However, the line descender has a negative value, hence the subtraction.
1991         selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1992       }
1993     }
1994   }
1995
1996   // Traverses all the lines and updates the min and max 'x' positions and the total height.
1997   // The final width is calculated after 'boxifying' the selection.
1998   for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
1999          endIt = selectionBoxLinesInfo.End();
2000        it != endIt;
2001        ++it )
2002   {
2003     const SelectionBoxInfo& info = *it;
2004
2005     // Update the size of the highlighted text.
2006     highLightSize.height += info.lineHeight;
2007     minHighlightX = std::min( minHighlightX, info.minX );
2008     maxHighlightX = std::max( maxHighlightX, info.maxX );
2009   }
2010
2011   // Add extra geometry to 'boxify' the selection.
2012
2013   if( 1u < numberOfLines )
2014   {
2015     // Boxify the first line.
2016     lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex;
2017     const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2018
2019     bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
2020     bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
2021
2022     if( boxifyBegin )
2023     {
2024       quad.x = 0.f;
2025       quad.y = firstSelectionBoxLineInfo.lineOffset;
2026       quad.z = firstSelectionBoxLineInfo.minX;
2027       quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2028
2029       // Boxify at the beginning of the line.
2030       mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2031                                             quad );
2032       ++actualNumberOfQuads;
2033
2034       // Update the size of the highlighted text.
2035       minHighlightX = 0.f;
2036     }
2037
2038     if( boxifyEnd )
2039     {
2040       quad.x = firstSelectionBoxLineInfo.maxX;
2041       quad.y = firstSelectionBoxLineInfo.lineOffset;
2042       quad.z = mModel->mVisualModel->mControlSize.width;
2043       quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2044
2045       // Boxify at the end of the line.
2046       mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2047                                             quad );
2048       ++actualNumberOfQuads;
2049
2050       // Update the size of the highlighted text.
2051       maxHighlightX = mModel->mVisualModel->mControlSize.width;
2052     }
2053
2054     // Boxify the central lines.
2055     if( 2u < numberOfLines )
2056     {
2057       for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
2058              endIt = selectionBoxLinesInfo.End() - 1u;
2059            it != endIt;
2060            ++it )
2061       {
2062         const SelectionBoxInfo& info = *it;
2063
2064         quad.x = 0.f;
2065         quad.y = info.lineOffset;
2066         quad.z = info.minX;
2067         quad.w = info.lineOffset + info.lineHeight;
2068
2069         mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2070                                               quad );
2071         ++actualNumberOfQuads;
2072
2073         quad.x = info.maxX;
2074         quad.y = info.lineOffset;
2075         quad.z = mModel->mVisualModel->mControlSize.width;
2076         quad.w = info.lineOffset + info.lineHeight;
2077
2078         mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2079                                               quad );
2080         ++actualNumberOfQuads;
2081       }
2082
2083       // Update the size of the highlighted text.
2084       minHighlightX = 0.f;
2085       maxHighlightX = mModel->mVisualModel->mControlSize.width;
2086     }
2087
2088     // Boxify the last line.
2089     lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
2090     const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
2091
2092     boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
2093     boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
2094
2095     if( boxifyBegin )
2096     {
2097       quad.x = 0.f;
2098       quad.y = lastSelectionBoxLineInfo.lineOffset;
2099       quad.z = lastSelectionBoxLineInfo.minX;
2100       quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2101
2102       // Boxify at the beginning of the line.
2103       mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2104                                             quad );
2105       ++actualNumberOfQuads;
2106
2107       // Update the size of the highlighted text.
2108       minHighlightX = 0.f;
2109     }
2110
2111     if( boxifyEnd )
2112     {
2113       quad.x = lastSelectionBoxLineInfo.maxX;
2114       quad.y = lastSelectionBoxLineInfo.lineOffset;
2115       quad.z = mModel->mVisualModel->mControlSize.width;
2116       quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2117
2118       // Boxify at the end of the line.
2119       mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2120                                             quad );
2121       ++actualNumberOfQuads;
2122
2123       // Update the size of the highlighted text.
2124       maxHighlightX = mModel->mVisualModel->mControlSize.width;
2125     }
2126   }
2127
2128   // Set the actual number of quads.
2129   mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
2130
2131   // Sets the highlight's size and position. In decorator's coords.
2132   // The highlight's height has been calculated above (before 'boxifying' the highlight).
2133   highLightSize.width = maxHighlightX - minHighlightX;
2134
2135   highLightPosition.x = minHighlightX;
2136   const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2137   highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
2138
2139   mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize );
2140
2141   if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
2142   {
2143     CursorInfo primaryCursorInfo;
2144     GetCursorPosition( mEventData->mLeftSelectionPosition,
2145                        primaryCursorInfo );
2146
2147     const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2148
2149     mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
2150                                          primaryPosition.x,
2151                                          primaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2152                                          primaryCursorInfo.lineHeight );
2153
2154     CursorInfo secondaryCursorInfo;
2155     GetCursorPosition( mEventData->mRightSelectionPosition,
2156                        secondaryCursorInfo );
2157
2158     const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2159
2160     mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
2161                                          secondaryPosition.x,
2162                                          secondaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2163                                          secondaryCursorInfo.lineHeight );
2164   }
2165
2166   // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2167   mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
2168
2169   // Set the flag to update the decorator.
2170   mEventData->mDecoratorUpdated = true;
2171 }
2172
2173 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
2174 {
2175   if( NULL == mEventData )
2176   {
2177     // Nothing to do if there is no text input.
2178     return;
2179   }
2180
2181   if( IsShowingPlaceholderText() )
2182   {
2183     // Nothing to do if there is the place-holder text.
2184     return;
2185   }
2186
2187   const Length numberOfGlyphs = mModel->mVisualModel->mGlyphs.Count();
2188   const Length numberOfLines  = mModel->mVisualModel->mLines.Count();
2189   if( ( 0 == numberOfGlyphs ) ||
2190       ( 0 == numberOfLines ) )
2191   {
2192     // Nothing to do if there is no text.
2193     return;
2194   }
2195
2196   // Find which word was selected
2197   CharacterIndex selectionStart( 0 );
2198   CharacterIndex selectionEnd( 0 );
2199   const bool indicesFound = FindSelectionIndices( mModel->mVisualModel,
2200                                                   mModel->mLogicalModel,
2201                                                   mMetrics,
2202                                                   visualX,
2203                                                   visualY,
2204                                                   selectionStart,
2205                                                   selectionEnd );
2206   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2207
2208   if( indicesFound )
2209   {
2210     ChangeState( EventData::SELECTING );
2211
2212     mEventData->mLeftSelectionPosition = selectionStart;
2213     mEventData->mRightSelectionPosition = selectionEnd;
2214
2215     mEventData->mUpdateLeftSelectionPosition = true;
2216     mEventData->mUpdateRightSelectionPosition = true;
2217     mEventData->mUpdateHighlightBox = true;
2218
2219     // It may happen an IMF commit event arrives before the selection event
2220     // if the IMF manager is in pre-edit state. The commit event will set the
2221     // mEventData->mUpdateCursorPosition flag to true. If it's not set back
2222     // to false, the highlight box won't be updated.
2223     mEventData->mUpdateCursorPosition = false;
2224
2225     mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2226   }
2227   else
2228   {
2229     // Nothing to select. i.e. a white space, out of bounds
2230     ChangeState( EventData::EDITING );
2231
2232     mEventData->mPrimaryCursorPosition = selectionEnd;
2233
2234     mEventData->mUpdateCursorPosition = true;
2235     mEventData->mUpdateGrabHandlePosition = true;
2236     mEventData->mScrollAfterUpdatePosition = true;
2237     mEventData->mUpdateInputStyle = true;
2238   }
2239 }
2240
2241 void Controller::Impl::SetPopupButtons()
2242 {
2243   /**
2244    *  Sets the Popup buttons to be shown depending on State.
2245    *
2246    *  If SELECTING :  CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2247    *
2248    *  If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2249    */
2250
2251   TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2252
2253   if( EventData::SELECTING == mEventData->mState )
2254   {
2255     buttonsToShow = TextSelectionPopup::Buttons(  TextSelectionPopup::CUT | TextSelectionPopup::COPY );
2256
2257     if( !IsClipboardEmpty() )
2258     {
2259       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2260       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2261     }
2262
2263     if( !mEventData->mAllTextSelected )
2264     {
2265       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2266     }
2267   }
2268   else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2269   {
2270     if( mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2271     {
2272       buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2273     }
2274
2275     if( !IsClipboardEmpty() )
2276     {
2277       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2278       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2279     }
2280   }
2281   else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2282   {
2283     if ( !IsClipboardEmpty() )
2284     {
2285       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2286       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2287     }
2288   }
2289
2290   mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2291 }
2292
2293 void Controller::Impl::ChangeState( EventData::State newState )
2294 {
2295   if( NULL == mEventData )
2296   {
2297     // Nothing to do if there is no text input.
2298     return;
2299   }
2300
2301   DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d  newstate:%d\n", mEventData->mState, newState );
2302
2303   if( mEventData->mState != newState )
2304   {
2305     mEventData->mPreviousState = mEventData->mState;
2306     mEventData->mState = newState;
2307
2308     switch( mEventData->mState )
2309     {
2310       case EventData::INACTIVE:
2311       {
2312         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2313         mEventData->mDecorator->StopCursorBlink();
2314         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2315         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2316         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2317         mEventData->mDecorator->SetHighlightActive( false );
2318         mEventData->mDecorator->SetPopupActive( false );
2319         mEventData->mDecoratorUpdated = true;
2320         break;
2321       }
2322       case EventData::INTERRUPTED:
2323       {
2324         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2325         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2326         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2327         mEventData->mDecorator->SetHighlightActive( false );
2328         mEventData->mDecorator->SetPopupActive( false );
2329         mEventData->mDecoratorUpdated = true;
2330         break;
2331       }
2332       case EventData::SELECTING:
2333       {
2334         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2335         mEventData->mDecorator->StopCursorBlink();
2336         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2337         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2338         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2339         mEventData->mDecorator->SetHighlightActive( true );
2340         if( mEventData->mGrabHandlePopupEnabled )
2341         {
2342           SetPopupButtons();
2343           mEventData->mDecorator->SetPopupActive( true );
2344         }
2345         mEventData->mDecoratorUpdated = true;
2346         break;
2347       }
2348       case EventData::EDITING:
2349       {
2350         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2351         if( mEventData->mCursorBlinkEnabled )
2352         {
2353           mEventData->mDecorator->StartCursorBlink();
2354         }
2355         // Grab handle is not shown until a tap is received whilst EDITING
2356         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2357         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2358         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2359         mEventData->mDecorator->SetHighlightActive( false );
2360         if( mEventData->mGrabHandlePopupEnabled )
2361         {
2362           mEventData->mDecorator->SetPopupActive( false );
2363         }
2364         mEventData->mDecoratorUpdated = true;
2365         break;
2366       }
2367       case EventData::EDITING_WITH_POPUP:
2368       {
2369         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2370
2371         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2372         if( mEventData->mCursorBlinkEnabled )
2373         {
2374           mEventData->mDecorator->StartCursorBlink();
2375         }
2376         if( mEventData->mSelectionEnabled )
2377         {
2378           mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2379           mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2380           mEventData->mDecorator->SetHighlightActive( false );
2381         }
2382         else
2383         {
2384           mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2385         }
2386         if( mEventData->mGrabHandlePopupEnabled )
2387         {
2388           SetPopupButtons();
2389           mEventData->mDecorator->SetPopupActive( true );
2390         }
2391         mEventData->mDecoratorUpdated = true;
2392         break;
2393       }
2394       case EventData::EDITING_WITH_GRAB_HANDLE:
2395       {
2396         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2397
2398         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2399         if( mEventData->mCursorBlinkEnabled )
2400         {
2401           mEventData->mDecorator->StartCursorBlink();
2402         }
2403         // Grab handle is not shown until a tap is received whilst EDITING
2404         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2405         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2406         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2407         mEventData->mDecorator->SetHighlightActive( false );
2408         if( mEventData->mGrabHandlePopupEnabled )
2409         {
2410           mEventData->mDecorator->SetPopupActive( false );
2411         }
2412         mEventData->mDecoratorUpdated = true;
2413         break;
2414       }
2415       case EventData::SELECTION_HANDLE_PANNING:
2416       {
2417         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2418         mEventData->mDecorator->StopCursorBlink();
2419         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2420         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2421         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2422         mEventData->mDecorator->SetHighlightActive( true );
2423         if( mEventData->mGrabHandlePopupEnabled )
2424         {
2425           mEventData->mDecorator->SetPopupActive( false );
2426         }
2427         mEventData->mDecoratorUpdated = true;
2428         break;
2429       }
2430       case EventData::GRAB_HANDLE_PANNING:
2431       {
2432         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2433
2434         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2435         if( mEventData->mCursorBlinkEnabled )
2436         {
2437           mEventData->mDecorator->StartCursorBlink();
2438         }
2439         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2440         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2441         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2442         mEventData->mDecorator->SetHighlightActive( false );
2443         if( mEventData->mGrabHandlePopupEnabled )
2444         {
2445           mEventData->mDecorator->SetPopupActive( false );
2446         }
2447         mEventData->mDecoratorUpdated = true;
2448         break;
2449       }
2450       case EventData::EDITING_WITH_PASTE_POPUP:
2451       {
2452         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2453
2454         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2455         if( mEventData->mCursorBlinkEnabled )
2456         {
2457           mEventData->mDecorator->StartCursorBlink();
2458         }
2459
2460         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2461         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2462         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2463         mEventData->mDecorator->SetHighlightActive( false );
2464
2465         if( mEventData->mGrabHandlePopupEnabled )
2466         {
2467           SetPopupButtons();
2468           mEventData->mDecorator->SetPopupActive( true );
2469         }
2470         mEventData->mDecoratorUpdated = true;
2471         break;
2472       }
2473       case EventData::TEXT_PANNING:
2474       {
2475         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2476         mEventData->mDecorator->StopCursorBlink();
2477         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2478         if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2479             mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2480         {
2481           mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2482           mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2483           mEventData->mDecorator->SetHighlightActive( true );
2484         }
2485
2486         if( mEventData->mGrabHandlePopupEnabled )
2487         {
2488           mEventData->mDecorator->SetPopupActive( false );
2489         }
2490
2491         mEventData->mDecoratorUpdated = true;
2492         break;
2493       }
2494     }
2495   }
2496 }
2497
2498 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2499                                           CursorInfo& cursorInfo )
2500 {
2501   if( !IsShowingRealText() )
2502   {
2503     // Do not want to use the place-holder text to set the cursor position.
2504
2505     // Use the line's height of the font's family set to set the cursor's size.
2506     // If there is no font's family set, use the default font.
2507     // Use the current alignment to place the cursor at the beginning, center or end of the box.
2508
2509     cursorInfo.lineOffset = 0.f;
2510     cursorInfo.lineHeight = GetDefaultFontLineHeight();
2511     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2512
2513     switch( mModel->mHorizontalAlignment )
2514     {
2515       case Layout::HORIZONTAL_ALIGN_BEGIN:
2516       {
2517         cursorInfo.primaryPosition.x = 0.f;
2518         break;
2519       }
2520       case Layout::HORIZONTAL_ALIGN_CENTER:
2521       {
2522         cursorInfo.primaryPosition.x = floorf( 0.5f * mModel->mVisualModel->mControlSize.width );
2523         break;
2524       }
2525       case Layout::HORIZONTAL_ALIGN_END:
2526       {
2527         cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2528         break;
2529       }
2530     }
2531
2532     // Nothing else to do.
2533     return;
2534   }
2535
2536   Text::GetCursorPosition( mModel->mVisualModel,
2537                            mModel->mLogicalModel,
2538                            mMetrics,
2539                            logical,
2540                            cursorInfo );
2541
2542   if( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
2543   {
2544     // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2545
2546     // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2547     // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2548
2549     if( 0.f > cursorInfo.primaryPosition.x )
2550     {
2551       cursorInfo.primaryPosition.x = 0.f;
2552     }
2553
2554     const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2555     if( cursorInfo.primaryPosition.x > edgeWidth )
2556     {
2557       cursorInfo.primaryPosition.x = edgeWidth;
2558     }
2559   }
2560 }
2561
2562 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2563 {
2564   if( NULL == mEventData )
2565   {
2566     // Nothing to do if there is no text input.
2567     return 0u;
2568   }
2569
2570   CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2571
2572   const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
2573   const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
2574
2575   GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2576   Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2577
2578   if( numberOfCharacters > 1u )
2579   {
2580     const Script script = mModel->mLogicalModel->GetScript( index );
2581     if( HasLigatureMustBreak( script ) )
2582     {
2583       // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ﻻ, ...
2584       numberOfCharacters = 1u;
2585     }
2586   }
2587   else
2588   {
2589     while( 0u == numberOfCharacters )
2590     {
2591       ++glyphIndex;
2592       numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2593     }
2594   }
2595
2596   if( index < mEventData->mPrimaryCursorPosition )
2597   {
2598     cursorIndex -= numberOfCharacters;
2599   }
2600   else
2601   {
2602     cursorIndex += numberOfCharacters;
2603   }
2604
2605   // Will update the cursor hook position.
2606   mEventData->mUpdateCursorHookPosition = true;
2607
2608   return cursorIndex;
2609 }
2610
2611 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2612 {
2613   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2614   if( NULL == mEventData )
2615   {
2616     // Nothing to do if there is no text input.
2617     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2618     return;
2619   }
2620
2621   const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2622
2623   // Sets the cursor position.
2624   mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2625                                        cursorPosition.x,
2626                                        cursorPosition.y,
2627                                        cursorInfo.primaryCursorHeight,
2628                                        cursorInfo.lineHeight );
2629   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2630
2631   if( mEventData->mUpdateGrabHandlePosition )
2632   {
2633     // Sets the grab handle position.
2634     mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2635                                          cursorPosition.x,
2636                                          cursorInfo.lineOffset + mModel->mScrollPosition.y,
2637                                          cursorInfo.lineHeight );
2638   }
2639
2640   if( cursorInfo.isSecondaryCursor )
2641   {
2642     mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2643                                          cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
2644                                          cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
2645                                          cursorInfo.secondaryCursorHeight,
2646                                          cursorInfo.lineHeight );
2647     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y );
2648   }
2649
2650   // Set which cursors are active according the state.
2651   if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2652   {
2653     if( cursorInfo.isSecondaryCursor )
2654     {
2655       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2656     }
2657     else
2658     {
2659       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2660     }
2661   }
2662   else
2663   {
2664     mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2665   }
2666
2667   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2668 }
2669
2670 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2671                                               const CursorInfo& cursorInfo )
2672 {
2673   if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2674       ( RIGHT_SELECTION_HANDLE != handleType ) )
2675   {
2676     return;
2677   }
2678
2679   const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2680
2681   // Sets the handle's position.
2682   mEventData->mDecorator->SetPosition( handleType,
2683                                        cursorPosition.x,
2684                                        cursorInfo.lineOffset + mModel->mScrollPosition.y,
2685                                        cursorInfo.lineHeight );
2686
2687   // If selection handle at start of the text and other at end of the text then all text is selected.
2688   const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2689   const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2690   mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mModel->mLogicalModel->mText.Count() );
2691 }
2692
2693 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
2694 {
2695   // Clamp between -space & -alignment offset.
2696
2697   if( layoutSize.width > mModel->mVisualModel->mControlSize.width )
2698   {
2699     const float space = ( layoutSize.width - mModel->mVisualModel->mControlSize.width ) + mModel->mAlignmentOffset;
2700     mModel->mScrollPosition.x = ( mModel->mScrollPosition.x < -space ) ? -space : mModel->mScrollPosition.x;
2701     mModel->mScrollPosition.x = ( mModel->mScrollPosition.x > -mModel->mAlignmentOffset ) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
2702
2703     mEventData->mDecoratorUpdated = true;
2704   }
2705   else
2706   {
2707     mModel->mScrollPosition.x = 0.f;
2708   }
2709 }
2710
2711 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
2712 {
2713   if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
2714   {
2715     // Nothing to do if the text is single line.
2716     return;
2717   }
2718
2719   // Clamp between -space & 0.
2720   if( layoutSize.height > mModel->mVisualModel->mControlSize.height )
2721   {
2722     const float space = ( layoutSize.height - mModel->mVisualModel->mControlSize.height );
2723     mModel->mScrollPosition.y = ( mModel->mScrollPosition.y < -space ) ? -space : mModel->mScrollPosition.y;
2724     mModel->mScrollPosition.y = ( mModel->mScrollPosition.y > 0.f ) ? 0.f : mModel->mScrollPosition.y;
2725
2726     mEventData->mDecoratorUpdated = true;
2727   }
2728   else
2729   {
2730     mModel->mScrollPosition.y = 0.f;
2731   }
2732 }
2733
2734 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
2735 {
2736   const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
2737
2738   // position is in actor's coords.
2739   const float positionEndX = position.x + cursorWidth;
2740   const float positionEndY = position.y + lineHeight;
2741
2742   // Transform the position to decorator coords.
2743   const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
2744   const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
2745
2746   const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
2747   const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
2748
2749   if( decoratorPositionBeginX < 0.f )
2750   {
2751     mModel->mScrollPosition.x = -position.x;
2752   }
2753   else if( decoratorPositionEndX > mModel->mVisualModel->mControlSize.width )
2754   {
2755     mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
2756   }
2757
2758   if( decoratorPositionBeginY < 0.f )
2759   {
2760     mModel->mScrollPosition.y = -position.y;
2761   }
2762   else if( decoratorPositionEndY > mModel->mVisualModel->mControlSize.height )
2763   {
2764     mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
2765   }
2766 }
2767
2768 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
2769 {
2770   // Get the current cursor position in decorator coords.
2771   const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2772
2773   // Calculate the offset to match the cursor position before the character was deleted.
2774   mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2775   mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset;
2776
2777   ClampHorizontalScroll( mModel->mVisualModel->GetLayoutSize() );
2778   ClampVerticalScroll( mModel->mVisualModel->GetLayoutSize() );
2779
2780   // Makes the new cursor position visible if needed.
2781   ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
2782 }
2783
2784 void Controller::Impl::RequestRelayout()
2785 {
2786   if( NULL != mControlInterface )
2787   {
2788     mControlInterface->RequestTextRelayout();
2789   }
2790 }
2791
2792 } // namespace Text
2793
2794 } // namespace Toolkit
2795
2796 } // namespace Dali