Fix when typing text and the grab handle is visible/pressed.
[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]
85  * @param[in]
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->mLeftSelectionPosition != handlePosition ) && ( 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->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
685         mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateRightSelectionPosition;
686
687         if( mEventData->mUpdateRightSelectionPosition )
688         {
689           mEventData->mRightSelectionPosition = handlePosition;
690           RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
691                                       mEventData->mRightSelectionPosition );
692         }
693       }
694     }
695
696     mEventData->mDecoratorUpdated = true;
697   } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
698   else if( HANDLE_SCROLLING == state )
699   {
700     const float xSpeed = event.p2.mFloat;
701     const Vector2& actualSize = mVisualModel->GetActualSize();
702     const Vector2 currentScrollPosition = mEventData->mScrollPosition;
703
704     mEventData->mScrollPosition.x += xSpeed;
705
706     ClampHorizontalScroll( actualSize );
707
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     }
714     else
715     {
716       const bool scrollRightDirection = xSpeed > 0.f;
717       const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
718       const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
719
720       if( Event::GRAB_HANDLE_EVENT == event.type )
721       {
722         ChangeState( EventData::GRAB_HANDLE_PANNING );
723
724         Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
725
726         // Position the grag handle close to either the left or right edge.
727         position.x = scrollRightDirection ? 0.f : mControlSize.width;
728
729         // Get the new handle position.
730         // The grab handle's position is in decorator coords. Need to transforms to text coords.
731         const CharacterIndex handlePosition = GetClosestCursorIndex( position.x - mEventData->mScrollPosition.x - mAlignmentOffset.x,
732                                                                      position.y - mEventData->mScrollPosition.y - mAlignmentOffset.y );
733
734         mEventData->mUpdateCursorPosition = mEventData->mPrimaryCursorPosition != handlePosition;
735         mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateCursorPosition;
736         mEventData->mPrimaryCursorPosition = handlePosition;
737       }
738       else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
739       {
740         // TODO: This is recalculating the selection box every time the text is scrolled with the selection handles.
741         //       Think if something can be done to save power.
742
743         ChangeState( EventData::SELECTION_HANDLE_PANNING );
744
745         Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
746
747         // Position the selection handle close to either the left or right edge.
748         position.x = scrollRightDirection ? 0.f : mControlSize.width;
749
750         // Get the new handle position.
751         // The selection handle's position is in decorator coords. Need to transforms to text coords.
752         const CharacterIndex handlePosition = GetClosestCursorIndex( position.x - mEventData->mScrollPosition.x - mAlignmentOffset.x,
753                                                                      position.y - mEventData->mScrollPosition.y - mAlignmentOffset.y );
754
755         if( leftSelectionHandleEvent )
756         {
757           mEventData->mUpdateLeftSelectionPosition = handlePosition != mEventData->mLeftSelectionPosition;
758           mEventData->mLeftSelectionPosition = handlePosition;
759         }
760         else
761         {
762           mEventData->mUpdateRightSelectionPosition = handlePosition != mEventData->mRightSelectionPosition;
763           mEventData->mRightSelectionPosition = handlePosition;
764         }
765
766         if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
767         {
768           RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
769                                       mEventData->mRightSelectionPosition );
770
771           mEventData->mScrollAfterUpdatePosition = true;
772         }
773       }
774       mEventData->mDecoratorUpdated = true;
775     }
776   } // end ( HANDLE_SCROLLING == state )
777 }
778
779 void Controller::Impl::OnSelectEvent( const Event& event )
780 {
781   if( NULL == mEventData )
782   {
783     // Nothing to do if there is no text.
784     return;
785   }
786
787   if( mEventData->mSelectionEnabled )
788   {
789     // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
790     const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
791     const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
792
793     const CharacterIndex leftPosition = mEventData->mLeftSelectionPosition;
794     const CharacterIndex rightPosition = mEventData->mRightSelectionPosition;
795
796     RepositionSelectionHandles( xPosition,
797                                 yPosition );
798
799     mEventData->mUpdateLeftSelectionPosition = leftPosition != mEventData->mLeftSelectionPosition;
800     mEventData->mUpdateRightSelectionPosition = rightPosition != mEventData->mRightSelectionPosition;
801
802     mEventData->mScrollAfterUpdatePosition = ( ( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition ) &&
803                                                ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition ) );
804   }
805 }
806
807 void Controller::Impl::OnSelectAllEvent()
808 {
809   if( NULL == mEventData )
810   {
811     // Nothing to do if there is no text.
812     return;
813   }
814
815   if( mEventData->mSelectionEnabled )
816   {
817     RepositionSelectionHandles( 0u,
818                                 mLogicalModel->mText.Count() );
819
820     mEventData->mScrollAfterUpdatePosition = true;
821     mEventData->mUpdateLeftSelectionPosition = true;
822     mEventData->mUpdateRightSelectionPosition = true;
823   }
824 }
825
826 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetreival )
827 {
828   if( mEventData->mLeftSelectionPosition ==  mEventData->mRightSelectionPosition )
829   {
830     // Nothing to select if handles are in the same place.
831     selectedText="";
832     return;
833   }
834
835   //Get start and end position of selection
836   uint32_t startOfSelectedText = mEventData->mLeftSelectionPosition;
837   uint32_t lengthOfSelectedText =  mEventData->mRightSelectionPosition - startOfSelectedText;
838
839   // Validate the start and end selection points
840   if( ( startOfSelectedText >= 0 ) && (  ( startOfSelectedText + lengthOfSelectedText ) <=  mLogicalModel->mText.Count() ) )
841   {
842     //Get text as a UTF8 string
843     Vector<Character>& utf32Characters = mLogicalModel->mText;
844
845     Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
846
847     if ( deleteAfterRetreival  ) // Only delete text if copied successfully
848     {
849       // Delete text between handles
850       Vector<Character>& currentText = mLogicalModel->mText;
851
852       Vector<Character>::Iterator first = currentText.Begin() + startOfSelectedText;
853       Vector<Character>::Iterator last  = first + lengthOfSelectedText;
854       currentText.Erase( first, last );
855     }
856     mEventData->mPrimaryCursorPosition = mEventData->mLeftSelectionPosition;
857     mEventData->mScrollAfterDelete = true;
858     mEventData->mDecoratorUpdated = true;
859   }
860 }
861
862 void Controller::Impl::ShowClipboard()
863 {
864   if ( mClipboard )
865   {
866     mClipboard.ShowClipboard();
867   }
868 }
869
870 void Controller::Impl::HideClipboard()
871 {
872   if ( mClipboard )
873   {
874     mClipboard.HideClipboard();
875   }
876 }
877
878 bool Controller::Impl::CopyStringToClipboard( std::string& source )
879 {
880   //Send string to clipboard
881   return ( mClipboard && mClipboard.SetItem( source ) );
882 }
883
884 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
885 {
886   std::string selectedText;
887   RetrieveSelection( selectedText, deleteAfterSending );
888   CopyStringToClipboard( selectedText );
889   ChangeState( EventData::EDITING );
890 }
891
892 void Controller::Impl::GetTextFromClipboard( unsigned int itemIndex, std::string& retreivedString )
893 {
894   if ( mClipboard )
895   {
896     retreivedString =  mClipboard.GetItem( itemIndex );
897   }
898 }
899
900 void Controller::Impl::RepositionSelectionHandles( CharacterIndex selectionStart, CharacterIndex selectionEnd )
901 {
902   if( selectionStart == selectionEnd )
903   {
904     // Nothing to select if handles are in the same place.
905     return;
906   }
907
908   mEventData->mDecorator->ClearHighlights();
909
910   mEventData->mLeftSelectionPosition = selectionStart;
911   mEventData->mRightSelectionPosition = selectionEnd;
912
913   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
914   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
915   const GlyphInfo* const glyphsBuffer = mVisualModel->mGlyphs.Begin();
916   const Vector2* const positionsBuffer = mVisualModel->mGlyphPositions.Begin();
917
918   // TODO: Better algorithm to create the highlight box.
919   // TODO: Multi-line.
920
921   const Vector<LineRun>& lines = mVisualModel->mLines;
922   const LineRun& firstLine = *lines.Begin();
923   const float height = firstLine.ascender + -firstLine.descender;
924
925   const bool indicesSwapped = ( selectionStart > selectionEnd );
926   if( indicesSwapped )
927   {
928     std::swap( selectionStart, selectionEnd );
929   }
930
931   GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
932   GlyphIndex glyphEnd = *( charactersToGlyphBuffer + ( selectionEnd - 1u ) ) + *( glyphsPerCharacterBuffer + ( selectionEnd - 1u ) ) - 1u;
933
934   mEventData->mDecorator->SwapSelectionHandlesEnabled( firstLine.direction != indicesSwapped );
935
936   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
937
938   for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
939   {
940     const GlyphInfo& glyph = *( glyphsBuffer + index );
941     const Vector2& position = *( positionsBuffer + index );
942
943     const float xPosition = position.x - glyph.xBearing + offset.x;
944     mEventData->mDecorator->AddHighlight( xPosition, offset.y, xPosition + glyph.advance, height );
945   }
946
947   CursorInfo primaryCursorInfo;
948   GetCursorPosition( mEventData->mLeftSelectionPosition,
949                      primaryCursorInfo );
950
951   CursorInfo secondaryCursorInfo;
952   GetCursorPosition( mEventData->mRightSelectionPosition,
953                      secondaryCursorInfo );
954
955   const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + offset;
956   const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + offset;
957
958   mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE, primaryPosition.x, primaryPosition.y, primaryCursorInfo.lineHeight );
959
960   mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE, secondaryPosition.x, secondaryPosition.y, secondaryCursorInfo.lineHeight );
961
962   // Set the flag to update the decorator.
963   mEventData->mDecoratorUpdated = true;
964 }
965
966 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
967 {
968   if( NULL == mEventData )
969   {
970     // Nothing to do if there is no text input.
971     return;
972   }
973
974   if( IsShowingPlaceholderText() )
975   {
976     // Nothing to do if there is the place-holder text.
977     return;
978   }
979
980   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
981   const Length numberOfLines  = mVisualModel->mLines.Count();
982   if( 0 == numberOfGlyphs ||
983       0 == numberOfLines )
984   {
985     // Nothing to do if there is no text.
986     return;
987   }
988
989   // Find which word was selected
990   CharacterIndex selectionStart( 0 );
991   CharacterIndex selectionEnd( 0 );
992   FindSelectionIndices( visualX, visualY, selectionStart, selectionEnd );
993   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
994
995   if( selectionStart == selectionEnd )
996   {
997     ChangeState( EventData::EDITING );
998     // Nothing to select. i.e. a white space, out of bounds
999     return;
1000   }
1001
1002   RepositionSelectionHandles( selectionStart, selectionEnd );
1003 }
1004
1005 void Controller::Impl::SetPopupButtons()
1006 {
1007   /**
1008    *  Sets the Popup buttons to be shown depending on State.
1009    *
1010    *  If SELECTING :  CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
1011    *
1012    *  If EDITING_WITH_POPUP : SELECT & SELECT_ALL
1013    */
1014
1015   TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
1016
1017   if ( ( EventData::SELECTING == mEventData->mState ) || ( EventData::SELECTION_CHANGED == mEventData->mState ) )
1018   {
1019     buttonsToShow = TextSelectionPopup::Buttons(  TextSelectionPopup::CUT | TextSelectionPopup::COPY );
1020
1021     if ( !IsClipboardEmpty() )
1022     {
1023       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1024       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1025     }
1026
1027     if ( !mEventData->mAllTextSelected )
1028     {
1029       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
1030     }
1031   }
1032   else if  ( EventData::EDITING_WITH_POPUP == mEventData->mState )
1033   {
1034     if ( mLogicalModel->mText.Count() && !IsShowingPlaceholderText())
1035     {
1036       buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
1037     }
1038
1039     if ( !IsClipboardEmpty() )
1040     {
1041       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1042       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1043     }
1044   }
1045
1046   mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
1047 }
1048
1049 void Controller::Impl::ChangeState( EventData::State newState )
1050 {
1051   if( NULL == mEventData )
1052   {
1053     // Nothing to do if there is no text input.
1054     return;
1055   }
1056
1057   if( mEventData->mState != newState )
1058   {
1059     mEventData->mState = newState;
1060
1061     if( EventData::INACTIVE == mEventData->mState )
1062     {
1063       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1064       mEventData->mDecorator->StopCursorBlink();
1065       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1066       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1067       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1068       mEventData->mDecorator->SetPopupActive( false );
1069       mEventData->mDecoratorUpdated = true;
1070       HideClipboard();
1071     }
1072     else if ( EventData::INTERRUPTED  == mEventData->mState)
1073     {
1074       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1075       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1076       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1077       mEventData->mDecorator->SetPopupActive( false );
1078       mEventData->mDecoratorUpdated = true;
1079       HideClipboard();
1080     }
1081     else if ( EventData::SELECTING == mEventData->mState )
1082     {
1083       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1084       mEventData->mDecorator->StopCursorBlink();
1085       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1086       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
1087       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
1088       if( mEventData->mGrabHandlePopupEnabled )
1089       {
1090         SetPopupButtons();
1091         mEventData->mDecorator->SetPopupActive( true );
1092       }
1093       mEventData->mDecoratorUpdated = true;
1094     }
1095     else if ( EventData::SELECTION_CHANGED  == mEventData->mState )
1096     {
1097       if( mEventData->mGrabHandlePopupEnabled )
1098       {
1099         SetPopupButtons();
1100         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1101         mEventData->mDecorator->SetPopupActive( true );
1102       }
1103       mEventData->mDecoratorUpdated = true;
1104     }
1105     else if( EventData::EDITING == mEventData->mState )
1106     {
1107       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1108       if( mEventData->mCursorBlinkEnabled )
1109       {
1110         mEventData->mDecorator->StartCursorBlink();
1111       }
1112       // Grab handle is not shown until a tap is received whilst EDITING
1113       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1114       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1115       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1116       if( mEventData->mGrabHandlePopupEnabled )
1117       {
1118         mEventData->mDecorator->SetPopupActive( false );
1119       }
1120       mEventData->mDecoratorUpdated = true;
1121       HideClipboard();
1122     }
1123     else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
1124     {
1125       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1126       if( mEventData->mCursorBlinkEnabled )
1127       {
1128         mEventData->mDecorator->StartCursorBlink();
1129       }
1130       if( mEventData->mSelectionEnabled )
1131       {
1132         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1133         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1134       }
1135       else
1136       {
1137         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1138       }
1139       if( mEventData->mGrabHandlePopupEnabled )
1140       {
1141         SetPopupButtons();
1142         mEventData->mDecorator->SetPopupActive( true );
1143       }
1144       HideClipboard();
1145       mEventData->mDecoratorUpdated = true;
1146     }
1147     else if( EventData::EDITING_WITH_GRAB_HANDLE == mEventData->mState )
1148     {
1149       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1150       if( mEventData->mCursorBlinkEnabled )
1151       {
1152         mEventData->mDecorator->StartCursorBlink();
1153       }
1154       // Grab handle is not shown until a tap is received whilst EDITING
1155       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1156       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1157       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1158       if( mEventData->mGrabHandlePopupEnabled )
1159       {
1160         mEventData->mDecorator->SetPopupActive( false );
1161       }
1162       mEventData->mDecoratorUpdated = true;
1163       HideClipboard();
1164     }
1165     else if ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState )
1166     {
1167       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1168       mEventData->mDecorator->StopCursorBlink();
1169       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1170       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
1171       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
1172       if( mEventData->mGrabHandlePopupEnabled )
1173       {
1174         mEventData->mDecorator->SetPopupActive( false );
1175       }
1176       mEventData->mDecoratorUpdated = true;
1177     }
1178     else if ( EventData::GRAB_HANDLE_PANNING == mEventData->mState )
1179     {
1180       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1181       if( mEventData->mCursorBlinkEnabled )
1182       {
1183         mEventData->mDecorator->StartCursorBlink();
1184       }
1185       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1186       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1187       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1188       if( mEventData->mGrabHandlePopupEnabled )
1189       {
1190         mEventData->mDecorator->SetPopupActive( false );
1191       }
1192       mEventData->mDecoratorUpdated = true;
1193     }
1194   }
1195 }
1196
1197 LineIndex Controller::Impl::GetClosestLine( float y ) const
1198 {
1199   float totalHeight = 0.f;
1200   LineIndex lineIndex = 0u;
1201
1202   const Vector<LineRun>& lines = mVisualModel->mLines;
1203   for( LineIndex endLine = lines.Count();
1204        lineIndex < endLine;
1205        ++lineIndex )
1206   {
1207     const LineRun& lineRun = lines[lineIndex];
1208     totalHeight += lineRun.ascender + -lineRun.descender;
1209     if( y < totalHeight )
1210     {
1211       return lineIndex;
1212     }
1213   }
1214
1215   if( lineIndex == 0 )
1216   {
1217     return 0;
1218   }
1219
1220   return lineIndex-1;
1221 }
1222
1223 void Controller::Impl::FindSelectionIndices( float visualX, float visualY, CharacterIndex& startIndex, CharacterIndex& endIndex )
1224 {
1225   CharacterIndex hitCharacter = GetClosestCursorIndex( visualX, visualY );
1226   if( hitCharacter >= mLogicalModel->mText.Count() )
1227   {
1228     // Selection out of bounds.
1229     return;
1230   }
1231
1232   startIndex = hitCharacter;
1233   endIndex = hitCharacter;
1234
1235   if( !TextAbstraction::IsWhiteSpace( mLogicalModel->mText[hitCharacter] ) )
1236   {
1237     // Find the start and end of the text
1238     for( startIndex = hitCharacter; startIndex > 0; --startIndex )
1239     {
1240       Character charCode = mLogicalModel->mText[ startIndex-1 ];
1241       if( TextAbstraction::IsWhiteSpace( charCode ) )
1242       {
1243         break;
1244       }
1245     }
1246     const CharacterIndex pastTheEnd = mLogicalModel->mText.Count();
1247     for( endIndex = hitCharacter + 1u; endIndex < pastTheEnd; ++endIndex )
1248     {
1249       Character charCode = mLogicalModel->mText[ endIndex ];
1250       if( TextAbstraction::IsWhiteSpace( charCode ) )
1251       {
1252         break;
1253       }
1254     }
1255   }
1256 }
1257
1258 CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
1259                                                         float visualY )
1260 {
1261   if( NULL == mEventData )
1262   {
1263     // Nothing to do if there is no text input.
1264     return 0u;
1265   }
1266
1267   CharacterIndex logicalIndex = 0u;
1268
1269   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
1270   const Length numberOfLines  = mVisualModel->mLines.Count();
1271   if( 0 == numberOfGlyphs ||
1272       0 == numberOfLines )
1273   {
1274     return logicalIndex;
1275   }
1276
1277   // Find which line is closest
1278   const LineIndex lineIndex = GetClosestLine( visualY );
1279   const LineRun& line = mVisualModel->mLines[lineIndex];
1280
1281   // Get the positions of the glyphs.
1282   const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
1283   const Vector2* const positionsBuffer = positions.Begin();
1284
1285   // Get the visual to logical conversion tables.
1286   const CharacterIndex* const visualToLogicalBuffer = ( 0u != mLogicalModel->mVisualToLogicalMap.Count() ) ? mLogicalModel->mVisualToLogicalMap.Begin() : NULL;
1287   const CharacterIndex* const visualToLogicalCursorBuffer = mLogicalModel->mVisualToLogicalCursorMap.Begin();
1288
1289   // Get the character to glyph conversion table.
1290   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1291
1292   // Get the glyphs per character table.
1293   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
1294   const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1295
1296   // If the vector is void, there is no right to left characters.
1297   const bool hasRightToLeftCharacters = NULL != visualToLogicalBuffer;
1298
1299   const CharacterIndex startCharacter = line.characterRun.characterIndex;
1300   const CharacterIndex endCharacter   = line.characterRun.characterIndex + line.characterRun.numberOfCharacters;
1301   DALI_ASSERT_DEBUG( endCharacter <= mLogicalModel->mText.Count() && "Invalid line info" );
1302
1303   // Whether there is a hit on a glyph.
1304   bool matched = false;
1305
1306   // Traverses glyphs in visual order. To do that use the visual to logical conversion table.
1307   CharacterIndex visualIndex = startCharacter;
1308   for( ; !matched && ( visualIndex < endCharacter ); ++visualIndex )
1309   {
1310     // The character in logical order.
1311     const CharacterIndex characterLogicalOrderIndex = hasRightToLeftCharacters ? *( visualToLogicalBuffer + visualIndex ) : visualIndex;
1312
1313     // Get the script of the character.
1314     const Script script = mLogicalModel->GetScript( characterLogicalOrderIndex );
1315
1316     // The first glyph for that character in logical order.
1317     const GlyphIndex glyphLogicalOrderIndex = *( charactersToGlyphBuffer + characterLogicalOrderIndex );
1318     // The number of glyphs for that character
1319     const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + characterLogicalOrderIndex );
1320
1321     // Get the metrics for the group of glyphs.
1322     GlyphMetrics glyphMetrics;
1323     GetGlyphsMetrics( glyphLogicalOrderIndex,
1324                       numberOfGlyphs,
1325                       glyphMetrics,
1326                       mVisualModel,
1327                       mFontClient );
1328
1329     const Vector2& position = *( positionsBuffer + glyphLogicalOrderIndex );
1330
1331     // Prevents to jump the whole Latin ligatures like fi, ff, ...
1332     const Length numberOfCharactersInLigature = ( TextAbstraction::LATIN == script ) ? *( charactersPerGlyphBuffer + glyphLogicalOrderIndex ) : 1u;
1333     const float glyphAdvance = glyphMetrics.advance / static_cast<float>( numberOfCharactersInLigature );
1334
1335     for( GlyphIndex index = 0u; !matched && ( index < numberOfCharactersInLigature ); ++index )
1336     {
1337       // Find the mid-point of the area containing the glyph
1338       const float glyphCenter = -glyphMetrics.xBearing + position.x + ( static_cast<float>( index ) + 0.5f ) * glyphAdvance;
1339
1340       if( visualX < glyphCenter )
1341       {
1342         visualIndex += index;
1343         matched = true;
1344         break;
1345       }
1346     }
1347
1348     if( matched )
1349     {
1350       break;
1351     }
1352   }
1353
1354   // Return the logical position of the cursor in characters.
1355
1356   if( !matched )
1357   {
1358     visualIndex = endCharacter;
1359   }
1360
1361   logicalIndex = hasRightToLeftCharacters ? *( visualToLogicalCursorBuffer + visualIndex ) : visualIndex;
1362   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p closest visualIndex %d logicalIndex %d\n", this, visualIndex, logicalIndex );
1363
1364   return logicalIndex;
1365 }
1366
1367 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
1368                                           CursorInfo& cursorInfo )
1369 {
1370   // TODO: Check for multiline with \n, etc...
1371
1372   // Check if the logical position is the first or the last one of the text.
1373   const bool isFirstPosition = 0u == logical;
1374   const bool isLastPosition = mLogicalModel->mText.Count() == logical;
1375
1376   if( isFirstPosition && isLastPosition )
1377   {
1378     // There is zero characters. Get the default font's line height.
1379     cursorInfo.lineHeight = GetDefaultFontLineHeight();
1380     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
1381
1382     cursorInfo.primaryPosition.x = 1.f;
1383     cursorInfo.primaryPosition.y = 0.f;
1384
1385     // Nothing else to do.
1386     return;
1387   }
1388
1389   // Get the previous logical index.
1390   const CharacterIndex previousLogical = isFirstPosition ? 0u : logical - 1u;
1391
1392   // Decrease the logical index if it's the last one.
1393   if( isLastPosition )
1394   {
1395     --logical;
1396   }
1397
1398   // Get the direction of the character and the previous one.
1399   const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
1400
1401   CharacterDirection isCurrentRightToLeft = false;
1402   CharacterDirection isPreviousRightToLeft = false;
1403   if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1404   {
1405     isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + logical );
1406     isPreviousRightToLeft = *( modelCharacterDirectionsBuffer + previousLogical );
1407   }
1408
1409   // Get the line where the character is laid-out.
1410   const LineRun* modelLines = mVisualModel->mLines.Begin();
1411
1412   const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( logical );
1413   const LineRun& line = *( modelLines + lineIndex );
1414
1415   // Get the paragraph's direction.
1416   const CharacterDirection isRightToLeftParagraph = line.direction;
1417
1418   // Check whether there is an alternative position:
1419
1420   cursorInfo.isSecondaryCursor = ( isCurrentRightToLeft != isPreviousRightToLeft ) ||
1421     ( isLastPosition && ( isRightToLeftParagraph != isCurrentRightToLeft ) );
1422
1423   // Set the line height.
1424   cursorInfo.lineHeight = line.ascender + -line.descender;
1425
1426   // Convert the cursor position into the glyph position.
1427   CharacterIndex characterIndex = logical;
1428   if( cursorInfo.isSecondaryCursor &&
1429       ( isRightToLeftParagraph != isCurrentRightToLeft ) )
1430   {
1431     characterIndex = previousLogical;
1432   }
1433
1434   const GlyphIndex currentGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
1435   const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
1436   const Length numberOfCharacters = *( mVisualModel->mCharactersPerGlyph.Begin() +currentGlyphIndex );
1437
1438   // Get the metrics for the group of glyphs.
1439   GlyphMetrics glyphMetrics;
1440   GetGlyphsMetrics( currentGlyphIndex,
1441                     numberOfGlyphs,
1442                     glyphMetrics,
1443                     mVisualModel,
1444                     mFontClient );
1445
1446   float interGlyphAdvance = 0.f;
1447   if( !isLastPosition &&
1448       ( numberOfCharacters > 1u ) )
1449   {
1450     const CharacterIndex firstIndex = *( mVisualModel->mGlyphsToCharacters.Begin() + currentGlyphIndex );
1451     interGlyphAdvance = static_cast<float>( characterIndex - firstIndex ) * glyphMetrics.advance / static_cast<float>( numberOfCharacters );
1452   }
1453
1454   // Get the glyph position and x bearing.
1455   const Vector2& currentPosition = *( mVisualModel->mGlyphPositions.Begin() + currentGlyphIndex );
1456
1457   // Set the cursor's height.
1458   cursorInfo.primaryCursorHeight = glyphMetrics.fontHeight;
1459
1460   // Set the position.
1461   cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + currentPosition.x + ( isCurrentRightToLeft ? glyphMetrics.advance : interGlyphAdvance );
1462   cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
1463
1464   if( isLastPosition )
1465   {
1466     // The position of the cursor after the last character needs special
1467     // care depending on its direction and the direction of the paragraph.
1468
1469     if( cursorInfo.isSecondaryCursor )
1470     {
1471       // Need to find the first character after the last character with the paragraph's direction.
1472       // i.e l0 l1 l2 r0 r1 should find r0.
1473
1474       // TODO: check for more than one line!
1475       characterIndex = isRightToLeftParagraph ? line.characterRun.characterIndex : line.characterRun.characterIndex + line.characterRun.numberOfCharacters - 1u;
1476       characterIndex = mLogicalModel->GetLogicalCharacterIndex( characterIndex );
1477
1478       const GlyphIndex glyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
1479       const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
1480
1481       const Vector2& position = *( mVisualModel->mGlyphPositions.Begin() + glyphIndex );
1482
1483       // Get the metrics for the group of glyphs.
1484       GlyphMetrics glyphMetrics;
1485       GetGlyphsMetrics( glyphIndex,
1486                         numberOfGlyphs,
1487                         glyphMetrics,
1488                         mVisualModel,
1489                         mFontClient );
1490
1491       cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + position.x + ( isRightToLeftParagraph ? 0.f : glyphMetrics.advance );
1492
1493       cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
1494     }
1495     else
1496     {
1497       if( !isCurrentRightToLeft )
1498       {
1499         cursorInfo.primaryPosition.x += glyphMetrics.advance;
1500       }
1501       else
1502       {
1503         cursorInfo.primaryPosition.x -= glyphMetrics.advance;
1504       }
1505     }
1506   }
1507
1508   // Set the alternative cursor position.
1509   if( cursorInfo.isSecondaryCursor )
1510   {
1511     // Convert the cursor position into the glyph position.
1512     const CharacterIndex previousCharacterIndex = ( ( isRightToLeftParagraph != isCurrentRightToLeft ) ? logical : previousLogical );
1513     const GlyphIndex previousGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + previousCharacterIndex );
1514     const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + previousCharacterIndex );
1515
1516     // Get the glyph position.
1517     const Vector2& previousPosition = *( mVisualModel->mGlyphPositions.Begin() + previousGlyphIndex );
1518
1519     // Get the metrics for the group of glyphs.
1520     GlyphMetrics glyphMetrics;
1521     GetGlyphsMetrics( previousGlyphIndex,
1522                       numberOfGlyphs,
1523                       glyphMetrics,
1524                       mVisualModel,
1525                       mFontClient );
1526
1527     // Set the cursor position and height.
1528     cursorInfo.secondaryPosition.x = -glyphMetrics.xBearing + previousPosition.x + ( ( ( isLastPosition && !isCurrentRightToLeft ) ||
1529                                                                                        ( !isLastPosition && isCurrentRightToLeft )    ) ? glyphMetrics.advance : 0.f );
1530
1531     cursorInfo.secondaryCursorHeight = 0.5f * glyphMetrics.fontHeight;
1532
1533     cursorInfo.secondaryPosition.y = cursorInfo.lineHeight - cursorInfo.secondaryCursorHeight - line.descender - ( glyphMetrics.fontHeight - glyphMetrics.ascender );
1534
1535     // Update the primary cursor height as well.
1536     cursorInfo.primaryCursorHeight *= 0.5f;
1537   }
1538 }
1539
1540 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
1541 {
1542   if( NULL == mEventData )
1543   {
1544     // Nothing to do if there is no text input.
1545     return 0u;
1546   }
1547
1548   CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
1549
1550   const Script script = mLogicalModel->GetScript( index );
1551   const GlyphIndex* charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1552   const Length* charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1553
1554   Length numberOfCharacters = 0u;
1555   if( TextAbstraction::LATIN == script )
1556   {
1557     // Prevents to jump the whole Latin ligatures like fi, ff, ...
1558     numberOfCharacters = 1u;
1559   }
1560   else
1561   {
1562     GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
1563     numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1564
1565     while( 0u == numberOfCharacters )
1566     {
1567       numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1568       ++glyphIndex;
1569     }
1570   }
1571
1572   if( index < mEventData->mPrimaryCursorPosition )
1573   {
1574     cursorIndex -= numberOfCharacters;
1575   }
1576   else
1577   {
1578     cursorIndex += numberOfCharacters;
1579   }
1580
1581   return cursorIndex;
1582 }
1583
1584 void Controller::Impl::UpdateCursorPosition()
1585 {
1586   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
1587   if( NULL == mEventData )
1588   {
1589     // Nothing to do if there is no text input.
1590     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
1591     return;
1592   }
1593
1594   if( IsShowingPlaceholderText() )
1595   {
1596     // Do not want to use the place-holder text to set the cursor position.
1597
1598     // Use the line's height of the font's family set to set the cursor's size.
1599     // If there is no font's family set, use the default font.
1600     // Use the current alignment to place the cursor at the beginning, center or end of the box.
1601
1602     float lineHeight = 0.f;
1603
1604     FontId defaultFontId = 0u;
1605     if( NULL == mFontDefaults )
1606     {
1607       defaultFontId = mFontClient.GetFontId( EMPTY_STRING,
1608                                              EMPTY_STRING );
1609     }
1610     else
1611     {
1612       defaultFontId = mFontDefaults->GetFontId( mFontClient );
1613     }
1614
1615     Text::FontMetrics fontMetrics;
1616     mFontClient.GetFontMetrics( defaultFontId, fontMetrics );
1617
1618     lineHeight = fontMetrics.ascender - fontMetrics.descender;
1619
1620
1621     Vector2 cursorPosition;
1622
1623     switch( mLayoutEngine.GetHorizontalAlignment() )
1624     {
1625       case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
1626       {
1627         cursorPosition.x = 1.f;
1628         break;
1629       }
1630       case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
1631       {
1632         cursorPosition.x = floor( 0.5f * mControlSize.width );
1633         break;
1634       }
1635       case LayoutEngine::HORIZONTAL_ALIGN_END:
1636       {
1637         cursorPosition.x = mControlSize.width;
1638         break;
1639       }
1640     }
1641
1642     switch( mLayoutEngine.GetVerticalAlignment() )
1643     {
1644       case LayoutEngine::VERTICAL_ALIGN_TOP:
1645       {
1646         cursorPosition.y = 0.f;
1647         break;
1648       }
1649       case LayoutEngine::VERTICAL_ALIGN_CENTER:
1650       {
1651         cursorPosition.y = floorf( 0.5f * ( mControlSize.height - lineHeight ) );
1652         break;
1653       }
1654       case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
1655       {
1656         cursorPosition.y = mControlSize.height - lineHeight;
1657         break;
1658       }
1659     }
1660
1661     mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1662                                          cursorPosition.x,
1663                                          cursorPosition.y,
1664                                          lineHeight,
1665                                          lineHeight );
1666   }
1667   else
1668   {
1669     CursorInfo cursorInfo;
1670     GetCursorPosition( mEventData->mPrimaryCursorPosition,
1671                        cursorInfo );
1672
1673     const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1674     const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1675
1676     // Sets the cursor position.
1677     mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1678                                          cursorPosition.x,
1679                                          cursorPosition.y,
1680                                          cursorInfo.primaryCursorHeight,
1681                                          cursorInfo.lineHeight );
1682     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
1683
1684     // Sets the grab handle position.
1685     mEventData->mDecorator->SetPosition( GRAB_HANDLE,
1686                                          cursorPosition.x,
1687                                          cursorPosition.y,
1688                                          cursorInfo.lineHeight );
1689
1690     if( cursorInfo.isSecondaryCursor )
1691     {
1692       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
1693       mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
1694                                            cursorInfo.secondaryPosition.x + offset.x,
1695                                            cursorInfo.secondaryPosition.y + offset.y,
1696                                            cursorInfo.secondaryCursorHeight,
1697                                            cursorInfo.lineHeight );
1698       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + offset.x, cursorInfo.secondaryPosition.y + offset.y );
1699     }
1700     else
1701     {
1702       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1703     }
1704   }
1705   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
1706 }
1707
1708 void Controller::Impl::UpdateSelectionHandle( HandleType handleType )
1709 {
1710   if( ( LEFT_SELECTION_HANDLE != handleType ) &&
1711       ( RIGHT_SELECTION_HANDLE != handleType ) )
1712   {
1713     return;
1714   }
1715
1716   const bool leftSelectionHandle = LEFT_SELECTION_HANDLE == handleType;
1717   const CharacterIndex index = leftSelectionHandle ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1718
1719   CursorInfo cursorInfo;
1720   GetCursorPosition( index,
1721                      cursorInfo );
1722
1723   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1724   const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1725
1726   // Sets the grab handle position.
1727   mEventData->mDecorator->SetPosition( handleType,
1728                                        cursorPosition.x,
1729                                        cursorPosition.y,
1730                                        cursorInfo.lineHeight );
1731
1732   // If selection handle at start of the text and other at end of the text then all text is selected.
1733   const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
1734   const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
1735   mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mLogicalModel->mText.Count() );
1736 }
1737
1738 void Controller::Impl::ClampHorizontalScroll( const Vector2& actualSize )
1739 {
1740   // Clamp between -space & 0 (and the text alignment).
1741   if( actualSize.width > mControlSize.width )
1742   {
1743     const float space = ( actualSize.width - mControlSize.width ) + mAlignmentOffset.x;
1744     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x < -space ) ? -space : mEventData->mScrollPosition.x;
1745     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x > -mAlignmentOffset.x ) ? -mAlignmentOffset.x : mEventData->mScrollPosition.x;
1746
1747     mEventData->mDecoratorUpdated = true;
1748   }
1749   else
1750   {
1751     mEventData->mScrollPosition.x = 0.f;
1752   }
1753 }
1754
1755 void Controller::Impl::ClampVerticalScroll( const Vector2& actualSize )
1756 {
1757   // Clamp between -space & 0 (and the text alignment).
1758   if( actualSize.height > mControlSize.height )
1759   {
1760     const float space = ( actualSize.height - mControlSize.height ) + mAlignmentOffset.y;
1761     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y < -space ) ? -space : mEventData->mScrollPosition.y;
1762     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y > -mAlignmentOffset.y ) ? -mAlignmentOffset.y : mEventData->mScrollPosition.y;
1763
1764     mEventData->mDecoratorUpdated = true;
1765   }
1766   else
1767   {
1768     mEventData->mScrollPosition.y = 0.f;
1769   }
1770 }
1771
1772 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position )
1773 {
1774   Vector2 offset;
1775   bool updateDecorator = false;
1776   if( position.x < 0.f )
1777   {
1778     offset.x = -position.x;
1779     mEventData->mScrollPosition.x += offset.x;
1780     updateDecorator = true;
1781   }
1782   else if( position.x > mControlSize.width )
1783   {
1784     offset.x = mControlSize.width - position.x;
1785     mEventData->mScrollPosition.x += offset.x;
1786     updateDecorator = true;
1787   }
1788
1789   if( updateDecorator && mEventData->mDecorator )
1790   {
1791     mEventData->mDecorator->UpdatePositions( offset );
1792   }
1793
1794   // TODO : calculate the vertical scroll.
1795 }
1796
1797 void Controller::Impl::ScrollTextToMatchCursor()
1798 {
1799   // Get the current cursor position in decorator coords.
1800   const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
1801
1802   // Calculate the new cursor position.
1803   CursorInfo cursorInfo;
1804   GetCursorPosition( mEventData->mPrimaryCursorPosition,
1805                      cursorInfo );
1806
1807   // Calculate the offset to match the cursor position before the character was deleted.
1808   mEventData->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x - mAlignmentOffset.x;
1809
1810   ClampHorizontalScroll( mVisualModel->GetActualSize() );
1811
1812   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1813   const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1814
1815   // Sets the cursor position.
1816   mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1817                                        cursorPosition.x,
1818                                        cursorPosition.y,
1819                                        cursorInfo.primaryCursorHeight,
1820                                        cursorInfo.lineHeight );
1821
1822   // Sets the grab handle position.
1823   mEventData->mDecorator->SetPosition( GRAB_HANDLE,
1824                                        cursorPosition.x,
1825                                        cursorPosition.y,
1826                                        cursorInfo.lineHeight );
1827
1828   if( cursorInfo.isSecondaryCursor )
1829   {
1830     mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
1831                                          cursorInfo.secondaryPosition.x + offset.x,
1832                                          cursorInfo.secondaryPosition.y + offset.y,
1833                                          cursorInfo.secondaryCursorHeight,
1834                                          cursorInfo.lineHeight );
1835     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + offset.x, cursorInfo.secondaryPosition.y + offset.y );
1836   }
1837
1838   // Set which cursors are active according the state.
1839   if( ( EventData::EDITING == mEventData->mState ) ||
1840       ( EventData::EDITING_WITH_POPUP == mEventData->mState ) ||
1841       ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
1842   {
1843     if( cursorInfo.isSecondaryCursor )
1844     {
1845       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
1846     }
1847     else
1848     {
1849       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1850     }
1851   }
1852   else
1853   {
1854     mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1855   }
1856 }
1857
1858 void Controller::Impl::RequestRelayout()
1859 {
1860   mControlInterface.RequestTextRelayout();
1861 }
1862
1863 } // namespace Text
1864
1865 } // namespace Toolkit
1866
1867 } // namespace Dali