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