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