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