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