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