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