(Text) Fix bug where we cannot jump to the first line from the start of the second...
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-controller-impl.cpp
1 /*
2  * Copyright (c) 2017 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     const LineIndex previousLineIndex = ( lineIndex > 0 ? lineIndex - 1u : lineIndex );
1155
1156     if( previousLineIndex >= 0u )
1157     {
1158       // Retrieve the cursor position info.
1159       CursorInfo cursorInfo;
1160       GetCursorPosition( mEventData->mPrimaryCursorPosition,
1161                          cursorInfo );
1162
1163       // Get the line above.
1164       const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + previousLineIndex );
1165
1166       // Get the next hit 'y' point.
1167       const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender );
1168
1169       // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1170       mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1171                                                                         mModel->mLogicalModel,
1172                                                                         mMetrics,
1173                                                                         mEventData->mCursorHookPositionX,
1174                                                                         hitPointY );
1175     }
1176   }
1177   else if(   Dali::DALI_KEY_CURSOR_DOWN == keyCode )
1178   {
1179     // Get first the line index of the current cursor position index.
1180     CharacterIndex characterIndex = 0u;
1181
1182     if( mEventData->mPrimaryCursorPosition > 0u )
1183     {
1184       characterIndex = mEventData->mPrimaryCursorPosition - 1u;
1185     }
1186
1187     const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex );
1188
1189     if( lineIndex + 1u < mModel->mVisualModel->mLines.Count() )
1190     {
1191       // Retrieve the cursor position info.
1192       CursorInfo cursorInfo;
1193       GetCursorPosition( mEventData->mPrimaryCursorPosition,
1194                          cursorInfo );
1195
1196       // Get the line below.
1197       const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + lineIndex + 1u );
1198
1199       // Get the next hit 'y' point.
1200       const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender );
1201
1202       // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
1203       mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1204                                                                         mModel->mLogicalModel,
1205                                                                         mMetrics,
1206                                                                         mEventData->mCursorHookPositionX,
1207                                                                         hitPointY );
1208     }
1209   }
1210
1211   mEventData->mUpdateCursorPosition = true;
1212   mEventData->mUpdateInputStyle = true;
1213   mEventData->mScrollAfterUpdatePosition = true;
1214 }
1215
1216 void Controller::Impl::OnTapEvent( const Event& event )
1217 {
1218   if( NULL != mEventData )
1219   {
1220     const unsigned int tapCount = event.p1.mUint;
1221
1222     if( 1u == tapCount )
1223     {
1224       if( IsShowingRealText() )
1225       {
1226         // Convert from control's coords to text's coords.
1227         const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1228         const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1229
1230         // Keep the tap 'x' position. Used to move the cursor.
1231         mEventData->mCursorHookPositionX = xPosition;
1232
1233         mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1234                                                                           mModel->mLogicalModel,
1235                                                                           mMetrics,
1236                                                                           xPosition,
1237                                                                           yPosition );
1238
1239         // When the cursor position is changing, delay cursor blinking
1240         mEventData->mDecorator->DelayCursorBlink();
1241       }
1242       else
1243       {
1244         mEventData->mPrimaryCursorPosition = 0u;
1245       }
1246
1247       mEventData->mUpdateCursorPosition = true;
1248       mEventData->mUpdateGrabHandlePosition = true;
1249       mEventData->mScrollAfterUpdatePosition = true;
1250       mEventData->mUpdateInputStyle = true;
1251
1252       // Notify the cursor position to the imf manager.
1253       if( mEventData->mImfManager )
1254       {
1255         mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition );
1256         mEventData->mImfManager.NotifyCursorPosition();
1257       }
1258     }
1259   }
1260 }
1261
1262 void Controller::Impl::OnPanEvent( const Event& event )
1263 {
1264   if( NULL == mEventData )
1265   {
1266     // Nothing to do if there is no text input.
1267     return;
1268   }
1269
1270   const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled();
1271   const bool isVerticalScrollEnabled = mEventData->mDecorator->IsVerticalScrollEnabled();
1272
1273   if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled )
1274   {
1275     // Nothing to do if scrolling is not enabled.
1276     return;
1277   }
1278
1279   const int state = event.p1.mInt;
1280
1281   switch( state )
1282   {
1283     case Gesture::Started:
1284     {
1285       // Will remove the cursor, handles or text's popup, ...
1286       ChangeState( EventData::TEXT_PANNING );
1287       break;
1288     }
1289     case Gesture::Continuing:
1290     {
1291       const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1292       const Vector2 currentScroll = mModel->mScrollPosition;
1293
1294       if( isHorizontalScrollEnabled )
1295       {
1296         const float displacementX = event.p2.mFloat;
1297         mModel->mScrollPosition.x += displacementX;
1298
1299         ClampHorizontalScroll( layoutSize );
1300       }
1301
1302       if( isVerticalScrollEnabled )
1303       {
1304         const float displacementY = event.p3.mFloat;
1305         mModel->mScrollPosition.y += displacementY;
1306
1307         ClampVerticalScroll( layoutSize );
1308       }
1309
1310       mEventData->mDecorator->UpdatePositions( mModel->mScrollPosition - currentScroll );
1311       break;
1312     }
1313     case Gesture::Finished:
1314     case Gesture::Cancelled: // FALLTHROUGH
1315     {
1316       // Will go back to the previous state to show the cursor, handles, the text's popup, ...
1317       ChangeState( mEventData->mPreviousState );
1318       break;
1319     }
1320     default:
1321       break;
1322   }
1323 }
1324
1325 void Controller::Impl::OnLongPressEvent( const Event& event )
1326 {
1327   DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" );
1328
1329   if( EventData::EDITING == mEventData->mState )
1330   {
1331     ChangeState ( EventData::EDITING_WITH_POPUP );
1332     mEventData->mDecoratorUpdated = true;
1333     mEventData->mUpdateInputStyle = true;
1334   }
1335 }
1336
1337 void Controller::Impl::OnHandleEvent( const Event& event )
1338 {
1339   if( NULL == mEventData )
1340   {
1341     // Nothing to do if there is no text input.
1342     return;
1343   }
1344
1345   const unsigned int state = event.p1.mUint;
1346   const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
1347   const bool isSmoothHandlePanEnabled = mEventData->mDecorator->IsSmoothHandlePanEnabled();
1348
1349   if( HANDLE_PRESSED == state )
1350   {
1351     // Convert from decorator's coords to text's coords.
1352     const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1353     const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1354
1355     // Need to calculate the handle's new position.
1356     const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1357                                                                           mModel->mLogicalModel,
1358                                                                           mMetrics,
1359                                                                           xPosition,
1360                                                                           yPosition );
1361
1362     if( Event::GRAB_HANDLE_EVENT == event.type )
1363     {
1364       ChangeState ( EventData::GRAB_HANDLE_PANNING );
1365
1366       if( handleNewPosition != mEventData->mPrimaryCursorPosition )
1367       {
1368         // Updates the cursor position if the handle's new position is different than the current one.
1369         mEventData->mUpdateCursorPosition = true;
1370         // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
1371         mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1372         mEventData->mPrimaryCursorPosition = handleNewPosition;
1373       }
1374
1375       // 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.
1376       mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1377     }
1378     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1379     {
1380       ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1381
1382       if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
1383           ( handleNewPosition != mEventData->mRightSelectionPosition ) )
1384       {
1385         // Updates the highlight box if the handle's new position is different than the current one.
1386         mEventData->mUpdateHighlightBox = true;
1387         // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1388         mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1389         mEventData->mLeftSelectionPosition = handleNewPosition;
1390       }
1391
1392       // 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.
1393       mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1394
1395       // Will define the order to scroll the text to match the handle position.
1396       mEventData->mIsLeftHandleSelected = true;
1397     }
1398     else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1399     {
1400       ChangeState ( EventData::SELECTION_HANDLE_PANNING );
1401
1402       if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
1403           ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
1404       {
1405         // Updates the highlight box if the handle's new position is different than the current one.
1406         mEventData->mUpdateHighlightBox = true;
1407         // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
1408         mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1409         mEventData->mRightSelectionPosition = handleNewPosition;
1410       }
1411
1412       // 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.
1413       mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1414
1415       // Will define the order to scroll the text to match the handle position.
1416       mEventData->mIsLeftHandleSelected = false;
1417     }
1418   } // end ( HANDLE_PRESSED == state )
1419   else if( ( HANDLE_RELEASED == state ) ||
1420            handleStopScrolling )
1421   {
1422     CharacterIndex handlePosition = 0u;
1423     if( handleStopScrolling || isSmoothHandlePanEnabled )
1424     {
1425       // Convert from decorator's coords to text's coords.
1426       const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1427       const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1428
1429       handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1430                                                     mModel->mLogicalModel,
1431                                                     mMetrics,
1432                                                     xPosition,
1433                                                     yPosition );
1434     }
1435
1436     if( Event::GRAB_HANDLE_EVENT == event.type )
1437     {
1438       mEventData->mUpdateCursorPosition = true;
1439       mEventData->mUpdateGrabHandlePosition = true;
1440       mEventData->mUpdateInputStyle = true;
1441
1442       if( !IsClipboardEmpty() )
1443       {
1444         ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup
1445       }
1446
1447       if( handleStopScrolling || isSmoothHandlePanEnabled )
1448       {
1449         mEventData->mScrollAfterUpdatePosition = true;
1450         mEventData->mPrimaryCursorPosition = handlePosition;
1451       }
1452     }
1453     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
1454     {
1455       ChangeState( EventData::SELECTING );
1456
1457       mEventData->mUpdateHighlightBox = true;
1458       mEventData->mUpdateLeftSelectionPosition = true;
1459       mEventData->mUpdateRightSelectionPosition = true;
1460
1461       if( handleStopScrolling || isSmoothHandlePanEnabled )
1462       {
1463         mEventData->mScrollAfterUpdatePosition = true;
1464
1465         if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1466             ( handlePosition != mEventData->mLeftSelectionPosition ) )
1467         {
1468           mEventData->mLeftSelectionPosition = handlePosition;
1469         }
1470       }
1471     }
1472     else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
1473     {
1474       ChangeState( EventData::SELECTING );
1475
1476       mEventData->mUpdateHighlightBox = true;
1477       mEventData->mUpdateRightSelectionPosition = true;
1478       mEventData->mUpdateLeftSelectionPosition = true;
1479
1480       if( handleStopScrolling || isSmoothHandlePanEnabled )
1481       {
1482         mEventData->mScrollAfterUpdatePosition = true;
1483         if( ( handlePosition != mEventData->mRightSelectionPosition ) &&
1484             ( handlePosition != mEventData->mLeftSelectionPosition ) )
1485         {
1486           mEventData->mRightSelectionPosition = handlePosition;
1487         }
1488       }
1489     }
1490
1491     mEventData->mDecoratorUpdated = true;
1492   } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
1493   else if( HANDLE_SCROLLING == state )
1494   {
1495     const float xSpeed = event.p2.mFloat;
1496     const float ySpeed = event.p3.mFloat;
1497     const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1498     const Vector2 currentScrollPosition = mModel->mScrollPosition;
1499
1500     mModel->mScrollPosition.x += xSpeed;
1501     mModel->mScrollPosition.y += ySpeed;
1502
1503     ClampHorizontalScroll( layoutSize );
1504     ClampVerticalScroll( layoutSize );
1505
1506     bool endOfScroll = false;
1507     if( Vector2::ZERO == ( currentScrollPosition - mModel->mScrollPosition ) )
1508     {
1509       // Notify the decorator there is no more text to scroll.
1510       // The decorator won't send more scroll events.
1511       mEventData->mDecorator->NotifyEndOfScroll();
1512       // Still need to set the position of the handle.
1513       endOfScroll = true;
1514     }
1515
1516     // Set the position of the handle.
1517     const bool scrollRightDirection = xSpeed > 0.f;
1518     const bool scrollBottomDirection = ySpeed > 0.f;
1519     const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
1520     const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
1521
1522     if( Event::GRAB_HANDLE_EVENT == event.type )
1523     {
1524       ChangeState( EventData::GRAB_HANDLE_PANNING );
1525
1526       // Get the grab handle position in decorator coords.
1527       Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
1528
1529       if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1530       {
1531         // Position the grag handle close to either the left or right edge.
1532         position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1533       }
1534
1535       if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1536       {
1537         position.x = mEventData->mCursorHookPositionX;
1538
1539         // Position the grag handle close to either the top or bottom edge.
1540         position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1541       }
1542
1543       // Get the new handle position.
1544       // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
1545       const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1546                                                                          mModel->mLogicalModel,
1547                                                                          mMetrics,
1548                                                                          position.x - mModel->mScrollPosition.x,
1549                                                                          position.y - mModel->mScrollPosition.y );
1550
1551       if( mEventData->mPrimaryCursorPosition != handlePosition )
1552       {
1553         mEventData->mUpdateCursorPosition = true;
1554         mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1555         mEventData->mScrollAfterUpdatePosition = true;
1556         mEventData->mPrimaryCursorPosition = handlePosition;
1557       }
1558       mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition;
1559
1560       // Updates the decorator if the soft handle panning is enabled.
1561       mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled;
1562     }
1563     else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
1564     {
1565       ChangeState( EventData::SELECTION_HANDLE_PANNING );
1566
1567       // Get the selection handle position in decorator coords.
1568       Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
1569
1570       if( mEventData->mDecorator->IsHorizontalScrollEnabled() )
1571       {
1572         // Position the selection handle close to either the left or right edge.
1573         position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width;
1574       }
1575
1576       if( mEventData->mDecorator->IsVerticalScrollEnabled() )
1577       {
1578         position.x = mEventData->mCursorHookPositionX;
1579
1580         // Position the grag handle close to either the top or bottom edge.
1581         position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height;
1582       }
1583
1584       // Get the new handle position.
1585       // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1586       const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel,
1587                                                                          mModel->mLogicalModel,
1588                                                                          mMetrics,
1589                                                                          position.x - mModel->mScrollPosition.x,
1590                                                                          position.y - mModel->mScrollPosition.y );
1591
1592       if( leftSelectionHandleEvent )
1593       {
1594         const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
1595
1596         if( differentHandles || endOfScroll )
1597         {
1598           mEventData->mUpdateHighlightBox = true;
1599           mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1600           mEventData->mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1601           mEventData->mLeftSelectionPosition = handlePosition;
1602         }
1603       }
1604       else
1605       {
1606         const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
1607         if( differentHandles || endOfScroll )
1608         {
1609           mEventData->mUpdateHighlightBox = true;
1610           mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1611           mEventData->mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1612           mEventData->mRightSelectionPosition = handlePosition;
1613         }
1614       }
1615
1616       if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
1617       {
1618         RepositionSelectionHandles();
1619
1620         mEventData->mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1621       }
1622     }
1623     mEventData->mDecoratorUpdated = true;
1624   } // end ( HANDLE_SCROLLING == state )
1625 }
1626
1627 void Controller::Impl::OnSelectEvent( const Event& event )
1628 {
1629   if( NULL == mEventData )
1630   {
1631     // Nothing to do if there is no text.
1632     return;
1633   }
1634
1635   if( mEventData->mSelectionEnabled )
1636   {
1637     // Convert from control's coords to text's coords.
1638     const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x;
1639     const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y;
1640
1641     // Calculates the logical position from the x,y coords.
1642     RepositionSelectionHandles( xPosition,
1643                                 yPosition );
1644   }
1645 }
1646
1647 void Controller::Impl::OnSelectAllEvent()
1648 {
1649   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false");
1650
1651   if( NULL == mEventData )
1652   {
1653     // Nothing to do if there is no text.
1654     return;
1655   }
1656
1657   if( mEventData->mSelectionEnabled )
1658   {
1659     ChangeState( EventData::SELECTING );
1660
1661     mEventData->mLeftSelectionPosition = 0u;
1662     mEventData->mRightSelectionPosition = mModel->mLogicalModel->mText.Count();
1663
1664     mEventData->mScrollAfterUpdatePosition = true;
1665     mEventData->mUpdateLeftSelectionPosition = true;
1666     mEventData->mUpdateRightSelectionPosition = true;
1667     mEventData->mUpdateHighlightBox = true;
1668   }
1669 }
1670
1671 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval )
1672 {
1673   if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition )
1674   {
1675     // Nothing to select if handles are in the same place.
1676     selectedText.clear();
1677     return;
1678   }
1679
1680   const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1681
1682   //Get start and end position of selection
1683   const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1684   const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText;
1685
1686   Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
1687   const Length numberOfCharacters = utf32Characters.Count();
1688
1689   // Validate the start and end selection points
1690   if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters )
1691   {
1692     //Get text as a UTF8 string
1693     Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
1694
1695     if( deleteAfterRetrieval ) // Only delete text if copied successfully
1696     {
1697       // Keep a copy of the current input style.
1698       InputStyle currentInputStyle;
1699       currentInputStyle.Copy( mEventData->mInputStyle );
1700
1701       // Set as input style the style of the first deleted character.
1702       mModel->mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle );
1703
1704       // Compare if the input style has changed.
1705       const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle );
1706
1707       if( hasInputStyleChanged )
1708       {
1709         const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle );
1710         // Queue the input style changed signal.
1711         mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
1712       }
1713
1714       mModel->mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast<int>( lengthOfSelectedText ) );
1715
1716       // Mark the paragraphs to be updated.
1717       mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1718       mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1719
1720       // Delete text between handles
1721       Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1722       Vector<Character>::Iterator last  = first + lengthOfSelectedText;
1723       utf32Characters.Erase( first, last );
1724
1725       // Will show the cursor at the first character of the selection.
1726       mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1727     }
1728     else
1729     {
1730       // Will show the cursor at the last character of the selection.
1731       mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1732     }
1733
1734     mEventData->mDecoratorUpdated = true;
1735   }
1736 }
1737
1738 void Controller::Impl::ShowClipboard()
1739 {
1740   if( mClipboard )
1741   {
1742     mClipboard.ShowClipboard();
1743   }
1744 }
1745
1746 void Controller::Impl::HideClipboard()
1747 {
1748   if( mClipboard && mClipboardHideEnabled )
1749   {
1750     mClipboard.HideClipboard();
1751   }
1752 }
1753
1754 void Controller::Impl::SetClipboardHideEnable(bool enable)
1755 {
1756   mClipboardHideEnabled = enable;
1757 }
1758
1759 bool Controller::Impl::CopyStringToClipboard( std::string& source )
1760 {
1761   //Send string to clipboard
1762   return ( mClipboard && mClipboard.SetItem( source ) );
1763 }
1764
1765 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
1766 {
1767   std::string selectedText;
1768   RetrieveSelection( selectedText, deleteAfterSending );
1769   CopyStringToClipboard( selectedText );
1770   ChangeState( EventData::EDITING );
1771 }
1772
1773 void Controller::Impl::RequestGetTextFromClipboard()
1774 {
1775   if ( mClipboard )
1776   {
1777     mClipboard.RequestItem();
1778   }
1779 }
1780
1781 void Controller::Impl::RepositionSelectionHandles()
1782 {
1783   CharacterIndex selectionStart = mEventData->mLeftSelectionPosition;
1784   CharacterIndex selectionEnd = mEventData->mRightSelectionPosition;
1785
1786   if( selectionStart == selectionEnd )
1787   {
1788     // Nothing to select if handles are in the same place.
1789     return;
1790   }
1791
1792   mEventData->mDecorator->ClearHighlights();
1793
1794   const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1795   const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
1796   const GlyphInfo* const glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
1797   const Vector2* const positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
1798   const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
1799   const CharacterIndex* const glyphToCharacterBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
1800   const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mModel->mLogicalModel->mCharacterDirections.Count() ) ? mModel->mLogicalModel->mCharacterDirections.Begin() : NULL;
1801
1802   const bool isLastCharacter = selectionEnd >= mModel->mLogicalModel->mText.Count();
1803   const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) );
1804   const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) );
1805
1806   // Swap the indices if the start is greater than the end.
1807   const bool indicesSwapped = selectionStart > selectionEnd;
1808
1809   // Tell the decorator to flip the selection handles if needed.
1810   mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection );
1811
1812   if( indicesSwapped )
1813   {
1814     std::swap( selectionStart, selectionEnd );
1815   }
1816
1817   // Get the indices to the first and last selected glyphs.
1818   const CharacterIndex selectionEndMinusOne = selectionEnd - 1u;
1819   const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
1820   const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne );
1821   const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
1822
1823   // Get the lines where the glyphs are laid-out.
1824   const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
1825
1826   LineIndex lineIndex = 0u;
1827   Length numberOfLines = 0u;
1828   mModel->mVisualModel->GetNumberOfLines( glyphStart,
1829                                           1u + glyphEnd - glyphStart,
1830                                           lineIndex,
1831                                           numberOfLines );
1832   const LineIndex firstLineIndex = lineIndex;
1833
1834   // Create the structure to store some selection box info.
1835   Vector<SelectionBoxInfo> selectionBoxLinesInfo;
1836   selectionBoxLinesInfo.Resize( numberOfLines );
1837
1838   SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin();
1839   selectionBoxInfo->minX = MAX_FLOAT;
1840   selectionBoxInfo->maxX = MIN_FLOAT;
1841
1842   // Keep the min and max 'x' position to calculate the size and position of the highlighed text.
1843   float minHighlightX = std::numeric_limits<float>::max();
1844   float maxHighlightX = std::numeric_limits<float>::min();
1845   Size highLightSize;
1846   Vector2 highLightPosition; // The highlight position in decorator's coords.
1847
1848   // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph.
1849
1850   // The line's vertical offset of all the lines before the line where the first glyph is laid-out.
1851   selectionBoxInfo->lineOffset = CalculateLineOffset( mModel->mVisualModel->mLines,
1852                                                       firstLineIndex );
1853
1854   // Transform to decorator's (control) coords.
1855   selectionBoxInfo->lineOffset += mModel->mScrollPosition.y;
1856
1857   lineRun += firstLineIndex;
1858
1859   // The line height is the addition of the line ascender and the line descender.
1860   // However, the line descender has a negative value, hence the subtraction.
1861   selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1862
1863   GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1864
1865   // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
1866   const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart );
1867   bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionStart ) );
1868
1869   // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ﻻ, etc which needs special code.
1870   const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd );
1871   bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionEndMinusOne ) );
1872
1873   // The number of quads of the selection box.
1874   const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u );
1875   mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads );
1876
1877   // Count the actual number of quads.
1878   unsigned int actualNumberOfQuads = 0u;
1879   Vector4 quad;
1880
1881   // Traverse the glyphs.
1882   for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
1883   {
1884     const GlyphInfo& glyph = *( glyphsBuffer + index );
1885     const Vector2& position = *( positionsBuffer + index );
1886
1887     if( splitStartGlyph )
1888     {
1889       // 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.
1890
1891       const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersStart );
1892       const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart );
1893       // Get the direction of the character.
1894       CharacterDirection isCurrentRightToLeft = false;
1895       if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1896       {
1897         isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart );
1898       }
1899
1900       // The end point could be in the middle of the ligature.
1901       // Calculate the number of characters selected.
1902       const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex );
1903
1904       quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + glyphAdvance * static_cast<float>( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex );
1905       quad.y = selectionBoxInfo->lineOffset;
1906       quad.z = quad.x + static_cast<float>( numberOfCharacters ) * glyphAdvance;
1907       quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight;
1908
1909       // Store the min and max 'x' for each line.
1910       selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1911       selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1912
1913       mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad );
1914       ++actualNumberOfQuads;
1915
1916       splitStartGlyph = false;
1917       continue;
1918     }
1919
1920     if( splitEndGlyph && ( index == glyphEnd ) )
1921     {
1922       // 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.
1923
1924       const float glyphAdvance = glyph.advance / static_cast<float>( numberOfCharactersEnd );
1925       const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd );
1926       // Get the direction of the character.
1927       CharacterDirection isCurrentRightToLeft = false;
1928       if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1929       {
1930         isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd );
1931       }
1932
1933       const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex;
1934
1935       quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast<float>( numberOfCharacters ) ) : 0.f );
1936       quad.y = selectionBoxInfo->lineOffset;
1937       quad.z = quad.x + static_cast<float>( interGlyphIndex ) * glyphAdvance;
1938       quad.w = quad.y + selectionBoxInfo->lineHeight;
1939
1940       // Store the min and max 'x' for each line.
1941       selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1942       selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1943
1944       mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1945                                             quad );
1946       ++actualNumberOfQuads;
1947
1948       splitEndGlyph = false;
1949       continue;
1950     }
1951
1952     quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x;
1953     quad.y = selectionBoxInfo->lineOffset;
1954     quad.z = quad.x + glyph.advance;
1955     quad.w = quad.y + selectionBoxInfo->lineHeight;
1956
1957     // Store the min and max 'x' for each line.
1958     selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x );
1959     selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z );
1960
1961     mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
1962                                           quad );
1963     ++actualNumberOfQuads;
1964
1965     // Whether to retrieve the next line.
1966     if( index == lastGlyphOfLine )
1967     {
1968       ++lineIndex;
1969       if( lineIndex < firstLineIndex + numberOfLines )
1970       {
1971         // Retrieve the next line.
1972         ++lineRun;
1973
1974         // Get the last glyph of the new line.
1975         lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u;
1976
1977         // Keep the offset and height of the current selection box.
1978         const float currentLineOffset = selectionBoxInfo->lineOffset;
1979         const float currentLineHeight = selectionBoxInfo->lineHeight;
1980
1981         // Get the selection box info for the next line.
1982         ++selectionBoxInfo;
1983
1984         selectionBoxInfo->minX = MAX_FLOAT;
1985         selectionBoxInfo->maxX = MIN_FLOAT;
1986
1987         // Update the line's vertical offset.
1988         selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight;
1989
1990         // The line height is the addition of the line ascender and the line descender.
1991         // However, the line descender has a negative value, hence the subtraction.
1992         selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender;
1993       }
1994     }
1995   }
1996
1997   // Traverses all the lines and updates the min and max 'x' positions and the total height.
1998   // The final width is calculated after 'boxifying' the selection.
1999   for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin(),
2000          endIt = selectionBoxLinesInfo.End();
2001        it != endIt;
2002        ++it )
2003   {
2004     const SelectionBoxInfo& info = *it;
2005
2006     // Update the size of the highlighted text.
2007     highLightSize.height += info.lineHeight;
2008     minHighlightX = std::min( minHighlightX, info.minX );
2009     maxHighlightX = std::max( maxHighlightX, info.maxX );
2010   }
2011
2012   // Add extra geometry to 'boxify' the selection.
2013
2014   if( 1u < numberOfLines )
2015   {
2016     // Boxify the first line.
2017     lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex;
2018     const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2019
2020     bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection );
2021     bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection );
2022
2023     if( boxifyBegin )
2024     {
2025       quad.x = 0.f;
2026       quad.y = firstSelectionBoxLineInfo.lineOffset;
2027       quad.z = firstSelectionBoxLineInfo.minX;
2028       quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2029
2030       // Boxify at the beginning of the line.
2031       mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2032                                             quad );
2033       ++actualNumberOfQuads;
2034
2035       // Update the size of the highlighted text.
2036       minHighlightX = 0.f;
2037     }
2038
2039     if( boxifyEnd )
2040     {
2041       quad.x = firstSelectionBoxLineInfo.maxX;
2042       quad.y = firstSelectionBoxLineInfo.lineOffset;
2043       quad.z = mModel->mVisualModel->mControlSize.width;
2044       quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight;
2045
2046       // Boxify at the end of the line.
2047       mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2048                                             quad );
2049       ++actualNumberOfQuads;
2050
2051       // Update the size of the highlighted text.
2052       maxHighlightX = mModel->mVisualModel->mControlSize.width;
2053     }
2054
2055     // Boxify the central lines.
2056     if( 2u < numberOfLines )
2057     {
2058       for( Vector<SelectionBoxInfo>::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u,
2059              endIt = selectionBoxLinesInfo.End() - 1u;
2060            it != endIt;
2061            ++it )
2062       {
2063         const SelectionBoxInfo& info = *it;
2064
2065         quad.x = 0.f;
2066         quad.y = info.lineOffset;
2067         quad.z = info.minX;
2068         quad.w = info.lineOffset + info.lineHeight;
2069
2070         mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2071                                               quad );
2072         ++actualNumberOfQuads;
2073
2074         quad.x = info.maxX;
2075         quad.y = info.lineOffset;
2076         quad.z = mModel->mVisualModel->mControlSize.width;
2077         quad.w = info.lineOffset + info.lineHeight;
2078
2079         mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2080                                               quad );
2081         ++actualNumberOfQuads;
2082       }
2083
2084       // Update the size of the highlighted text.
2085       minHighlightX = 0.f;
2086       maxHighlightX = mModel->mVisualModel->mControlSize.width;
2087     }
2088
2089     // Boxify the last line.
2090     lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u;
2091     const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u );
2092
2093     boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection );
2094     boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection );
2095
2096     if( boxifyBegin )
2097     {
2098       quad.x = 0.f;
2099       quad.y = lastSelectionBoxLineInfo.lineOffset;
2100       quad.z = lastSelectionBoxLineInfo.minX;
2101       quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2102
2103       // Boxify at the beginning of the line.
2104       mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2105                                             quad );
2106       ++actualNumberOfQuads;
2107
2108       // Update the size of the highlighted text.
2109       minHighlightX = 0.f;
2110     }
2111
2112     if( boxifyEnd )
2113     {
2114       quad.x = lastSelectionBoxLineInfo.maxX;
2115       quad.y = lastSelectionBoxLineInfo.lineOffset;
2116       quad.z = mModel->mVisualModel->mControlSize.width;
2117       quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight;
2118
2119       // Boxify at the end of the line.
2120       mEventData->mDecorator->AddHighlight( actualNumberOfQuads,
2121                                             quad );
2122       ++actualNumberOfQuads;
2123
2124       // Update the size of the highlighted text.
2125       maxHighlightX = mModel->mVisualModel->mControlSize.width;
2126     }
2127   }
2128
2129   // Set the actual number of quads.
2130   mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads );
2131
2132   // Sets the highlight's size and position. In decorator's coords.
2133   // The highlight's height has been calculated above (before 'boxifying' the highlight).
2134   highLightSize.width = maxHighlightX - minHighlightX;
2135
2136   highLightPosition.x = minHighlightX;
2137   const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() );
2138   highLightPosition.y = firstSelectionBoxLineInfo.lineOffset;
2139
2140   mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize );
2141
2142   if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() )
2143   {
2144     CursorInfo primaryCursorInfo;
2145     GetCursorPosition( mEventData->mLeftSelectionPosition,
2146                        primaryCursorInfo );
2147
2148     const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2149
2150     mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,
2151                                          primaryPosition.x,
2152                                          primaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2153                                          primaryCursorInfo.lineHeight );
2154
2155     CursorInfo secondaryCursorInfo;
2156     GetCursorPosition( mEventData->mRightSelectionPosition,
2157                        secondaryCursorInfo );
2158
2159     const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mModel->mScrollPosition;
2160
2161     mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE,
2162                                          secondaryPosition.x,
2163                                          secondaryCursorInfo.lineOffset + mModel->mScrollPosition.y,
2164                                          secondaryCursorInfo.lineHeight );
2165   }
2166
2167   // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection
2168   mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
2169
2170   // Set the flag to update the decorator.
2171   mEventData->mDecoratorUpdated = true;
2172 }
2173
2174 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
2175 {
2176   if( NULL == mEventData )
2177   {
2178     // Nothing to do if there is no text input.
2179     return;
2180   }
2181
2182   if( IsShowingPlaceholderText() )
2183   {
2184     // Nothing to do if there is the place-holder text.
2185     return;
2186   }
2187
2188   const Length numberOfGlyphs = mModel->mVisualModel->mGlyphs.Count();
2189   const Length numberOfLines  = mModel->mVisualModel->mLines.Count();
2190   if( ( 0 == numberOfGlyphs ) ||
2191       ( 0 == numberOfLines ) )
2192   {
2193     // Nothing to do if there is no text.
2194     return;
2195   }
2196
2197   // Find which word was selected
2198   CharacterIndex selectionStart( 0 );
2199   CharacterIndex selectionEnd( 0 );
2200   const bool indicesFound = FindSelectionIndices( mModel->mVisualModel,
2201                                                   mModel->mLogicalModel,
2202                                                   mMetrics,
2203                                                   visualX,
2204                                                   visualY,
2205                                                   selectionStart,
2206                                                   selectionEnd );
2207   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
2208
2209   if( indicesFound )
2210   {
2211     ChangeState( EventData::SELECTING );
2212
2213     mEventData->mLeftSelectionPosition = selectionStart;
2214     mEventData->mRightSelectionPosition = selectionEnd;
2215
2216     mEventData->mUpdateLeftSelectionPosition = true;
2217     mEventData->mUpdateRightSelectionPosition = true;
2218     mEventData->mUpdateHighlightBox = true;
2219
2220     // It may happen an IMF commit event arrives before the selection event
2221     // if the IMF manager is in pre-edit state. The commit event will set the
2222     // mEventData->mUpdateCursorPosition flag to true. If it's not set back
2223     // to false, the highlight box won't be updated.
2224     mEventData->mUpdateCursorPosition = false;
2225
2226     mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition );
2227   }
2228   else
2229   {
2230     // Nothing to select. i.e. a white space, out of bounds
2231     ChangeState( EventData::EDITING );
2232
2233     mEventData->mPrimaryCursorPosition = selectionEnd;
2234
2235     mEventData->mUpdateCursorPosition = true;
2236     mEventData->mUpdateGrabHandlePosition = true;
2237     mEventData->mScrollAfterUpdatePosition = true;
2238     mEventData->mUpdateInputStyle = true;
2239   }
2240 }
2241
2242 void Controller::Impl::SetPopupButtons()
2243 {
2244   /**
2245    *  Sets the Popup buttons to be shown depending on State.
2246    *
2247    *  If SELECTING :  CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
2248    *
2249    *  If EDITING_WITH_POPUP : SELECT & SELECT_ALL
2250    */
2251
2252   TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
2253
2254   if( EventData::SELECTING == mEventData->mState )
2255   {
2256     buttonsToShow = TextSelectionPopup::Buttons(  TextSelectionPopup::CUT | TextSelectionPopup::COPY );
2257
2258     if( !IsClipboardEmpty() )
2259     {
2260       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2261       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2262     }
2263
2264     if( !mEventData->mAllTextSelected )
2265     {
2266       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
2267     }
2268   }
2269   else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
2270   {
2271     if( mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText() )
2272     {
2273       buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
2274     }
2275
2276     if( !IsClipboardEmpty() )
2277     {
2278       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2279       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2280     }
2281   }
2282   else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState )
2283   {
2284     if ( !IsClipboardEmpty() )
2285     {
2286       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
2287       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
2288     }
2289   }
2290
2291   mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
2292 }
2293
2294 void Controller::Impl::ChangeState( EventData::State newState )
2295 {
2296   if( NULL == mEventData )
2297   {
2298     // Nothing to do if there is no text input.
2299     return;
2300   }
2301
2302   DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d  newstate:%d\n", mEventData->mState, newState );
2303
2304   if( mEventData->mState != newState )
2305   {
2306     mEventData->mPreviousState = mEventData->mState;
2307     mEventData->mState = newState;
2308
2309     switch( mEventData->mState )
2310     {
2311       case EventData::INACTIVE:
2312       {
2313         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2314         mEventData->mDecorator->StopCursorBlink();
2315         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2316         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2317         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2318         mEventData->mDecorator->SetHighlightActive( false );
2319         mEventData->mDecorator->SetPopupActive( false );
2320         mEventData->mDecoratorUpdated = true;
2321         break;
2322       }
2323       case EventData::INTERRUPTED:
2324       {
2325         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2326         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2327         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2328         mEventData->mDecorator->SetHighlightActive( false );
2329         mEventData->mDecorator->SetPopupActive( false );
2330         mEventData->mDecoratorUpdated = true;
2331         break;
2332       }
2333       case EventData::SELECTING:
2334       {
2335         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2336         mEventData->mDecorator->StopCursorBlink();
2337         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2338         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2339         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2340         mEventData->mDecorator->SetHighlightActive( true );
2341         if( mEventData->mGrabHandlePopupEnabled )
2342         {
2343           SetPopupButtons();
2344           mEventData->mDecorator->SetPopupActive( true );
2345         }
2346         mEventData->mDecoratorUpdated = true;
2347         break;
2348       }
2349       case EventData::EDITING:
2350       {
2351         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2352         if( mEventData->mCursorBlinkEnabled )
2353         {
2354           mEventData->mDecorator->StartCursorBlink();
2355         }
2356         // Grab handle is not shown until a tap is received whilst EDITING
2357         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2358         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2359         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2360         mEventData->mDecorator->SetHighlightActive( false );
2361         if( mEventData->mGrabHandlePopupEnabled )
2362         {
2363           mEventData->mDecorator->SetPopupActive( false );
2364         }
2365         mEventData->mDecoratorUpdated = true;
2366         break;
2367       }
2368       case EventData::EDITING_WITH_POPUP:
2369       {
2370         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState );
2371
2372         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2373         if( mEventData->mCursorBlinkEnabled )
2374         {
2375           mEventData->mDecorator->StartCursorBlink();
2376         }
2377         if( mEventData->mSelectionEnabled )
2378         {
2379           mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2380           mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2381           mEventData->mDecorator->SetHighlightActive( false );
2382         }
2383         else
2384         {
2385           mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2386         }
2387         if( mEventData->mGrabHandlePopupEnabled )
2388         {
2389           SetPopupButtons();
2390           mEventData->mDecorator->SetPopupActive( true );
2391         }
2392         mEventData->mDecoratorUpdated = true;
2393         break;
2394       }
2395       case EventData::EDITING_WITH_GRAB_HANDLE:
2396       {
2397         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState );
2398
2399         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2400         if( mEventData->mCursorBlinkEnabled )
2401         {
2402           mEventData->mDecorator->StartCursorBlink();
2403         }
2404         // Grab handle is not shown until a tap is received whilst EDITING
2405         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2406         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2407         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2408         mEventData->mDecorator->SetHighlightActive( false );
2409         if( mEventData->mGrabHandlePopupEnabled )
2410         {
2411           mEventData->mDecorator->SetPopupActive( false );
2412         }
2413         mEventData->mDecoratorUpdated = true;
2414         break;
2415       }
2416       case EventData::SELECTION_HANDLE_PANNING:
2417       {
2418         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2419         mEventData->mDecorator->StopCursorBlink();
2420         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2421         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
2422         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
2423         mEventData->mDecorator->SetHighlightActive( true );
2424         if( mEventData->mGrabHandlePopupEnabled )
2425         {
2426           mEventData->mDecorator->SetPopupActive( false );
2427         }
2428         mEventData->mDecoratorUpdated = true;
2429         break;
2430       }
2431       case EventData::GRAB_HANDLE_PANNING:
2432       {
2433         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState );
2434
2435         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2436         if( mEventData->mCursorBlinkEnabled )
2437         {
2438           mEventData->mDecorator->StartCursorBlink();
2439         }
2440         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2441         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2442         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2443         mEventData->mDecorator->SetHighlightActive( false );
2444         if( mEventData->mGrabHandlePopupEnabled )
2445         {
2446           mEventData->mDecorator->SetPopupActive( false );
2447         }
2448         mEventData->mDecoratorUpdated = true;
2449         break;
2450       }
2451       case EventData::EDITING_WITH_PASTE_POPUP:
2452       {
2453         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState );
2454
2455         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2456         if( mEventData->mCursorBlinkEnabled )
2457         {
2458           mEventData->mDecorator->StartCursorBlink();
2459         }
2460
2461         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
2462         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2463         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2464         mEventData->mDecorator->SetHighlightActive( false );
2465
2466         if( mEventData->mGrabHandlePopupEnabled )
2467         {
2468           SetPopupButtons();
2469           mEventData->mDecorator->SetPopupActive( true );
2470         }
2471         mEventData->mDecoratorUpdated = true;
2472         break;
2473       }
2474       case EventData::TEXT_PANNING:
2475       {
2476         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2477         mEventData->mDecorator->StopCursorBlink();
2478         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
2479         if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) ||
2480             mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) )
2481         {
2482           mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
2483           mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
2484           mEventData->mDecorator->SetHighlightActive( true );
2485         }
2486
2487         if( mEventData->mGrabHandlePopupEnabled )
2488         {
2489           mEventData->mDecorator->SetPopupActive( false );
2490         }
2491
2492         mEventData->mDecoratorUpdated = true;
2493         break;
2494       }
2495     }
2496   }
2497 }
2498
2499 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
2500                                           CursorInfo& cursorInfo )
2501 {
2502   if( !IsShowingRealText() )
2503   {
2504     // Do not want to use the place-holder text to set the cursor position.
2505
2506     // Use the line's height of the font's family set to set the cursor's size.
2507     // If there is no font's family set, use the default font.
2508     // Use the current alignment to place the cursor at the beginning, center or end of the box.
2509
2510     cursorInfo.lineOffset = 0.f;
2511     cursorInfo.lineHeight = GetDefaultFontLineHeight();
2512     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
2513
2514     switch( mModel->mHorizontalAlignment )
2515     {
2516       case Layout::HORIZONTAL_ALIGN_BEGIN:
2517       {
2518         cursorInfo.primaryPosition.x = 0.f;
2519         break;
2520       }
2521       case Layout::HORIZONTAL_ALIGN_CENTER:
2522       {
2523         cursorInfo.primaryPosition.x = floorf( 0.5f * mModel->mVisualModel->mControlSize.width );
2524         break;
2525       }
2526       case Layout::HORIZONTAL_ALIGN_END:
2527       {
2528         cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
2529         break;
2530       }
2531     }
2532
2533     // Nothing else to do.
2534     return;
2535   }
2536
2537   Text::GetCursorPosition( mModel->mVisualModel,
2538                            mModel->mLogicalModel,
2539                            mMetrics,
2540                            logical,
2541                            cursorInfo );
2542
2543   if( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() )
2544   {
2545     // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
2546
2547     // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
2548     // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
2549
2550     if( 0.f > cursorInfo.primaryPosition.x )
2551     {
2552       cursorInfo.primaryPosition.x = 0.f;
2553     }
2554
2555     const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>( mEventData->mDecorator->GetCursorWidth() );
2556     if( cursorInfo.primaryPosition.x > edgeWidth )
2557     {
2558       cursorInfo.primaryPosition.x = edgeWidth;
2559     }
2560   }
2561 }
2562
2563 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
2564 {
2565   if( NULL == mEventData )
2566   {
2567     // Nothing to do if there is no text input.
2568     return 0u;
2569   }
2570
2571   CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
2572
2573   const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
2574   const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
2575
2576   GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
2577   Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2578
2579   if( numberOfCharacters > 1u )
2580   {
2581     const Script script = mModel->mLogicalModel->GetScript( index );
2582     if( HasLigatureMustBreak( script ) )
2583     {
2584       // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ﻻ, ...
2585       numberOfCharacters = 1u;
2586     }
2587   }
2588   else
2589   {
2590     while( 0u == numberOfCharacters )
2591     {
2592       ++glyphIndex;
2593       numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
2594     }
2595   }
2596
2597   if( index < mEventData->mPrimaryCursorPosition )
2598   {
2599     cursorIndex -= numberOfCharacters;
2600   }
2601   else
2602   {
2603     cursorIndex += numberOfCharacters;
2604   }
2605
2606   // Will update the cursor hook position.
2607   mEventData->mUpdateCursorHookPosition = true;
2608
2609   return cursorIndex;
2610 }
2611
2612 void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo )
2613 {
2614   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
2615   if( NULL == mEventData )
2616   {
2617     // Nothing to do if there is no text input.
2618     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
2619     return;
2620   }
2621
2622   const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2623
2624   // Sets the cursor position.
2625   mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
2626                                        cursorPosition.x,
2627                                        cursorPosition.y,
2628                                        cursorInfo.primaryCursorHeight,
2629                                        cursorInfo.lineHeight );
2630   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
2631
2632   if( mEventData->mUpdateGrabHandlePosition )
2633   {
2634     // Sets the grab handle position.
2635     mEventData->mDecorator->SetPosition( GRAB_HANDLE,
2636                                          cursorPosition.x,
2637                                          cursorInfo.lineOffset + mModel->mScrollPosition.y,
2638                                          cursorInfo.lineHeight );
2639   }
2640
2641   if( cursorInfo.isSecondaryCursor )
2642   {
2643     mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
2644                                          cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
2645                                          cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
2646                                          cursorInfo.secondaryCursorHeight,
2647                                          cursorInfo.lineHeight );
2648     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y );
2649   }
2650
2651   // Set which cursors are active according the state.
2652   if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
2653   {
2654     if( cursorInfo.isSecondaryCursor )
2655     {
2656       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
2657     }
2658     else
2659     {
2660       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
2661     }
2662   }
2663   else
2664   {
2665     mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
2666   }
2667
2668   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
2669 }
2670
2671 void Controller::Impl::UpdateSelectionHandle( HandleType handleType,
2672                                               const CursorInfo& cursorInfo )
2673 {
2674   if( ( LEFT_SELECTION_HANDLE != handleType ) &&
2675       ( RIGHT_SELECTION_HANDLE != handleType ) )
2676   {
2677     return;
2678   }
2679
2680   const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
2681
2682   // Sets the handle's position.
2683   mEventData->mDecorator->SetPosition( handleType,
2684                                        cursorPosition.x,
2685                                        cursorInfo.lineOffset + mModel->mScrollPosition.y,
2686                                        cursorInfo.lineHeight );
2687
2688   // If selection handle at start of the text and other at end of the text then all text is selected.
2689   const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2690   const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
2691   mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mModel->mLogicalModel->mText.Count() );
2692 }
2693
2694 void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize )
2695 {
2696   // Clamp between -space & -alignment offset.
2697
2698   if( layoutSize.width > mModel->mVisualModel->mControlSize.width )
2699   {
2700     const float space = ( layoutSize.width - mModel->mVisualModel->mControlSize.width ) + mModel->mAlignmentOffset;
2701     mModel->mScrollPosition.x = ( mModel->mScrollPosition.x < -space ) ? -space : mModel->mScrollPosition.x;
2702     mModel->mScrollPosition.x = ( mModel->mScrollPosition.x > -mModel->mAlignmentOffset ) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
2703
2704     mEventData->mDecoratorUpdated = true;
2705   }
2706   else
2707   {
2708     mModel->mScrollPosition.x = 0.f;
2709   }
2710 }
2711
2712 void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize )
2713 {
2714   if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() )
2715   {
2716     // Nothing to do if the text is single line.
2717     return;
2718   }
2719
2720   // Clamp between -space & 0.
2721   if( layoutSize.height > mModel->mVisualModel->mControlSize.height )
2722   {
2723     const float space = ( layoutSize.height - mModel->mVisualModel->mControlSize.height );
2724     mModel->mScrollPosition.y = ( mModel->mScrollPosition.y < -space ) ? -space : mModel->mScrollPosition.y;
2725     mModel->mScrollPosition.y = ( mModel->mScrollPosition.y > 0.f ) ? 0.f : mModel->mScrollPosition.y;
2726
2727     mEventData->mDecoratorUpdated = true;
2728   }
2729   else
2730   {
2731     mModel->mScrollPosition.y = 0.f;
2732   }
2733 }
2734
2735 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight )
2736 {
2737   const float cursorWidth = mEventData->mDecorator ? static_cast<float>( mEventData->mDecorator->GetCursorWidth() ) : 0.f;
2738
2739   // position is in actor's coords.
2740   const float positionEndX = position.x + cursorWidth;
2741   const float positionEndY = position.y + lineHeight;
2742
2743   // Transform the position to decorator coords.
2744   const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
2745   const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
2746
2747   const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
2748   const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
2749
2750   if( decoratorPositionBeginX < 0.f )
2751   {
2752     mModel->mScrollPosition.x = -position.x;
2753   }
2754   else if( decoratorPositionEndX > mModel->mVisualModel->mControlSize.width )
2755   {
2756     mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
2757   }
2758
2759   if( decoratorPositionBeginY < 0.f )
2760   {
2761     mModel->mScrollPosition.y = -position.y;
2762   }
2763   else if( decoratorPositionEndY > mModel->mVisualModel->mControlSize.height )
2764   {
2765     mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
2766   }
2767 }
2768
2769 void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo )
2770 {
2771   // Get the current cursor position in decorator coords.
2772   const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
2773
2774   // Calculate the offset to match the cursor position before the character was deleted.
2775   mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2776   mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset;
2777
2778   ClampHorizontalScroll( mModel->mVisualModel->GetLayoutSize() );
2779   ClampVerticalScroll( mModel->mVisualModel->GetLayoutSize() );
2780
2781   // Makes the new cursor position visible if needed.
2782   ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight );
2783 }
2784
2785 void Controller::Impl::RequestRelayout()
2786 {
2787   if( NULL != mControlInterface )
2788   {
2789     mControlInterface->RequestTextRelayout();
2790   }
2791 }
2792
2793 } // namespace Text
2794
2795 } // namespace Toolkit
2796
2797 } // namespace Dali