Merge "Fix for cursor position when there is no text and no place holder text." into...
[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::SELECTION_HANDLE_PANNING == mEventData->mState )
1148     {
1149       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1150       mEventData->mDecorator->StopCursorBlink();
1151       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1152       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
1153       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
1154       if( mEventData->mGrabHandlePopupEnabled )
1155       {
1156         mEventData->mDecorator->SetPopupActive( false );
1157       }
1158       mEventData->mDecoratorUpdated = true;
1159     }
1160     else if ( EventData::GRAB_HANDLE_PANNING == mEventData->mState )
1161     {
1162       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1163       if( mEventData->mCursorBlinkEnabled )
1164       {
1165         mEventData->mDecorator->StartCursorBlink();
1166       }
1167       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1168       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1169       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1170       if( mEventData->mGrabHandlePopupEnabled )
1171       {
1172         mEventData->mDecorator->SetPopupActive( false );
1173       }
1174       mEventData->mDecoratorUpdated = true;
1175     }
1176   }
1177 }
1178
1179 LineIndex Controller::Impl::GetClosestLine( float y ) const
1180 {
1181   float totalHeight = 0.f;
1182   LineIndex lineIndex = 0u;
1183
1184   const Vector<LineRun>& lines = mVisualModel->mLines;
1185   for( LineIndex endLine = lines.Count();
1186        lineIndex < endLine;
1187        ++lineIndex )
1188   {
1189     const LineRun& lineRun = lines[lineIndex];
1190     totalHeight += lineRun.ascender + -lineRun.descender;
1191     if( y < totalHeight )
1192     {
1193       return lineIndex;
1194     }
1195   }
1196
1197   if( lineIndex == 0 )
1198   {
1199     return 0;
1200   }
1201
1202   return lineIndex-1;
1203 }
1204
1205 void Controller::Impl::FindSelectionIndices( float visualX, float visualY, CharacterIndex& startIndex, CharacterIndex& endIndex )
1206 {
1207   CharacterIndex hitCharacter = GetClosestCursorIndex( visualX, visualY );
1208   if( hitCharacter >= mLogicalModel->mText.Count() )
1209   {
1210     // Selection out of bounds.
1211     return;
1212   }
1213
1214   startIndex = hitCharacter;
1215   endIndex = hitCharacter;
1216
1217   if( !TextAbstraction::IsWhiteSpace( mLogicalModel->mText[hitCharacter] ) )
1218   {
1219     // Find the start and end of the text
1220     for( startIndex = hitCharacter; startIndex > 0; --startIndex )
1221     {
1222       Character charCode = mLogicalModel->mText[ startIndex-1 ];
1223       if( TextAbstraction::IsWhiteSpace( charCode ) )
1224       {
1225         break;
1226       }
1227     }
1228     const CharacterIndex pastTheEnd = mLogicalModel->mText.Count();
1229     for( endIndex = hitCharacter + 1u; endIndex < pastTheEnd; ++endIndex )
1230     {
1231       Character charCode = mLogicalModel->mText[ endIndex ];
1232       if( TextAbstraction::IsWhiteSpace( charCode ) )
1233       {
1234         break;
1235       }
1236     }
1237   }
1238 }
1239
1240 CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
1241                                                         float visualY )
1242 {
1243   if( NULL == mEventData )
1244   {
1245     // Nothing to do if there is no text input.
1246     return 0u;
1247   }
1248
1249   CharacterIndex logicalIndex = 0u;
1250
1251   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
1252   const Length numberOfLines  = mVisualModel->mLines.Count();
1253   if( 0 == numberOfGlyphs ||
1254       0 == numberOfLines )
1255   {
1256     return logicalIndex;
1257   }
1258
1259   // Find which line is closest
1260   const LineIndex lineIndex = GetClosestLine( visualY );
1261   const LineRun& line = mVisualModel->mLines[lineIndex];
1262
1263   // Get the positions of the glyphs.
1264   const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
1265   const Vector2* const positionsBuffer = positions.Begin();
1266
1267   // Get the visual to logical conversion tables.
1268   const CharacterIndex* const visualToLogicalBuffer = ( 0u != mLogicalModel->mVisualToLogicalMap.Count() ) ? mLogicalModel->mVisualToLogicalMap.Begin() : NULL;
1269   const CharacterIndex* const visualToLogicalCursorBuffer = mLogicalModel->mVisualToLogicalCursorMap.Begin();
1270
1271   // Get the character to glyph conversion table.
1272   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1273
1274   // Get the glyphs per character table.
1275   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
1276   const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1277
1278   // If the vector is void, there is no right to left characters.
1279   const bool hasRightToLeftCharacters = NULL != visualToLogicalBuffer;
1280
1281   const CharacterIndex startCharacter = line.characterRun.characterIndex;
1282   const CharacterIndex endCharacter   = line.characterRun.characterIndex + line.characterRun.numberOfCharacters;
1283   DALI_ASSERT_DEBUG( endCharacter <= mLogicalModel->mText.Count() && "Invalid line info" );
1284
1285   // Whether there is a hit on a glyph.
1286   bool matched = false;
1287
1288   // Traverses glyphs in visual order. To do that use the visual to logical conversion table.
1289   CharacterIndex visualIndex = startCharacter;
1290   for( ; !matched && ( visualIndex < endCharacter ); ++visualIndex )
1291   {
1292     // The character in logical order.
1293     const CharacterIndex characterLogicalOrderIndex = hasRightToLeftCharacters ? *( visualToLogicalBuffer + visualIndex ) : visualIndex;
1294
1295     // Get the script of the character.
1296     const Script script = mLogicalModel->GetScript( characterLogicalOrderIndex );
1297
1298     // The first glyph for that character in logical order.
1299     const GlyphIndex glyphLogicalOrderIndex = *( charactersToGlyphBuffer + characterLogicalOrderIndex );
1300     // The number of glyphs for that character
1301     const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + characterLogicalOrderIndex );
1302
1303     // Get the metrics for the group of glyphs.
1304     GlyphMetrics glyphMetrics;
1305     GetGlyphsMetrics( glyphLogicalOrderIndex,
1306                       numberOfGlyphs,
1307                       glyphMetrics,
1308                       mVisualModel,
1309                       mFontClient );
1310
1311     const Vector2& position = *( positionsBuffer + glyphLogicalOrderIndex );
1312
1313     // Prevents to jump the whole Latin ligatures like fi, ff, ...
1314     const Length numberOfCharactersInLigature = ( TextAbstraction::LATIN == script ) ? *( charactersPerGlyphBuffer + glyphLogicalOrderIndex ) : 1u;
1315     const float glyphAdvance = glyphMetrics.advance / static_cast<float>( numberOfCharactersInLigature );
1316
1317     for( GlyphIndex index = 0u; !matched && ( index < numberOfCharactersInLigature ); ++index )
1318     {
1319       // Find the mid-point of the area containing the glyph
1320       const float glyphCenter = -glyphMetrics.xBearing + position.x + ( static_cast<float>( index ) + 0.5f ) * glyphAdvance;
1321
1322       if( visualX < glyphCenter )
1323       {
1324         visualIndex += index;
1325         matched = true;
1326         break;
1327       }
1328     }
1329
1330     if( matched )
1331     {
1332       break;
1333     }
1334   }
1335
1336   // Return the logical position of the cursor in characters.
1337
1338   if( !matched )
1339   {
1340     visualIndex = endCharacter;
1341   }
1342
1343   logicalIndex = hasRightToLeftCharacters ? *( visualToLogicalCursorBuffer + visualIndex ) : visualIndex;
1344   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p closest visualIndex %d logicalIndex %d\n", this, visualIndex, logicalIndex );
1345
1346   return logicalIndex;
1347 }
1348
1349 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
1350                                           CursorInfo& cursorInfo )
1351 {
1352   // TODO: Check for multiline with \n, etc...
1353
1354   // Check if the logical position is the first or the last one of the text.
1355   const bool isFirstPosition = 0u == logical;
1356   const bool isLastPosition = mLogicalModel->mText.Count() == logical;
1357
1358   if( isFirstPosition && isLastPosition )
1359   {
1360     // There is zero characters. Get the default font's line height.
1361     cursorInfo.lineHeight = GetDefaultFontLineHeight();
1362     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
1363
1364     cursorInfo.primaryPosition.x = 1.f;
1365     cursorInfo.primaryPosition.y = 0.f;
1366
1367     // Nothing else to do.
1368     return;
1369   }
1370
1371   // Get the previous logical index.
1372   const CharacterIndex previousLogical = isFirstPosition ? 0u : logical - 1u;
1373
1374   // Decrease the logical index if it's the last one.
1375   if( isLastPosition )
1376   {
1377     --logical;
1378   }
1379
1380   // Get the direction of the character and the previous one.
1381   const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
1382
1383   CharacterDirection isCurrentRightToLeft = false;
1384   CharacterDirection isPreviousRightToLeft = false;
1385   if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1386   {
1387     isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + logical );
1388     isPreviousRightToLeft = *( modelCharacterDirectionsBuffer + previousLogical );
1389   }
1390
1391   // Get the line where the character is laid-out.
1392   const LineRun* modelLines = mVisualModel->mLines.Begin();
1393
1394   const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( logical );
1395   const LineRun& line = *( modelLines + lineIndex );
1396
1397   // Get the paragraph's direction.
1398   const CharacterDirection isRightToLeftParagraph = line.direction;
1399
1400   // Check whether there is an alternative position:
1401
1402   cursorInfo.isSecondaryCursor = ( isCurrentRightToLeft != isPreviousRightToLeft ) ||
1403     ( isLastPosition && ( isRightToLeftParagraph != isCurrentRightToLeft ) );
1404
1405   // Set the line height.
1406   cursorInfo.lineHeight = line.ascender + -line.descender;
1407
1408   // Convert the cursor position into the glyph position.
1409   CharacterIndex characterIndex = logical;
1410   if( cursorInfo.isSecondaryCursor &&
1411       ( isRightToLeftParagraph != isCurrentRightToLeft ) )
1412   {
1413     characterIndex = previousLogical;
1414   }
1415
1416   const GlyphIndex currentGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
1417   const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
1418   const Length numberOfCharacters = *( mVisualModel->mCharactersPerGlyph.Begin() +currentGlyphIndex );
1419
1420   // Get the metrics for the group of glyphs.
1421   GlyphMetrics glyphMetrics;
1422   GetGlyphsMetrics( currentGlyphIndex,
1423                     numberOfGlyphs,
1424                     glyphMetrics,
1425                     mVisualModel,
1426                     mFontClient );
1427
1428   float interGlyphAdvance = 0.f;
1429   if( !isLastPosition &&
1430       ( numberOfCharacters > 1u ) )
1431   {
1432     const CharacterIndex firstIndex = *( mVisualModel->mGlyphsToCharacters.Begin() + currentGlyphIndex );
1433     interGlyphAdvance = static_cast<float>( characterIndex - firstIndex ) * glyphMetrics.advance / static_cast<float>( numberOfCharacters );
1434   }
1435
1436   // Get the glyph position and x bearing.
1437   const Vector2& currentPosition = *( mVisualModel->mGlyphPositions.Begin() + currentGlyphIndex );
1438
1439   // Set the cursor's height.
1440   cursorInfo.primaryCursorHeight = glyphMetrics.fontHeight;
1441
1442   // Set the position.
1443   cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + currentPosition.x + ( isCurrentRightToLeft ? glyphMetrics.advance : interGlyphAdvance );
1444   cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
1445
1446   if( isLastPosition )
1447   {
1448     // The position of the cursor after the last character needs special
1449     // care depending on its direction and the direction of the paragraph.
1450
1451     if( cursorInfo.isSecondaryCursor )
1452     {
1453       // Need to find the first character after the last character with the paragraph's direction.
1454       // i.e l0 l1 l2 r0 r1 should find r0.
1455
1456       // TODO: check for more than one line!
1457       characterIndex = isRightToLeftParagraph ? line.characterRun.characterIndex : line.characterRun.characterIndex + line.characterRun.numberOfCharacters - 1u;
1458       characterIndex = mLogicalModel->GetLogicalCharacterIndex( characterIndex );
1459
1460       const GlyphIndex glyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
1461       const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
1462
1463       const Vector2& position = *( mVisualModel->mGlyphPositions.Begin() + glyphIndex );
1464
1465       // Get the metrics for the group of glyphs.
1466       GlyphMetrics glyphMetrics;
1467       GetGlyphsMetrics( glyphIndex,
1468                         numberOfGlyphs,
1469                         glyphMetrics,
1470                         mVisualModel,
1471                         mFontClient );
1472
1473       cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + position.x + ( isRightToLeftParagraph ? 0.f : glyphMetrics.advance );
1474
1475       cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
1476     }
1477     else
1478     {
1479       if( !isCurrentRightToLeft )
1480       {
1481         cursorInfo.primaryPosition.x += glyphMetrics.advance;
1482       }
1483       else
1484       {
1485         cursorInfo.primaryPosition.x -= glyphMetrics.advance;
1486       }
1487     }
1488   }
1489
1490   // Set the alternative cursor position.
1491   if( cursorInfo.isSecondaryCursor )
1492   {
1493     // Convert the cursor position into the glyph position.
1494     const CharacterIndex previousCharacterIndex = ( ( isRightToLeftParagraph != isCurrentRightToLeft ) ? logical : previousLogical );
1495     const GlyphIndex previousGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + previousCharacterIndex );
1496     const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + previousCharacterIndex );
1497
1498     // Get the glyph position.
1499     const Vector2& previousPosition = *( mVisualModel->mGlyphPositions.Begin() + previousGlyphIndex );
1500
1501     // Get the metrics for the group of glyphs.
1502     GlyphMetrics glyphMetrics;
1503     GetGlyphsMetrics( previousGlyphIndex,
1504                       numberOfGlyphs,
1505                       glyphMetrics,
1506                       mVisualModel,
1507                       mFontClient );
1508
1509     // Set the cursor position and height.
1510     cursorInfo.secondaryPosition.x = -glyphMetrics.xBearing + previousPosition.x + ( ( ( isLastPosition && !isCurrentRightToLeft ) ||
1511                                                                                        ( !isLastPosition && isCurrentRightToLeft )    ) ? glyphMetrics.advance : 0.f );
1512
1513     cursorInfo.secondaryCursorHeight = 0.5f * glyphMetrics.fontHeight;
1514
1515     cursorInfo.secondaryPosition.y = cursorInfo.lineHeight - cursorInfo.secondaryCursorHeight - line.descender - ( glyphMetrics.fontHeight - glyphMetrics.ascender );
1516
1517     // Update the primary cursor height as well.
1518     cursorInfo.primaryCursorHeight *= 0.5f;
1519   }
1520 }
1521
1522 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
1523 {
1524   if( NULL == mEventData )
1525   {
1526     // Nothing to do if there is no text input.
1527     return 0u;
1528   }
1529
1530   CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
1531
1532   const Script script = mLogicalModel->GetScript( index );
1533   const GlyphIndex* charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1534   const Length* charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1535
1536   Length numberOfCharacters = 0u;
1537   if( TextAbstraction::LATIN == script )
1538   {
1539     // Prevents to jump the whole Latin ligatures like fi, ff, ...
1540     numberOfCharacters = 1u;
1541   }
1542   else
1543   {
1544     GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
1545     numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1546
1547     while( 0u == numberOfCharacters )
1548     {
1549       numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1550       ++glyphIndex;
1551     }
1552   }
1553
1554   if( index < mEventData->mPrimaryCursorPosition )
1555   {
1556     cursorIndex -= numberOfCharacters;
1557   }
1558   else
1559   {
1560     cursorIndex += numberOfCharacters;
1561   }
1562
1563   return cursorIndex;
1564 }
1565
1566 void Controller::Impl::UpdateCursorPosition()
1567 {
1568   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
1569   if( NULL == mEventData )
1570   {
1571     // Nothing to do if there is no text input.
1572     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
1573     return;
1574   }
1575
1576   if( IsShowingPlaceholderText() )
1577   {
1578     // Do not want to use the place-holder text to set the cursor position.
1579
1580     // Use the line's height of the font's family set to set the cursor's size.
1581     // If there is no font's family set, use the default font.
1582     // Use the current alignment to place the cursor at the beginning, center or end of the box.
1583
1584     float lineHeight = 0.f;
1585
1586     FontId defaultFontId = 0u;
1587     if( NULL == mFontDefaults )
1588     {
1589       defaultFontId = mFontClient.GetFontId( EMPTY_STRING,
1590                                              EMPTY_STRING );
1591     }
1592     else
1593     {
1594       defaultFontId = mFontDefaults->GetFontId( mFontClient );
1595     }
1596
1597     Text::FontMetrics fontMetrics;
1598     mFontClient.GetFontMetrics( defaultFontId, fontMetrics );
1599
1600     lineHeight = fontMetrics.ascender - fontMetrics.descender;
1601
1602
1603     Vector2 cursorPosition;
1604
1605     switch( mLayoutEngine.GetHorizontalAlignment() )
1606     {
1607       case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
1608       {
1609         cursorPosition.x = 1.f;
1610         break;
1611       }
1612       case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
1613       {
1614         cursorPosition.x = floor( 0.5f * mControlSize.width );
1615         break;
1616       }
1617       case LayoutEngine::HORIZONTAL_ALIGN_END:
1618       {
1619         cursorPosition.x = mControlSize.width;
1620         break;
1621       }
1622     }
1623
1624     switch( mLayoutEngine.GetVerticalAlignment() )
1625     {
1626       case LayoutEngine::VERTICAL_ALIGN_TOP:
1627       {
1628         cursorPosition.y = 0.f;
1629         break;
1630       }
1631       case LayoutEngine::VERTICAL_ALIGN_CENTER:
1632       {
1633         cursorPosition.y = floorf( 0.5f * ( mControlSize.height - lineHeight ) );
1634         break;
1635       }
1636       case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
1637       {
1638         cursorPosition.y = mControlSize.height - lineHeight;
1639         break;
1640       }
1641     }
1642
1643     mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1644                                          cursorPosition.x,
1645                                          cursorPosition.y,
1646                                          lineHeight,
1647                                          lineHeight );
1648   }
1649   else
1650   {
1651     CursorInfo cursorInfo;
1652     GetCursorPosition( mEventData->mPrimaryCursorPosition,
1653                        cursorInfo );
1654
1655     const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1656     const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1657
1658     // Sets the cursor position.
1659     mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1660                                          cursorPosition.x,
1661                                          cursorPosition.y,
1662                                          cursorInfo.primaryCursorHeight,
1663                                          cursorInfo.lineHeight );
1664     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
1665
1666     // Sets the grab handle position.
1667     mEventData->mDecorator->SetPosition( GRAB_HANDLE,
1668                                          cursorPosition.x,
1669                                          cursorPosition.y,
1670                                          cursorInfo.lineHeight );
1671
1672     if( cursorInfo.isSecondaryCursor )
1673     {
1674       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
1675       mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
1676                                            cursorInfo.secondaryPosition.x + offset.x,
1677                                            cursorInfo.secondaryPosition.y + offset.y,
1678                                            cursorInfo.secondaryCursorHeight,
1679                                            cursorInfo.lineHeight );
1680       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + offset.x, cursorInfo.secondaryPosition.y + offset.y );
1681     }
1682     else
1683     {
1684       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1685     }
1686   }
1687   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
1688 }
1689
1690 void Controller::Impl::UpdateSelectionHandle( HandleType handleType )
1691 {
1692   if( ( LEFT_SELECTION_HANDLE != handleType ) &&
1693       ( RIGHT_SELECTION_HANDLE != handleType ) )
1694   {
1695     return;
1696   }
1697
1698   const bool leftSelectionHandle = LEFT_SELECTION_HANDLE == handleType;
1699   const CharacterIndex index = leftSelectionHandle ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1700
1701   CursorInfo cursorInfo;
1702   GetCursorPosition( index,
1703                      cursorInfo );
1704
1705   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1706   const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1707
1708   // Sets the grab handle position.
1709   mEventData->mDecorator->SetPosition( handleType,
1710                                        cursorPosition.x,
1711                                        cursorPosition.y,
1712                                        cursorInfo.lineHeight );
1713
1714   // If selection handle at start of the text and other at end of the text then all text is selected.
1715   const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
1716   const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
1717   mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mLogicalModel->mText.Count() );
1718 }
1719
1720 void Controller::Impl::ClampHorizontalScroll( const Vector2& actualSize )
1721 {
1722   // Clamp between -space & 0 (and the text alignment).
1723   if( actualSize.width > mControlSize.width )
1724   {
1725     const float space = ( actualSize.width - mControlSize.width ) + mAlignmentOffset.x;
1726     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x < -space ) ? -space : mEventData->mScrollPosition.x;
1727     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x > -mAlignmentOffset.x ) ? -mAlignmentOffset.x : mEventData->mScrollPosition.x;
1728
1729     mEventData->mDecoratorUpdated = true;
1730   }
1731   else
1732   {
1733     mEventData->mScrollPosition.x = 0.f;
1734   }
1735 }
1736
1737 void Controller::Impl::ClampVerticalScroll( const Vector2& actualSize )
1738 {
1739   // Clamp between -space & 0 (and the text alignment).
1740   if( actualSize.height > mControlSize.height )
1741   {
1742     const float space = ( actualSize.height - mControlSize.height ) + mAlignmentOffset.y;
1743     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y < -space ) ? -space : mEventData->mScrollPosition.y;
1744     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y > -mAlignmentOffset.y ) ? -mAlignmentOffset.y : mEventData->mScrollPosition.y;
1745
1746     mEventData->mDecoratorUpdated = true;
1747   }
1748   else
1749   {
1750     mEventData->mScrollPosition.y = 0.f;
1751   }
1752 }
1753
1754 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position )
1755 {
1756   Vector2 offset;
1757   bool updateDecorator = false;
1758   if( position.x < 0.f )
1759   {
1760     offset.x = -position.x;
1761     mEventData->mScrollPosition.x += offset.x;
1762     updateDecorator = true;
1763   }
1764   else if( position.x > mControlSize.width )
1765   {
1766     offset.x = mControlSize.width - position.x;
1767     mEventData->mScrollPosition.x += offset.x;
1768     updateDecorator = true;
1769   }
1770
1771   if( updateDecorator && mEventData->mDecorator )
1772   {
1773     mEventData->mDecorator->UpdatePositions( offset );
1774   }
1775
1776   // TODO : calculate the vertical scroll.
1777 }
1778
1779 void Controller::Impl::ScrollTextToMatchCursor()
1780 {
1781   // Get the current cursor position in decorator coords.
1782   const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
1783
1784   // Calculate the new cursor position.
1785   CursorInfo cursorInfo;
1786   GetCursorPosition( mEventData->mPrimaryCursorPosition,
1787                      cursorInfo );
1788
1789   // Calculate the offset to match the cursor position before the character was deleted.
1790   mEventData->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x - mAlignmentOffset.x;
1791
1792   ClampHorizontalScroll( mVisualModel->GetActualSize() );
1793
1794   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1795   const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1796
1797   // Sets the cursor position.
1798   mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1799                                        cursorPosition.x,
1800                                        cursorPosition.y,
1801                                        cursorInfo.primaryCursorHeight,
1802                                        cursorInfo.lineHeight );
1803
1804   // Sets the grab handle position.
1805   mEventData->mDecorator->SetPosition( GRAB_HANDLE,
1806                                        cursorPosition.x,
1807                                        cursorPosition.y,
1808                                        cursorInfo.lineHeight );
1809
1810   if( cursorInfo.isSecondaryCursor )
1811   {
1812     mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
1813                                          cursorInfo.secondaryPosition.x + offset.x,
1814                                          cursorInfo.secondaryPosition.y + offset.y,
1815                                          cursorInfo.secondaryCursorHeight,
1816                                          cursorInfo.lineHeight );
1817     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + offset.x, cursorInfo.secondaryPosition.y + offset.y );
1818   }
1819
1820   // Set which cursors are active according the state.
1821   if( ( EventData::EDITING == mEventData->mState ) ||
1822       ( EventData::EDITING_WITH_POPUP == mEventData->mState ) ||
1823       ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
1824   {
1825     if( cursorInfo.isSecondaryCursor )
1826     {
1827       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
1828     }
1829     else
1830     {
1831       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1832     }
1833   }
1834   else
1835   {
1836     mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1837   }
1838 }
1839
1840 void Controller::Impl::RequestRelayout()
1841 {
1842   mControlInterface.RequestTextRelayout();
1843 }
1844
1845 } // namespace Text
1846
1847 } // namespace Toolkit
1848
1849 } // namespace Dali