Show CopyPaste Poup on long press
[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 void Controller::Impl::OnCursorKeyEvent( const Event& event )
445 {
446   if( NULL == mEventData )
447   {
448     // Nothing to do if there is no text input.
449     return;
450   }
451
452   int keyCode = event.p1.mInt;
453
454   if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
455   {
456     if( mEventData->mPrimaryCursorPosition > 0u )
457     {
458       mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
459     }
460   }
461   else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
462   {
463     if( mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
464     {
465       mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
466     }
467   }
468   else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
469   {
470     // TODO
471   }
472   else if(   Dali::DALI_KEY_CURSOR_DOWN == keyCode )
473   {
474     // TODO
475   }
476
477   mEventData->mUpdateCursorPosition = true;
478   mEventData->mScrollAfterUpdatePosition = true;
479 }
480
481 void Controller::Impl::OnTapEvent( const Event& event )
482 {
483   if( NULL != mEventData )
484   {
485     const unsigned int tapCount = event.p1.mUint;
486
487     if( 1u == tapCount )
488     {
489       if( ! IsShowingPlaceholderText() )
490       {
491         const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
492         const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
493
494         mEventData->mPrimaryCursorPosition = GetClosestCursorIndex( xPosition,
495                                                                     yPosition );
496       }
497       else
498       {
499         mEventData->mPrimaryCursorPosition = 0u;
500       }
501
502       mEventData->mUpdateCursorPosition = true;
503       mEventData->mScrollAfterUpdatePosition = true;
504     }
505   }
506 }
507
508 void Controller::Impl::OnPanEvent( const Event& event )
509 {
510   if( NULL == mEventData )
511   {
512     // Nothing to do if there is no text input.
513     return;
514   }
515
516   int state = event.p1.mInt;
517
518   if( Gesture::Started    == state ||
519       Gesture::Continuing == state )
520   {
521     const Vector2& actualSize = mVisualModel->GetActualSize();
522     const Vector2 currentScroll = mEventData->mScrollPosition;
523
524     if( mEventData->mHorizontalScrollingEnabled )
525     {
526       const float displacementX = event.p2.mFloat;
527       mEventData->mScrollPosition.x += displacementX;
528
529       ClampHorizontalScroll( actualSize );
530     }
531
532     if( mEventData->mVerticalScrollingEnabled )
533     {
534       const float displacementY = event.p3.mFloat;
535       mEventData->mScrollPosition.y += displacementY;
536
537       ClampVerticalScroll( actualSize );
538     }
539
540     if( mEventData->mDecorator )
541     {
542       mEventData->mDecorator->UpdatePositions( mEventData->mScrollPosition - currentScroll );
543     }
544   }
545 }
546
547 void Controller::Impl::OnLongPressEvent( const Event& event )
548 {
549   if  ( EventData::EDITING == mEventData->mState )
550   {
551     ChangeState ( EventData::EDITING_WITH_POPUP );
552     mEventData->mDecoratorUpdated = true;
553   }
554 }
555
556 void Controller::Impl::OnHandleEvent( const Event& event )
557 {
558   if( NULL == mEventData )
559   {
560     // Nothing to do if there is no text input.
561     return;
562   }
563
564   const unsigned int state = event.p1.mUint;
565   const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
566
567   if( HANDLE_PRESSED == state )
568   {
569     // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
570     const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
571     const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
572
573     const CharacterIndex handleNewPosition = GetClosestCursorIndex( xPosition, yPosition );
574
575     if( Event::GRAB_HANDLE_EVENT == event.type )
576     {
577       ChangeState ( EventData::GRAB_HANDLE_PANNING );
578
579       if( handleNewPosition != mEventData->mPrimaryCursorPosition )
580       {
581         mEventData->mPrimaryCursorPosition = handleNewPosition;
582         mEventData->mUpdateCursorPosition = true;
583       }
584     }
585     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
586     {
587       ChangeState ( EventData::SELECTION_HANDLE_PANNING );
588
589       if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
590           ( handleNewPosition != mEventData->mRightSelectionPosition ) )
591       {
592         mEventData->mLeftSelectionPosition = handleNewPosition;
593
594         RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
595                                     mEventData->mRightSelectionPosition );
596
597         mEventData->mUpdateLeftSelectionPosition = true;
598       }
599     }
600     else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
601     {
602       ChangeState ( EventData::SELECTION_HANDLE_PANNING );
603
604       if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
605           ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
606       {
607         mEventData->mRightSelectionPosition = handleNewPosition;
608
609         RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
610                                     mEventData->mRightSelectionPosition );
611
612         mEventData->mUpdateRightSelectionPosition = true;
613       }
614     }
615   } // end ( HANDLE_PRESSED == state )
616   else if( ( HANDLE_RELEASED == state ) ||
617            handleStopScrolling )
618   {
619     CharacterIndex handlePosition = 0u;
620     if( handleStopScrolling )
621     {
622       // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
623       const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
624       const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
625
626       handlePosition = GetClosestCursorIndex( xPosition, yPosition );
627     }
628
629     if( Event::GRAB_HANDLE_EVENT == event.type )
630     {
631       mEventData->mUpdateCursorPosition = true;
632
633       ChangeState( EventData::EDITING_WITH_POPUP );
634
635       if( handleStopScrolling )
636       {
637         mEventData->mScrollAfterUpdatePosition = mEventData->mPrimaryCursorPosition != handlePosition;
638         mEventData->mPrimaryCursorPosition = handlePosition;
639       }
640     }
641     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
642     {
643       ChangeState( EventData::SELECTING );
644
645       if( handleStopScrolling )
646       {
647         mEventData->mUpdateLeftSelectionPosition = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition);
648         mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateLeftSelectionPosition;
649
650         if( mEventData->mUpdateLeftSelectionPosition )
651         {
652           mEventData->mLeftSelectionPosition = handlePosition;
653
654           RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
655                                       mEventData->mRightSelectionPosition );
656         }
657       }
658     }
659     else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
660     {
661       ChangeState( EventData::SELECTING );
662
663       if( handleStopScrolling )
664       {
665         mEventData->mUpdateRightSelectionPosition = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
666         mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateRightSelectionPosition;
667
668         if( mEventData->mUpdateRightSelectionPosition )
669         {
670           mEventData->mRightSelectionPosition = handlePosition;
671           RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
672                                       mEventData->mRightSelectionPosition );
673         }
674       }
675     }
676
677     mEventData->mDecoratorUpdated = true;
678   } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
679   else if( HANDLE_SCROLLING == state )
680   {
681     const float xSpeed = event.p2.mFloat;
682     const Vector2& actualSize = mVisualModel->GetActualSize();
683     const Vector2 currentScrollPosition = mEventData->mScrollPosition;
684
685     mEventData->mScrollPosition.x += xSpeed;
686
687     ClampHorizontalScroll( actualSize );
688
689     if( Vector2::ZERO == ( currentScrollPosition - mEventData->mScrollPosition ) )
690     {
691       // Notify the decorator there is no more text to scroll.
692       // The decorator won't send more scroll events.
693       mEventData->mDecorator->NotifyEndOfScroll();
694     }
695     else
696     {
697       const bool scrollRightDirection = xSpeed > 0.f;
698       const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
699       const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
700
701       if( Event::GRAB_HANDLE_EVENT == event.type )
702       {
703         ChangeState( EventData::GRAB_HANDLE_PANNING );
704
705         Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
706
707         // Position the grag handle close to either the left or right edge.
708         position.x = scrollRightDirection ? 0.f : mControlSize.width;
709
710         // Get the new handle position.
711         // The grab handle's position is in decorator coords. Need to transforms to text coords.
712         const CharacterIndex handlePosition = GetClosestCursorIndex( position.x - mEventData->mScrollPosition.x - mAlignmentOffset.x,
713                                                                      position.y - mEventData->mScrollPosition.y - mAlignmentOffset.y );
714
715         mEventData->mUpdateCursorPosition = mEventData->mPrimaryCursorPosition != handlePosition;
716         mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateCursorPosition;
717         mEventData->mPrimaryCursorPosition = handlePosition;
718       }
719       else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
720       {
721         // TODO: This is recalculating the selection box every time the text is scrolled with the selection handles.
722         //       Think if something can be done to save power.
723
724         ChangeState( EventData::SELECTION_HANDLE_PANNING );
725
726         Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
727
728         // Position the selection handle close to either the left or right edge.
729         position.x = scrollRightDirection ? 0.f : mControlSize.width;
730
731         // Get the new handle position.
732         // The selection handle's position is in decorator coords. Need to transforms to text coords.
733         const CharacterIndex handlePosition = GetClosestCursorIndex( position.x - mEventData->mScrollPosition.x - mAlignmentOffset.x,
734                                                                      position.y - mEventData->mScrollPosition.y - mAlignmentOffset.y );
735
736         if( leftSelectionHandleEvent )
737         {
738           mEventData->mUpdateLeftSelectionPosition = handlePosition != mEventData->mLeftSelectionPosition;
739           mEventData->mLeftSelectionPosition = handlePosition;
740         }
741         else
742         {
743           mEventData->mUpdateRightSelectionPosition = handlePosition != mEventData->mRightSelectionPosition;
744           mEventData->mRightSelectionPosition = handlePosition;
745         }
746
747         if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
748         {
749           RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
750                                       mEventData->mRightSelectionPosition );
751
752           mEventData->mScrollAfterUpdatePosition = true;
753         }
754       }
755       mEventData->mDecoratorUpdated = true;
756     }
757   } // end ( HANDLE_SCROLLING == state )
758 }
759
760 void Controller::Impl::OnSelectEvent( const Event& event )
761 {
762   if( NULL == mEventData )
763   {
764     // Nothing to do if there is no text.
765     return;
766   }
767
768   if( mEventData->mSelectionEnabled )
769   {
770     // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
771     const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
772     const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
773
774     const CharacterIndex leftPosition = mEventData->mLeftSelectionPosition;
775     const CharacterIndex rightPosition = mEventData->mRightSelectionPosition;
776
777     RepositionSelectionHandles( xPosition,
778                                 yPosition );
779
780     mEventData->mUpdateLeftSelectionPosition = leftPosition != mEventData->mLeftSelectionPosition;
781     mEventData->mUpdateRightSelectionPosition = rightPosition != mEventData->mRightSelectionPosition;
782
783     mEventData->mScrollAfterUpdatePosition = ( ( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition ) &&
784                                                ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition ) );
785   }
786 }
787
788 void Controller::Impl::OnSelectAllEvent()
789 {
790   if( NULL == mEventData )
791   {
792     // Nothing to do if there is no text.
793     return;
794   }
795
796   if( mEventData->mSelectionEnabled )
797   {
798     RepositionSelectionHandles( 0u,
799                                 mLogicalModel->mText.Count() );
800
801     mEventData->mScrollAfterUpdatePosition = true;
802     mEventData->mUpdateLeftSelectionPosition = true;
803     mEventData->mUpdateRightSelectionPosition = true;
804   }
805 }
806
807 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetreival )
808 {
809   if( mEventData->mLeftSelectionPosition ==  mEventData->mRightSelectionPosition )
810   {
811     // Nothing to select if handles are in the same place.
812     selectedText="";
813     return;
814   }
815
816   //Get start and end position of selection
817   uint32_t startOfSelectedText = mEventData->mLeftSelectionPosition;
818   uint32_t lengthOfSelectedText =  mEventData->mRightSelectionPosition - startOfSelectedText;
819
820   // Validate the start and end selection points
821   if( ( startOfSelectedText >= 0 ) && (  ( startOfSelectedText + lengthOfSelectedText ) <=  mLogicalModel->mText.Count() ) )
822   {
823     //Get text as a UTF8 string
824     Vector<Character>& utf32Characters = mLogicalModel->mText;
825
826     Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
827
828     if ( deleteAfterRetreival  ) // Only delete text if copied successfully
829     {
830       // Delete text between handles
831       Vector<Character>& currentText = mLogicalModel->mText;
832
833       Vector<Character>::Iterator first = currentText.Begin() + startOfSelectedText;
834       Vector<Character>::Iterator last  = first + lengthOfSelectedText;
835       currentText.Erase( first, last );
836     }
837     mEventData->mPrimaryCursorPosition = mEventData->mLeftSelectionPosition;
838     mEventData->mScrollAfterDelete = true;
839     mEventData->mDecoratorUpdated = true;
840   }
841 }
842
843 void Controller::Impl::ShowClipboard()
844 {
845   if ( mClipboard )
846   {
847     mClipboard.ShowClipboard();
848   }
849 }
850
851 void Controller::Impl::HideClipboard()
852 {
853   if ( mClipboard )
854   {
855     mClipboard.HideClipboard();
856   }
857 }
858
859 bool Controller::Impl::CopyStringToClipboard( std::string& source )
860 {
861   //Send string to clipboard
862   return ( mClipboard && mClipboard.SetItem( source ) );
863 }
864
865 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
866 {
867   std::string selectedText;
868   RetrieveSelection( selectedText, deleteAfterSending );
869   CopyStringToClipboard( selectedText );
870   ChangeState( EventData::EDITING );
871 }
872
873 void Controller::Impl::GetTextFromClipboard( unsigned int itemIndex, std::string& retreivedString )
874 {
875   if ( mClipboard )
876   {
877     retreivedString =  mClipboard.GetItem( itemIndex );
878   }
879 }
880
881 void Controller::Impl::RepositionSelectionHandles( CharacterIndex selectionStart, CharacterIndex selectionEnd )
882 {
883   if( selectionStart == selectionEnd )
884   {
885     // Nothing to select if handles are in the same place.
886     return;
887   }
888
889   mEventData->mDecorator->ClearHighlights();
890
891   mEventData->mLeftSelectionPosition = selectionStart;
892   mEventData->mRightSelectionPosition = selectionEnd;
893
894   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
895   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
896   const GlyphInfo* const glyphsBuffer = mVisualModel->mGlyphs.Begin();
897   const Vector2* const positionsBuffer = mVisualModel->mGlyphPositions.Begin();
898
899   // TODO: Better algorithm to create the highlight box.
900   // TODO: Multi-line.
901
902   const Vector<LineRun>& lines = mVisualModel->mLines;
903   const LineRun& firstLine = *lines.Begin();
904   const float height = firstLine.ascender + -firstLine.descender;
905
906   const bool indicesSwapped = ( selectionStart > selectionEnd );
907   if( indicesSwapped )
908   {
909     std::swap( selectionStart, selectionEnd );
910   }
911
912   GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
913   GlyphIndex glyphEnd = *( charactersToGlyphBuffer + ( selectionEnd - 1u ) ) + *( glyphsPerCharacterBuffer + ( selectionEnd - 1u ) ) - 1u;
914
915   mEventData->mDecorator->SwapSelectionHandlesEnabled( firstLine.direction != indicesSwapped );
916
917   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
918
919   for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
920   {
921     const GlyphInfo& glyph = *( glyphsBuffer + index );
922     const Vector2& position = *( positionsBuffer + index );
923
924     const float xPosition = position.x - glyph.xBearing + offset.x;
925     mEventData->mDecorator->AddHighlight( xPosition, offset.y, xPosition + glyph.advance, height );
926   }
927
928   CursorInfo primaryCursorInfo;
929   GetCursorPosition( mEventData->mLeftSelectionPosition,
930                      primaryCursorInfo );
931
932   CursorInfo secondaryCursorInfo;
933   GetCursorPosition( mEventData->mRightSelectionPosition,
934                      secondaryCursorInfo );
935
936   const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + offset;
937   const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + offset;
938
939   mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE, primaryPosition.x, primaryPosition.y, primaryCursorInfo.lineHeight );
940
941   mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE, secondaryPosition.x, secondaryPosition.y, secondaryCursorInfo.lineHeight );
942
943   // Set the flag to update the decorator.
944   mEventData->mDecoratorUpdated = true;
945 }
946
947 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
948 {
949   if( NULL == mEventData )
950   {
951     // Nothing to do if there is no text input.
952     return;
953   }
954
955   if( IsShowingPlaceholderText() )
956   {
957     // Nothing to do if there is the place-holder text.
958     return;
959   }
960
961   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
962   const Length numberOfLines  = mVisualModel->mLines.Count();
963   if( 0 == numberOfGlyphs ||
964       0 == numberOfLines )
965   {
966     // Nothing to do if there is no text.
967     return;
968   }
969
970   // Find which word was selected
971   CharacterIndex selectionStart( 0 );
972   CharacterIndex selectionEnd( 0 );
973   FindSelectionIndices( visualX, visualY, selectionStart, selectionEnd );
974   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
975
976   if( selectionStart == selectionEnd )
977   {
978     ChangeState( EventData::EDITING );
979     // Nothing to select. i.e. a white space, out of bounds
980     return;
981   }
982
983   RepositionSelectionHandles( selectionStart, selectionEnd );
984 }
985
986 void Controller::Impl::SetPopupButtons()
987 {
988   /**
989    *  Sets the Popup buttons to be shown depending on State.
990    *
991    *  If SELECTING :  CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
992    *
993    *  If EDITING_WITH_POPUP : SELECT & SELECT_ALL
994    */
995
996   TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
997
998   if ( ( EventData::SELECTING == mEventData->mState ) || ( EventData::SELECTION_CHANGED == mEventData->mState ) )
999   {
1000     buttonsToShow = TextSelectionPopup::Buttons(  TextSelectionPopup::CUT | TextSelectionPopup::COPY );
1001
1002     if ( !IsClipboardEmpty() )
1003     {
1004       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1005       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1006     }
1007
1008     if ( !mEventData->mAllTextSelected )
1009     {
1010       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
1011     }
1012   }
1013   else if  ( EventData::EDITING_WITH_POPUP == mEventData->mState )
1014   {
1015     if ( mLogicalModel->mText.Count() && !IsShowingPlaceholderText())
1016     {
1017       buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
1018     }
1019
1020     if ( !IsClipboardEmpty() )
1021     {
1022       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1023       buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1024     }
1025   }
1026
1027   mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
1028 }
1029
1030 void Controller::Impl::ChangeState( EventData::State newState )
1031 {
1032   if( NULL == mEventData )
1033   {
1034     // Nothing to do if there is no text input.
1035     return;
1036   }
1037
1038   if( mEventData->mState != newState )
1039   {
1040     mEventData->mState = newState;
1041
1042     if( EventData::INACTIVE == mEventData->mState )
1043     {
1044       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1045       mEventData->mDecorator->StopCursorBlink();
1046       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1047       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1048       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1049       mEventData->mDecorator->SetPopupActive( false );
1050       mEventData->mDecoratorUpdated = true;
1051       HideClipboard();
1052     }
1053     else if ( EventData::SELECTING == mEventData->mState )
1054     {
1055       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1056       mEventData->mDecorator->StopCursorBlink();
1057       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1058       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
1059       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
1060       if( mEventData->mGrabHandlePopupEnabled )
1061       {
1062         SetPopupButtons();
1063         mEventData->mDecorator->SetPopupActive( true );
1064       }
1065       mEventData->mDecoratorUpdated = true;
1066     }
1067     else if ( EventData::SELECTION_CHANGED  == mEventData->mState )
1068     {
1069       if( mEventData->mGrabHandlePopupEnabled )
1070       {
1071         SetPopupButtons();
1072         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1073         mEventData->mDecorator->SetPopupActive( true );
1074       }
1075       mEventData->mDecoratorUpdated = true;
1076     }
1077     else if( EventData::EDITING == mEventData->mState )
1078     {
1079       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1080       if( mEventData->mCursorBlinkEnabled )
1081       {
1082         mEventData->mDecorator->StartCursorBlink();
1083       }
1084       // Grab handle is not shown until a tap is received whilst EDITING
1085       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1086       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1087       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1088       if( mEventData->mGrabHandlePopupEnabled )
1089       {
1090         mEventData->mDecorator->SetPopupActive( false );
1091       }
1092       mEventData->mDecoratorUpdated = true;
1093       HideClipboard();
1094     }
1095     else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
1096     {
1097       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1098       if( mEventData->mCursorBlinkEnabled )
1099       {
1100         mEventData->mDecorator->StartCursorBlink();
1101       }
1102       if( mEventData->mSelectionEnabled )
1103       {
1104         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1105         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1106       }
1107       else
1108       {
1109         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1110       }
1111       if( mEventData->mGrabHandlePopupEnabled )
1112       {
1113         SetPopupButtons();
1114         mEventData->mDecorator->SetPopupActive( true );
1115       }
1116       HideClipboard();
1117       mEventData->mDecoratorUpdated = true;
1118     }
1119     else if ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState )
1120     {
1121       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1122       mEventData->mDecorator->StopCursorBlink();
1123       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1124       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
1125       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
1126       if( mEventData->mGrabHandlePopupEnabled )
1127       {
1128         mEventData->mDecorator->SetPopupActive( false );
1129       }
1130       mEventData->mDecoratorUpdated = true;
1131     }
1132     else if ( EventData::GRAB_HANDLE_PANNING == mEventData->mState )
1133     {
1134       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1135       if( mEventData->mCursorBlinkEnabled )
1136       {
1137         mEventData->mDecorator->StartCursorBlink();
1138       }
1139       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1140       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1141       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1142       if( mEventData->mGrabHandlePopupEnabled )
1143       {
1144         mEventData->mDecorator->SetPopupActive( false );
1145       }
1146       mEventData->mDecoratorUpdated = true;
1147     }
1148   }
1149 }
1150
1151 LineIndex Controller::Impl::GetClosestLine( float y ) const
1152 {
1153   float totalHeight = 0.f;
1154   LineIndex lineIndex = 0u;
1155
1156   const Vector<LineRun>& lines = mVisualModel->mLines;
1157   for( LineIndex endLine = lines.Count();
1158        lineIndex < endLine;
1159        ++lineIndex )
1160   {
1161     const LineRun& lineRun = lines[lineIndex];
1162     totalHeight += lineRun.ascender + -lineRun.descender;
1163     if( y < totalHeight )
1164     {
1165       return lineIndex;
1166     }
1167   }
1168
1169   if( lineIndex == 0 )
1170   {
1171     return 0;
1172   }
1173
1174   return lineIndex-1;
1175 }
1176
1177 void Controller::Impl::FindSelectionIndices( float visualX, float visualY, CharacterIndex& startIndex, CharacterIndex& endIndex )
1178 {
1179   CharacterIndex hitCharacter = GetClosestCursorIndex( visualX, visualY );
1180   if( hitCharacter >= mLogicalModel->mText.Count() )
1181   {
1182     // Selection out of bounds.
1183     return;
1184   }
1185
1186   startIndex = hitCharacter;
1187   endIndex = hitCharacter;
1188
1189   if( !TextAbstraction::IsWhiteSpace( mLogicalModel->mText[hitCharacter] ) )
1190   {
1191     // Find the start and end of the text
1192     for( startIndex = hitCharacter; startIndex > 0; --startIndex )
1193     {
1194       Character charCode = mLogicalModel->mText[ startIndex-1 ];
1195       if( TextAbstraction::IsWhiteSpace( charCode ) )
1196       {
1197         break;
1198       }
1199     }
1200     const CharacterIndex pastTheEnd = mLogicalModel->mText.Count();
1201     for( endIndex = hitCharacter + 1u; endIndex < pastTheEnd; ++endIndex )
1202     {
1203       Character charCode = mLogicalModel->mText[ endIndex ];
1204       if( TextAbstraction::IsWhiteSpace( charCode ) )
1205       {
1206         break;
1207       }
1208     }
1209   }
1210 }
1211
1212 CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
1213                                                         float visualY )
1214 {
1215   if( NULL == mEventData )
1216   {
1217     // Nothing to do if there is no text input.
1218     return 0u;
1219   }
1220
1221   CharacterIndex logicalIndex = 0u;
1222
1223   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
1224   const Length numberOfLines  = mVisualModel->mLines.Count();
1225   if( 0 == numberOfGlyphs ||
1226       0 == numberOfLines )
1227   {
1228     return logicalIndex;
1229   }
1230
1231   // Find which line is closest
1232   const LineIndex lineIndex = GetClosestLine( visualY );
1233   const LineRun& line = mVisualModel->mLines[lineIndex];
1234
1235   // Get the positions of the glyphs.
1236   const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
1237   const Vector2* const positionsBuffer = positions.Begin();
1238
1239   // Get the visual to logical conversion tables.
1240   const CharacterIndex* const visualToLogicalBuffer = ( 0u != mLogicalModel->mVisualToLogicalMap.Count() ) ? mLogicalModel->mVisualToLogicalMap.Begin() : NULL;
1241   const CharacterIndex* const visualToLogicalCursorBuffer = mLogicalModel->mVisualToLogicalCursorMap.Begin();
1242
1243   // Get the character to glyph conversion table.
1244   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1245
1246   // Get the glyphs per character table.
1247   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
1248
1249   // If the vector is void, there is no right to left characters.
1250   const bool hasRightToLeftCharacters = NULL != visualToLogicalBuffer;
1251
1252   const CharacterIndex startCharacter = line.characterRun.characterIndex;
1253   const CharacterIndex endCharacter   = line.characterRun.characterIndex + line.characterRun.numberOfCharacters;
1254   DALI_ASSERT_DEBUG( endCharacter <= mLogicalModel->mText.Count() && "Invalid line info" );
1255
1256   // Whether there is a hit on a glyph.
1257   bool matched = false;
1258
1259   // Traverses glyphs in visual order. To do that use the visual to logical conversion table.
1260   CharacterIndex visualIndex = startCharacter;
1261   for( ; !matched && ( visualIndex < endCharacter ); ++visualIndex )
1262   {
1263     // The character in logical order.
1264     const CharacterIndex characterLogicalOrderIndex = hasRightToLeftCharacters ? *( visualToLogicalBuffer + visualIndex ) : visualIndex;
1265
1266     // The first glyph for that character in logical order.
1267     const GlyphIndex glyphLogicalOrderIndex = *( charactersToGlyphBuffer + characterLogicalOrderIndex );
1268
1269     // The number of glyphs for that character
1270     const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + characterLogicalOrderIndex );
1271
1272     // Get the metrics for the group of glyphs.
1273     GlyphMetrics glyphMetrics;
1274     GetGlyphsMetrics( glyphLogicalOrderIndex,
1275                       numberOfGlyphs,
1276                       glyphMetrics,
1277                       mVisualModel,
1278                       mFontClient );
1279
1280     const Vector2& position = *( positionsBuffer + glyphLogicalOrderIndex );
1281
1282     // Find the mid-point of the area containing the glyph
1283     const float glyphCenter = -glyphMetrics.xBearing + position.x + 0.5f * glyphMetrics.advance;
1284
1285     if( visualX < glyphCenter )
1286     {
1287       matched = true;
1288       break;
1289     }
1290   }
1291
1292   // Return the logical position of the cursor in characters.
1293
1294   if( !matched )
1295   {
1296     visualIndex = endCharacter;
1297   }
1298
1299   logicalIndex = hasRightToLeftCharacters ? *( visualToLogicalCursorBuffer + visualIndex ) : visualIndex;
1300   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p closest visualIndex %d logicalIndex %d\n", this, visualIndex, logicalIndex );
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