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