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