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