Merge "Make internal actors size match TextLabel size" into devel/master
[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
25 // INTERNAL INCLUDES
26 #include <dali-toolkit/internal/text/bidirectional-support.h>
27 #include <dali-toolkit/internal/text/character-set-conversion.h>
28 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
29 #include <dali-toolkit/internal/text/multi-language-support.h>
30 #include <dali-toolkit/internal/text/script-run.h>
31 #include <dali-toolkit/internal/text/segmentation.h>
32 #include <dali-toolkit/internal/text/shaper.h>
33 #include <dali-toolkit/internal/text/text-io.h>
34 #include <dali-toolkit/internal/text/text-view.h>
35
36 namespace
37 {
38
39 #if defined(DEBUG_ENABLED)
40   Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_CONTROLS");
41 #endif
42
43 /**
44  * @brief Some characters can be shaped in more than one glyph.
45  * This struct is used to retrieve metrics from these group of glyphs.
46  */
47 struct GlyphMetrics
48 {
49   GlyphMetrics()
50   : fontHeight( 0.f ),
51     advance( 0.f ),
52     ascender( 0.f ),
53     xBearing( 0.f )
54   {}
55
56   ~GlyphMetrics()
57   {}
58
59   float fontHeight; ///< The font's height of that glyphs.
60   float advance;    ///< The sum of all the advances of all the glyphs.
61   float ascender;   ///< The font's ascender.
62   float xBearing;   ///< The x bearing of the first glyph.
63 };
64
65 const std::string EMPTY_STRING("");
66
67 } // namespace
68
69 namespace Dali
70 {
71
72 namespace Toolkit
73 {
74
75 namespace Text
76 {
77
78 /**
79  * @brief Get some glyph's metrics of a group of glyphs formed as a result of shaping one character.
80  *
81  * @param[in] glyphIndex The index to the first glyph.
82  * @param[in] numberOfGlyphs The number of glyphs.
83  * @param[out] glyphMetrics Some glyph metrics (font height, advance, ascender and x bearing).
84  * @param[in] visualModel The visual model.
85  * @param[in] fontClient The font client.
86  */
87 void GetGlyphsMetrics( GlyphIndex glyphIndex,
88                        Length numberOfGlyphs,
89                        GlyphMetrics& glyphMetrics,
90                        VisualModelPtr visualModel,
91                        TextAbstraction::FontClient& fontClient )
92 {
93   const GlyphInfo* glyphsBuffer = visualModel->mGlyphs.Begin();
94
95   const GlyphInfo& firstGlyph = *( glyphsBuffer + glyphIndex );
96
97   Text::FontMetrics fontMetrics;
98   fontClient.GetFontMetrics( firstGlyph.fontId, fontMetrics );
99
100   glyphMetrics.fontHeight = fontMetrics.height;
101   glyphMetrics.advance = firstGlyph.advance;
102   glyphMetrics.ascender = fontMetrics.ascender;
103   glyphMetrics.xBearing = firstGlyph.xBearing;
104
105   for( unsigned int i = 1u; i < numberOfGlyphs; ++i )
106   {
107     const GlyphInfo& glyphInfo = *( glyphsBuffer + glyphIndex + i );
108
109     glyphMetrics.advance += glyphInfo.advance;
110   }
111 }
112
113 EventData::EventData( DecoratorPtr decorator )
114 : mDecorator( decorator ),
115   mPlaceholderTextActive(),
116   mPlaceholderTextInactive(),
117   mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ),
118   mEventQueue(),
119   mScrollPosition(),
120   mState( INACTIVE ),
121   mPrimaryCursorPosition( 0u ),
122   mLeftSelectionPosition( 0u ),
123   mRightSelectionPosition( 0u ),
124   mPreEditStartPosition( 0u ),
125   mPreEditLength( 0u ),
126   mIsShowingPlaceholderText( false ),
127   mPreEditFlag( false ),
128   mDecoratorUpdated( false ),
129   mCursorBlinkEnabled( true ),
130   mGrabHandleEnabled( true ),
131   mGrabHandlePopupEnabled( true ),
132   mSelectionEnabled( true ),
133   mHorizontalScrollingEnabled( true ),
134   mVerticalScrollingEnabled( false ),
135   mUpdateCursorPosition( false ),
136   mUpdateLeftSelectionPosition( false ),
137   mUpdateRightSelectionPosition( false ),
138   mScrollAfterUpdatePosition( false ),
139   mScrollAfterDelete( false ),
140   mAllTextSelected( false )
141 {}
142
143 EventData::~EventData()
144 {}
145
146 bool Controller::Impl::ProcessInputEvents()
147 {
148   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
149   if( NULL == mEventData )
150   {
151     // Nothing to do if there is no text input.
152     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
153     return false;
154   }
155
156   if( mEventData->mDecorator )
157   {
158     for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
159          iter != mEventData->mEventQueue.end();
160          ++iter )
161     {
162       switch( iter->type )
163       {
164         case Event::CURSOR_KEY_EVENT:
165         {
166           OnCursorKeyEvent( *iter );
167           break;
168         }
169         case Event::TAP_EVENT:
170         {
171           OnTapEvent( *iter );
172           break;
173         }
174         case Event::LONG_PRESS_EVENT:
175         {
176           OnLongPressEvent( *iter );
177           break;
178         }
179         case Event::PAN_EVENT:
180         {
181           OnPanEvent( *iter );
182           break;
183         }
184         case Event::GRAB_HANDLE_EVENT:
185         case Event::LEFT_SELECTION_HANDLE_EVENT:
186         case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
187         {
188           OnHandleEvent( *iter );
189           break;
190         }
191         case Event::SELECT:
192         {
193           OnSelectEvent( *iter );
194           break;
195         }
196         case Event::SELECT_ALL:
197         {
198           OnSelectAllEvent();
199           break;
200         }
201       }
202     }
203   }
204
205   // The cursor must also be repositioned after inserts into the model
206   if( mEventData->mUpdateCursorPosition )
207   {
208     // Updates the cursor position and scrolls the text to make it visible.
209
210     UpdateCursorPosition();
211
212     if( mEventData->mScrollAfterUpdatePosition )
213     {
214       const Vector2& primaryCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
215
216       ScrollToMakePositionVisible( primaryCursorPosition );
217       mEventData->mScrollAfterUpdatePosition = false;
218     }
219
220     mEventData->mDecoratorUpdated = true;
221     mEventData->mUpdateCursorPosition = false;
222   }
223   else if( mEventData->mScrollAfterDelete )
224   {
225     ScrollTextToMatchCursor();
226     mEventData->mDecoratorUpdated = true;
227     mEventData->mScrollAfterDelete = false;
228   }
229   else
230   {
231     bool leftScroll = false;
232     bool rightScroll = false;
233
234     if( mEventData->mUpdateLeftSelectionPosition )
235     {
236       UpdateSelectionHandle( LEFT_SELECTION_HANDLE );
237
238       if( mEventData->mScrollAfterUpdatePosition )
239       {
240         const Vector2& leftHandlePosition = mEventData->mDecorator->GetPosition( LEFT_SELECTION_HANDLE );
241
242         ScrollToMakePositionVisible( leftHandlePosition );
243         leftScroll = true;
244       }
245
246       SetPopupButtons();
247       mEventData->mDecoratorUpdated = true;
248       mEventData->mUpdateLeftSelectionPosition = false;
249     }
250
251     if( mEventData->mUpdateRightSelectionPosition )
252     {
253       UpdateSelectionHandle( RIGHT_SELECTION_HANDLE );
254
255       if( mEventData->mScrollAfterUpdatePosition )
256       {
257         const Vector2& rightHandlePosition = mEventData->mDecorator->GetPosition( RIGHT_SELECTION_HANDLE );
258
259         ScrollToMakePositionVisible( rightHandlePosition );
260         rightScroll = true;
261       }
262
263       SetPopupButtons();
264       mEventData->mDecoratorUpdated = true;
265       mEventData->mUpdateRightSelectionPosition = false;
266     }
267
268     if( leftScroll || rightScroll )
269     {
270       mEventData->mScrollAfterUpdatePosition = false;
271     }
272   }
273
274   mEventData->mEventQueue.clear();
275
276   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
277
278   const bool decoratorUpdated = mEventData->mDecoratorUpdated;
279   mEventData->mDecoratorUpdated = false;
280
281   return decoratorUpdated;
282 }
283
284 void Controller::Impl::UpdateModel( OperationsMask operationsRequired )
285 {
286   // Calculate the operations to be done.
287   const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
288
289   Vector<Character>& utf32Characters = mLogicalModel->mText;
290
291   const Length numberOfCharacters = utf32Characters.Count();
292
293   Vector<LineBreakInfo>& lineBreakInfo = mLogicalModel->mLineBreakInfo;
294   if( GET_LINE_BREAKS & operations )
295   {
296     // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
297     // calculate the bidirectional info for each 'paragraph'.
298     // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
299     // is not shaped together).
300     lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
301
302     SetLineBreakInfo( utf32Characters,
303                       lineBreakInfo );
304   }
305
306   Vector<WordBreakInfo>& wordBreakInfo = mLogicalModel->mWordBreakInfo;
307   if( GET_WORD_BREAKS & operations )
308   {
309     // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
310     wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
311
312     SetWordBreakInfo( utf32Characters,
313                       wordBreakInfo );
314   }
315
316   const bool getScripts = GET_SCRIPTS & operations;
317   const bool validateFonts = VALIDATE_FONTS & operations;
318
319   Vector<ScriptRun>& scripts = mLogicalModel->mScriptRuns;
320   Vector<FontRun>& validFonts = mLogicalModel->mFontRuns;
321
322   if( getScripts || validateFonts )
323   {
324     // Validates the fonts assigned by the application or assigns default ones.
325     // It makes sure all the characters are going to be rendered by the correct font.
326     MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
327
328     if( getScripts )
329     {
330       // Retrieves the scripts used in the text.
331       multilanguageSupport.SetScripts( utf32Characters,
332                                        lineBreakInfo,
333                                        scripts );
334     }
335
336     if( validateFonts )
337     {
338       if( 0u == validFonts.Count() )
339       {
340         // Copy the requested font defaults received via the property system.
341         // These may not be valid i.e. may not contain glyphs for the necessary scripts.
342         GetDefaultFonts( validFonts, numberOfCharacters );
343       }
344
345       // Validates the fonts. If there is a character with no assigned font it sets a default one.
346       // After this call, fonts are validated.
347       multilanguageSupport.ValidateFonts( utf32Characters,
348                                           scripts,
349                                           validFonts );
350     }
351   }
352
353   Vector<Character> mirroredUtf32Characters;
354   bool textMirrored = false;
355   if( BIDI_INFO & operations )
356   {
357     // Count the number of LINE_NO_BREAK to reserve some space for the vector of paragraph's
358     // bidirectional info.
359
360     Length numberOfParagraphs = 0u;
361
362     const TextAbstraction::LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
363     for( Length index = 0u; index < numberOfCharacters; ++index )
364     {
365       if( TextAbstraction::LINE_NO_BREAK == *( lineBreakInfoBuffer + index ) )
366       {
367         ++numberOfParagraphs;
368       }
369     }
370
371     Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mLogicalModel->mBidirectionalParagraphInfo;
372     bidirectionalInfo.Reserve( numberOfParagraphs );
373
374     // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
375     SetBidirectionalInfo( utf32Characters,
376                           scripts,
377                           lineBreakInfo,
378                           bidirectionalInfo );
379
380     if( 0u != bidirectionalInfo.Count() )
381     {
382       // This paragraph has right to left text. Some characters may need to be mirrored.
383       // TODO: consider if the mirrored string can be stored as well.
384
385       textMirrored = GetMirroredText( utf32Characters, mirroredUtf32Characters );
386
387       // Only set the character directions if there is right to left characters.
388       Vector<CharacterDirection>& directions = mLogicalModel->mCharacterDirections;
389       directions.Resize( numberOfCharacters );
390
391       GetCharactersDirection( bidirectionalInfo,
392                               directions );
393     }
394     else
395     {
396       // There is no right to left characters. Clear the directions vector.
397       mLogicalModel->mCharacterDirections.Clear();
398     }
399
400    }
401
402   Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
403   Vector<CharacterIndex>& glyphsToCharactersMap = mVisualModel->mGlyphsToCharacters;
404   Vector<Length>& charactersPerGlyph = mVisualModel->mCharactersPerGlyph;
405   if( SHAPE_TEXT & operations )
406   {
407     const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
408     // Shapes the text.
409     ShapeText( textToShape,
410                lineBreakInfo,
411                scripts,
412                validFonts,
413                glyphs,
414                glyphsToCharactersMap,
415                charactersPerGlyph );
416
417     // Create the 'number of glyphs' per character and the glyph to character conversion tables.
418     mVisualModel->CreateGlyphsPerCharacterTable( numberOfCharacters );
419     mVisualModel->CreateCharacterToGlyphTable( numberOfCharacters );
420   }
421
422   const Length numberOfGlyphs = glyphs.Count();
423
424   if( GET_GLYPH_METRICS & operations )
425   {
426     mFontClient.GetGlyphMetrics( glyphs.Begin(), numberOfGlyphs );
427   }
428 }
429
430 void Controller::Impl::GetDefaultFonts( Vector<FontRun>& fonts, Length numberOfCharacters )
431 {
432   if( mFontDefaults )
433   {
434     FontRun fontRun;
435     fontRun.characterRun.characterIndex = 0;
436     fontRun.characterRun.numberOfCharacters = numberOfCharacters;
437     fontRun.fontId = mFontDefaults->GetFontId( mFontClient );
438     fontRun.isDefault = true;
439
440     fonts.PushBack( fontRun );
441   }
442 }
443
444 float Controller::Impl::GetDefaultFontLineHeight()
445 {
446   FontId defaultFontId = 0u;
447   if( NULL == mFontDefaults )
448   {
449     defaultFontId = mFontClient.GetFontId( EMPTY_STRING,
450                                            EMPTY_STRING );
451   }
452   else
453   {
454     defaultFontId = mFontDefaults->GetFontId( mFontClient );
455   }
456
457   Text::FontMetrics fontMetrics;
458   mFontClient.GetFontMetrics( defaultFontId, fontMetrics );
459
460   return( fontMetrics.ascender - fontMetrics.descender );
461 }
462
463 void Controller::Impl::OnCursorKeyEvent( const Event& event )
464 {
465   if( NULL == mEventData )
466   {
467     // Nothing to do if there is no text input.
468     return;
469   }
470
471   int keyCode = event.p1.mInt;
472
473   if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
474   {
475     if( mEventData->mPrimaryCursorPosition > 0u )
476     {
477       mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
478     }
479   }
480   else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
481   {
482     if( mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
483     {
484       mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
485     }
486   }
487   else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
488   {
489     // TODO
490   }
491   else if(   Dali::DALI_KEY_CURSOR_DOWN == keyCode )
492   {
493     // TODO
494   }
495
496   mEventData->mUpdateCursorPosition = true;
497   mEventData->mScrollAfterUpdatePosition = true;
498 }
499
500 void Controller::Impl::OnTapEvent( const Event& event )
501 {
502   if( NULL != mEventData )
503   {
504     const unsigned int tapCount = event.p1.mUint;
505
506     if( 1u == tapCount )
507     {
508       if( ! IsShowingPlaceholderText() )
509       {
510         const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
511         const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
512
513         mEventData->mPrimaryCursorPosition = GetClosestCursorIndex( xPosition,
514                                                                     yPosition );
515       }
516       else
517       {
518         mEventData->mPrimaryCursorPosition = 0u;
519       }
520
521       mEventData->mUpdateCursorPosition = true;
522       mEventData->mScrollAfterUpdatePosition = true;
523     }
524   }
525 }
526
527 void Controller::Impl::OnPanEvent( const Event& event )
528 {
529   if( NULL == mEventData )
530   {
531     // Nothing to do if there is no text input.
532     return;
533   }
534
535   int state = event.p1.mInt;
536
537   if( Gesture::Started    == state ||
538       Gesture::Continuing == state )
539   {
540     const Vector2& actualSize = mVisualModel->GetActualSize();
541     const Vector2 currentScroll = mEventData->mScrollPosition;
542
543     if( mEventData->mHorizontalScrollingEnabled )
544     {
545       const float displacementX = event.p2.mFloat;
546       mEventData->mScrollPosition.x += displacementX;
547
548       ClampHorizontalScroll( actualSize );
549     }
550
551     if( mEventData->mVerticalScrollingEnabled )
552     {
553       const float displacementY = event.p3.mFloat;
554       mEventData->mScrollPosition.y += displacementY;
555
556       ClampVerticalScroll( actualSize );
557     }
558
559     if( mEventData->mDecorator )
560     {
561       mEventData->mDecorator->UpdatePositions( mEventData->mScrollPosition - currentScroll );
562     }
563   }
564 }
565
566 void Controller::Impl::OnLongPressEvent( const Event& event )
567 {
568   if  ( EventData::EDITING == mEventData->mState )
569   {
570     ChangeState ( EventData::EDITING_WITH_POPUP );
571     mEventData->mDecoratorUpdated = true;
572   }
573 }
574
575 void Controller::Impl::OnHandleEvent( const Event& event )
576 {
577   if( NULL == mEventData )
578   {
579     // Nothing to do if there is no text input.
580     return;
581   }
582
583   const unsigned int state = event.p1.mUint;
584   const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
585
586   if( HANDLE_PRESSED == state )
587   {
588     // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
589     const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
590     const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
591
592     const CharacterIndex handleNewPosition = GetClosestCursorIndex( xPosition, yPosition );
593
594     if( Event::GRAB_HANDLE_EVENT == event.type )
595     {
596       ChangeState ( EventData::GRAB_HANDLE_PANNING );
597
598       if( handleNewPosition != mEventData->mPrimaryCursorPosition )
599       {
600         mEventData->mPrimaryCursorPosition = handleNewPosition;
601         mEventData->mUpdateCursorPosition = true;
602       }
603     }
604     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
605     {
606       ChangeState ( EventData::SELECTION_HANDLE_PANNING );
607
608       if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
609           ( handleNewPosition != mEventData->mRightSelectionPosition ) )
610       {
611         mEventData->mLeftSelectionPosition = handleNewPosition;
612
613         RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
614                                     mEventData->mRightSelectionPosition );
615
616         mEventData->mUpdateLeftSelectionPosition = true;
617       }
618     }
619     else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
620     {
621       ChangeState ( EventData::SELECTION_HANDLE_PANNING );
622
623       if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
624           ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
625       {
626         mEventData->mRightSelectionPosition = handleNewPosition;
627
628         RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
629                                     mEventData->mRightSelectionPosition );
630
631         mEventData->mUpdateRightSelectionPosition = true;
632       }
633     }
634   } // end ( HANDLE_PRESSED == state )
635   else if( ( HANDLE_RELEASED == state ) ||
636            handleStopScrolling )
637   {
638     CharacterIndex handlePosition = 0u;
639     if( handleStopScrolling )
640     {
641       // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
642       const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
643       const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
644
645       handlePosition = GetClosestCursorIndex( xPosition, yPosition );
646     }
647
648     if( Event::GRAB_HANDLE_EVENT == event.type )
649     {
650       mEventData->mUpdateCursorPosition = true;
651
652       ChangeState( EventData::EDITING_WITH_POPUP );
653
654       if( handleStopScrolling )
655       {
656         mEventData->mScrollAfterUpdatePosition = mEventData->mPrimaryCursorPosition != handlePosition;
657         mEventData->mPrimaryCursorPosition = handlePosition;
658       }
659     }
660     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
661     {
662       ChangeState( EventData::SELECTING );
663
664       if( handleStopScrolling )
665       {
666         mEventData->mUpdateLeftSelectionPosition = ( mEventData->mRightSelectionPosition != handlePosition );
667         mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateLeftSelectionPosition;
668
669         if( mEventData->mUpdateLeftSelectionPosition )
670         {
671           mEventData->mLeftSelectionPosition = handlePosition;
672
673           RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
674                                       mEventData->mRightSelectionPosition );
675         }
676       }
677     }
678     else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
679     {
680       ChangeState( EventData::SELECTING );
681
682       if( handleStopScrolling )
683       {
684         mEventData->mUpdateRightSelectionPosition = ( mEventData->mLeftSelectionPosition != handlePosition );
685         mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateRightSelectionPosition;
686         if( mEventData->mUpdateRightSelectionPosition )
687         {
688           mEventData->mRightSelectionPosition = handlePosition;
689           RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
690                                       mEventData->mRightSelectionPosition );
691         }
692       }
693     }
694
695     mEventData->mDecoratorUpdated = true;
696   } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
697   else if( HANDLE_SCROLLING == state )
698   {
699     const float xSpeed = event.p2.mFloat;
700     const Vector2& actualSize = mVisualModel->GetActualSize();
701     const Vector2 currentScrollPosition = mEventData->mScrollPosition;
702
703     mEventData->mScrollPosition.x += xSpeed;
704
705     ClampHorizontalScroll( actualSize );
706
707     bool endOfScroll = false;
708     if( Vector2::ZERO == ( currentScrollPosition - mEventData->mScrollPosition ) )
709     {
710       // Notify the decorator there is no more text to scroll.
711       // The decorator won't send more scroll events.
712       mEventData->mDecorator->NotifyEndOfScroll();
713       // Still need to set the position of the handle.
714       endOfScroll = true;
715     }
716
717     // Set the position of the handle.
718     const bool scrollRightDirection = xSpeed > 0.f;
719     const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
720     const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
721
722     if( Event::GRAB_HANDLE_EVENT == event.type )
723     {
724       ChangeState( EventData::GRAB_HANDLE_PANNING );
725
726       Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
727
728       // Position the grag handle close to either the left or right edge.
729       position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
730
731       // Get the new handle position.
732       // The grab handle's position is in decorator coords. Need to transforms to text coords.
733       const CharacterIndex handlePosition = GetClosestCursorIndex( position.x - mEventData->mScrollPosition.x - mAlignmentOffset.x,
734                                                                    position.y - mEventData->mScrollPosition.y - mAlignmentOffset.y );
735
736       mEventData->mUpdateCursorPosition = mEventData->mPrimaryCursorPosition != handlePosition;
737       mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateCursorPosition;
738       mEventData->mPrimaryCursorPosition = handlePosition;
739     }
740     else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
741     {
742       // TODO: This is recalculating the selection box every time the text is scrolled with the selection handles.
743       //       Think if something can be done to save power.
744
745       ChangeState( EventData::SELECTION_HANDLE_PANNING );
746
747       Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
748
749       // Position the selection handle close to either the left or right edge.
750       position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width;
751
752       // Get the new handle position.
753       // The selection handle's position is in decorator coords. Need to transforms to text coords.
754       const CharacterIndex handlePosition = GetClosestCursorIndex( position.x - mEventData->mScrollPosition.x - mAlignmentOffset.x,
755                                                                    position.y - mEventData->mScrollPosition.y - mAlignmentOffset.y );
756
757       if( leftSelectionHandleEvent )
758       {
759         const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition );
760         mEventData->mUpdateLeftSelectionPosition = endOfScroll || differentHandles;
761         if( differentHandles )
762         {
763           mEventData->mLeftSelectionPosition = handlePosition;
764         }
765       }
766       else
767       {
768         const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
769         mEventData->mUpdateRightSelectionPosition = endOfScroll || differentHandles;
770         if( differentHandles )
771         {
772           mEventData->mRightSelectionPosition = handlePosition;
773         }
774       }
775
776       if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
777       {
778         RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
779                                     mEventData->mRightSelectionPosition );
780
781         mEventData->mScrollAfterUpdatePosition = true;
782       }
783     }
784     mEventData->mDecoratorUpdated = true;
785   } // end ( HANDLE_SCROLLING == state )
786 }
787
788 void Controller::Impl::OnSelectEvent( const Event& event )
789 {
790   if( NULL == mEventData )
791   {
792     // Nothing to do if there is no text.
793     return;
794   }
795
796   if( mEventData->mSelectionEnabled )
797   {
798     // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
799     const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
800     const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
801
802     const CharacterIndex leftPosition = mEventData->mLeftSelectionPosition;
803     const CharacterIndex rightPosition = mEventData->mRightSelectionPosition;
804
805     RepositionSelectionHandles( xPosition,
806                                 yPosition );
807
808     mEventData->mUpdateLeftSelectionPosition = leftPosition != mEventData->mLeftSelectionPosition;
809     mEventData->mUpdateRightSelectionPosition = rightPosition != mEventData->mRightSelectionPosition;
810
811     mEventData->mScrollAfterUpdatePosition = ( ( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition ) &&
812                                                ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition ) );
813   }
814 }
815
816 void Controller::Impl::OnSelectAllEvent()
817 {
818   if( NULL == mEventData )
819   {
820     // Nothing to do if there is no text.
821     return;
822   }
823
824   if( mEventData->mSelectionEnabled )
825   {
826     RepositionSelectionHandles( 0u,
827                                 mLogicalModel->mText.Count() );
828
829     mEventData->mScrollAfterUpdatePosition = true;
830     mEventData->mUpdateLeftSelectionPosition = true;
831     mEventData->mUpdateRightSelectionPosition = true;
832   }
833 }
834
835 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetreival )
836 {
837   if( mEventData->mLeftSelectionPosition ==  mEventData->mRightSelectionPosition )
838   {
839     // Nothing to select if handles are in the same place.
840     selectedText="";
841     return;
842   }
843
844   //Get start and end position of selection
845   uint32_t startOfSelectedText = mEventData->mLeftSelectionPosition;
846   uint32_t lengthOfSelectedText =  mEventData->mRightSelectionPosition - startOfSelectedText;
847
848   // Validate the start and end selection points
849   if( ( startOfSelectedText >= 0 ) && (  ( startOfSelectedText + lengthOfSelectedText ) <=  mLogicalModel->mText.Count() ) )
850   {
851     //Get text as a UTF8 string
852     Vector<Character>& utf32Characters = mLogicalModel->mText;
853
854     Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
855
856     if ( deleteAfterRetreival  ) // Only delete text if copied successfully
857     {
858       // Delete text between handles
859       Vector<Character>& currentText = mLogicalModel->mText;
860
861       Vector<Character>::Iterator first = currentText.Begin() + startOfSelectedText;
862       Vector<Character>::Iterator last  = first + lengthOfSelectedText;
863       currentText.Erase( first, last );
864     }
865     mEventData->mPrimaryCursorPosition = mEventData->mLeftSelectionPosition;
866     mEventData->mScrollAfterDelete = true;
867     mEventData->mDecoratorUpdated = true;
868   }
869 }
870
871 void Controller::Impl::ShowClipboard()
872 {
873   if ( mClipboard )
874   {
875     mClipboard.ShowClipboard();
876   }
877 }
878
879 void Controller::Impl::HideClipboard()
880 {
881   if ( mClipboard )
882   {
883     mClipboard.HideClipboard();
884   }
885 }
886
887 bool Controller::Impl::CopyStringToClipboard( std::string& source )
888 {
889   //Send string to clipboard
890   return ( mClipboard && mClipboard.SetItem( source ) );
891 }
892
893 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
894 {
895   std::string selectedText;
896   RetrieveSelection( selectedText, deleteAfterSending );
897   CopyStringToClipboard( selectedText );
898   ChangeState( EventData::EDITING );
899 }
900
901 void Controller::Impl::GetTextFromClipboard( unsigned int itemIndex, std::string& retreivedString )
902 {
903   if ( mClipboard )
904   {
905     retreivedString =  mClipboard.GetItem( itemIndex );
906   }
907 }
908
909 void Controller::Impl::RepositionSelectionHandles( CharacterIndex selectionStart, CharacterIndex selectionEnd )
910 {
911   if( selectionStart == selectionEnd )
912   {
913     // Nothing to select if handles are in the same place.
914     return;
915   }
916
917   mEventData->mDecorator->ClearHighlights();
918
919   mEventData->mLeftSelectionPosition = selectionStart;
920   mEventData->mRightSelectionPosition = selectionEnd;
921
922   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
923   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
924   const GlyphInfo* const glyphsBuffer = mVisualModel->mGlyphs.Begin();
925   const Vector2* const positionsBuffer = mVisualModel->mGlyphPositions.Begin();
926
927   // TODO: Better algorithm to create the highlight box.
928   // TODO: Multi-line.
929
930   const Vector<LineRun>& lines = mVisualModel->mLines;
931   const LineRun& firstLine = *lines.Begin();
932   const float height = firstLine.ascender + -firstLine.descender;
933
934   const bool indicesSwapped = ( selectionStart > selectionEnd );
935   if( indicesSwapped )
936   {
937     std::swap( selectionStart, selectionEnd );
938   }
939
940   const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
941   const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + ( selectionEnd - 1u ) );
942   const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + ( selectionEnd - 1u ) ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u );
943
944   mEventData->mDecorator->SwapSelectionHandlesEnabled( firstLine.direction != indicesSwapped );
945
946   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
947
948   for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
949   {
950     // TODO: Fix the LATIN ligatures. i.e ff, fi, etc...
951     const GlyphInfo& glyph = *( glyphsBuffer + index );
952     const Vector2& position = *( positionsBuffer + index );
953
954     const float xPosition = position.x - glyph.xBearing + offset.x;
955     mEventData->mDecorator->AddHighlight( xPosition, offset.y, xPosition + glyph.advance, height );
956   }
957
958   CursorInfo primaryCursorInfo;
959   GetCursorPosition( mEventData->mLeftSelectionPosition,
960                      primaryCursorInfo );
961
962   CursorInfo secondaryCursorInfo;
963   GetCursorPosition( mEventData->mRightSelectionPosition,
964                      secondaryCursorInfo );
965
966   const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + offset;
967   const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + offset;
968
969   mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE, primaryPosition.x, primaryPosition.y, primaryCursorInfo.lineHeight );
970
971   mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE, secondaryPosition.x, secondaryPosition.y, secondaryCursorInfo.lineHeight );
972
973   // Set the flag to update the decorator.
974   mEventData->mDecoratorUpdated = true;
975 }
976
977 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
978 {
979   if( NULL == mEventData )
980   {
981     // Nothing to do if there is no text input.
982     return;
983   }
984
985   if( IsShowingPlaceholderText() )
986   {
987     // Nothing to do if there is the place-holder text.
988     return;
989   }
990
991   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
992   const Length numberOfLines  = mVisualModel->mLines.Count();
993   if( 0 == numberOfGlyphs ||
994       0 == numberOfLines )
995   {
996     // Nothing to do if there is no text.
997     return;
998   }
999
1000   // Find which word was selected
1001   CharacterIndex selectionStart( 0 );
1002   CharacterIndex selectionEnd( 0 );
1003   FindSelectionIndices( visualX, visualY, selectionStart, selectionEnd );
1004   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
1005
1006   if( selectionStart == selectionEnd )
1007   {
1008     ChangeState( EventData::EDITING );
1009     // Nothing to select. i.e. a white space, out of bounds
1010     return;
1011   }
1012
1013   RepositionSelectionHandles( selectionStart, selectionEnd );
1014 }
1015
1016 void Controller::Impl::SetPopupButtons()
1017 {
1018   /**
1019    *  Sets the Popup buttons to be shown depending on State.
1020    *
1021    *  If SELECTING :  CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
1022    *
1023    *  If EDITING_WITH_POPUP : SELECT & SELECT_ALL
1024    */
1025
1026   TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
1027
1028   if ( ( EventData::SELECTING == mEventData->mState ) || ( EventData::SELECTION_CHANGED == mEventData->mState ) )
1029   {
1030     buttonsToShow = TextSelectionPopup::Buttons(  TextSelectionPopup::CUT | TextSelectionPopup::COPY );
1031
1032     if ( !IsClipboardEmpty() )
1033     {
1034       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1035       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1036     }
1037
1038     if ( !mEventData->mAllTextSelected )
1039     {
1040       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
1041     }
1042   }
1043   else if  ( EventData::EDITING_WITH_POPUP == mEventData->mState )
1044   {
1045     if ( mLogicalModel->mText.Count() && !IsShowingPlaceholderText())
1046     {
1047       buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
1048     }
1049
1050     if ( !IsClipboardEmpty() )
1051     {
1052       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1053       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1054     }
1055   }
1056
1057   mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
1058 }
1059
1060 void Controller::Impl::ChangeState( EventData::State newState )
1061 {
1062   if( NULL == mEventData )
1063   {
1064     // Nothing to do if there is no text input.
1065     return;
1066   }
1067
1068   if( mEventData->mState != newState )
1069   {
1070     mEventData->mState = newState;
1071
1072     if( EventData::INACTIVE == mEventData->mState )
1073     {
1074       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1075       mEventData->mDecorator->StopCursorBlink();
1076       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1077       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1078       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1079       mEventData->mDecorator->SetPopupActive( false );
1080       mEventData->mDecoratorUpdated = true;
1081       HideClipboard();
1082     }
1083     else if ( EventData::INTERRUPTED  == mEventData->mState)
1084     {
1085       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1086       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1087       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1088       mEventData->mDecorator->SetPopupActive( false );
1089       mEventData->mDecoratorUpdated = true;
1090       HideClipboard();
1091     }
1092     else if ( EventData::SELECTING == mEventData->mState )
1093     {
1094       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1095       mEventData->mDecorator->StopCursorBlink();
1096       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1097       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
1098       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
1099       if( mEventData->mGrabHandlePopupEnabled )
1100       {
1101         SetPopupButtons();
1102         mEventData->mDecorator->SetPopupActive( true );
1103       }
1104       mEventData->mDecoratorUpdated = true;
1105     }
1106     else if ( EventData::SELECTION_CHANGED  == mEventData->mState )
1107     {
1108       if( mEventData->mGrabHandlePopupEnabled )
1109       {
1110         SetPopupButtons();
1111         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1112         mEventData->mDecorator->SetPopupActive( true );
1113       }
1114       mEventData->mDecoratorUpdated = true;
1115     }
1116     else if( EventData::EDITING == mEventData->mState )
1117     {
1118       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1119       if( mEventData->mCursorBlinkEnabled )
1120       {
1121         mEventData->mDecorator->StartCursorBlink();
1122       }
1123       // Grab handle is not shown until a tap is received whilst EDITING
1124       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1125       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1126       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1127       if( mEventData->mGrabHandlePopupEnabled )
1128       {
1129         mEventData->mDecorator->SetPopupActive( false );
1130       }
1131       mEventData->mDecoratorUpdated = true;
1132       HideClipboard();
1133     }
1134     else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
1135     {
1136       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1137       if( mEventData->mCursorBlinkEnabled )
1138       {
1139         mEventData->mDecorator->StartCursorBlink();
1140       }
1141       if( mEventData->mSelectionEnabled )
1142       {
1143         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1144         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1145       }
1146       else
1147       {
1148         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1149       }
1150       if( mEventData->mGrabHandlePopupEnabled )
1151       {
1152         SetPopupButtons();
1153         mEventData->mDecorator->SetPopupActive( true );
1154       }
1155       HideClipboard();
1156       mEventData->mDecoratorUpdated = true;
1157     }
1158     else if( EventData::EDITING_WITH_GRAB_HANDLE == mEventData->mState )
1159     {
1160       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1161       if( mEventData->mCursorBlinkEnabled )
1162       {
1163         mEventData->mDecorator->StartCursorBlink();
1164       }
1165       // Grab handle is not shown until a tap is received whilst EDITING
1166       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1167       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1168       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1169       if( mEventData->mGrabHandlePopupEnabled )
1170       {
1171         mEventData->mDecorator->SetPopupActive( false );
1172       }
1173       mEventData->mDecoratorUpdated = true;
1174       HideClipboard();
1175     }
1176     else if ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState )
1177     {
1178       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1179       mEventData->mDecorator->StopCursorBlink();
1180       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1181       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
1182       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
1183       if( mEventData->mGrabHandlePopupEnabled )
1184       {
1185         mEventData->mDecorator->SetPopupActive( false );
1186       }
1187       mEventData->mDecoratorUpdated = true;
1188     }
1189     else if ( EventData::GRAB_HANDLE_PANNING == mEventData->mState )
1190     {
1191       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1192       if( mEventData->mCursorBlinkEnabled )
1193       {
1194         mEventData->mDecorator->StartCursorBlink();
1195       }
1196       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1197       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1198       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1199       if( mEventData->mGrabHandlePopupEnabled )
1200       {
1201         mEventData->mDecorator->SetPopupActive( false );
1202       }
1203       mEventData->mDecoratorUpdated = true;
1204     }
1205   }
1206 }
1207
1208 LineIndex Controller::Impl::GetClosestLine( float y ) const
1209 {
1210   float totalHeight = 0.f;
1211   LineIndex lineIndex = 0u;
1212
1213   const Vector<LineRun>& lines = mVisualModel->mLines;
1214   for( LineIndex endLine = lines.Count();
1215        lineIndex < endLine;
1216        ++lineIndex )
1217   {
1218     const LineRun& lineRun = lines[lineIndex];
1219     totalHeight += lineRun.ascender + -lineRun.descender;
1220     if( y < totalHeight )
1221     {
1222       return lineIndex;
1223     }
1224   }
1225
1226   if( lineIndex == 0 )
1227   {
1228     return 0;
1229   }
1230
1231   return lineIndex-1;
1232 }
1233
1234 void Controller::Impl::FindSelectionIndices( float visualX, float visualY, CharacterIndex& startIndex, CharacterIndex& endIndex )
1235 {
1236   CharacterIndex hitCharacter = GetClosestCursorIndex( visualX, visualY );
1237   if( hitCharacter >= mLogicalModel->mText.Count() )
1238   {
1239     // Selection out of bounds.
1240     return;
1241   }
1242
1243   startIndex = hitCharacter;
1244   endIndex = hitCharacter;
1245
1246   if( !TextAbstraction::IsWhiteSpace( mLogicalModel->mText[hitCharacter] ) )
1247   {
1248     // Find the start and end of the text
1249     for( startIndex = hitCharacter; startIndex > 0; --startIndex )
1250     {
1251       Character charCode = mLogicalModel->mText[ startIndex-1 ];
1252       if( TextAbstraction::IsWhiteSpace( charCode ) )
1253       {
1254         break;
1255       }
1256     }
1257     const CharacterIndex pastTheEnd = mLogicalModel->mText.Count();
1258     for( endIndex = hitCharacter + 1u; endIndex < pastTheEnd; ++endIndex )
1259     {
1260       Character charCode = mLogicalModel->mText[ endIndex ];
1261       if( TextAbstraction::IsWhiteSpace( charCode ) )
1262       {
1263         break;
1264       }
1265     }
1266   }
1267 }
1268
1269 CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
1270                                                         float visualY )
1271 {
1272   if( NULL == mEventData )
1273   {
1274     // Nothing to do if there is no text input.
1275     return 0u;
1276   }
1277
1278   CharacterIndex logicalIndex = 0u;
1279
1280   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
1281   const Length numberOfLines  = mVisualModel->mLines.Count();
1282   if( 0 == numberOfGlyphs ||
1283       0 == numberOfLines )
1284   {
1285     return logicalIndex;
1286   }
1287
1288   // Find which line is closest
1289   const LineIndex lineIndex = GetClosestLine( visualY );
1290   const LineRun& line = mVisualModel->mLines[lineIndex];
1291
1292   // Get the positions of the glyphs.
1293   const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
1294   const Vector2* const positionsBuffer = positions.Begin();
1295
1296   // Get the visual to logical conversion tables.
1297   const CharacterIndex* const visualToLogicalBuffer = ( 0u != mLogicalModel->mVisualToLogicalMap.Count() ) ? mLogicalModel->mVisualToLogicalMap.Begin() : NULL;
1298   const CharacterIndex* const visualToLogicalCursorBuffer = mLogicalModel->mVisualToLogicalCursorMap.Begin();
1299
1300   // Get the character to glyph conversion table.
1301   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1302
1303   // Get the glyphs per character table.
1304   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
1305   const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1306
1307   // If the vector is void, there is no right to left characters.
1308   const bool hasRightToLeftCharacters = NULL != visualToLogicalBuffer;
1309
1310   const CharacterIndex startCharacter = line.characterRun.characterIndex;
1311   const CharacterIndex endCharacter   = line.characterRun.characterIndex + line.characterRun.numberOfCharacters;
1312   DALI_ASSERT_DEBUG( endCharacter <= mLogicalModel->mText.Count() && "Invalid line info" );
1313
1314   // Whether there is a hit on a glyph.
1315   bool matched = false;
1316
1317   // Traverses glyphs in visual order. To do that use the visual to logical conversion table.
1318   CharacterIndex visualIndex = startCharacter;
1319   for( ; !matched && ( visualIndex < endCharacter ); ++visualIndex )
1320   {
1321     // The character in logical order.
1322     const CharacterIndex characterLogicalOrderIndex = hasRightToLeftCharacters ? *( visualToLogicalBuffer + visualIndex ) : visualIndex;
1323
1324     // Get the script of the character.
1325     const Script script = mLogicalModel->GetScript( characterLogicalOrderIndex );
1326
1327     // The first glyph for that character in logical order.
1328     const GlyphIndex glyphLogicalOrderIndex = *( charactersToGlyphBuffer + characterLogicalOrderIndex );
1329     // The number of glyphs for that character
1330     const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + characterLogicalOrderIndex );
1331
1332     // Get the metrics for the group of glyphs.
1333     GlyphMetrics glyphMetrics;
1334     GetGlyphsMetrics( glyphLogicalOrderIndex,
1335                       numberOfGlyphs,
1336                       glyphMetrics,
1337                       mVisualModel,
1338                       mFontClient );
1339
1340     const Vector2& position = *( positionsBuffer + glyphLogicalOrderIndex );
1341
1342     // Prevents to jump the whole Latin ligatures like fi, ff, ...
1343     const Length numberOfCharactersInLigature = ( TextAbstraction::LATIN == script ) ? *( charactersPerGlyphBuffer + glyphLogicalOrderIndex ) : 1u;
1344     const float glyphAdvance = glyphMetrics.advance / static_cast<float>( numberOfCharactersInLigature );
1345
1346     for( GlyphIndex index = 0u; !matched && ( index < numberOfCharactersInLigature ); ++index )
1347     {
1348       // Find the mid-point of the area containing the glyph
1349       const float glyphCenter = -glyphMetrics.xBearing + position.x + ( static_cast<float>( index ) + 0.5f ) * glyphAdvance;
1350
1351       if( visualX < glyphCenter )
1352       {
1353         visualIndex += index;
1354         matched = true;
1355         break;
1356       }
1357     }
1358
1359     if( matched )
1360     {
1361       break;
1362     }
1363   }
1364
1365   // Return the logical position of the cursor in characters.
1366
1367   if( !matched )
1368   {
1369     visualIndex = endCharacter;
1370   }
1371
1372   logicalIndex = hasRightToLeftCharacters ? *( visualToLogicalCursorBuffer + visualIndex ) : visualIndex;
1373   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p closest visualIndex %d logicalIndex %d\n", this, visualIndex, logicalIndex );
1374
1375   return logicalIndex;
1376 }
1377
1378 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
1379                                           CursorInfo& cursorInfo )
1380 {
1381   // TODO: Check for multiline with \n, etc...
1382
1383   // Check if the logical position is the first or the last one of the text.
1384   const bool isFirstPosition = 0u == logical;
1385   const bool isLastPosition = mLogicalModel->mText.Count() == logical;
1386
1387   if( isFirstPosition && isLastPosition )
1388   {
1389     // There is zero characters. Get the default font's line height.
1390     cursorInfo.lineHeight = GetDefaultFontLineHeight();
1391     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
1392
1393     cursorInfo.primaryPosition.x = 1.f;
1394     cursorInfo.primaryPosition.y = 0.f;
1395
1396     // Nothing else to do.
1397     return;
1398   }
1399
1400   // 'logical' is the logical 'cursor' index.
1401   // Get the next and current logical 'character' index.
1402   const CharacterIndex nextCharacterIndex = logical;
1403   const CharacterIndex characterIndex = isFirstPosition ? logical : logical - 1u;
1404
1405   // Get the direction of the character and the next one.
1406   const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
1407
1408   CharacterDirection isCurrentRightToLeft = false;
1409   CharacterDirection isNextRightToLeft = false;
1410   if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1411   {
1412     isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + characterIndex );
1413     isNextRightToLeft = *( modelCharacterDirectionsBuffer + nextCharacterIndex );
1414   }
1415
1416   // Get the line where the character is laid-out.
1417   const LineRun* modelLines = mVisualModel->mLines.Begin();
1418
1419   const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( characterIndex );
1420   const LineRun& line = *( modelLines + lineIndex );
1421
1422   // Get the paragraph's direction.
1423   const CharacterDirection isRightToLeftParagraph = line.direction;
1424
1425   // Check whether there is an alternative position:
1426
1427   cursorInfo.isSecondaryCursor = ( !isLastPosition && ( isCurrentRightToLeft != isNextRightToLeft ) ) ||
1428                                  ( isLastPosition && ( isRightToLeftParagraph != isCurrentRightToLeft ) );
1429
1430   // Set the line height.
1431   cursorInfo.lineHeight = line.ascender + -line.descender;
1432
1433   // Calculate the primary cursor.
1434
1435   CharacterIndex index = characterIndex;
1436   if( cursorInfo.isSecondaryCursor )
1437   {
1438     // If there is a secondary position, the primary cursor may be in a different place than the logical index.
1439
1440     if( isLastPosition )
1441     {
1442       // The position of the cursor after the last character needs special
1443       // care depending on its direction and the direction of the paragraph.
1444
1445       // Need to find the first character after the last character with the paragraph's direction.
1446       // i.e l0 l1 l2 r0 r1 should find r0.
1447
1448       // TODO: check for more than one line!
1449       index = isRightToLeftParagraph ? line.characterRun.characterIndex : line.characterRun.characterIndex + line.characterRun.numberOfCharacters - 1u;
1450       index = mLogicalModel->GetLogicalCharacterIndex( index );
1451     }
1452     else
1453     {
1454       index = ( isRightToLeftParagraph == isCurrentRightToLeft ) ? characterIndex : nextCharacterIndex;
1455     }
1456   }
1457
1458   // Convert the cursor position into the glyph position.
1459   const GlyphIndex primaryGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + index );
1460   const Length primaryNumberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + index );
1461   const Length primaryNumberOfCharacters = *( mVisualModel->mCharactersPerGlyph.Begin() + primaryGlyphIndex );
1462
1463   // Get the metrics for the group of glyphs.
1464   GlyphMetrics glyphMetrics;
1465   GetGlyphsMetrics( primaryGlyphIndex,
1466                     primaryNumberOfGlyphs,
1467                     glyphMetrics,
1468                     mVisualModel,
1469                     mFontClient );
1470
1471   float glyphAdvance = 0.f;
1472   if( !isLastPosition &&
1473       ( primaryNumberOfCharacters > 1u ) )
1474   {
1475     const CharacterIndex firstIndex = *( mVisualModel->mGlyphsToCharacters.Begin() + primaryGlyphIndex );
1476     glyphAdvance = static_cast<float>( 1u + characterIndex - firstIndex ) * glyphMetrics.advance / static_cast<float>( primaryNumberOfCharacters );
1477   }
1478   else
1479   {
1480     glyphAdvance = glyphMetrics.advance;
1481   }
1482
1483   // Get the glyph position and x bearing.
1484   const Vector2& primaryPosition = *( mVisualModel->mGlyphPositions.Begin() + primaryGlyphIndex );
1485
1486   // Set the primary cursor's height.
1487   cursorInfo.primaryCursorHeight = cursorInfo.isSecondaryCursor ? 0.5f * glyphMetrics.fontHeight : glyphMetrics.fontHeight;
1488
1489   // Set the primary cursor's position.
1490   if( isLastPosition )
1491   {
1492     cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + primaryPosition.x + ( isRightToLeftParagraph ? 0.f : glyphMetrics.advance );
1493   }
1494   else
1495   {
1496     cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + primaryPosition.x + ( ( ( isFirstPosition && !isCurrentRightToLeft ) || ( !isFirstPosition && isCurrentRightToLeft ) ) ? 0.f : glyphAdvance );
1497   }
1498   cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
1499
1500   // Calculate the secondary cursor.
1501
1502   if( cursorInfo.isSecondaryCursor )
1503   {
1504     // Set the secondary cursor's height.
1505     cursorInfo.secondaryCursorHeight = 0.5f * glyphMetrics.fontHeight;
1506
1507     CharacterIndex index = characterIndex;
1508     if( !isLastPosition )
1509     {
1510       index = ( isRightToLeftParagraph == isCurrentRightToLeft ) ? nextCharacterIndex : characterIndex;
1511     }
1512
1513     const GlyphIndex secondaryGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + index );
1514     const Length secondaryNumberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + index );
1515
1516     const Vector2& secondaryPosition = *( mVisualModel->mGlyphPositions.Begin() + index );
1517
1518     GetGlyphsMetrics( secondaryGlyphIndex,
1519                       secondaryNumberOfGlyphs,
1520                       glyphMetrics,
1521                       mVisualModel,
1522                       mFontClient );
1523
1524     // Set the secondary cursor's position.
1525     cursorInfo.secondaryPosition.x = -glyphMetrics.xBearing + secondaryPosition.x + ( isCurrentRightToLeft ? 0.f : glyphMetrics.advance );
1526     cursorInfo.secondaryPosition.y = cursorInfo.lineHeight - cursorInfo.secondaryCursorHeight - line.descender - ( glyphMetrics.fontHeight - glyphMetrics.ascender );
1527   }
1528 }
1529
1530 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
1531 {
1532   if( NULL == mEventData )
1533   {
1534     // Nothing to do if there is no text input.
1535     return 0u;
1536   }
1537
1538   CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
1539
1540   const Script script = mLogicalModel->GetScript( index );
1541   const GlyphIndex* charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1542   const Length* charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1543
1544   Length numberOfCharacters = 0u;
1545   if( TextAbstraction::LATIN == script )
1546   {
1547     // Prevents to jump the whole Latin ligatures like fi, ff, ...
1548     numberOfCharacters = 1u;
1549   }
1550   else
1551   {
1552     GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
1553     numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1554
1555     while( 0u == numberOfCharacters )
1556     {
1557       numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1558       ++glyphIndex;
1559     }
1560   }
1561
1562   if( index < mEventData->mPrimaryCursorPosition )
1563   {
1564     cursorIndex -= numberOfCharacters;
1565   }
1566   else
1567   {
1568     cursorIndex += numberOfCharacters;
1569   }
1570
1571   return cursorIndex;
1572 }
1573
1574 void Controller::Impl::UpdateCursorPosition()
1575 {
1576   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
1577   if( NULL == mEventData )
1578   {
1579     // Nothing to do if there is no text input.
1580     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
1581     return;
1582   }
1583
1584   if( IsShowingPlaceholderText() )
1585   {
1586     // Do not want to use the place-holder text to set the cursor position.
1587
1588     // Use the line's height of the font's family set to set the cursor's size.
1589     // If there is no font's family set, use the default font.
1590     // Use the current alignment to place the cursor at the beginning, center or end of the box.
1591
1592     float lineHeight = 0.f;
1593
1594     FontId defaultFontId = 0u;
1595     if( NULL == mFontDefaults )
1596     {
1597       defaultFontId = mFontClient.GetFontId( EMPTY_STRING,
1598                                              EMPTY_STRING );
1599     }
1600     else
1601     {
1602       defaultFontId = mFontDefaults->GetFontId( mFontClient );
1603     }
1604
1605     Text::FontMetrics fontMetrics;
1606     mFontClient.GetFontMetrics( defaultFontId, fontMetrics );
1607
1608     lineHeight = fontMetrics.ascender - fontMetrics.descender;
1609
1610
1611     Vector2 cursorPosition;
1612
1613     switch( mLayoutEngine.GetHorizontalAlignment() )
1614     {
1615       case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
1616       {
1617         cursorPosition.x = 1.f;
1618         break;
1619       }
1620       case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
1621       {
1622         cursorPosition.x = floor( 0.5f * mVisualModel->mControlSize.width );
1623         break;
1624       }
1625       case LayoutEngine::HORIZONTAL_ALIGN_END:
1626       {
1627         cursorPosition.x = mVisualModel->mControlSize.width;
1628         break;
1629       }
1630     }
1631
1632     switch( mLayoutEngine.GetVerticalAlignment() )
1633     {
1634       case LayoutEngine::VERTICAL_ALIGN_TOP:
1635       {
1636         cursorPosition.y = 0.f;
1637         break;
1638       }
1639       case LayoutEngine::VERTICAL_ALIGN_CENTER:
1640       {
1641         cursorPosition.y = floorf( 0.5f * ( mVisualModel->mControlSize.height - lineHeight ) );
1642         break;
1643       }
1644       case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
1645       {
1646         cursorPosition.y = mVisualModel->mControlSize.height - lineHeight;
1647         break;
1648       }
1649     }
1650
1651     mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1652                                          cursorPosition.x,
1653                                          cursorPosition.y,
1654                                          lineHeight,
1655                                          lineHeight );
1656   }
1657   else
1658   {
1659     CursorInfo cursorInfo;
1660     GetCursorPosition( mEventData->mPrimaryCursorPosition,
1661                        cursorInfo );
1662
1663     const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1664     const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1665
1666     // Sets the cursor position.
1667     mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1668                                          cursorPosition.x,
1669                                          cursorPosition.y,
1670                                          cursorInfo.primaryCursorHeight,
1671                                          cursorInfo.lineHeight );
1672     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
1673
1674     // Sets the grab handle position.
1675     mEventData->mDecorator->SetPosition( GRAB_HANDLE,
1676                                          cursorPosition.x,
1677                                          cursorPosition.y,
1678                                          cursorInfo.lineHeight );
1679
1680     if( cursorInfo.isSecondaryCursor )
1681     {
1682       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
1683       mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
1684                                            cursorInfo.secondaryPosition.x + offset.x,
1685                                            cursorInfo.secondaryPosition.y + offset.y,
1686                                            cursorInfo.secondaryCursorHeight,
1687                                            cursorInfo.lineHeight );
1688       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + offset.x, cursorInfo.secondaryPosition.y + offset.y );
1689     }
1690     else
1691     {
1692       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1693     }
1694   }
1695   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
1696 }
1697
1698 void Controller::Impl::UpdateSelectionHandle( HandleType handleType )
1699 {
1700   if( ( LEFT_SELECTION_HANDLE != handleType ) &&
1701       ( RIGHT_SELECTION_HANDLE != handleType ) )
1702   {
1703     return;
1704   }
1705
1706   const bool leftSelectionHandle = LEFT_SELECTION_HANDLE == handleType;
1707   const CharacterIndex index = leftSelectionHandle ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1708
1709   CursorInfo cursorInfo;
1710   GetCursorPosition( index,
1711                      cursorInfo );
1712
1713   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1714   const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1715
1716   // Sets the grab handle position.
1717   mEventData->mDecorator->SetPosition( handleType,
1718                                        cursorPosition.x,
1719                                        cursorPosition.y,
1720                                        cursorInfo.lineHeight );
1721
1722   // If selection handle at start of the text and other at end of the text then all text is selected.
1723   const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
1724   const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
1725   mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mLogicalModel->mText.Count() );
1726 }
1727
1728 void Controller::Impl::ClampHorizontalScroll( const Vector2& actualSize )
1729 {
1730   // Clamp between -space & 0 (and the text alignment).
1731   if( actualSize.width > mVisualModel->mControlSize.width )
1732   {
1733     const float space = ( actualSize.width - mVisualModel->mControlSize.width ) + mAlignmentOffset.x;
1734     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x < -space ) ? -space : mEventData->mScrollPosition.x;
1735     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x > -mAlignmentOffset.x ) ? -mAlignmentOffset.x : mEventData->mScrollPosition.x;
1736
1737     mEventData->mDecoratorUpdated = true;
1738   }
1739   else
1740   {
1741     mEventData->mScrollPosition.x = 0.f;
1742   }
1743 }
1744
1745 void Controller::Impl::ClampVerticalScroll( const Vector2& actualSize )
1746 {
1747   // Clamp between -space & 0 (and the text alignment).
1748   if( actualSize.height > mVisualModel->mControlSize.height )
1749   {
1750     const float space = ( actualSize.height - mVisualModel->mControlSize.height ) + mAlignmentOffset.y;
1751     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y < -space ) ? -space : mEventData->mScrollPosition.y;
1752     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y > -mAlignmentOffset.y ) ? -mAlignmentOffset.y : mEventData->mScrollPosition.y;
1753
1754     mEventData->mDecoratorUpdated = true;
1755   }
1756   else
1757   {
1758     mEventData->mScrollPosition.y = 0.f;
1759   }
1760 }
1761
1762 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position )
1763 {
1764   Vector2 offset;
1765   bool updateDecorator = false;
1766   if( position.x < 0.f )
1767   {
1768     offset.x = -position.x;
1769     mEventData->mScrollPosition.x += offset.x;
1770     updateDecorator = true;
1771   }
1772   else if( position.x > mVisualModel->mControlSize.width )
1773   {
1774     offset.x = mVisualModel->mControlSize.width - position.x;
1775     mEventData->mScrollPosition.x += offset.x;
1776     updateDecorator = true;
1777   }
1778
1779   if( updateDecorator && mEventData->mDecorator )
1780   {
1781     mEventData->mDecorator->UpdatePositions( offset );
1782   }
1783
1784   // TODO : calculate the vertical scroll.
1785 }
1786
1787 void Controller::Impl::ScrollTextToMatchCursor()
1788 {
1789   // Get the current cursor position in decorator coords.
1790   const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
1791
1792   // Calculate the new cursor position.
1793   CursorInfo cursorInfo;
1794   GetCursorPosition( mEventData->mPrimaryCursorPosition,
1795                      cursorInfo );
1796
1797   // Calculate the offset to match the cursor position before the character was deleted.
1798   mEventData->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x - mAlignmentOffset.x;
1799
1800   ClampHorizontalScroll( mVisualModel->GetActualSize() );
1801
1802   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1803   const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1804
1805   // Sets the cursor position.
1806   mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1807                                        cursorPosition.x,
1808                                        cursorPosition.y,
1809                                        cursorInfo.primaryCursorHeight,
1810                                        cursorInfo.lineHeight );
1811
1812   // Sets the grab handle position.
1813   mEventData->mDecorator->SetPosition( GRAB_HANDLE,
1814                                        cursorPosition.x,
1815                                        cursorPosition.y,
1816                                        cursorInfo.lineHeight );
1817
1818   if( cursorInfo.isSecondaryCursor )
1819   {
1820     mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
1821                                          cursorInfo.secondaryPosition.x + offset.x,
1822                                          cursorInfo.secondaryPosition.y + offset.y,
1823                                          cursorInfo.secondaryCursorHeight,
1824                                          cursorInfo.lineHeight );
1825     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + offset.x, cursorInfo.secondaryPosition.y + offset.y );
1826   }
1827
1828   // Set which cursors are active according the state.
1829   if( ( EventData::EDITING == mEventData->mState ) ||
1830       ( EventData::EDITING_WITH_POPUP == mEventData->mState ) ||
1831       ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
1832   {
1833     if( cursorInfo.isSecondaryCursor )
1834     {
1835       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
1836     }
1837     else
1838     {
1839       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1840     }
1841   }
1842   else
1843   {
1844     mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1845   }
1846 }
1847
1848 void Controller::Impl::RequestRelayout()
1849 {
1850   mControlInterface.RequestTextRelayout();
1851 }
1852
1853 } // namespace Text
1854
1855 } // namespace Toolkit
1856
1857 } // namespace Dali